基于FPGA的百兆以太网通信


一、UDP协议介绍

  UDP是User Datagram Protocol 的简称,中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联)参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC768是UDP的正式规范。UDP在IP报文的协议号是17(即0x17)。

二、数据、UDP、IP、MAC四个报文的关系

  数据是打包在UDP协议中,UDP协议又是基于IP协议之上的,IP协议又是走MAC层发送的,即从包含关系来说:MAC帧中的数据段为IP数据报,IP报文中的数据段为UDP报文,UDP报文中的数据段为用户希望传输的数据内容,如“Hello,welcome to FPGA !”。下图为使用UDP协议发送“Hello,welcome to FPGA !”的数据层层打包示意图:

 图1

 图2

三、UDP封包格式

        

   各个字段的组成

     (1) 前导码:

      8'h55、8'h55、8'h55、8'h55、8'h55、8'h55、8'h55、8'hd5

     (2)mac首部:

    

     (3)IP首部:前20个字节是IP首部

       具体的各个字节就不细讲了,讲下IP首部校验和(ip_checksum).

      手动计算:

           在发送数据时,计算IP数据报的校验和,步骤如下:

           a、将校验和字段置为 0 ,然后将IP包头按照16比特分成多个单元,如包头不是16比特的倍数,则用0比特填充到16位比特的倍数;

           b、对各个单元采用反码加法运算(即高位溢出位会加到低位,通常的补码运算时直接丢掉溢出的高位),将得到的和的反码填入校验字段;

    (4)UDP首部

 

 ******* 16位UDP长度:UDP包头 + 数据;

 ******* 16位UDP校验和:要求不高时可以设为全零;

    (5)Crc

        Crc这块是从前导码之后,开始计算,直接例化现有的CRC32。

