当前位置:   article > 正文

FPGA - 以太网UDP通信(二)

FPGA - 以太网UDP通信(二)

一,引言

前文链接:FPGA - 以太网UDP通信(一)

在上文章中介绍了以太网简介,以太网UDP通信硬件结构,以及PHY芯片RGMII接口-GMII接口转换逻辑,接下来介绍UDP通信结构框图以及数据链路层(MAC层)接受发送逻辑。

二,以太网UDP通信结构框图

在上一篇文章中介绍了以太网UDP通信组包过程图:

可以看到用户数据通过UDP层添加udp头部,然后经过IP层添加ip头部,在经过数据链路层(MAC层)添加mac头部,送到PHY芯片物理层。一步步完成组包发送过程。同样的,从PHT芯片输出的数据经过MAC层IP层UDP层一步步完成解包校验过程。因此根据此思路画出简易结构框图如下:

上篇文章中实现了rgmii_interface(rgmii_receive和rgmii_send)模块,接下来实现mac_layermac_receivemac_send)模块。

三,数据链路层(MAC层)

MAC不是物理层,MAC层是数据链路层的两个子层之一。

以太网V2的MAC帧格式:

------------------------------------- mac数据包头  22byte  -------------------------------------      

|     前导码(前同步码) 7 byte                                                                           |  
|     8'h55             ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                                              ​​​​​​​        ​​​​​​​   |

|     SFD(帧开始定结符)  1byte     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​   |
|     8'hd5     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                                         |

|     目的mac地址 6byte     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​            ​​​​​​​      |

|     源mac地址   6byte     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                    |

|     类型/长度   2byte     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​            ​​​​​​​        ​​​​​​​   |
|     小于1536表示长度,     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​            ​​​​​​​     |
|     大于1536表示类型 arp:16'h0806 , ip: 16'h0800     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​|

|     数据 46-1500byte     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                      |

|     FCS(帧校验序列CRC) 4byte     ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​|

---------------------------------------------------------------------------------------------------------- 

四,MAC层代码设计

在上面的结构框图中,可以看到,mac_layer中包括mac_receivemac_send模块。

----------------------------------------------------------  思路 ------------------------------------------------------------

1,在MAC帧格式中,最后4byte 是FCS(帧校验序列CRC), 所以在接受和发送过程中必须把帧校验序列前面的数据存储(fifo)起来,等到CRC校验完成,再进行数据传输。

2,在以太网UDP通信中,用户端数据传输是在用户时钟下进行的,而PHY芯片数据传输是在PHY芯片的时钟下进行的。所以在这里我们要做一个跨时钟域的处理。

3,跨时钟域处理:在多bit信号跨时钟域中常常使用异步fifo来处理,但如何很好的设计这一处理过程呢?在这里我们进行双fifo跨时钟域(其实在之前文章中也用过这样的处理方式)。

首先第一个是数据fifo,缓存有效数据+last数据(用来指示有效数据的最后一个数据)

其次第二个是控制fifo,缓存数据对应的地址,长度,类型.....(在mac_layer中缓存的是帧类型和CRC校验结果)

然后判断fifo非空(rdemoty信号为低), 就把有效数据和控制信息从fifo里面读出来,

控制fifo的读使能rden只需要拉高一拍,数据fifo读使能rden要一直拉高,直到读出来最后一个数据,通过判断读出来的last信号,来拉低rden。

4,CRC校验

5,如何准确有效的进行数据组包,解包校验呢?

设置计数器,接受端设置rx_cnt,发送端设置tx_cnt。用计数器控制组包解包校验过程。

下图是mac_lay层的结构框图:

-----------------------------------------------------------------------------------------------------------------------------

五,MAC层代码编写

CRC32校验:

