• 以太网UDP数据回环实验


    一、TCP/IP协议簇

            前面说到TCP/IP是一个协议簇,其中包含有IP协议、TCP协议、UDP协议、ARP协议、DNS协议、FTP协议等。设备之间要想完成通信,就必须通过这些网络通信协议。

             物理层的主要作用就是传输比特流(将1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,即数模转换与模数转换)。
            数据链路层接收来自物理层的位流形式的数据,并封装成帧,传送到上一层;也将来自上层的数据帧,拆装为位流形式的数据转发到物理层。MAC数据包位于数据链路层,当MAC数据包经过数据链路层到达网络层时,前导码、帧起始界定符、目的 MAC 地址、源 MAC 地址、类型/长度以及校验字节均被去除,只有有效数据传入了网络层。
            网路层通过路由选择算法,为报文(该层的数据单位,由上一层数据打包而来)通过通信子网选择最适当的路径。这一层定义的是IP地址,通过IP地址寻址,所以产生了IP协议。传入网络层的数据包并不完全是需要传输的有效数据,他的前面还包含着 20 字节的IP协议首部。网络层在接收到数据包后,取下数据包的IP首部,将剩余有效数据包发送到传输层。
            而传输层提供了主机应用程序进程之间的端到端的服务,基本功能是:分割与重组数据、按端口号寻址、连接管理、差错控制和流量控制、纠错功能。若传输层使用UDP协议,那么传入传输层的数据包为UDP数据包。
            应用层是计算机用户,以及各种应用程序和网络之间的接口。

             以太网数据包就是对各层协议的逐层封装来实现数据传输,MAC帧中的数据段为IP数据报文,IP报文中的数据段位UDP报文,UDP报文中的数据段为传输数据。

    1.1 IP

            IP协议规定了数据传输时的基本单元和格式,位于以太网MAC格式的数据段,由IP首部和数据字段组成。

    • 版本(4bit):定义IP协议版本,设置为二进制的0100时表示IPv4,设置为二进制的0110时表示IPv6。
    • 首部长度 (4bit):定义数据报协议头长度,表示IP首部一共有多少个32位。协议头最小值为5,最大值为15。
    • 服务类型 (8bit):定义上层协议对处理当前数据报所期望的服务质量,并对数据报按照重要性级别进行分配。前3位成为优先位,后面4位成为服务类型,最后1位没有定义。这些8位字段用于分配优先级、延迟、吞吐量以及可靠性。
    • 总长度(16bit):定义整个IP数据报的字节长度,包括协议头部和数据。其最大值为65535字节。
    • 标识 (16bit):包含一个整数,用于标识主机发送的数据报,通常每发送一份数据包值加一。
    • 标记(3bit):由3位字段构成,其中最低位 (ME控制分段,存在下一个分段置为1,否则置0代表该分段是最后一个分段。中间位(DF) 指出数据报是否可进行分段,如果为1则机器不能将该数据报进行分段。第三位即最高位保留不使用,值为0。
    • 分段偏移 (13bit):在接收方进行数据报重组时用来标识分段的顺序。
    • 生存时间 (8bit):一种计数器,在丢弃数据报的每个点值依次减1直至减少为0。这样确保数据报拥有有限的环路过程(即TTL),限制了数据报的寿命。
    • 协议 (8bit):该字段指出在IP处理过程完成之后,有哪种上层协议接收导入数据报。
    • 首部校验和(16bit):该字段帮助确保IP协议头的完整性。由于某些协议头字段的改变,这就需要对每个点重新计算和检验。计算过程是先将校验和字段置为0,然后将整个头部每 16 位划分为一部分,将个部分相加,再将计算结果取反码,插入到校验和字段中。
    • 源地址 (32bit):发送端IP地址,该字段在IPV4数据报从源主机到目的主机传输期间必须保持不变。
    • 目的地址 (32bit):接收端IP地址,该字段在IPv4数据报从源主机到目的主机传输期间同样必须保持不变。

    1.1.1 IP首部校验和

    IP首部校验和计算步骤:

    1. 将16位检验和字段置为0,然后将IP首部按照16位分成多个单元;
    2. 对各个单元采用反码加法运算(即高位溢出位会加到低位,通常的补码运算是直接丢掉溢出的高位);
    3. 此时仍然可能出现进位的情况,将得到的和再次分成高16位和低16位进行累加;
    4. 最后将得到的和的反码填入校验和字段。

    IP首部校验和校验

    对IP首部中每个16bit进行二进制反码求和,将计算结果再取反码,若结果为0,通过检验,否则不通过检验。

    例:验证IP首部 45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d
    (1)对 IP 首部进行反码求和:4500+0030+804c+4000+8006+b52e+d343+117b+cb51+153d=3fffc
         0300+3fffc=ffff
    (2)对和结果取反码:~ffff=0,校验正确。

    1.2 UDP

            UDP (User Datagram Protocol),即用户数据报协议,是一种面向无连接的传输层协议。无连接是指在传输数据时,数据的发送端和接收端不建立逻辑连接。即发送端只管发送,不会管接收端到底有没有接收到数据,而接收端也不会向发送端反馈反馈是否收到数据。

    UDP首部共8个字节,同IP首部一样,也是一行以32位(4个字节)为单位。

    •  源端口号(16byte):用于区分不同服务的端口,端口号的范围从0到 65535。
    • 目的端口号(16byte):16位接收端端口号。
    • UDP长度(16byte):包含 UDP 首部长度+数据长度。
    • UDP校验和(16byte):提供了与TCP校验字段相同的功能,可选。

    1.2.1 UDP校验和

            UDP校验和的计算需要三部分数据:UDP 伪首部、UDP首部和有效数据。伪首部包含IP首部一些字段,其目的是让UDP两次检查数据是否已经正确到达目的地,只是单纯做校验使用。

    UDP校验和计算步骤:校验字节强制置0,将三部分数据按2字节, 即16比特,分开分别相加,若如果大于 FFFF 那么把高16位与低16位相加,直到最终结果为16比特数据。将计算结果取反作为 UDP 校验和字节。 

    例:

    (1) 将校验和字段00 92置为00 00:
    a9 fe bf lf a9 fe 01 17 00 11 00 28 04 d2 04 d2 00 28 00 00 68 74 74 70 3a 2f 2f 77 7777 2e 63 6d 73 6f 66 74 2e 63 6e 20 51 51 3a 31 30 38 36 35 36 30 30
    (2) 以2字节为单位,数据反码求和:
    a9fe + bflf + a9fe + 0117 + 0011 + 0028 + 04d2 + 04d2 + 0028 + 0000 + 6874 + 7470+ 3a2f + 2f77 + 7777 + 2e63 + 6d73 + 6f66 + 742e + 636e + 2051 + 513a + 3130 +3836 + 3536 + 3030 = 6 ff67
    (3) 将进位(6)加到低 16 位(f67)上:6 + ff67 = ff6d
    (4)将 ff6d 取反得: checksum = 0092 

    二、以太网UDP数据回环实验

            上位机通过网口调试助手发送数据给FPGA,FPGA通过以太网接口接收数据并将收到的数据发送给上位机,完成以太网UDP数据的回环。

            GMII TO RGMII模块负责将双沿 (DDR) 数据和单沿 (SDR) 数据之间的转换,GMII接收侧的引脚同时连接至 ARP顶层模块和UDP顶层模块,这个两个模块会分别根据ARP协议和UDP协议解析数据。而GMII发送侧引脚只能和ARP顶层模块和UDP顶层模块的其中一个连接,因此以太网控制模块会根据当前接收到的协议类型,选择切换GMII发送侧引脚和ARP顶层模块或者UDP顶层模块连接,并且根据输入的ARP接收的类型,控制ARP顶层模块返回ARP应答信号。ARP顶层模块解析ARP 请求命令,并返回开发板的MAC地址。UDP顶层模块实现了以太网 UDP 数据包的接收、发送以及 CRC 校验的功能。以太网单次会接收到大量数据,因此需要FIFO模块用来缓存数据,由于所使用的GMII接收时钟和GMII发送时钟实际上为同一个时钟,因此这里使用同步FIFO。

    2.1 UDP模块

            UDP模块实现了以太网帧格式和UDP协议功能,由UDP接收模块、UDP发送模块和CRC校验模块组成。

    UDP接收模块:接收模块较为简单只需要判断目的MAC地址与开发板MAC地址、目的IP地址与开发板IP地址是否一致。接收模块的解析顺序是:前导码+顺起始界定符→以太网头→IP首部→UDP首部→UDP数据(有效数据)→接收结束。IP数据报一般以32bit为单位,为了和IP数据报格式保持一致,所以要把8位数据转成32位数据,因此接收模块实际上是完成了8位数据转32位数据的功能。

    UDP发送模块:发送模块多了IP首部校验和和CRC循环冗余校验的计算,CRC的校验在CRC校验模块里完成。发送模块的发送顺序是前导码+起始界定符→以太网头→IP首部→UDP首部→UDP数据(有效数据)→CRC校验。输入的有效数据为32位数据,GMII接口为8位数据接口,因此发送模块实际上完成的是32位数据转8位数据的功能。

    CRC校验模块:CRC校验模块是对UDP发送模块的数据(不包括前导码和起始界定符)做校验,把校验结果值拼在以太网倾格式的FCS字段,如果CRC校验值计算错误或者没有的话,那么电脑网卡会直接丢弃该顿导致收不到数据。CRC32 校验在FPGA实现的原理是LFSR (Linear Feedback Shif Register,线性反馈移位寄存器),其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值。

    2.1.1 UDP接收模块

            UDP接收模块按照UDP的数据格式解析数据,并实现将8位用户数据转成32位数据的功能,通过三段式状态机来解析以太网包。

    1. module udp_rx(
    2. input clk , //时钟信号
    3. input rst_n , //复位信号,低电平有效
    4. input gmii_rx_dv , //GMII输入数据有效信号
    5. input [7:0] gmii_rxd , //GMII输入数据
    6. output reg rec_pkt_done, //以太网单包数据接收完成信号
    7. output reg rec_en , //以太网接收的数据使能信号
    8. output reg [31:0] rec_data , //以太网接收的数据
    9. output reg [15:0] rec_byte_num //以太网接收的有效字节数 单位:byte
    10. );
    11. //parameter define
    12. //开发板MAC地址 00-11-22-33-44-55
    13. parameter BOARD_MAC = 48'h00_11_22_33_44_55;
    14. //开发板IP地址 192.168.1.10
    15. parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
    16. localparam st_idle = 7'b000_0001; //初始状态,等待接收前导码
    17. localparam st_preamble = 7'b000_0010; //接收前导码状态
    18. localparam st_eth_head = 7'b000_0100; //接收以太网帧头
    19. localparam st_ip_head = 7'b000_1000; //接收IP首部
    20. localparam st_udp_head = 7'b001_0000; //接收UDP首部
    21. localparam st_rx_data = 7'b010_0000; //接收有效数据
    22. localparam st_rx_end = 7'b100_0000; //接收结束
    23. localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
    24. localparam UDP_TYPE = 8'd17 ; //UDP协议类型
    25. //reg define
    26. reg [6:0] cur_state ;
    27. reg [6:0] next_state ;
    28. reg skip_en ; //控制状态跳转使能信号
    29. reg error_en ; //解析错误使能信号
    30. reg [4:0] cnt ; //解析数据计数器
    31. reg [47:0] des_mac ; //目的MAC地址
    32. reg [15:0] eth_type ; //以太网类型
    33. reg [31:0] des_ip ; //目的IP地址
    34. reg [5:0] ip_head_byte_num; //IP首部长度
    35. reg [15:0] udp_byte_num ; //UDP长度
    36. reg [15:0] data_byte_num ; //数据长度
    37. reg [15:0] data_cnt ; //有效数据计数
    38. reg [1:0] rec_en_cnt ; //8bit转32bit计数器
    39. //*****************************************************
    40. //** main code
    41. //*****************************************************
    42. //(三段式状态机)同步时序描述状态转移
    43. always @(posedge clk or negedge rst_n) begin
    44. if(!rst_n)
    45. cur_state <= st_idle;
    46. else
    47. cur_state <= next_state;
    48. end
    49. //组合逻辑判断状态转移条件
    50. always @(*) begin
    51. next_state = st_idle;
    52. case(cur_state)
    53. st_idle : begin //等待接收前导码
    54. if(skip_en)
    55. next_state = st_preamble;
    56. else
    57. next_state = st_idle;
    58. end
    59. st_preamble : begin //接收前导码
    60. if(skip_en)
    61. next_state = st_eth_head;
    62. else if(error_en)
    63. next_state = st_rx_end;
    64. else
    65. next_state = st_preamble;
    66. end
    67. st_eth_head : begin //接收以太网帧头
    68. if(skip_en)
    69. next_state = st_ip_head;
    70. else if(error_en)
    71. next_state = st_rx_end;
    72. else
    73. next_state = st_eth_head;
    74. end
    75. st_ip_head : begin //接收IP首部
    76. if(skip_en)
    77. next_state = st_udp_head;
    78. else if(error_en)
    79. next_state = st_rx_end;
    80. else
    81. next_state = st_ip_head;
    82. end
    83. st_udp_head : begin //接收UDP首部
    84. if(skip_en)
    85. next_state = st_rx_data;
    86. else
    87. next_state = st_udp_head;
    88. end
    89. st_rx_data : begin //接收有效数据
    90. if(skip_en)
    91. next_state = st_rx_end;
    92. else
    93. next_state = st_rx_data;
    94. end
    95. st_rx_end : begin //接收结束
    96. if(skip_en)
    97. next_state = st_idle;
    98. else
    99. next_state = st_rx_end;
    100. end
    101. default : next_state = st_idle;
    102. endcase
    103. end
    104. //时序电路描述状态输出,解析以太网数据
    105. always @(posedge clk or negedge rst_n) begin
    106. if(!rst_n) begin
    107. skip_en <= 1'b0;
    108. error_en <= 1'b0;
    109. cnt <= 5'd0;
    110. des_mac <= 48'd0;
    111. eth_type <= 16'd0;
    112. des_ip <= 32'd0;
    113. ip_head_byte_num <= 6'd0;
    114. udp_byte_num <= 16'd0;
    115. data_byte_num <= 16'd0;
    116. data_cnt <= 16'd0;
    117. rec_en_cnt <= 2'd0;
    118. rec_en <= 1'b0;
    119. rec_data <= 32'd0;
    120. rec_pkt_done <= 1'b0;
    121. rec_byte_num <= 16'd0;
    122. end
    123. else begin
    124. skip_en <= 1'b0;
    125. error_en <= 1'b0;
    126. rec_en <= 1'b0;
    127. rec_pkt_done <= 1'b0;
    128. case(next_state)
    129. st_idle : begin
    130. if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))
    131. skip_en <= 1'b1;
    132. end
    133. st_preamble : begin
    134. if(gmii_rx_dv) begin //解析前导码
    135. cnt <= cnt + 5'd1;
    136. if((cnt < 5'd6) && (gmii_rxd != 8'h55)) //7个8'h55
    137. error_en <= 1'b1;
    138. else if(cnt==5'd6) begin
    139. cnt <= 5'd0;
    140. if(gmii_rxd==8'hd5) //1个8'hd5
    141. skip_en <= 1'b1;
    142. else
    143. error_en <= 1'b1;
    144. end
    145. end
    146. end
    147. st_eth_head : begin
    148. if(gmii_rx_dv) begin
    149. cnt <= cnt + 5'b1;
    150. if(cnt < 5'd6)
    151. des_mac <= {des_mac[39:0],gmii_rxd}; //目的MAC地址
    152. else if(cnt == 5'd12)
    153. eth_type[15:8] <= gmii_rxd; //以太网协议类型
    154. else if(cnt == 5'd13) begin
    155. eth_type[7:0] <= gmii_rxd;
    156. cnt <= 5'd0;
    157. //判断MAC地址是否为开发板MAC地址或者公共地址
    158. if(((des_mac == BOARD_MAC) ||(des_mac == 48'hff_ff_ff_ff_ff_ff))
    159. && eth_type[15:8] == ETH_TYPE[15:8] && gmii_rxd == ETH_TYPE[7:0])
    160. skip_en <= 1'b1;
    161. else
    162. error_en <= 1'b1;
    163. end
    164. end
    165. end
    166. st_ip_head : begin
    167. if(gmii_rx_dv) begin
    168. cnt <= cnt + 5'd1;
    169. if(cnt == 5'd0)
    170. ip_head_byte_num <= {gmii_rxd[3:0],2'd0}; //寄存IP首部长度
    171. else if(cnt == 5'd9) begin
    172. if(gmii_rxd != UDP_TYPE) begin
    173. //如果当前接收的数据不是UDP协议,停止解析数据
    174. error_en <= 1'b1;
    175. cnt <= 5'd0;
    176. end
    177. end
    178. else if((cnt >= 5'd16) && (cnt <= 5'd18))
    179. des_ip <= {des_ip[23:0],gmii_rxd}; //寄存目的IP地址
    180. else if(cnt == 5'd19) begin
    181. des_ip <= {des_ip[23:0],gmii_rxd};
    182. //判断IP地址是否为开发板IP地址
    183. if((des_ip[23:0] == BOARD_IP[31:8])
    184. && (gmii_rxd == BOARD_IP[7:0])) begin
    185. if(cnt == ip_head_byte_num - 1'b1) begin
    186. skip_en <=1'b1;
    187. cnt <= 5'd0;
    188. end
    189. end
    190. else begin
    191. //IP错误,停止解析数据
    192. error_en <= 1'b1;
    193. cnt <= 5'd0;
    194. end
    195. end
    196. else if(cnt == ip_head_byte_num - 1'b1) begin
    197. skip_en <=1'b1; //IP首部解析完成
    198. cnt <= 5'd0;
    199. end
    200. end
    201. end
    202. st_udp_head : begin
    203. if(gmii_rx_dv) begin
    204. cnt <= cnt + 5'd1;
    205. if(cnt == 5'd4)
    206. udp_byte_num[15:8] <= gmii_rxd; //解析UDP字节长度
    207. else if(cnt == 5'd5)
    208. udp_byte_num[7:0] <= gmii_rxd;
    209. else if(cnt == 5'd7) begin
    210. //有效数据字节长度,(UDP首部8个字节,所以减去8)
    211. data_byte_num <= udp_byte_num - 16'd8;
    212. skip_en <= 1'b1;
    213. cnt <= 5'd0;
    214. end
    215. end
    216. end
    217. st_rx_data : begin
    218. //接收数据,转换成32bit
    219. if(gmii_rx_dv) begin
    220. data_cnt <= data_cnt + 16'd1;
    221. rec_en_cnt <= rec_en_cnt + 2'd1;
    222. if(data_cnt == data_byte_num - 16'd1) begin
    223. skip_en <= 1'b1; //有效数据接收完成
    224. data_cnt <= 16'd0;
    225. rec_en_cnt <= 2'd0;
    226. rec_pkt_done <= 1'b1;
    227. rec_en <= 1'b1;
    228. rec_byte_num <= data_byte_num;
    229. end
    230. //先收到的数据放在了rec_data的高位,所以当数据不是4的倍数时,
    231. //低位数据为无效数据,可根据有效字节数来判断(rec_byte_num)
    232. if(rec_en_cnt == 2'd0)
    233. rec_data[31:24] <= gmii_rxd;
    234. else if(rec_en_cnt == 2'd1)
    235. rec_data[23:16] <= gmii_rxd;
    236. else if(rec_en_cnt == 2'd2)
    237. rec_data[15:8] <= gmii_rxd;
    238. else if(rec_en_cnt==2'd3) begin
    239. rec_en <= 1'b1;
    240. rec_data[7:0] <= gmii_rxd;
    241. end
    242. end
    243. end
    244. st_rx_end : begin //单包数据接收完成
    245. if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
    246. skip_en <= 1'b1;
    247. end
    248. default : ;
    249. endcase
    250. end
    251. end
    252. endmodule

    2.1.2 UDP发送模块

    1. module udp_tx(
    2. input clk , //时钟信号
    3. input rst_n , //复位信号,低电平有效
    4. input tx_start_en, //以太网开始发送信号
    5. input [31:0] tx_data , //以太网待发送数据
    6. input [15:0] tx_byte_num, //以太网发送的有效字节数
    7. input [47:0] des_mac , //发送的目标MAC地址
    8. input [31:0] des_ip , //发送的目标IP地址
    9. input [31:0] crc_data , //CRC校验数据
    10. input [7:0] crc_next , //CRC下次校验完成数据
    11. output reg tx_done , //以太网发送完成信号
    12. output reg tx_req , //读数据请求信号
    13. output reg gmii_tx_en , //GMII输出数据有效信号
    14. output reg [7:0] gmii_txd , //GMII输出数据
    15. output reg crc_en , //CRC开始校验使能
    16. output reg crc_clr //CRC数据复位信号
    17. );
    18. //parameter define
    19. //开发板MAC地址 00-11-22-33-44-55
    20. parameter BOARD_MAC = 48'h00_11_22_33_44_55;
    21. //开发板IP地址 192.168.1.10
    22. parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
    23. //目的MAC地址 ff_ff_ff_ff_ff_ff
    24. parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
    25. //目的IP地址 192.168.1.102
    26. parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
    27. localparam st_idle = 7'b000_0001; //初始状态,等待开始发送信号
    28. localparam st_check_sum = 7'b000_0010; //IP首部校验和
    29. localparam st_preamble = 7'b000_0100; //发送前导码+帧起始界定符
    30. localparam st_eth_head = 7'b000_1000; //发送以太网帧头
    31. localparam st_ip_head = 7'b001_0000; //发送IP首部+UDP首部
    32. localparam st_tx_data = 7'b010_0000; //发送数据
    33. localparam st_crc = 7'b100_0000; //发送CRC校验值
    34. localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
    35. //以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
    36. //所以数据至少46-20-8=18个字节
    37. localparam MIN_DATA_NUM = 16'd18 ;
    38. localparam UDP_TYPE = 8'd17 ; //UDP协议类型
    39. //reg define
    40. reg [6:0] cur_state ;
    41. reg [6:0] next_state ;
    42. reg [7:0] preamble[7:0] ; //前导码
    43. reg [7:0] eth_head[13:0] ; //以太网首部
    44. reg [31:0] ip_head[6:0] ; //IP首部 + UDP首部
    45. reg start_en_d0 ;
    46. reg start_en_d1 ;
    47. reg [15:0] tx_data_num ; //发送的有效数据字节个数
    48. reg [15:0] total_num ; //总字节数
    49. reg trig_tx_en ;
    50. reg [15:0] udp_num ; //UDP字节数
    51. reg skip_en ; //控制状态跳转使能信号
    52. reg [4:0] cnt ;
    53. reg [31:0] check_buffer ; //首部校验和
    54. reg [1:0] tx_byte_sel ; //32位数据转8位数据计数器
    55. reg [15:0] data_cnt ; //发送数据个数计数器
    56. reg tx_done_t ;
    57. reg [4:0] real_add_cnt ; //以太网数据实际多发的字节数
    58. //wire define
    59. wire pos_start_en ;//开始发送数据上升沿
    60. wire [15:0] real_tx_data_num;//实际发送的字节数(以太网最少字节要求)
    61. //*****************************************************
    62. //** main code
    63. //*****************************************************
    64. assign pos_start_en = (~start_en_d1) & start_en_d0;
    65. assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM)
    66. ? tx_data_num : MIN_DATA_NUM;
    67. //采tx_start_en的上升沿
    68. always @(posedge clk or negedge rst_n) begin
    69. if(!rst_n) begin
    70. start_en_d0 <= 1'b0;
    71. start_en_d1 <= 1'b0;
    72. end
    73. else begin
    74. start_en_d0 <= tx_start_en;
    75. start_en_d1 <= start_en_d0;
    76. end
    77. end
    78. //寄存数据有效字节
    79. always @(posedge clk or negedge rst_n) begin
    80. if(!rst_n) begin
    81. tx_data_num <= 16'd0;
    82. total_num <= 16'd0;
    83. udp_num <= 16'd0;
    84. end
    85. else begin
    86. if(pos_start_en && cur_state==st_idle) begin
    87. //数据长度
    88. tx_data_num <= tx_byte_num;
    89. //UDP长度:UDP首部长度 + 有效数据
    90. udp_num <= tx_byte_num + 16'd8;
    91. //IP长度:IP首部长度 + UDP首部 + 有效数据
    92. total_num <= tx_byte_num + 16'd20 + 16'd8;
    93. end
    94. end
    95. end
    96. //触发发送信号
    97. always @(posedge clk or negedge rst_n) begin
    98. if(!rst_n)
    99. trig_tx_en <= 1'b0;
    100. else
    101. trig_tx_en <= pos_start_en;
    102. end
    103. always @(posedge clk or negedge rst_n) begin
    104. if(!rst_n)
    105. cur_state <= st_idle;
    106. else
    107. cur_state <= next_state;
    108. end
    109. always @(*) begin
    110. next_state = st_idle;
    111. case(cur_state)
    112. st_idle : begin //等待发送数据
    113. if(skip_en)
    114. next_state = st_check_sum;
    115. else
    116. next_state = st_idle;
    117. end
    118. st_check_sum: begin //IP首部校验
    119. if(skip_en)
    120. next_state = st_preamble;
    121. else
    122. next_state = st_check_sum;
    123. end
    124. st_preamble : begin //发送前导码+帧起始界定符
    125. if(skip_en)
    126. next_state = st_eth_head;
    127. else
    128. next_state = st_preamble;
    129. end
    130. st_eth_head : begin //发送以太网首部
    131. if(skip_en)
    132. next_state = st_ip_head;
    133. else
    134. next_state = st_eth_head;
    135. end
    136. st_ip_head : begin //发送IP首部+UDP首部
    137. if(skip_en)
    138. next_state = st_tx_data;
    139. else
    140. next_state = st_ip_head;
    141. end
    142. st_tx_data : begin //发送数据
    143. if(skip_en)
    144. next_state = st_crc;
    145. else
    146. next_state = st_tx_data;
    147. end
    148. st_crc: begin //发送CRC校验值
    149. if(skip_en)
    150. next_state = st_idle;
    151. else
    152. next_state = st_crc;
    153. end
    154. default : next_state = st_idle;
    155. endcase
    156. end
    157. //发送数据
    158. always @(posedge clk or negedge rst_n) begin
    159. if(!rst_n) begin
    160. skip_en <= 1'b0;
    161. cnt <= 5'd0;
    162. check_buffer <= 32'd0;
    163. ip_head[1][31:16] <= 16'd0;
    164. tx_byte_sel <= 2'b0;
    165. crc_en <= 1'b0;
    166. gmii_tx_en <= 1'b0;
    167. gmii_txd <= 8'd0;
    168. tx_req <= 1'b0;
    169. tx_done_t <= 1'b0;
    170. data_cnt <= 16'd0;
    171. real_add_cnt <= 5'd0;
    172. //初始化数组
    173. //前导码 7个8'h55 + 1个8'hd5
    174. preamble[0] <= 8'h55;
    175. preamble[1] <= 8'h55;
    176. preamble[2] <= 8'h55;
    177. preamble[3] <= 8'h55;
    178. preamble[4] <= 8'h55;
    179. preamble[5] <= 8'h55;
    180. preamble[6] <= 8'h55;
    181. preamble[7] <= 8'hd5;
    182. //目的MAC地址
    183. eth_head[0] <= DES_MAC[47:40];
    184. eth_head[1] <= DES_MAC[39:32];
    185. eth_head[2] <= DES_MAC[31:24];
    186. eth_head[3] <= DES_MAC[23:16];
    187. eth_head[4] <= DES_MAC[15:8];
    188. eth_head[5] <= DES_MAC[7:0];
    189. //源MAC地址
    190. eth_head[6] <= BOARD_MAC[47:40];
    191. eth_head[7] <= BOARD_MAC[39:32];
    192. eth_head[8] <= BOARD_MAC[31:24];
    193. eth_head[9] <= BOARD_MAC[23:16];
    194. eth_head[10] <= BOARD_MAC[15:8];
    195. eth_head[11] <= BOARD_MAC[7:0];
    196. //以太网类型
    197. eth_head[12] <= ETH_TYPE[15:8];
    198. eth_head[13] <= ETH_TYPE[7:0];
    199. end
    200. else begin
    201. skip_en <= 1'b0;
    202. tx_req <= 1'b0;
    203. crc_en <= 1'b0;
    204. gmii_tx_en <= 1'b0;
    205. tx_done_t <= 1'b0;
    206. case(next_state)
    207. st_idle : begin
    208. if(trig_tx_en) begin
    209. skip_en <= 1'b1;
    210. //版本号:4 首部长度:5(单位:32bit,20byte/4=5)
    211. ip_head[0] <= {8'h45,8'h00,total_num};
    212. //16位标识,每次发送累加1
    213. ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
    214. //bit[15:13]: 010表示不分片
    215. ip_head[1][15:0] <= 16'h4000;
    216. //协议:17(udp)
    217. ip_head[2] <= {8'h40,UDP_TYPE,16'h0};
    218. //源IP地址
    219. ip_head[3] <= BOARD_IP;
    220. //目的IP地址
    221. if(des_ip != 32'd0)
    222. ip_head[4] <= des_ip;
    223. else
    224. ip_head[4] <= DES_IP;
    225. //16位源端口号:1234 16位目的端口号:1234
    226. ip_head[5] <= {16'd1234,16'd1234};
    227. //16位udp长度,16位udp校验和
    228. ip_head[6] <= {udp_num,16'h0000};
    229. //更新MAC地址
    230. if(des_mac != 48'b0) begin
    231. //目的MAC地址
    232. eth_head[0] <= des_mac[47:40];
    233. eth_head[1] <= des_mac[39:32];
    234. eth_head[2] <= des_mac[31:24];
    235. eth_head[3] <= des_mac[23:16];
    236. eth_head[4] <= des_mac[15:8];
    237. eth_head[5] <= des_mac[7:0];
    238. end
    239. end
    240. end
    241. st_check_sum: begin //IP首部校验
    242. cnt <= cnt + 5'd1;
    243. if(cnt == 5'd0) begin
    244. check_buffer <= ip_head[0][31:16] + ip_head[0][15:0]
    245. + ip_head[1][31:16] + ip_head[1][15:0]
    246. + ip_head[2][31:16] + ip_head[2][15:0]
    247. + ip_head[3][31:16] + ip_head[3][15:0]
    248. + ip_head[4][31:16] + ip_head[4][15:0];
    249. end
    250. else if(cnt == 5'd1) //可能出现进位,累加一次
    251. check_buffer <= check_buffer[31:16] + check_buffer[15:0];
    252. else if(cnt == 5'd2) begin //可能再次出现进位,累加一次
    253. check_buffer <= check_buffer[31:16] + check_buffer[15:0];
    254. end
    255. else if(cnt == 5'd3) begin //按位取反
    256. skip_en <= 1'b1;
    257. cnt <= 5'd0;
    258. ip_head[2][15:0] <= ~check_buffer[15:0];
    259. end
    260. end
    261. st_preamble : begin //发送前导码+帧起始界定符
    262. gmii_tx_en <= 1'b1;
    263. gmii_txd <= preamble[cnt];
    264. if(cnt == 5'd7) begin
    265. skip_en <= 1'b1;
    266. cnt <= 5'd0;
    267. end
    268. else
    269. cnt <= cnt + 5'd1;
    270. end
    271. st_eth_head : begin //发送以太网首部
    272. gmii_tx_en <= 1'b1;
    273. crc_en <= 1'b1;
    274. gmii_txd <= eth_head[cnt];
    275. if (cnt == 5'd13) begin
    276. skip_en <= 1'b1;
    277. cnt <= 5'd0;
    278. end
    279. else
    280. cnt <= cnt + 5'd1;
    281. end
    282. st_ip_head : begin //发送IP首部 + UDP首部
    283. crc_en <= 1'b1;
    284. gmii_tx_en <= 1'b1;
    285. tx_byte_sel <= tx_byte_sel + 2'd1;
    286. if(tx_byte_sel == 2'd0)
    287. gmii_txd <= ip_head[cnt][31:24];
    288. else if(tx_byte_sel == 2'd1)
    289. gmii_txd <= ip_head[cnt][23:16];
    290. else if(tx_byte_sel == 2'd2) begin
    291. gmii_txd <= ip_head[cnt][15:8];
    292. if(cnt == 5'd6) begin
    293. //提前读请求数据,等待数据有效时发送
    294. tx_req <= 1'b1;
    295. end
    296. end
    297. else if(tx_byte_sel == 2'd3) begin
    298. gmii_txd <= ip_head[cnt][7:0];
    299. if(cnt == 5'd6) begin
    300. skip_en <= 1'b1;
    301. cnt <= 5'd0;
    302. end
    303. else
    304. cnt <= cnt + 5'd1;
    305. end
    306. end
    307. st_tx_data : begin //发送数据
    308. crc_en <= 1'b1;
    309. gmii_tx_en <= 1'b1;
    310. tx_byte_sel <= tx_byte_sel + 2'd1;
    311. if(tx_byte_sel == 1'b0)
    312. gmii_txd <= tx_data[31:24];
    313. else if(tx_byte_sel == 2'd1)
    314. gmii_txd <= tx_data[23:16];
    315. else if(tx_byte_sel == 2'd2) begin
    316. gmii_txd <= tx_data[15:8];
    317. if(data_cnt != tx_data_num - 16'd2)
    318. tx_req <= 1'b1;
    319. end
    320. else if(tx_byte_sel == 2'd3)
    321. gmii_txd <= tx_data[7:0];
    322. if(data_cnt < tx_data_num - 16'd1)
    323. data_cnt <= data_cnt + 16'd1;
    324. else if(data_cnt == tx_data_num - 16'd1)begin
    325. //如果发送的有效数据少于18个字节,在后面填补充位
    326. //填充的值为0
    327. tx_req <= 1'b0;
    328. if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)
    329. real_add_cnt <= real_add_cnt + 5'd1;
    330. else begin
    331. skip_en <= 1'b1;
    332. data_cnt <= 16'd0;
    333. real_add_cnt <= 5'd0;
    334. tx_byte_sel <= 2'd0;
    335. end
    336. if(real_add_cnt > 0) begin
    337. gmii_txd <= 8'd0;
    338. end
    339. end
    340. end
    341. st_crc : begin //发送CRC校验值
    342. gmii_tx_en <= 1'b1;
    343. tx_byte_sel <= tx_byte_sel + 2'd1;
    344. if(tx_byte_sel == 2'd0)
    345. gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
    346. ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
    347. else if(tx_byte_sel == 2'd1)
    348. gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],
    349. ~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};
    350. else if(tx_byte_sel == 2'd2) begin
    351. gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],
    352. ~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};
    353. end
    354. else if(tx_byte_sel == 2'd3) begin
    355. gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
    356. ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
    357. tx_done_t <= 1'b1;
    358. skip_en <= 1'b1;
    359. end
    360. end
    361. default :;
    362. endcase
    363. end
    364. end
    365. //发送完成信号及crc值复位信号
    366. always @(posedge clk or negedge rst_n) begin
    367. if(!rst_n) begin
    368. tx_done <= 1'b0;
    369. crc_clr <= 1'b0;
    370. end
    371. else begin
    372. tx_done <= tx_done_t;
    373. crc_clr <= tx_done_t;
    374. end
    375. end
    376. endmodule

    2.2 UDP控制模块

    1. module eth_ctrl(
    2. input clk , //系统时钟
    3. input rst_n , //系统复位信号,低电平有效
    4. //ARP相关端口信号
    5. input arp_rx_done, //ARP接收完成信号
    6. input arp_rx_type, //ARP接收类型 0:请求 1:应答
    7. output reg arp_tx_en, //ARP发送使能信号
    8. output arp_tx_type, //ARP发送类型 0:请求 1:应答
    9. input arp_tx_done, //ARP发送完成信号
    10. input arp_gmii_tx_en, //ARP GMII输出数据有效信号
    11. input [7:0] arp_gmii_txd, //ARP GMII输出数据
    12. //UDP相关端口信号
    13. input udp_tx_start_en,//UDP开始发送信号
    14. input udp_tx_done, //UDP发送完成信号
    15. input udp_gmii_tx_en, //UDP GMII输出数据有效信号
    16. input [7:0] udp_gmii_txd, //UDP GMII输出数据
    17. //GMII发送引脚
    18. output gmii_tx_en, //GMII输出数据有效信号
    19. output [7:0] gmii_txd //UDP GMII输出数据
    20. );
    21. //reg define
    22. reg protocol_sw; //协议切换信号
    23. reg udp_tx_busy; //UDP正在发送数据标志信号
    24. reg arp_rx_flag; //接收到ARP请求信号的标志
    25. //*****************************************************
    26. //** main code
    27. //*****************************************************
    28. assign arp_tx_type = 1'b1; //ARP发送类型固定为ARP应答
    29. assign gmii_tx_en = protocol_sw ? udp_gmii_tx_en : arp_gmii_tx_en;
    30. assign gmii_txd = protocol_sw ? udp_gmii_txd : arp_gmii_txd;
    31. //控制UDP发送忙信号
    32. always @(posedge clk or negedge rst_n) begin
    33. if(!rst_n)
    34. udp_tx_busy <= 1'b0;
    35. else if(udp_tx_start_en)
    36. udp_tx_busy <= 1'b1;
    37. else if(udp_tx_done)
    38. udp_tx_busy <= 1'b0;
    39. end
    40. //控制接收到ARP请求信号的标志
    41. always @(posedge clk or negedge rst_n) begin
    42. if(!rst_n)
    43. arp_rx_flag <= 1'b0;
    44. else if(arp_rx_done && (arp_rx_type == 1'b0))
    45. arp_rx_flag <= 1'b1;
    46. else if(protocol_sw == 1'b0)
    47. arp_rx_flag <= 1'b0;
    48. end
    49. //控制protocol_sw和arp_tx_en信号
    50. always @(posedge clk or negedge rst_n) begin
    51. if(!rst_n) begin
    52. protocol_sw <= 1'b0;
    53. arp_tx_en <= 1'b0;
    54. end
    55. else begin
    56. arp_tx_en <= 1'b0;
    57. if(udp_tx_start_en)
    58. protocol_sw <= 1'b1;
    59. else if(arp_rx_flag && (udp_tx_busy == 1'b0)) begin
    60. protocol_sw <= 1'b0;
    61. arp_tx_en <= 1'b1;
    62. end
    63. end
    64. end
    65. endmodule

    其他模块和以太网ARP测试实验一样

     

  • 相关阅读:
    Ajax——Ajax基于JSON和XML的数据交换格式以及乱码解决
    写python用GitHub-Copilot编程提效 - pycharm
    Vue packages version mismatch: vue@xxx vue-server-renderer@xxx
    【Java】一只小菜坤的编程题之旅【4】
    Gitea+Jenkins+webhooks-多分支触发+备份构建前端资源
    java项目之见福便利店信息管理系统(ssm框架)
    CMS与FullGC
    CVPR2022 | 无需对齐就能胜任大运动超分的内存增强非局部注意方法
    WebRTC模块化设计思想之编解码
    系统常用的命令
  • 原文地址:https://blog.csdn.net/STATEABC/article/details/132172695