四、代码设计

  1 // Time : 2020.04.11 21:22
  2 // Describe : udp_test 
  3 
  4 module udp_test(
  5       rst_n,
  6     
  7     //MII 接口信号    
  8     mii_tx_clk,
  9     mii_tx_en,
 10     mii_tx_er,
 11     mii_tx_data,
 12     
 13     phy_rst_n
 14 );
 15 
 16 input rst_n;
 17 
 18 input  mii_tx_clk;         //MII接口发送时钟,由PHY芯片产生,25MHz
 19 output mii_tx_en;           //MII接口发送数据使能信号,高电平有效
 20 output mii_tx_er;           //发送错误,用以破坏数据包发送
 21 output reg[3:0]mii_tx_data; //MII接口发送数据线,FPGA通过该数据线将需要发送的数据依次送给PHY芯片
 22 output phy_rst_n;           //PHY 复位信号
 23 
 24 assign phy_rst_n = 1'b1;
 25 
 26 parameter des_mac     = 48'hc4_54_44_97_c5_d7;  //目标MAC地址
 27 parameter src_mac     = 48'h00_0a_35_01_fe_c0;  //本机/源MAC地址
 28 parameter type_length = 16'h08_00;                //数据帧类型
 29 parameter data_total_len = 16'd22;                   //数据长度(因为MII接口一个字节分两个时钟,每个时钟4位的方式发送,因此本值为实际数据所占字节数的2倍)
 30 
 31 parameter src_port = 16'd5000;
 32 parameter des_port = 16'd6000;
 33 
 34 parameter ver        = 4'h4;                  //版本
 35 parameter hdr_len    = 4'h5;                //首部长度
 36 parameter tos        = 8'h00;                  //服务类型
 37 //parameter total_len  = ip_total_len;            //IP报文总长
 38 parameter id         = 16'h0000;               //分段标识
 39 parameter offset     = 16'h0000;               //偏移
 40 parameter ttl        = 16'h40;               //生存周期
 41 parameter protocol   = 8'h11;               //上层协议类型
 42 parameter src_ip     = 32'hc0_a8_00_02;      //源IP地址
 43 parameter dst_ip     = 32'hc0_a8_00_03;      //目的IP地址
 44 
 45 wire[15:0] ip_total_len;
 46 wire[15:0] udp_total_len;
 47 assign ip_total_len = data_total_len + 16'd28;
 48 assign udp_total_len = data_total_len + 16'd8;
 49 
 50 wire[31:0] CRC_Result;
 51 reg [7:0] lsm_cnt;           //序列机计数器,本以太网帧发送系统使用线性序列机方式设计
 52 wire CRC_EN;
 53 assign CRC_EN = (lsm_cnt >= 17 && lsm_cnt <= 144);
 54 
 55 crc32_d4 u0(
 56   .Clk(mii_tx_clk),
 57   .Rst_n(rst_n), 
 58   .Data(mii_tx_data), 
 59   .Enable(CRC_EN),
 60   .Initialize(~mii_tx_en),
 61   .Crc(), 
 62   .CrcError(),
 63   .Crc_eth(CRC_Result)
 64  );
 65 
 66 wire [31:0]sum;
 67 wire [15:0] ip_checksum;
 68 assign sum = {ver,hdr_len,tos} + ip_total_len + id + offset + {ttl,protocol} + src_ip[31:16]+ src_ip[15:0] + dst_ip[31:16] + dst_ip[15:0];
 69 
 70 assign ip_checksum = ~(sum[31:16] + sum[15:0]);
 71 
 72 wire tx_go;                // 使能发送
 73 
 74 reg en_tx;                   //内部的数据帧发送使能信号,高电平时将数据通过MII接口送出
 75 
 76 reg [28:0]cnt;  //发送间隔计数器
 77 always@(posedge mii_tx_clk or negedge rst_n)
 78   if(!rst_n)
 79      cnt <= 28'd0;
 80   else if(cnt == 28'd1000)  // 35000000
 81      cnt <= 28'd0;
 82   else
 83     cnt <= cnt + 1'b1;
 84     
 85 //每671ms启动一次发送数据    
 86 assign tx_go = (cnt == 28'd1000) ? 1'b1 : 1'b0;  // 35000000
 87 
 88 //根据发送启动信号产生内部发送使能信号
 89 always@(posedge mii_tx_clk or negedge rst_n) 
 90   if(!rst_n)
 91      en_tx <= 1'd0;
 92   else if(tx_go)
 93      en_tx <= 1'd1;
 94   else if(lsm_cnt >= 153)      //一帧数据发送完成,清零发送使能信号
 95      en_tx <= 1'd0;
 96 
 97 always@(posedge mii_tx_clk or negedge rst_n) //主序列机计数器
 98   if(!rst_n)
 99      lsm_cnt <= 8'd0;
100   else if(en_tx) begin
101     if(lsm_cnt == 8'd153)
102        lsm_cnt <= 8'd0;
103      else
104        lsm_cnt <= lsm_cnt + 1'b1;
105   end
106   else
107      lsm_cnt <= 8'd0;    
108 
109 always@(*) begin
110     case(lsm_cnt)
111        1: mii_tx_data <= 4'h5;  // 前导码 + 分隔符的一个5
112         2: mii_tx_data <= 4'h5;
113         3: mii_tx_data <= 4'h5;
114         4: mii_tx_data <= 4'h5;
115         5: mii_tx_data <= 4'h5;
116         6: mii_tx_data <= 4'h5;
117         7: mii_tx_data <= 4'h5;
118         8: mii_tx_data <= 4'h5;
119         9: mii_tx_data <= 4'h5;
120         10:mii_tx_data <= 4'h5;
121         11:mii_tx_data <= 4'h5;
122         12:mii_tx_data <= 4'h5;
123         13:mii_tx_data <= 4'h5;
124         14:mii_tx_data <= 4'h5;
125         15:mii_tx_data <= 4'h5;
126             
127         16: mii_tx_data <= 4'hd;  // 分隔符
128         
129       17: mii_tx_data <= des_mac[43:40];  // 目的MAC地址,先发高八位中的低四位 48'c4_54_44_97_c5_d7
130         18: mii_tx_data <= des_mac[47:44];
131         19: mii_tx_data <= des_mac[35:32];
132         20: mii_tx_data <= des_mac[39:36];
133         21: mii_tx_data <= des_mac[27:24];
134         22: mii_tx_data <= des_mac[31:28];
135         23: mii_tx_data <= des_mac[19:16];
136         24: mii_tx_data <= des_mac[23:20];
137         25: mii_tx_data <= des_mac[11:8];
138         26: mii_tx_data <= des_mac[15:12];
139         27: mii_tx_data <= des_mac[3:0];
140         28: mii_tx_data <= des_mac[7:4];    
141     
142                   
143         29: mii_tx_data <= src_mac[43:40];// 0  //源MAC地址 48'h00_0a_35_01_fe_c0
144         30: mii_tx_data <= src_mac[47:44];// 0
145         31: mii_tx_data <= src_mac[35:32];// a
146         32: mii_tx_data <= src_mac[39:36];// 0
147         33: mii_tx_data <= src_mac[27:24];// 5
148         34: mii_tx_data <= src_mac[31:28];// 3
149         35: mii_tx_data <= src_mac[19:16];// 1
150         36: mii_tx_data <= src_mac[23:20];// 0
151         37: mii_tx_data <= src_mac[11:8]; // e
152         38: mii_tx_data <= src_mac[15:12];// f
153         39: mii_tx_data <= src_mac[3:0]; // 0
154         40: mii_tx_data <= src_mac[7:4];    // c
155                 
156         41: mii_tx_data <= type_length[11:8];  //以太网帧类型/长度,0x0800
157         42: mii_tx_data <= type_length[15:12];
158         43: mii_tx_data <= type_length[3:0];
159         44: mii_tx_data <= type_length[7:4];
160         
161         45: mii_tx_data =    4'h5;  // IP首部长度
162         46: mii_tx_data =    4'h4;  // IPv4协议
163         
164         47: mii_tx_data =    4'h0;  // 服务类型
165         48: mii_tx_data =    4'h0;
166         
167         49: mii_tx_data =    ip_total_len[11:8];   // IP数据报总长度(IP报头+数据)
168         50: mii_tx_data =    ip_total_len[15:12];
169         51: mii_tx_data =    ip_total_len[3:0];
170         52: mii_tx_data =    ip_total_len[7:4];
171         
172         53: mii_tx_data =    4'h0;  // 数据包标识
173         54: mii_tx_data =    4'h0;
174         55: mii_tx_data =    4'h0;
175         56: mii_tx_data =    4'h0;
176         
177         57: mii_tx_data =    4'h0;  // 标识+分段偏移
178         58: mii_tx_data =    4'h0;
179         59: mii_tx_data =    4'h0;
180         60: mii_tx_data =    4'h0;
181         
182         61: mii_tx_data =    4'h0;  // 生存时间
183         62: mii_tx_data =    4'h4;
184         
185         63: mii_tx_data =    4'h1;  // 数据报类型 17: UDP
186         64: mii_tx_data =    4'h1;
187         
188         65: mii_tx_data =    ip_checksum[11:8];  // IP报头校验和 使用自动IP和校验逻辑生成的校验和值
189         66: mii_tx_data =    ip_checksum[15:12];
190         67: mii_tx_data =    ip_checksum[3:0];
191         68: mii_tx_data =    ip_checksum[7:4];
192         
193         //sender ip : 192.168.0.2
194         69: mii_tx_data =    4'h0; // 192
195         70: mii_tx_data =    4'hc;
196         
197         71: mii_tx_data =    4'h8; // 168
198         72: mii_tx_data =    4'ha;
199         
200         73: mii_tx_data =    4'h0; // 0
201         74: mii_tx_data =    4'h0;
202         
203         75: mii_tx_data =    4'h2;
204         76: mii_tx_data =    4'h0; // 2
205         
206         77: mii_tx_data =    4'h0;  // 目的192.168.0.3
207         78: mii_tx_data =    4'hc;
208         
209         79: mii_tx_data =    4'h8;
210         80: mii_tx_data =    4'ha;
211         
212         81: mii_tx_data =    4'h0;   
213         82: mii_tx_data =    4'h0;
214 
215       83: mii_tx_data = 4'h3;
216       84: mii_tx_data = 4'h0;        
217         
218         85: mii_tx_data =    src_port[11:8];    // 源端口号5000(0x1388)
219         86: mii_tx_data =    src_port[15:12];
220         87: mii_tx_data =    src_port[3:0];  
221         88: mii_tx_data =    src_port[7:4];
222         
223         89: mii_tx_data =    des_port[11:8];    // 目的端口号
224         90: mii_tx_data =    des_port[15:12];  
225         91: mii_tx_data =    des_port[3:0];  
226         92: mii_tx_data =    des_port[7:4];
227         
228         93: mii_tx_data =    udp_total_len[11:8];  // UDP数据报总长度(UDP报头+数据)
229         94: mii_tx_data =    udp_total_len[15:12]; 
230         95: mii_tx_data =    udp_total_len[3:0];
231         96: mii_tx_data =    udp_total_len[7:4];
232         
233         97: mii_tx_data =    4'h0;  // UDP报头校验和  忽略
234         98: mii_tx_data =    4'h0;
235         99: mii_tx_data =    4'h0;  
236         100: mii_tx_data = 4'h0;
237         
238         101: mii_tx_data = 4'h8;   // H        // 用户数据:Hello, welcom to FPGA!
239         102: mii_tx_data = 4'h4;
240         
241         103: mii_tx_data = 4'h5;  // e
242         104: mii_tx_data = 4'h6;
243         
244         105: mii_tx_data = 4'hc;  // l
245         106: mii_tx_data = 4'h6;
246         
247         107: mii_tx_data = 4'hc;  // l
248         108: mii_tx_data = 4'h6;
249         
250         109: mii_tx_data = 4'hf;  // o
251         110: mii_tx_data = 4'h6;
252         
253         111: mii_tx_data = 4'hc;  // ,
254         112: mii_tx_data = 4'h2;
255         
256         113: mii_tx_data = 4'h7;  // W
257         114: mii_tx_data = 4'h7;
258         
259         115: mii_tx_data = 4'h5;  // e
260         116: mii_tx_data = 4'h6;
261         
262         117: mii_tx_data = 4'hc;  // l
263         118: mii_tx_data = 4'h6;
264         
265         119: mii_tx_data = 4'h3;  // c
266         120: mii_tx_data = 4'h6;  
267         
268         121: mii_tx_data = 4'hf;  // o
269         122: mii_tx_data = 4'h6;
270         
271         123: mii_tx_data = 4'hd;  // m
272         124: mii_tx_data = 4'h6;
273         
274         125: mii_tx_data = 4'h5;  // e
275         126: mii_tx_data = 4'h6;
276         
277         127: mii_tx_data = 4'h0;  // 
278         128: mii_tx_data = 4'h2;
279         
280         129: mii_tx_data = 4'h4;  // t
281         130: mii_tx_data = 4'h7;
282         
283         131: mii_tx_data = 4'hf;  // o
284         132: mii_tx_data = 4'h6;
285         
286         133: mii_tx_data = 4'h0;  // 
287         134: mii_tx_data = 4'h2;
288         
289         135: mii_tx_data = 4'h6;  // F
290         136: mii_tx_data = 4'h4;
291         
292         137: mii_tx_data = 4'h0;  // P
293         138: mii_tx_data = 4'h5;
294         
295         139: mii_tx_data = 4'h7;  // G
296         140: mii_tx_data = 4'h4;
297         
298         141: mii_tx_data = 4'h1;  // A
299         142: mii_tx_data = 4'h4;
300         
301         143: mii_tx_data = 4'h1;  // !
302         144: mii_tx_data = 4'h2;
303         
304         145: mii_tx_data <= CRC_Result[27:24];  //发送CRC 校验结果
305         146: mii_tx_data <= CRC_Result[31:28];
306         147: mii_tx_data <= CRC_Result[19:16];
307         148: mii_tx_data <= CRC_Result[23:20];
308         149: mii_tx_data <= CRC_Result[11:8];
309         150: mii_tx_data <= CRC_Result[15:12];
310         151: mii_tx_data <= CRC_Result[3:0];
311         152: mii_tx_data <= CRC_Result[7:4];
312 
313         153: mii_tx_data <= 4'd0;
314         default: mii_tx_data <= 4'd0;
315      endcase
316   end
317 
318 assign mii_tx_en = ((lsm_cnt >= 1) && (lsm_cnt <= 153)) ? 1'b1 : 1'b0;
319 
320 endmodule
321     
 1 `timescale 1ns/1ns
 2 module crc32_d4 (Clk, Rst_n, Data, Enable, Initialize, Crc, CrcError, Crc_eth);
 3 
 4 
 5 parameter Tp = 1;
 6 
 7 input Clk;
 8 input Rst_n;
 9 input [0:3] Data;
10 input Enable;
11 input Initialize;
12 
13 output [31:0] Crc;
14 output [31:0] Crc_eth;
15 
16 output CrcError;
17 
18 reg  [31:0] Crc;
19 
20 wire [31:0] CrcNext;
21 
22 assign CrcNext[0] = Enable & (Data[0] ^ Crc[28]); 
23 assign CrcNext[1] = Enable & (Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29]); 
24 assign CrcNext[2] = Enable & (Data[2] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[30]); 
25 assign CrcNext[3] = Enable & (Data[3] ^ Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30] ^ Crc[31]); 
26 assign CrcNext[4] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[0]; 
27 assign CrcNext[5] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[1]; 
28 assign CrcNext[6] = (Enable & (Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30])) ^ Crc[ 2]; 
29 assign CrcNext[7] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[3]; 
30 assign CrcNext[8] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[4]; 
31 assign CrcNext[9] = (Enable & (Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30])) ^ Crc[5]; 
32 assign CrcNext[10] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[6]; 
33 assign CrcNext[11] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[7]; 
34 assign CrcNext[12] = (Enable & (Data[2] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[30])) ^ Crc[8]; 
35 assign CrcNext[13] = (Enable & (Data[3] ^ Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30] ^ Crc[31])) ^ Crc[9]; 
36 assign CrcNext[14] = (Enable & (Data[3] ^ Data[2] ^ Crc[30] ^ Crc[31])) ^ Crc[10]; 
37 assign CrcNext[15] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[11]; 
38 assign CrcNext[16] = (Enable & (Data[0] ^ Crc[28])) ^ Crc[12]; 
39 assign CrcNext[17] = (Enable & (Data[1] ^ Crc[29])) ^ Crc[13]; 
40 assign CrcNext[18] = (Enable & (Data[2] ^ Crc[30])) ^ Crc[14]; 
41 assign CrcNext[19] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[15]; 
42 assign CrcNext[20] = Crc[16]; 
43 assign CrcNext[21] = Crc[17]; 
44 assign CrcNext[22] = (Enable & (Data[0] ^ Crc[28])) ^ Crc[18]; 
45 assign CrcNext[23] = (Enable & (Data[1] ^ Data[0] ^ Crc[29] ^ Crc[28])) ^ Crc[19]; 
46 assign CrcNext[24] = (Enable & (Data[2] ^ Data[1] ^ Crc[30] ^ Crc[29])) ^ Crc[20]; 
47 assign CrcNext[25] = (Enable & (Data[3] ^ Data[2] ^ Crc[31] ^ Crc[30])) ^ Crc[21]; 
48 assign CrcNext[26] = (Enable & (Data[3] ^ Data[0] ^ Crc[31] ^ Crc[28])) ^ Crc[22]; 
49 assign CrcNext[27] = (Enable & (Data[1] ^ Crc[29])) ^ Crc[23]; 
50 assign CrcNext[28] = (Enable & (Data[2] ^ Crc[30])) ^ Crc[24]; 
51 assign CrcNext[29] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[25]; 
52 assign CrcNext[30] = Crc[26]; 
53 assign CrcNext[31] = Crc[27]; 
54 
55 
56 always @ (posedge Clk or negedge Rst_n)
57 begin
58   if (!Rst_n)
59     Crc <= #1 32'hffffffff;
60   else
61   if(Initialize)
62     Crc <= #Tp 32'hffffffff;
63   else if(Enable)
64     Crc <= #Tp CrcNext;
65 end
66 
67 assign Crc_eth = ~{
68                         CrcNext[28], CrcNext[29], CrcNext[30], CrcNext[31],
69                         Crc[24], Crc[25], Crc[26], Crc[27],
70                         Crc[20], Crc[21], Crc[22], Crc[23],
71                         Crc[16], Crc[17], Crc[18], Crc[19],
72                         Crc[12], Crc[13], Crc[14], Crc[15],
73                         Crc[ 8], Crc[ 9], Crc[10], Crc[11],
74                         Crc[ 4], Crc[ 5], Crc[ 6], Crc[ 7],
75                         Crc[ 0], Crc[ 1], Crc[ 2], Crc[ 3]};
76 
77 
78 assign CrcError = Crc[31:0] != 32'hc704dd7b;  // CRC not equal to magic number
79 
80 endmodule

仿真代码:

 1 `timescale 1ns/1ps
 2 module udp_test_tb;
 3   reg rst_n;
 4   reg  mii_tx_clk;           //MII接口发送时钟,由PHY芯片产生,25MHz
 5   wire mii_tx_en;           //MII接口发送数据使能信号,高电平有效
 6   wire mii_tx_er;           //发送错误,用以破坏数据包发送
 7   wire [3:0]mii_tx_data;   //MII接口发送数据线,FPGA通过该数据线将需要发送的数据依次送给PHY芯片
 8   wire phy_rst_n;     
 9   
10 udp_test u0(
11  .rst_n(rst_n),
12      
13  .mii_tx_clk(mii_tx_clk),
14  .mii_tx_en(mii_tx_en),
15  .mii_tx_er(mii_tx_er),
16  .mii_tx_data(mii_tx_data),
17  
18  .phy_rst_n(phy_rst_n)
19 );
20 
21 initial
22   mii_tx_clk = 1'b0;
23   always #20 mii_tx_clk = ~mii_tx_clk;
24   
25 initial
26   begin
27     rst_n = 1'b0;
28      #100;
29      rst_n = 1'b1;
30      
31      #100000;
32      $stop;
33   end
34 endmodule

注:代码设计参考小梅哥的设计思路