一、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
注:代碼設計參考小梅哥的設計思路