CRC校验

  1. `timescale 1ns / 1ps
  2. module crc32_d8(
  3. input clk,
  4. input reset,
  5. input crc_din_vld,
  6. input [7:0] crc_din ,
  7. input crc_done ,
  8. output [31:0] crc_dout
  9. );
  10. wire [7:0] crc_din_r;
  11. reg [31:0] crc_dout_r;
  12. wire [31:0] crc_data;
  13. assign crc_din_r = {crc_din[0],crc_din[1],crc_din[2],crc_din[3],crc_din[4],crc_din[5],crc_din[6],crc_din[7]};
  14. assign crc_dout = ~{crc_dout_r[0],crc_dout_r[1],crc_dout_r[2],crc_dout_r[3],crc_dout_r[4],crc_dout_r[5],crc_dout_r[6],crc_dout_r[7],
  15. crc_dout_r[8],crc_dout_r[9],crc_dout_r[10],crc_dout_r[11],crc_dout_r[12],crc_dout_r[13],crc_dout_r[14],crc_dout_r[15],
  16. crc_dout_r[16],crc_dout_r[17],crc_dout_r[18],crc_dout_r[19],crc_dout_r[20],crc_dout_r[21],crc_dout_r[22],crc_dout_r[23],
  17. crc_dout_r[24],crc_dout_r[25],crc_dout_r[26],crc_dout_r[27],crc_dout_r[28],crc_dout_r[29],crc_dout_r[30],crc_dout_r[31]
  18. };
  19. assign crc_data[0] = crc_din_r[6] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[30];
  20. assign crc_data[1] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[30] ^ crc_dout_r[31];
  21. assign crc_data[2] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[30] ^ crc_dout_r[31];
  22. assign crc_data[3] = crc_din_r[7] ^ crc_din_r[3] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[27] ^ crc_dout_r[31];
  23. assign crc_data[4] = crc_din_r[6] ^ crc_din_r[4] ^ crc_din_r[3] ^ crc_din_r[2] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[26] ^ crc_dout_r[27] ^ crc_dout_r[28] ^ crc_dout_r[30];
  24. assign crc_data[5] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[3] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[27] ^ crc_dout_r[28] ^ crc_dout_r[29] ^ crc_dout_r[30] ^ crc_dout_r[31];
  25. assign crc_data[6] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[28] ^ crc_dout_r[29] ^ crc_dout_r[30] ^ crc_dout_r[31];
  26. assign crc_data[7] = crc_din_r[7] ^ crc_din_r[5] ^ crc_din_r[3] ^ crc_din_r[2] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[26] ^ crc_dout_r[27] ^ crc_dout_r[29] ^ crc_dout_r[31];
  27. assign crc_data[8] = crc_din_r[4] ^ crc_din_r[3] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[0] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[27] ^ crc_dout_r[28];
  28. assign crc_data[9] = crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_dout_r[1] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[28] ^ crc_dout_r[29];
  29. assign crc_data[10] = crc_din_r[5] ^ crc_din_r[3] ^ crc_din_r[2] ^ crc_din_r[0] ^ crc_dout_r[2] ^ crc_dout_r[24] ^ crc_dout_r[26] ^ crc_dout_r[27] ^ crc_dout_r[29];
  30. assign crc_data[11] = crc_din_r[4] ^ crc_din_r[3] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[3] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[27] ^ crc_dout_r[28];
  31. assign crc_data[12] = crc_din_r[6] ^ crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[4] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[28] ^ crc_dout_r[29] ^ crc_dout_r[30];
  32. assign crc_data[13] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[5] ^ crc_din_r[3] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_dout_r[5] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[27] ^ crc_dout_r[29] ^ crc_dout_r[30] ^ crc_dout_r[31];
  33. assign crc_data[14] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[4] ^ crc_din_r[3] ^ crc_din_r[2] ^ crc_dout_r[6] ^ crc_dout_r[26] ^ crc_dout_r[27] ^ crc_dout_r[28] ^ crc_dout_r[30] ^ crc_dout_r[31];
  34. assign crc_data[15] = crc_din_r[7] ^ crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[3] ^ crc_dout_r[7] ^ crc_dout_r[27] ^ crc_dout_r[28] ^ crc_dout_r[29] ^ crc_dout_r[31];
  35. assign crc_data[16] = crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[0] ^ crc_dout_r[8] ^ crc_dout_r[24] ^ crc_dout_r[28] ^ crc_dout_r[29];
  36. assign crc_data[17] = crc_din_r[6] ^ crc_din_r[5] ^ crc_din_r[1] ^ crc_dout_r[9] ^ crc_dout_r[25] ^ crc_dout_r[29] ^ crc_dout_r[30];
  37. assign crc_data[18] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[2] ^ crc_dout_r[10] ^ crc_dout_r[26] ^ crc_dout_r[30] ^ crc_dout_r[31];
  38. assign crc_data[19] = crc_din_r[7] ^ crc_din_r[3] ^ crc_dout_r[11] ^ crc_dout_r[27] ^ crc_dout_r[31];
  39. assign crc_data[20] = crc_din_r[4] ^ crc_dout_r[12] ^ crc_dout_r[28];
  40. assign crc_data[21] = crc_din_r[5] ^ crc_dout_r[13] ^ crc_dout_r[29];
  41. assign crc_data[22] = crc_din_r[0] ^ crc_dout_r[14] ^ crc_dout_r[24];
  42. assign crc_data[23] = crc_din_r[6] ^ crc_din_r[1] ^ crc_din_r[0] ^ crc_dout_r[15] ^ crc_dout_r[24] ^ crc_dout_r[25] ^ crc_dout_r[30];
  43. assign crc_data[24] = crc_din_r[7] ^ crc_din_r[2] ^ crc_din_r[1] ^ crc_dout_r[16] ^ crc_dout_r[25] ^ crc_dout_r[26] ^ crc_dout_r[31];
  44. assign crc_data[25] = crc_din_r[3] ^ crc_din_r[2] ^ crc_dout_r[17] ^ crc_dout_r[26] ^ crc_dout_r[27];
  45. assign crc_data[26] = crc_din_r[6] ^ crc_din_r[4] ^ crc_din_r[3] ^ crc_din_r[0] ^ crc_dout_r[18] ^ crc_dout_r[24] ^ crc_dout_r[27] ^ crc_dout_r[28] ^ crc_dout_r[30];
  46. assign crc_data[27] = crc_din_r[7] ^ crc_din_r[5] ^ crc_din_r[4] ^ crc_din_r[1] ^ crc_dout_r[19] ^ crc_dout_r[25] ^ crc_dout_r[28] ^ crc_dout_r[29] ^ crc_dout_r[31];
  47. assign crc_data[28] = crc_din_r[6] ^ crc_din_r[5] ^ crc_din_r[2] ^ crc_dout_r[20] ^ crc_dout_r[26] ^ crc_dout_r[29] ^ crc_dout_r[30];
  48. assign crc_data[29] = crc_din_r[7] ^ crc_din_r[6] ^ crc_din_r[3] ^ crc_dout_r[21] ^ crc_dout_r[27] ^ crc_dout_r[30] ^ crc_dout_r[31];
  49. assign crc_data[30] = crc_din_r[7] ^ crc_din_r[4] ^ crc_dout_r[22] ^ crc_dout_r[28] ^ crc_dout_r[31];
  50. assign crc_data[31] = crc_din_r[5] ^ crc_dout_r[23] ^ crc_dout_r[29];
  51. always @(posedge clk) begin
  52. if (reset)
  53. crc_dout_r <= 32'hffffffff;
  54. else if (crc_done)
  55. crc_dout_r <= 32'hffffffff;
  56. else if (crc_din_vld)
  57. crc_dout_r <= crc_data;
  58. else
  59. crc_dout_r <= crc_dout_r;
  60. end
  61. endmodule

mac_receive

mac_receive代码:

  1. //功能 : ①完成校验,将有效数据和MAC头部分离出来
  2. // ②跨时钟域
  3. // -----------------------------------------------------------------------------
  4. `timescale 1ns / 1ps
  5. module mac_receive #(
  6. parameter LOCAL_MAC_ADDR = 48'hffffff_ffffff,
  7. parameter CRC_CHACK_EN = 1
  8. )(
  9. input clk , //用户接受端时钟
  10. input phy_rx_clk , //phy芯片提供的时钟
  11. input reset , //用户端复位信号
  12. input phy_rx_reset , //phy接受端复位
  13. /*-------rgmii_recive模块交互的信号----------------*/
  14. input gmii_rx_data_vld ,
  15. input [7:0] gmii_rx_data ,
  16. /*-------mac_to_arp_ip模块交互的信号----------------*/
  17. output reg mac_rx_data_vld ,
  18. output reg mac_rx_data_last ,
  19. output reg [7:0] mac_rx_data ,
  20. output reg [15:0] mac_rx_frame_type,
  21. /*-------rx_crc32_d8模块交互的信号----------------*/
  22. output reg rx_crc_din_vld ,
  23. output reg [7:0] rx_crc_din ,
  24. output reg rx_crc_done ,
  25. input [31:0] rx_crc_dout
  26. );
  27. endmodule

