以太网数据通信
物理层:网线+网卡(PHY芯片)
数据链路层:Mac层(数据有效传输)
如图所示:FPGA中的Mac层中的MII接口负责控制PHY芯片,PHY芯片通过网线与PC端进行以太网数据传输。
FPGA中Mac与PHY通信MII接口
FPGA通过MAC层发送ARP协议
发送ARP协议目的:知道IP地址后,得到MAC(主机硬件地址)
MAC层模块原理设计图:
使用MAC层发送ARP命令原理如图所示:
ARP模块设计
模块设计图
ARP发送模块设计时序
根据图上的ARP通信原理,设计时序图如下。
MAC层模块代码
module mac_mii_send(
input [47:0] mac_source , //Mac_source
input [47:0] mac_goal , //other_Mac
input rst_n ,
input mac_send_en , //pulse width > 40ns
input [15:0] data_type , //agreement type
input [10:0] data_length , //main data field length
input [31:0] crc_result , //Frame check sequence
// mii Interface
input mii_tx_clk , //mii interface clk
output reg mii_tx_en , //mii interface enable
output reg [3:0] mii_txd , //mii interface data
output mii_tx_error , //error signal high level effective
// fifo Interface
input [3:0] mac_send_data , //data read from fifo
output fifo_rd_clk , //read fifo clock
output data_req , //read request
output reg send_done //pulse signal = 40ns
);
reg [5:0] cnt ;
wire en_tx_data ;
reg [10:0] data_cnt ;
reg en ;
always @(posedge mii_tx_clk or negedge rst_n)
if(!rst_n)
en <= 1'b0;
else
if(mac_send_en)
en <= 1'b1;
else
if(cnt == 'h35) //'d53
en <= 1'b0;
always @(posedge mii_tx_clk or negedge rst_n)
if(!rst_n)
cnt <= 1'b0;
else
if(en)
begin
if(!en_tx_data)
begin
if(cnt <= 6'h36) //'d54
cnt <= cnt + 1'b1;
else
cnt <= 1'b0;
end
else
cnt <= cnt ;
end
else
cnt <= 1'b0;
assign en_tx_data = (cnt == 'h2D)&&(data_cnt < data_length - 1'b1) ; //'d45
assign data_req = en_tx_data; //fifo request signal
always @(posedge mii_tx_clk or negedge rst_n)
if(!rst_n)
data_cnt <= 1'b0;
else
if(en_tx_data)
data_cnt <= data_cnt + 1'b1;
else
if(data_cnt == data_length - 1'b1)
data_cnt <= 1'b0;
// mian Sequencer
always @(posedge mii_tx_clk or negedge rst_n)
if(!rst_n)
begin
mii_txd <= 1'b0;
send_done <= 1'b0;
end
else
begin
case(cnt)
6'h0:
begin
mii_txd <= 1'b0;
send_done <= 1'b0;
end
6'h1,6'h2,6'h3,6'h4,6'h5,6'h6,6'h7,6'h8,6'h9,6'ha,6'hb,6'hc,6'hd,6'he,6'hf:
mii_txd <= 4'h5; //前导码
6'h10:
mii_txd <= 4'hD; //帧开始符
6'h11:
mii_txd <= mac_goal[43:40] ; //目的MAC地址
6'h12:
mii_txd <= mac_goal[47:44] ;
6'h13:
mii_txd <= mac_goal[35:32] ;
6'h14:
mii_txd <= mac_goal[39:36] ;
6'h15:
mii_txd <= mac_goal[27:24] ;
6'h16:
mii_txd <= mac_goal[31:28] ;
6'h17:
mii_txd <= mac_goal[19:16] ;
6'h18:
mii_txd <= mac_goal[23:20] ;
6'h19:
mii_txd <= mac_goal[11:8] ;
6'h1a:
mii_txd <= mac_goal[15:12] ;
6'h1b:
mii_txd <= mac_goal[3:0] ;
6'h1c:
mii_txd <= mac_goal[7:4] ;
6'h1d:
mii_txd <= mac_source [43:40] ;
6'h1e:
mii_txd <= mac_source [47:44] ; //源MAC地址
6'h1f:
mii_txd <= mac_source [35:32] ;
6'h20:
mii_txd <= mac_source [39:36] ;
6'h21:
mii_txd <= mac_source [27:24] ;
6'h22:
mii_txd <= mac_source [31:28] ;
6'h23:
mii_txd <= mac_source [19:16] ;
6'h24:
mii_txd <= mac_source [23:20] ;
6'h25:
mii_txd <= mac_source [11:8] ;
6'h26:
mii_txd <= mac_source [15:12] ;
6'h27:
mii_txd <= mac_source [3:0] ;
6'h28:
mii_txd <= mac_source [7:4] ;
6'h29:
mii_txd <= data_type[11:8] ; //长度/类型
6'h2a:
mii_txd <= data_type[15:12] ;
6'h2b:
mii_txd <= data_type[3:0] ;
6'h2c:
mii_txd <= data_type[7:4] ;
6'h2d: //'d45
mii_txd <= mac_send_data ;
6'h2e: //'d46
mii_txd <= crc_result [27:24] ;
6'h2f:
mii_txd <= crc_result [31:28] ;
6'h30:
mii_txd <= crc_result [19:16] ;
6'h31:
mii_txd <= crc_result [23:20] ;
6'h32:
mii_txd <= crc_result [11:8] ;
6'h33:
mii_txd <= crc_result [15:12] ;
6'h34:
mii_txd <= crc_result [3:0] ;
6'h35:
mii_txd <= crc_result [7:4] ;
6'h36: //'d54
begin
send_done <= 1'b1;
mii_txd <= 1'b0 ;
end
default:
begin
send_done <= 1'b0;
mii_txd <= 1'b0 ;
end
endcase
end
always @(posedge mii_tx_clk or negedge rst_n)
if(!rst_n)
mii_tx_en <= 1'b0;
else
if(en && (cnt == 'h1)) //'d1
mii_tx_en <= 1'b1;
else
if(cnt == 'h36) //'d54
mii_tx_en <= 1'b0;
else
mii_tx_en <= mii_tx_en ;
assign fifo_rd_clk = mii_tx_clk;
endmodule
使用ARP工具生成Mac层传输的数据(不包含前导码和帧开始符),将数据复制到CRC生成工具,产生CRC校验值。
源主机通过广播形式发出 ARP 请求,以太网帧首部的硬件地址(目的MAC地址)填 FF:FF:FF:FF:FF:FF 表示广播。
发送ARP请求模块
使用case语句模拟FIFO写入数据。手动计算CRC码。
module mac_mii_send_test(
input rst_n ,
input eth_tx_clk ,
output eth_tx_er ,
output eth_tx_en ,
output [3:0] eth_tx_data ,
output eth_rst_n
);
parameter CRC = 32'h79D9D41B; //h42853ABE EA1C2E26 (PC IP 192.168.1.1 : h4DB06FEE) (PC IP 192.168.56.1 : hD892D6E1)
wire mac_send_en ;
wire [31:0] crc_result ;
wire data_req ;
wire send_done ;
wire fifo_rd_clk ;
reg [20:0] data_cnt ;
reg [31:0] time_cnt ;
reg [3:0] mac_send_data ;
mac_mii_send mac_mii_send_inst(
.mac_source (48'h17_31_81_ca_3c_7a), //Mac_source
.mac_goal (48'hff_ff_ff_ff_ff_ff), //other_Mac radio
.rst_n (rst_n),
.mac_send_en (mac_send_en), //pulse width > 40ns
.data_type (16'h08_06), //agreement type
.data_length ('d112), //main data field length ARP字段与填充数据 data_length/2个字节
.crc_result (crc_result), //Frame check sequence
// mii Interfac
.mii_tx_clk (eth_tx_clk), //mii interface clk
.mii_tx_en (eth_tx_en), //mii interface enable
.mii_txd (eth_tx_data), //mii interface data
.mii_tx_error (), //error signal high level effective
// fifo Interface fifo 设置为 showahead 模式
.mac_send_data (mac_send_data), //data read from fifo
.fifo_rd_clk (fifo_rd_clk), //read fifo clock
.data_req (data_req), //read request
.send_done (send_done) //pulse signal = 40ns
);
assign eth_rst_n = 1;
assign crc_result = {CRC[7:0],CRC[15:8],CRC[23:16],CRC[31:24]};
always @(posedge eth_tx_clk or negedge rst_n)
if(!rst_n)
data_cnt <= 1'b0;
else
if(data_req)
data_cnt <= data_cnt + 1'b1;
else
data_cnt <= 1'b0;
always @(posedge eth_tx_clk or negedge rst_n)
if(!rst_n)
time_cnt <= 'd0;
else
if(time_cnt == 'd80000) //
time_cnt <= 1'b0;
else
time_cnt <= time_cnt + 1'b1;
assign mac_send_en = (time_cnt == 'd50); //d2_499_999
always @(*) //ARP字段与填充数据
begin
case(data_cnt)
0, 1, 3 : mac_send_data = 4'h0;
2 : mac_send_data = 4'h1;
4 : mac_send_data = 4'h8;
5 : mac_send_data = 4'h0;
6 : mac_send_data = 4'h0;
7 : mac_send_data = 4'h0;
8 : mac_send_data = 4'h6; //MAC地址长度
9 : mac_send_data = 4'h0;
10 : mac_send_data = 4'h4; //IP地址长度
11 : mac_send_data = 4'h0;
12 : mac_send_data = 4'h0; //操作码 请求包
13 : mac_send_data = 4'h0;
14 : mac_send_data = 4'h1;
15 : mac_send_data = 4'h0;
16 : mac_send_data = 4'h7; //源MAC地址
17 : mac_send_data = 4'h1;
18 : mac_send_data = 4'h1;
19 : mac_send_data = 4'h3;
20 : mac_send_data = 4'h1;
21 : mac_send_data = 4'h8;
22 : mac_send_data = 4'ha;
23 : mac_send_data = 4'hc;
24 : mac_send_data = 4'hc;
25 : mac_send_data = 4'h3;
26 : mac_send_data = 4'ha;
27 : mac_send_data = 4'h7;
28 : mac_send_data = 4'h0; //源IP地址 192.168.1.201
29 : mac_send_data = 4'hc;
30 : mac_send_data = 4'h8;
31 : mac_send_data = 4'ha;
32 : mac_send_data = 4'h1;
33 : mac_send_data = 4'h0;
34 : mac_send_data = 4'h9;
35 : mac_send_data = 4'hc;
36 : mac_send_data = 4'h0; //目标MAC地址
37 : mac_send_data = 4'h0;
38 : mac_send_data = 4'h0;
39 : mac_send_data = 4'h0;
40 : mac_send_data = 4'h0;
41 : mac_send_data = 4'h0;
42 : mac_send_data = 4'h0;
43 : mac_send_data = 4'h0;
44 : mac_send_data = 4'h0;
45 : mac_send_data = 4'h0;
46 : mac_send_data = 4'h0;
47 : mac_send_data = 4'h0;
48 : mac_send_data = 4'h0; //目标IP 192.168.1.4
49 : mac_send_data = 4'hc;
50 : mac_send_data = 4'h8;
51 : mac_send_data = 4'ha;
52 : mac_send_data = 4'h1; //
53 : mac_send_data = 4'h0; //
54 : mac_send_data = 4'h4; //
55 : mac_send_data = 4'h0; //
56 : mac_send_data = 4'h0; //填充
57 : mac_send_data = 4'h0;
58 : mac_send_data = 4'h0;
59 : mac_send_data = 4'h0;
60 : mac_send_data = 4'hf;
61 : mac_send_data = 4'hf;
62 : mac_send_data = 4'hf;
63 : mac_send_data = 4'hf;
64 : mac_send_data = 4'hf;
65 : mac_send_data = 4'hf;
66 : mac_send_data = 4'hf;
67 : mac_send_data = 4'hf;
68 : mac_send_data = 4'hf;
69 : mac_send_data = 4'hf;
70 : mac_send_data = 4'hf;
71 : mac_send_data = 4'hf;
72 : mac_send_data = 4'h0;
73 : mac_send_data = 4'h0;
74 : mac_send_data = 4'h3;
75 : mac_send_data = 4'h2;
76 : mac_send_data = 4'hd;
77 : mac_send_data = 4'hc;
78 : mac_send_data = 4'h6;
79 : mac_send_data = 4'h7;
80 : mac_send_data = 4'h3;
81 : mac_send_data = 4'h6;
82 : mac_send_data = 4'ha;
83 : mac_send_data = 4'h1;
84 : mac_send_data = 4'h8;
85 : mac_send_data = 4'h0;
86 : mac_send_data = 4'h6;
87 : mac_send_data = 4'h0;
88 : mac_send_data = 4'h0;
89 : mac_send_data = 4'h0;
90 : mac_send_data = 4'h1;
91 : mac_send_data = 4'h0;
92 : mac_send_data = 4'h3;
93 : mac_send_data = 4'h7;
94 : mac_send_data = 4'h4;
95 : mac_send_data = 4'h7;
96 : mac_send_data = 4'h5;
97 : mac_send_data = 4'h7;
98 : mac_send_data = 4'h6;
99 : mac_send_data = 4'h7;
100 : mac_send_data = 4'h7;
101 : mac_send_data = 4'h7;
102 : mac_send_data = 4'h1;
103 : mac_send_data = 4'h6;
104 : mac_send_data = 4'h2;
105 : mac_send_data = 4'h6;
106 : mac_send_data = 4'h3;
107 : mac_send_data = 4'h6;
108 : mac_send_data = 4'h4;
109 : mac_send_data = 4'h6;
110 : mac_send_data = 4'h5;
111 : mac_send_data = 4'h6;
default : mac_send_data = 4'h0;
endcase
end
endmodule
使用Wireshark软件查看ARP字段与应答。
FPGA通过MAC层发送UDP协议
IP报头检验和计算模块
将IP报头以2字节为单位相加(此时IP报头检验和为零),求和结果将数据高位溢出位加低16位,求和结果取反为IP报头检验和。
module ip_check(
input [3:0] edition , //版本
input [3:0] head_length , //首部长度
input [7:0] service_type , //服务类型
input [15:0] ip_message_length , //ip报文长度
input [30:0] block_logo , //分段标识 + 偏移
input [7:0] life_cycle , //生存周期
input [7:0] agreement_type , //上层协议类型
// input [15:0] ip_checkout_0 , //计算时IP报头检验和为零
input [31:0] source_ip_address , //源IP地址
input [31:0] goal_ip_address , //目的IP地址
output [15:0] ip_checkout //IP报头检验和
);
wire [31:0] ip_check_sum ;
wire [31:0] ip_check_sum_reg ;
assign ip_check_sum_reg = ({edition,head_length,service_type} + ip_message_length + block_logo + {life_cycle,agreement_type} + source_ip_address[31:16] + source_ip_address[15:0] + goal_ip_address[31:16] + goal_ip_address[15:0]);
assign ip_check_sum = ~(ip_check_sum_reg[31:16] + ip_check_sum_reg[15:0]);
assign ip_checkout = ip_check_sum;
endmodule
发送DUP顶层模块
module mac_udp_send_test(
input rst_n ,
input eth_tx_clk ,
output eth_tx_er ,
output eth_tx_en ,
output [3:0] eth_tx_data ,
output eth_rst_n
);
parameter CRC = 32'hFE3A3426; //h42853ABE EA1C2E26 (PC IP 192.168.1.1 : h4DB06FEE) (PC IP 192.168.56.1 : hD892D6E1)
parameter DATA_LENFTH = 11'd92; //46字节
wire mac_send_en ;
wire [31:0] crc_result ;
wire data_req ;
wire send_done ;
wire fifo_rd_clk ;
reg [20:0] data_cnt ;
reg [31:0] time_cnt ;
reg [3:0] mac_send_data ;
wire crc_en ;
wire [31:0] crc ;
wire [15:0] ip_checkout ;
assign eth_rst_n = 1;
assign crc_result = {CRC[7:0],CRC[15:8],CRC[23:16],CRC[31:24]};
// assign crc_result = CRC ;
mac_udp_send mac_udp_send_inst(
.mac_source (48'h17_31_81_CA_3C_7A), //Mac_source
.mac_goal (48'h58_11_22_A0_E9_61), //目的MAC地址
.rst_n (rst_n),
.mac_send_en (mac_send_en), //pulse width > 40ns
.data_type (16'h08_00), // 长度/类型 UDP
.data_length (DATA_LENFTH), // 数据与填充 data_length/2个字节
.crc_result (crc_result), //Frame check sequence
// mii Interfac
.mii_tx_clk (eth_tx_clk), //mii interface clk
.mii_tx_en (eth_tx_en), //mii interface enable
.mii_txd (eth_tx_data), //mii interface data
.mii_tx_error (), //error signal high level effective
// fifo Interface fifo 设置为 showahead 模式
.mac_send_data (mac_send_data), //data read from fifo input
.fifo_rd_clk (fifo_rd_clk), //read fifo clock
.data_req (data_req), //read request
.crc_en (crc_en),
.send_done (send_done) //pulse signal = 40ns
);
ip_check ip_check_inst(
.edition (4'h4) , //版本
.head_length (4'h5) , //首部长度
.service_type (0) , //服务类型
.ip_message_length (16'h002E) , //ip报文长度
.block_logo (0) , //分段标识等
.life_cycle (8'h40) , //生存周期
.agreement_type ('d17) , //上层协议类型
.source_ip_address ('hC0_A8_01_C9) , //源IP地址
.goal_ip_address ('hC0_A8_01_04) , //目的IP地址
.ip_checkout (ip_checkout) //IP报头检验和
);
always @(posedge eth_tx_clk or negedge rst_n)
if(!rst_n)
data_cnt <= 1'b0;
else
if(data_req)
data_cnt <= data_cnt + 1'b1;
else
data_cnt <= 1'b0;
always @(posedge eth_tx_clk or negedge rst_n)
if(!rst_n)
time_cnt <= 'd0;
else
if(time_cnt == 'd80000) //
time_cnt <= 1'b0;
else
time_cnt <= time_cnt + 1'b1;
assign mac_send_en = (time_cnt == 'd50); //d2_499_999
always @(*) //ARP字段与填充数据
begin
case(data_cnt)
0 : mac_send_data = 4'h5; //IP首部长度
1 : mac_send_data = 4'h4; //IPV4
2 : mac_send_data = 4'h0; //服务类型
3 : mac_send_data = 4'h0;
4 : mac_send_data = 4'h0; //IP报文长度
5 : mac_send_data = 4'h0;
6 : mac_send_data = 4'he;
7 : mac_send_data = 4'h2;
8 : mac_send_data = 4'h0; //分段表示 设置0
9 : mac_send_data = 4'h0;
10 : mac_send_data = 4'h0;
11 : mac_send_data = 4'h0;
12 : mac_send_data = 4'h0;
13 : mac_send_data = 4'h0;
14 : mac_send_data = 4'h0;
15 : mac_send_data = 4'h0;
16 : mac_send_data = 4'h0; //周期 64
17 : mac_send_data = 4'h4;
18 : mac_send_data = 4'h1; //上层协议类型
19 : mac_send_data = 4'h1;
// 算
20 : mac_send_data = ip_checkout[11:8]; //IP报头检验和 逻辑模块算出 忽略 ip_checkout[11:8]
21 : mac_send_data = ip_checkout[15:12]; //IP版本+....+目的IP地址 ip_checkout[15:12]
22 : mac_send_data = ip_checkout[3:0]; //测试校验和 图7.5-11 ip_checkout[3:0]
23 : mac_send_data = ip_checkout[7:4]; //ip_checkout[7:4]
24 : mac_send_data = 4'h0; //源IP地址 192.168.1.201
25 : mac_send_data = 4'hc;
26 : mac_send_data = 4'h8;
27 : mac_send_data = 4'ha;
28 : mac_send_data = 4'h1;
29 : mac_send_data = 4'h0;
30 : mac_send_data = 4'h9;
31 : mac_send_data = 4'hc;
32 : mac_send_data = 4'h0; //目的IP地址 4字节 192.168.1.4
33 : mac_send_data = 4'hc;
34 : mac_send_data = 4'h8;
35 : mac_send_data = 4'ha;
36 : mac_send_data = 4'h1;
37 : mac_send_data = 4'h0;
38 : mac_send_data = 4'h4;
39 : mac_send_data = 4'h0;
//UDP协议层
40 : mac_send_data = 4'h9; //源端口号 2字节 6500
41 : mac_send_data = 4'h1;
42 : mac_send_data = 4'h4;
43 : mac_send_data = 4'h6;
44 : mac_send_data = 4'h5; //目的端口号 2字节 5500
45 : mac_send_data = 4'h1;
46 : mac_send_data = 4'hc;
47 : mac_send_data = 4'h7;
48 : mac_send_data = 4'h0; //UDP报文长度 26 2字节
49 : mac_send_data = 4'h0;
50 : mac_send_data = 4'ha;
51 : mac_send_data = 4'h1;
52 : mac_send_data = 4'h0; // UDP校验和 2字节 忽略全零
53 : mac_send_data = 4'h0;
54 : mac_send_data = 4'h0;
55 : mac_send_data = 4'h0;
56 : mac_send_data = 4'h8; //填充和数据
57 : mac_send_data = 4'h4;
58 : mac_send_data = 4'h5;
59 : mac_send_data = 4'h6;
60 : mac_send_data = 4'hc;
61 : mac_send_data = 4'h6;
62 : mac_send_data = 4'hc;
63 : mac_send_data = 4'h6;
64 : mac_send_data = 4'hf;
65 : mac_send_data = 4'h6;
66 : mac_send_data = 4'hc;
67 : mac_send_data = 4'h2;
68 : mac_send_data = 4'h7;
69 : mac_send_data = 4'h6;
70 : mac_send_data = 4'hf;
71 : mac_send_data = 4'h6;
72 : mac_send_data = 4'hf;
73 : mac_send_data = 4'h6;
74 : mac_send_data = 4'h4;
75 : mac_send_data = 4'h6;
76 : mac_send_data = 4'h0;
77 : mac_send_data = 4'h2;
78 : mac_send_data = 4'h4;
79 : mac_send_data = 4'h7;
80 : mac_send_data = 4'hf;
81 : mac_send_data = 4'h6;
82 : mac_send_data = 4'h0;
83 : mac_send_data = 4'h2;
84 : mac_send_data = 4'h6;
85 : mac_send_data = 4'h4;
86 : mac_send_data = 4'h0;
87 : mac_send_data = 4'h5;
88 : mac_send_data = 4'h7;
89 : mac_send_data = 4'h4;
90 : mac_send_data = 4'h1;
91 : mac_send_data = 4'h4;
default : mac_send_data = 4'h0;
endcase
end
endmodule
可以看到IP校验和无误,并且成功发送字母。