一、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
注:代码设计参考小梅哥的设计思路