mac_send

mac_send代码:

  1. //功能 ① 完成crc校验 mac头部 有效数据 crc校验 ...等组包
  2. // ② 跨时钟域
  3. // -----------------------------------------------------------------------------
  4. `timescale 1ns / 1ps
  5. module mac_send #(
  6. parameter LOCAL_MAC_ADDR = 48'hffffff_ffffff,
  7. parameter TARGET_MAC_ADDR = 48'hffffff_ffffff
  8. )(
  9. input clk , //用户发送端时钟
  10. input phy_tx_clk ,
  11. input reset ,
  12. input phy_tx_reset ,
  13. /*-------rgmii_send模块交互的信号----------------*/
  14. output reg gmii_tx_data_vld ,
  15. output reg [7:0] gmii_tx_data ,
  16. /*-------ip_send模块交互的信号--------------------*/
  17. input mac_tx_data_vld ,
  18. input mac_tx_data_last ,
  19. input [7:0] mac_tx_data ,
  20. input [15:0] mac_tx_frame_type,
  21. input [15:0] mac_tx_length ,
  22. /*-------tx_crc32_d8模块交互的信号----------------*/
  23. output reg tx_crc_din_vld ,
  24. output [7:0] tx_crc_din ,
  25. output reg tx_crc_done ,
  26. input [31:0] tx_crc_dout
  27. );
  28. endmodule

顶层设计

mac层分别实现了接收和发送两部分,将两部分例化封装顶层mac_layer

  1. `timescale 1ns / 1ps
  2. module mac_layer #(
  3. parameter LOCAL_MAC_ADDR = 48'hffffff_ffffff ,
  4. parameter TARGET_MAC_ADDR = 48'hffffff_ffffff ,
  5. parameter CRC_CHACK_EN = 1
  6. )(
  7. input app_tx_clk ,
  8. input app_rx_clk ,
  9. input phy_tx_clk ,
  10. input phy_rx_clk ,
  11. input app_tx_reset ,
  12. input app_rx_reset ,
  13. input phy_tx_reset ,
  14. input phy_rx_reset ,
  15. input gmii_rx_data_vld ,
  16. input [7:0] gmii_rx_data ,
  17. output gmii_tx_data_vld ,
  18. output [7:0] gmii_tx_data ,
  19. output mac_rx_data_vld ,
  20. output mac_rx_data_last ,
  21. output [7:0] mac_rx_data ,
  22. output [15:0] mac_rx_frame_type,
  23. input mac_tx_data_vld ,
  24. input mac_tx_data_last ,
  25. input [7:0] mac_tx_data ,
  26. input [15:0] mac_tx_frame_type,
  27. input [15:0] mac_tx_length
  28. );
  29. wire tx_crc_din_vld;
  30. wire [7:0] tx_crc_din ;
  31. wire tx_crc_done ;
  32. wire [31:0] tx_crc_dout ;
  33. wire rx_crc_din_vld;
  34. wire [7:0] rx_crc_din ;
  35. wire rx_crc_done ;
  36. wire [31:0] rx_crc_dout ;
  37. mac_send #(
  38. .LOCAL_MAC_ADDR(LOCAL_MAC_ADDR),
  39. .TARGET_MAC_ADDR(TARGET_MAC_ADDR)
  40. ) mac_send (
  41. .clk (app_tx_clk),
  42. .phy_tx_clk (phy_tx_clk),
  43. .reset (app_tx_reset),
  44. .phy_tx_reset (phy_tx_reset),
  45. .gmii_tx_data_vld (gmii_tx_data_vld),
  46. .gmii_tx_data (gmii_tx_data),
  47. .mac_tx_data_vld (mac_tx_data_vld),
  48. .mac_tx_data_last (mac_tx_data_last),
  49. .mac_tx_data (mac_tx_data),
  50. .mac_tx_frame_type (mac_tx_frame_type),
  51. .mac_tx_length (mac_tx_length),
  52. .tx_crc_din_vld (tx_crc_din_vld),
  53. .tx_crc_din (tx_crc_din),
  54. .tx_crc_done (tx_crc_done),
  55. .tx_crc_dout (tx_crc_dout)
  56. );
  57. mac_receive #(
  58. .LOCAL_MAC_ADDR(LOCAL_MAC_ADDR),
  59. .CRC_CHACK_EN (CRC_CHACK_EN)
  60. ) mac_receive (
  61. .clk (app_rx_clk),
  62. .phy_rx_clk (phy_rx_clk),
  63. .reset (app_rx_reset),
  64. .phy_rx_reset (phy_rx_reset),
  65. .gmii_rx_data_vld (gmii_rx_data_vld),
  66. .gmii_rx_data (gmii_rx_data),
  67. .mac_rx_data_vld (mac_rx_data_vld),
  68. .mac_rx_data_last (mac_rx_data_last),
  69. .mac_rx_data (mac_rx_data),
  70. .mac_rx_frame_type (mac_rx_frame_type),
  71. .rx_crc_din_vld (rx_crc_din_vld),
  72. .rx_crc_din (rx_crc_din),
  73. .rx_crc_done (rx_crc_done),
  74. .rx_crc_dout (rx_crc_dout)
  75. );
  76. crc32_d8 tx_crc32_d8
  77. (
  78. .clk (phy_tx_clk),
  79. .reset (phy_tx_reset),
  80. .crc_din_vld (tx_crc_din_vld),
  81. .crc_din (tx_crc_din),
  82. .crc_done (tx_crc_done),
  83. .crc_dout (tx_crc_dout)
  84. );
  85. crc32_d8 rx_crc32_d8
  86. (
  87. .clk (phy_rx_clk),
  88. .reset (phy_rx_reset),
  89. .crc_din_vld (rx_crc_din_vld),
  90. .crc_din (rx_crc_din),
  91. .crc_done (rx_crc_done),
  92. .crc_dout (rx_crc_dout)
  93. );
  94. endmodule

 六,总结

至此,我们完成了以太网发送过程中最底层 ,也是最重要的部分MAC层的发送与接受。 关于本节中的CRC校验部分没有过多介绍,这部分可通过CRC生成网页来生成CRC校验代码。

接下来,在下一篇博客中将会实现ip层的接收与发送。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/454218
推荐阅读
相关标签
  

闽ICP备14008679号