外贸多语言网站建设推广,网站备案拍照背景图,北京网页制作网络公司,石家庄建设银行网站一#xff0c;引言
前文链接#xff1a;FPGA - 以太网UDP通信#xff08;一#xff09;
在上文章中介绍了以太网简介#xff0c;以太网UDP通信硬件结构#xff0c;以及PHY芯片RGMII接口-GMII接口转换逻辑#xff0c;接下来介绍UDP通信结构框图以及数据链路层#xff…一引言
前文链接FPGA - 以太网UDP通信一
在上文章中介绍了以太网简介以太网UDP通信硬件结构以及PHY芯片RGMII接口-GMII接口转换逻辑接下来介绍UDP通信结构框图以及数据链路层MAC层接受发送逻辑。
二以太网UDP通信结构框图
在上一篇文章中介绍了以太网UDP通信组包过程图 可以看到用户数据通过UDP层添加udp头部然后经过IP层添加ip头部在经过数据链路层MAC层添加mac头部送到PHY芯片物理层。一步步完成组包发送过程。同样的从PHT芯片输出的数据经过MAC层IP层UDP层一步步完成解包校验过程。因此根据此思路画出简易结构框图如下 在上篇文章中实现了rgmii_interfacergmii_receive和rgmii_send模块,接下来实现mac_layermac_receive和mac_send模块。
三数据链路层MAC层
MAC不是物理层MAC层是数据链路层的两个子层之一。
以太网V2的MAC帧格式 ------------------------------------- mac数据包头 22byte -------------------------------------
| 前导码前同步码 7 byte | | 8h55 |
| SFD帧开始定结符 1byte || 8hd5 |
| 目的mac地址 6byte |
| 源mac地址 6byte |
| 类型/长度 2byte || 小于1536表示长度 || 大于1536表示类型 arp:16h0806 , ip: 16h0800 |
| 数据 46-1500byte |
| FCS帧校验序列CRC 4byte |
----------------------------------------------------------------------------------------------------------
四MAC层代码设计
在上面的结构框图中可以看到mac_layer中包括mac_receive和mac_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。
4CRC校验
5如何准确有效的进行数据组包解包校验呢
设置计数器接受端设置rx_cnt发送端设置tx_cnt。用计数器控制组包解包校验过程。
下图是mac_lay层的结构框图 -----------------------------------------------------------------------------------------------------------------------------
五MAC层代码编写
CRC32校验 CRC校验
timescale 1ns / 1psmodule crc32_d8(input clk,input reset,input crc_din_vld,input [7:0] crc_din ,input crc_done ,output [31:0] crc_dout );wire [7:0] crc_din_r;
reg [31:0] crc_dout_r;
wire [31:0] crc_data;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]};
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],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],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],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]};assign crc_data[0] crc_din_r[6] ^ crc_din_r[0] ^ crc_dout_r[24] ^ crc_dout_r[30];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
assign crc_data[19] crc_din_r[7] ^ crc_din_r[3] ^ crc_dout_r[11] ^ crc_dout_r[27] ^ crc_dout_r[31];
assign crc_data[20] crc_din_r[4] ^ crc_dout_r[12] ^ crc_dout_r[28];
assign crc_data[21] crc_din_r[5] ^ crc_dout_r[13] ^ crc_dout_r[29];
assign crc_data[22] crc_din_r[0] ^ crc_dout_r[14] ^ crc_dout_r[24];
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];
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];
assign crc_data[25] crc_din_r[3] ^ crc_din_r[2] ^ crc_dout_r[17] ^ crc_dout_r[26] ^ crc_dout_r[27];
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];
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];
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];
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];
assign crc_data[30] crc_din_r[7] ^ crc_din_r[4] ^ crc_dout_r[22] ^ crc_dout_r[28] ^ crc_dout_r[31];
assign crc_data[31] crc_din_r[5] ^ crc_dout_r[23] ^ crc_dout_r[29];always (posedge clk) beginif (reset) crc_dout_r 32hffffffff;else if (crc_done) crc_dout_r 32hffffffff;else if (crc_din_vld)crc_dout_r crc_data;else crc_dout_r crc_dout_r;
endendmodulemac_receive
mac_receive代码
//功能 ①完成校验将有效数据和MAC头部分离出来
// ②跨时钟域
// -----------------------------------------------------------------------------
timescale 1ns / 1psmodule mac_receive #(parameter LOCAL_MAC_ADDR 48hffffff_ffffff,parameter CRC_CHACK_EN 1)(input clk , //用户接受端时钟input phy_rx_clk , //phy芯片提供的时钟input reset , //用户端复位信号input phy_rx_reset , //phy接受端复位/*-------rgmii_recive模块交互的信号----------------*/input gmii_rx_data_vld ,input [7:0] gmii_rx_data ,/*-------mac_to_arp_ip模块交互的信号----------------*/ output reg mac_rx_data_vld ,output reg mac_rx_data_last ,output reg [7:0] mac_rx_data ,output reg [15:0] mac_rx_frame_type,/*-------rx_crc32_d8模块交互的信号----------------*/ output reg rx_crc_din_vld ,output reg [7:0] rx_crc_din ,output reg rx_crc_done ,input [31:0] rx_crc_dout );endmodule
mac_send
mac_send代码
//功能 ① 完成crc校验 mac头部 有效数据 crc校验 ...等组包
// ② 跨时钟域
// -----------------------------------------------------------------------------
timescale 1ns / 1ps
module mac_send #(parameter LOCAL_MAC_ADDR 48hffffff_ffffff,parameter TARGET_MAC_ADDR 48hffffff_ffffff )(input clk , //用户发送端时钟input phy_tx_clk , input reset , input phy_tx_reset , /*-------rgmii_send模块交互的信号----------------*/output reg gmii_tx_data_vld ,output reg [7:0] gmii_tx_data ,/*-------ip_send模块交互的信号--------------------*/ input mac_tx_data_vld ,input mac_tx_data_last ,input [7:0] mac_tx_data ,input [15:0] mac_tx_frame_type,input [15:0] mac_tx_length ,/*-------tx_crc32_d8模块交互的信号----------------*/ output reg tx_crc_din_vld ,output [7:0] tx_crc_din ,output reg tx_crc_done ,input [31:0] tx_crc_dout );endmodule
顶层设计
mac层分别实现了接收和发送两部分将两部分例化封装顶层mac_layer
timescale 1ns / 1psmodule mac_layer #(parameter LOCAL_MAC_ADDR 48hffffff_ffffff ,parameter TARGET_MAC_ADDR 48hffffff_ffffff , parameter CRC_CHACK_EN 1 )(input app_tx_clk ,input app_rx_clk ,input phy_tx_clk , input phy_rx_clk , input app_tx_reset ,input app_rx_reset ,input phy_tx_reset , input phy_rx_reset , input gmii_rx_data_vld ,input [7:0] gmii_rx_data ,output gmii_tx_data_vld ,output [7:0] gmii_tx_data , output mac_rx_data_vld ,output mac_rx_data_last ,output [7:0] mac_rx_data ,output [15:0] mac_rx_frame_type, input mac_tx_data_vld ,input mac_tx_data_last ,input [7:0] mac_tx_data ,input [15:0] mac_tx_frame_type,input [15:0] mac_tx_length );wire tx_crc_din_vld;wire [7:0] tx_crc_din ;wire tx_crc_done ;wire [31:0] tx_crc_dout ;wire rx_crc_din_vld;wire [7:0] rx_crc_din ;wire rx_crc_done ;wire [31:0] rx_crc_dout ;mac_send #(.LOCAL_MAC_ADDR(LOCAL_MAC_ADDR),.TARGET_MAC_ADDR(TARGET_MAC_ADDR)) mac_send (.clk (app_tx_clk),.phy_tx_clk (phy_tx_clk),.reset (app_tx_reset),.phy_tx_reset (phy_tx_reset),.gmii_tx_data_vld (gmii_tx_data_vld),.gmii_tx_data (gmii_tx_data),.mac_tx_data_vld (mac_tx_data_vld),.mac_tx_data_last (mac_tx_data_last),.mac_tx_data (mac_tx_data),.mac_tx_frame_type (mac_tx_frame_type),.mac_tx_length (mac_tx_length),.tx_crc_din_vld (tx_crc_din_vld),.tx_crc_din (tx_crc_din),.tx_crc_done (tx_crc_done),.tx_crc_dout (tx_crc_dout));mac_receive #(.LOCAL_MAC_ADDR(LOCAL_MAC_ADDR),.CRC_CHACK_EN (CRC_CHACK_EN)) mac_receive (.clk (app_rx_clk),.phy_rx_clk (phy_rx_clk),.reset (app_rx_reset),.phy_rx_reset (phy_rx_reset),.gmii_rx_data_vld (gmii_rx_data_vld),.gmii_rx_data (gmii_rx_data),.mac_rx_data_vld (mac_rx_data_vld),.mac_rx_data_last (mac_rx_data_last),.mac_rx_data (mac_rx_data),.mac_rx_frame_type (mac_rx_frame_type),.rx_crc_din_vld (rx_crc_din_vld),.rx_crc_din (rx_crc_din),.rx_crc_done (rx_crc_done),.rx_crc_dout (rx_crc_dout));crc32_d8 tx_crc32_d8(.clk (phy_tx_clk),.reset (phy_tx_reset),.crc_din_vld (tx_crc_din_vld),.crc_din (tx_crc_din),.crc_done (tx_crc_done),.crc_dout (tx_crc_dout));crc32_d8 rx_crc32_d8(.clk (phy_rx_clk),.reset (phy_rx_reset),.crc_din_vld (rx_crc_din_vld),.crc_din (rx_crc_din),.crc_done (rx_crc_done),.crc_dout (rx_crc_dout));endmodule六总结
至此我们完成了以太网发送过程中最底层 也是最重要的部分MAC层的发送与接受。 关于本节中的CRC校验部分没有过多介绍这部分可通过CRC生成网页来生成CRC校验代码。
接下来在下一篇博客中将会实现ip层的接收与发送。