rj45 千兆接口定义_肝了40张图,搞定千兆以太网,这一篇就够了

本文目录共分为四个大部分

  • 接收——RGMII协议和IDDR原语

  • 接收——包校验和数据筛选

  • 发送——组建以太网心跳包

  • 发送——ODDR原语和Wireshark抓包工具

接收——RGMII协议和IDDR原语

一、项目概况

1、项目流程图

a72da525c4b6a6908db70adbdb86c127.png

2、模块说明:

PC:个人电脑,有网线插槽的即可
RJ45接口:板卡上的网线插槽
PHY芯片:板卡上的以太网芯片,输入4对差分信号,转换为输出双沿4bit数据信号
FPGA:现场可编程逻辑门阵列,主控制器
DDR3芯片:第三代同步动态随机存取内存芯片,之前学过了
HDMI接口:高清多媒体接口,之前学过了

3、项目说明:

  电脑上位机将一幅 1024*768 图片通过双绞线(网线),发送给板卡网口(RJ45接口),RJ45接口将数据传输给网卡(PHY芯片),PHY 芯片将差分信号转换成双沿数据,IDDR将双沿数据转换成单沿数据传输给 FPGA,FPGA 处理完成后将图像数据缓存到DDR3 中,DDR3 中的图像数据使用 UDP 协议传回 PC 机,同时将 DDR3 中数据使用 HDMI 传输到显示器上。

二、PHY芯片

  详情参考 VSC8601-DS-r41-VMDS-10210 文档,该文档可以在 Xilinx 官网中下载。

1、以太网和 RJ45 接口

  以太网是一种产生较早,使用相当广泛的局域网。分类标准有标准以太网(10Mbit/s),快速以太网(100Mbit/s)和千兆以太网(1000Mbit/s)。随着以太网技术的飞速发展,市场上也出现了万兆以太网其技术支持10Gbit/s的传输速率。以太网连接端口:常见的以太网接口类型有RJ45接口,RJ11接口。SC光纤接口等。其中RJ45接口是我们常见的网络设备接口。

69d32e00e02045008fb2b0159c800c40.png

  RJ45接口定义以及各引脚功能说明如下图所示,在以太网中只使用了1、2、3、6这四根线,其中1、2这组负责传输数据(TX+、TX-),而3、6这组负责接收数据(RX+、RX-),另外四根线是备用的。

c94cfc05b370bb00477f8089faa02cfb.png

  总的来说,这一块部分知道即刻,不需要我们自己设计代码。

2、PHY 芯片介绍:

  本项目使用 VITESSE VSC8601 网卡芯片,该芯片支持 10/100/1000 BASE-T,常用的网卡芯片有 VITESSE、高通等公司产品,该芯片通称为 PHY 芯片。该芯片与我们电脑、FPGA、MCU 等微处理器通信必须遵循 RGMII 协议。

083e52c0b2d820e815e74369d507a126.png

3、PHY 芯片配置

①SMI串行管理接口

  VSC8601 设备包含一个 IEEE 802.3 兼容的串行管理接口(SMI),其中 MDC 和 MDIO 可对该芯片进行控制。SMI 提供了访问权限设备控制和状态寄存器。控制 SMI 的寄存器集由 32 个 16 位寄存器组成,包括所有需要的 IEEE 指定寄存器。此外,通过设备寄存器 31 可以访问寄存器的附加寄存器。SMI 是一个同步串行接口,其配置方式和 IIC 类似,MDIO 引脚具有有双向数据,在 MDC 信号的上升边缘上采集。MDC 采集速度应在 0 MHz 到 25mhz。数据通过 SMI 使用具有可选和任意长度前导的 32 位帧传输,下图(文档26页)显示了用于读操作和写操作的SMI框架格式。

1f42435372e825e8daf238f886acf30e.png

  由图可以看到 PHY Address 有 A0 - A4 共 5 bit,故一个处理器最多可以挂载这样的 PHY 芯片 2^5=32 个。用户可通过配置 PHY Address 或 Resgister Address 将该芯片按需配置。

②配置引脚和设备功能表(文档64页)

47d9f2c155d137b50361b6e5df45102a.png

③相关引脚的功能,用于解释上表(文档65页)

619e88b3888424d3e2572f0e7b6636cd.png

④电阻对应 Pin 值表(文档66页)

2f3ffbbdb580ec1d4a65d12d509fd4bd.png

⑤PHY芯片配置

  结合上面的表和 PHY 芯片的板卡原理图,即可确定出 CMODE Pin 的具体值了。

69c07c2edc16eb3471b868d7d02cb236.png

dc1880b7ed2cafc850d9dc54bc7b0773.png

三、PHY芯片的初始化和RGMII协议

1、PHY 芯片初始化

  VSC8601 芯片上电后需要进行复位配置,该芯片复位信号需要在上电保持一段时间后才能将复位信号置为高,否则 PHY 芯片不会工作,其配置过程如图(文档79页)所示。PHY 的复位信号引脚和 FPGA 相连,当上电后至少经过 4ms 以上才可以将 PHY 芯片复位引脚置高,这样就完成 PHY 芯片初始化了,还是蛮简单的。

d2f0691625c7434df40104d34fbe5b5f.png

2、PHY芯片的RGMII协议

  RGMII协议是PHY芯片和FPGA芯片之间的传输协议。RGMII 是 Reduced GMII(吉比特介质独立接口)。RGMII 均采用 4bit 数据接口,工作时钟 125MHz,并且在上升沿和下降沿同时传输数据,数据传输速率可以达到4*125*2=1000Mbps。同时兼容 MII 所规定的 10/100 Mbps 工作方式,支持传输速率:10M/100M/1000Mb/s ,其对应 clk 信号分别为:2.5MHz/25MHz/125MHz。RGMII 数据结构符合 IEEE 以太网标准,接口定义见 IEEE 802.3-2000。

  PHY 芯片与 FPGA 数据交互的端口有:发射端、接收端及HPY 的复位端口。发射端和接收端分别有 6 个引脚与 FPGA 相连,分别有一个时钟引脚、一个数据控制使能引脚,四个数据引脚。而我们想要达到 1.0Gb/s 带宽,HPY 对应时钟为 125MHZ,若为单沿采样那么带宽为 125M/s*4=500Mb/s,及达不到 1.0Gb/s,在原有的硬件基础上想要使用 125M 时钟产生带宽为 1.0Gb/b,那么需要双沿控制采样数据,及时钟上升沿 4bit 下降沿 4bit,带宽为125M/s*4*2=1000Mb/s=1.0 Gb/b。该协议为 RGMII(Reduced Gigabit Media Independent Interface)。RGMII 均采用 4 位数据接口,工作时钟 125MHz,并且在上升沿和下降沿同时传输数据,因此传输速率可达 1000Mbps。RGMII 无补偿工作时序如图(文档81页)所示。

caa7459e6c2bb58841c9d2afcec5a490.png

  观察图中 RX_CLK(at Transmitter) 和 RX_CLK(at Receiver),RX_CLK(at Transmitter)为 HPY 时钟,在上升沿时对应输出数据为 RXD[3:0] 和 RXDV,在下降沿时对应出数据为 RXD[7:4] 和 RXERR,及一个时钟周期对应 8bit 数、一个有效使能和一个错误信号。RX_CLK(at Receiver) 是在 RX_CLK(at Transmitter) 的基础上相移 90°左右而得,这样采集到的数据会更加稳定。

四、IDDR原语

  PHY 传输的数据为双沿数据,通常 FPGA 处理的为单沿数据,所以FPGA 接收到图像信息包后首先需要使用 IDDR 原语将双沿数据转换为单沿数据。通常情况下 FPGA 处理数据使用的时钟为晶振产生的时钟(FPGA 时钟),而 PHY 传输来的数据以及经过IDDR 原语后转换为单沿的数据都是和 PHY 的时钟同步,所以我们如果想使用 FPGA 时钟作为后续的图像数据处理必须要进行跨时钟域,将 PHY 时钟同步的数据转换为 FPGA 时钟同步的数据。此处双沿转单沿数据采用 Input DDR 原语,简称 IDDR,将双沿 4bit 数据转换为单沿 8bit 数据。

  IDDR原语参考 7 Series FPGAs SelectIO Resources 文档和 Xilinx 7 Series FPGA Libraries Guide for HDL Designs 。

1、IDDR原语获取途径

a1899ebe46904fcc9258dd319cd77f66.png

2、IDDR原语接口和属性(文档111页)

  D 为数据输入,CE 为 IDDR 工作使能,C 为时钟,S 和 R 分别为置位和复位。

f608d4f85137e4b21e239c4295c9674e.png

f6c2b818afd0233cf37d0f738549ef35.png

07bd2c9470403faea1659b87403038ed.png

3、IDDR原语工作模式(文档110页)

  IDDR 有三种工作模式,模式配置如下图所示,分别为:OPPOSITE_EDGE、SAME_EDGE 、SAME_EDGE_PIPELINED。

163e03f72a34f8026fee37e6e8f560ac.png

d27da021cef10505ee9123afae64a196.png

2e615c740992a6941929d541d6877798.png

  通过对比三图差异,结合本项目平台选择模式:SAME_EDGE_PIPELINED。因为时钟上升沿和下降沿各对应一个数据,那么千兆以太网数据传输:0bit 和 4bit 为一组,1bit 和 5bit 为一组,2bit 和 6bit 为一组,3bit 和 7bit 为一组。

4、IDDR双沿数据转单沿数据使用方式

  根据硬件电路图,PHY 传输给 FPGA 有一条时钟线、一条使能线和四条数据线,其中使能线和数据线都为双沿数据。一条使能线包含了 DV 和 ERR,其中上升沿传输的是 DV 信号(即数据有效信号),下降沿传输的为 ERR 信号(即数据出错信号,通常情况下不考虑 ERR,ERR 是由硬件引起,和软件无关);四条数据线包含了一个始终 8bit 数据,其中上升沿传输的是 8bit 数据中的[3:0],下降沿传输的是 8bit 数据中的[7:4]。 我们采用 IDDR 原语将双沿 4bit 数据转换为 8bit 数据,同时将 8bit 数据对应有效 DV 信号提取。那么可以建立实现双沿转单沿功能的模块:iddr_ctrl,其中输入与输出关系图如下所示。PHY 芯片传输来的 phy_rxd 和 phy_rx_ctl 都和 PHY 的时钟同步。图中 phy_rx_clk_90 为输入的时钟信号是 PHY 时钟相移90度后形成的,目的是采集 phy_rx_rxd 和 phy_rx_ctl 更稳定,rx_data 和 rx_en 即为该模块输出的单沿 8bit 数据和对应的数据有效使能。

bb98881d278b7124fa40e052f68b111d.png

千兆以太网数据传输经过 IDDR 后 8bit 数据 rx_data 与使能 rx_en 对应关系如下图所示。

a81bd795b1a8596b3b5982073614b085.png

  通过图可以看出,IDDR 时钟上升沿采集的 phy_rx_crl 信号为 EN(DV),代表数据有效,下降沿采集的为 ERR,代表数据错误,本项目此处不考虑错误信号,最终从 IDDR 输出的 rx_en 信号正好和拼接完成后的 rx_data 数据对应,即可作为 8bit 数据有效使能。 

五、上板验证

  代码写好后注意一下,前面说过 PHY 芯片初始化必须满足当上电后至少经过 4ms 以上才可以将 PHY 芯片复位引脚置高,因此顶层必须设置一下 PHY 芯片的复位引脚,该信号通过延时 4ms 即可产生,有了这个信号,PHY 芯片才能工作起来。

//延时 4ms 后启动 phy_rst_nalways @(posedge sclk) begin    if(rst) begin        phy_rst_cnt <= 'd0;    end    else if(phy_rst_cnt[18]==1'b0) begin        phy_rst_cnt <= phy_rst_cnt + 1'b1;    endendassign phy_rst_n = phy_rst_cnt[18];

  这次实验较简单,看不到任何现象,可以先生成一个 ila,将 rx_data 和 rx_en 放进去以待观察,最后不要忘记绑定引脚哦。生成bit文件后下载到板卡上,板卡上的网口和电脑网口连接,点击电脑中的 以太网设置 --- 更改适配器选项,即可看到网卡正常工作起来。

8c1f91a4eae2640b333cb3f7ae59ada3.png

  查看 ila,将 rx_en 的上升沿作为触发信号,得到如下波形:

13b9cd131bcf89477213b10fec485b67.png

da289983ad7f532252d2f2eed1a8544f.png

  如图可以观察到,千兆以太网数据以包为单位发送,每次发送有一定的数据,并且在每次发送前都会先发送 7 个 0x55 和 1 个 0xd5 作为包的针头,如果是这样的波形,那么表明此次试验设计成功。

接收——包校验和数据筛选

前面我们实现了FPGA板卡接收以太网的数据,但是里面的数据比较乱,而且可能出现无效帧,即便是有效帧,也不是所有数据都是我们要的,必须对数据进行筛选。本篇博客详细记录一下以太网数据的校验和筛选。

一、数据发送

  我们用 Matlab 软件实现电脑向以太网发送数据,FPGA板卡接收数据。具体程序网上很多,就不贴了。

二、数据的校验和筛选

1、UDP以太网结构

  以太网的发送以包为单位,每个包的结构如下图所示。图中有帧首部、MAC首部、IP首部、UDP首部、用户数据、帧尾部等。后面我们将利用这些数据进行校验和筛选出我们需要的值。

e375350977835c50eb57c1151cb09535.png

2、包有效校验

  如上图蓝色部分即是我们的包有效校验区,包发送数据过来,而刚好蓝色位置的 5byte(40bit)数据和标准值一样,那么就可以认定该包为有效包。

always @(posedge sclk) begin    if(rst) begin        pkg_vld_value <= 40'd0;    end    else if(rx_en_cnt==31 || (rx_en_cnt>=42 && rx_en_cnt<=45)) begin //udp、port源、port目的        pkg_vld_value <= {pkg_vld_value[31:0],rx_data};    endendalways @(posedge sclk) begin    if(rst) begin        pkg_vld <= 1'b0;    end    else if(rx_en_fall && pkg_vld_value==40'h11_04d2_007b) begin        pkg_vld <= 1'b1;    end    else begin        pkg_vld <= 1'b0;    endend

3、CRC校验

  CRC校验是为了证明一个数据包是否出错,多数为 8 位或 32 位,本次采用 32 位的CRC校验,但是记住,只能用于验证数据是否出错,而不能对错误进行纠正。此外 CRC 校验是去掉了帧首部的 8 byte 数据后的校验。如下所示是32位的CRC校验模块,注意校验和解校验的电路默认初始状态都是 32‘hffffffff,即全1状态。千兆以太网的解校验结果为 32’hc704dd7b。可以用例化的方式对该模块进行使用,而实际 CRC 校验的科学原理则略微高深,此处不做讲解。

always @(posedge sclk) begin    if(rst) begin        crc32_value <= 32'hFFFFFFFF;    end    else if(crc_en) begin        crc32_value[ 0] <= c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 1] <= c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 2] <= c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 3] <= c[27]^d[ 4]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[ 4] <= c[28]^d[ 3]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 5] <= c[29]^d[ 2]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[1]^d[7];        crc32_value[ 6] <= c[30]^d[ 1]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[ 7] <= c[31]^d[ 0]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^d[ 7];        crc32_value[ 8] <= c[ 0]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^d[ 6]^c[24]^d[ 7];        crc32_value[ 9] <= c[ 1]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^d[ 6];        crc32_value[10] <= c[ 2]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^d[ 7];        crc32_value[11] <= c[ 3]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^d[ 6]^c[24]^d[ 7];        crc32_value[12] <= c[ 4]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[13] <= c[ 5]^c[30]^d[ 1]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[14] <= c[ 6]^c[31]^d[ 0]^c[30]^d[ 1]^c[28]^d[ 3]^c[27]^d[ 4]^c[26]^d[5];        crc32_value[15] <= c[ 7]^c[31]^d[ 0]^c[29]^d[ 2]^c[28]^d[ 3]^c[27]^d[ 4];        crc32_value[16] <= c[ 8]^c[29]^d[ 2]^c[28]^d[ 3]^c[24]^d[ 7];        crc32_value[17] <= c[ 9]^c[30]^d[ 1]^c[29]^d[ 2]^c[25]^d[ 6];        crc32_value[18] <= c[10]^c[31]^d[ 0]^c[30]^d[ 1]^c[26]^d[ 5];        crc32_value[19] <= c[11]^c[31]^d[ 0]^c[27]^d[ 4];        crc32_value[20] <= c[12]^c[28]^d[ 3];        crc32_value[21] <= c[13]^c[29]^d[ 2];        crc32_value[22] <= c[14]^c[24]^d[ 7];        crc32_value[23] <= c[15]^c[25]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[24] <= c[16]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[25] <= c[17]^c[27]^d[ 4]^c[26]^d[ 5];        crc32_value[26] <= c[18]^c[28]^d[ 3]^c[27]^d[ 4]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[27] <= c[19]^c[29]^d[ 2]^c[28]^d[ 3]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[28] <= c[20]^c[30]^d[ 1]^c[29]^d[ 2]^c[26]^d[ 5];        crc32_value[29] <= c[21]^c[31]^d[ 0]^c[30]^d[ 1]^c[27]^d[ 4];        crc32_value[30] <= c[22]^c[31]^d[ 0]^c[28]^d[ 3];        crc32_value[31] <= c[23]^c[29]^d[ 2];    end    else if(!crc_en) begin        crc32_value <= 32'hFFFFFFFF;    endend

4、数据的筛选

  由上图可知,只有中间红色部分的 “用户数据”才是我们真正需要的,因此最终输出结果还需要进行一番筛选,去头去尾即可,这也没什么难的。

三、代码设计

  上面已经贴了部分代码了,难道这里我要全贴代码吗?不,贴代码没有意义,重要的是懂内部的含义。我们以 rx_filter 来命名此模块,rx_filter 的输入是网口数据经过前一讲中转换后的 8bit 数据值和对应使能,输出则是经过校验和筛选后的数据和使能。

  上面说的 包有效校验  CRC校验,代码本身都不难,用个计数器数进来的数据使能,然后对那些关键节点进行比较即可。但是那头数据边来这头又要边校验,很可能时序出错,因此有必要建立一个 data_fifo 先缓存住过来的数据,然后进行包有效校验和 CRC 校验。data_fifo 的深度可以深一点,例如8192,这样就能容纳多帧了。校验完了后是需要判断是否丢弃该包的,因此还需要另一个 status_fifo 对校验信息进行存储,同时只要 status_fifo 由空变成不空了,说明该包校验信息写入了,即该包校验结束,那就直接设计 status_fifo 的读使能让信息数据出来,并用一个寄存器锁存住后面用。status_fifo 的读使能有了后,data_fifo 的读使能也要立马设计出来,免得新来的各种包不断进入 data_fifo,那不得撑爆了。注意 data_fifo 的读使能持续时间应该和进来时的数据使能一样长,所以前面计数时不能光计数了,还得把一个完整包的长度存起来。怎么存?直接将长度寄存住后 和 包有效校验、CRC校验拼接一起写给 status_fifo 就行了,后面 status_fifo 读出来这些数据就能为我们所用了。现在 data_fifo 不断的写数据读数据,status_fifo 也不断的写信息数据读信息数据,下一步我们就能利用读出来的信息数据,判断里面的的信息(即包有效校验和CRC校验)是不是真的OK,如果是真的OK,那就将 data_fifo 的读数据和读使能进行数据剔除,留下中间“用户数据”部分再传出去,如果不OK就不管了,让那个 data_fifo 的读使能和读数据继续工作,但我们不使用它,相当于丢弃了。示意图如下所示:

45c111cb5f156e63db51f95add447666.png

波形图如下所示,结合上面所说,理解了下面的波形图那本模块就没问题了。

四、波形

  申请一个 ila 观察数据,可以让 Matab 发送一段 0-255 的数据,查看我们的包有无问题,波形图如下所示:

c0625aef36faa40b608e69129bcba1ff.png

五、以太网 + DDR3 + HDMI 显示

  将千兆以太网的上一讲和本讲结合,替代掉之前 DDR3 工程中的串口发送模块,即可实现 以太网 + DDR3 + HDMI 显示了,尤其注意输出端口、时钟连线和引脚约束。

e55e35d4952b1b02a01039399de3b049.png

  OK,到此为止,本项目的一半已经完成啦!

发送——组建以太网心跳包

心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包。心跳包在GPRS通信和CDMA通信的应用方面使用非常广泛。数据网关会定时清理没有数据的路由,心跳包通常设定在30-40秒之间。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。本次设计中,心跳包时间间隔为1秒。

一、心跳包触发器

  设计一个 1 秒定时器,每隔一秒就产生一个心跳包触发脉冲,用于下一步心跳包的组建。

二、心跳包粗略框架

  本次以太网的心跳包结构如下所示:

b3e4930be23cbbe4406eb0da9e10cee3.png

  本次发送 64 个全为0的数据,当然这个数据是自定义的,因此心跳包总长度为118。此外目的/源 MAC 地址、目的/源 IP 地址、目的/源 port 等值可以用参数的方式先写好。利用刚刚设计的心跳包触发脉冲,我们就能组建这个心跳包了。表中黄色部分为后面需要校验的值,可以先填0进去,后面再覆盖掉这三处即可。代码也没什么说的,用参数和case语句即可。

  点击电脑 Win + R 键,输入 cmd,再出入 ipconfig -all,按回车键,即可看到自己电脑的 MAC 地址等参数。

658bd39a8ecb1ed4d1b01460aaf95f12.png

三、心跳包填充:IP校验和UDP校验

1、IP校验方法

  IP 校验就是把 IP 首部 20byte 按 2byte(即 16bit)分开后相加,结果如果大于 16’hffff,就将超出 16’hffff 的部分与相加结果的低 16 位相加,直到最终结果小于 16’hffff 为止。最后把小于 16’hffff 的结果取反作为 ip_checksum。高字节在前,低字节在后,替换掉前面在心跳包中填充的 0。

(1) 校验和字段清0

  假设有一段以太网包前面没有对 IP 校验和字段清0,而是赋了别的值,例如 IP 首部为:45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d,b5 2e 字段即为 IP 校验和字段,清0后数据就变成了:45 00 00 30 80 4c 40 00 80 06 00 00 d3 43 11 7b cb 51 15 3d。

(2) 反码求和

  4500+0030+804c+4000+8006+0000+d343+117b+cb51+153d = 34ace,将超过 16’hffff 的部分(即3)与低 16 位(即4ace)上,结果为:3+4ace=4ad1,因为 4ad1 小于 ffff,故作为 Ip_checksum 的反码。

(3) 取反得最终结果

  将 4ad1 取反得 b52e,这就是本包数据最终的 IP 校验和,再将 b52e 填充到对应位置即可。

2、UDP校验方法

(1) 校验方法

  UDP_checksum 计算稍微复杂一点,需要加入 IP 伪头部,将 IP 伪头部、UDP 首部的 8 个 bytes 和数据包部分按 2byte(即 16bit) 分开后相加,结果如果大于16’hffff,就将超出 16’hffff 的部分与相加结果的低 16 位相加,直到最终结果小于 16’hffff 为止。最后把小于 16’hffff 的结果取反作为 UDP_checksum。高字节在前,低字节在后,替换掉前面在心跳包中填充的0。

(2) UDP结构

  UDP_checksum 的组成如图所示。

5cee217890314442a04009d8b535bd9a.png

  可以看到,IP 伪头部包含了 IP 源地址,IP 目的地址,一个字节的 0,协议号和 UDP_len ,在前面做的千兆以太网图像传输项目中 IP 源地址,IP 目的地址,协议号都是固定的,而通过上一篇博客设计的 UDP_len 为:UDP首部 8byte + 数据长度 64byte = 8+64='h0048。在设计的时候可以先单独将 IP 伪头部计算出来。

3、计算的时序安排

  ip_checksum 和 udp_checksum 计算完成,该数据填充的位置已经经过,那么就没办法将数据填充到原来填充 0 的位置了,但我们想要将其组成完整的以太网包,这一步是不可避免的,那么我们该怎么解决呢?我们可以考虑一下,建立一个足够大的 RAM ,在计算 ip_checksum 和  udp_checksum 的同时将前面心跳包数据存储到 RAM 中,当这一包数据全部计算完再将 RAM 中的数据读出。当读出到要填充 ip_checksum 和 udp_checksum 的位置时,将计算出的两个值取反后填充到对应位置即可。

bce4586b26f25e218ab76d9689f2b580.png

4、时序图

6709aeda5b4be22c7fbbdcf34819d5b3.png

四、心跳包填充:CRC校验

1、CRC校验范围

  CRC校验另起一段,是因为要先算完前面的 ip 校验值和 UDP 校验值才行。CRC校验时必须先去除帧头(即前面的7个55和1个d5),还得去掉帧尾(即后面4个数据,前面我们直接在这填了0)。当 CRC 校验值计算完成后,即可将其填充至数据的末尾 4 位即可。

2、CRC校验方法

(1)CRC校验初始值和空闲值都设置为 32‘hffffffff,即全 1 状态。

(2)计算结果需高低位对调,因为我们计算时是先从高位输入的,所以最后高低位需要对调。

(3)最后结果取反,高低位对调后的结果再取反,即可得到 CRC 的校验值。

3、CRC校验部分代码

always @(posedge sclk) begin    if(rst) begin        crc32_value <= 32'hFFFFFFFF;    end    else if(crc_en) begin        crc32_value[ 0] <= c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 1] <= c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 2] <= c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 3] <= c[27]^d[ 4]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[ 4] <= c[28]^d[ 3]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[ 5] <= c[29]^d[ 2]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[1]^d[7];        crc32_value[ 6] <= c[30]^d[ 1]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[ 7] <= c[31]^d[ 0]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^d[ 7];        crc32_value[ 8] <= c[ 0]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^d[ 6]^c[24]^d[ 7];        crc32_value[ 9] <= c[ 1]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^d[ 6];        crc32_value[10] <= c[ 2]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^d[ 7];        crc32_value[11] <= c[ 3]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^d[ 6]^c[24]^d[ 7];        crc32_value[12] <= c[ 4]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[13] <= c[ 5]^c[30]^d[ 1]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[14] <= c[ 6]^c[31]^d[ 0]^c[30]^d[ 1]^c[28]^d[ 3]^c[27]^d[ 4]^c[26]^d[5];        crc32_value[15] <= c[ 7]^c[31]^d[ 0]^c[29]^d[ 2]^c[28]^d[ 3]^c[27]^d[ 4];        crc32_value[16] <= c[ 8]^c[29]^d[ 2]^c[28]^d[ 3]^c[24]^d[ 7];        crc32_value[17] <= c[ 9]^c[30]^d[ 1]^c[29]^d[ 2]^c[25]^d[ 6];        crc32_value[18] <= c[10]^c[31]^d[ 0]^c[30]^d[ 1]^c[26]^d[ 5];        crc32_value[19] <= c[11]^c[31]^d[ 0]^c[27]^d[ 4];        crc32_value[20] <= c[12]^c[28]^d[ 3];        crc32_value[21] <= c[13]^c[29]^d[ 2];        crc32_value[22] <= c[14]^c[24]^d[ 7];        crc32_value[23] <= c[15]^c[25]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[24] <= c[16]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[25] <= c[17]^c[27]^d[ 4]^c[26]^d[ 5];        crc32_value[26] <= c[18]^c[28]^d[ 3]^c[27]^d[ 4]^c[24]^c[30]^d[ 1]^d[ 7];        crc32_value[27] <= c[19]^c[29]^d[ 2]^c[28]^d[ 3]^c[25]^c[31]^d[ 0]^d[ 6];        crc32_value[28] <= c[20]^c[30]^d[ 1]^c[29]^d[ 2]^c[26]^d[ 5];        crc32_value[29] <= c[21]^c[31]^d[ 0]^c[30]^d[ 1]^c[27]^d[ 4];        crc32_value[30] <= c[22]^c[31]^d[ 0]^c[28]^d[ 3];        crc32_value[31] <= c[23]^c[29]^d[ 2];    endend

4、时序图

  由于这次 CRC 校验值是在末尾,因此不需要用到 RAM 也来得及填充。

08d125617b7b707a55d951e1d37afc66.png

  至此,我们组建了以太网发送的心跳包,下一步就可以发送了。

发送——ODDR原语和Wireshark抓包工具

一、ODDR原语

  FPGA 传输的数据为单沿数据,而 PHY 传输的数据为双沿数据,所以FPGA 发送心跳包的最后需要使用 ODDR 原语将单沿数据转换为双沿数据。通常情况下 FPGA 处理数据使用的时钟为晶振产生的时钟(FPGA 时钟),而 FPGA 传输来的数据经过ODDR 原语后转换为双沿的数据都是和 PHY 的时钟同步,所以我们如果想把 FPGA 时钟的数据传给 PHY 芯片则需要进行跨时钟域,将 FPGA 时钟同步的数据转换为 PHY 时钟同步的数据。此处单沿转双沿数据采用 Output DDR 原语,简称 ODDR,将单沿 8bit 数据转换为双沿 4bit 数据。

  ODDR原语参考 7 Series FPGAs SelectIO Resources 文档和 Xilinx 7 Series FPGA Libraries Guide for HDL Designs 。

 1、ODDR原语获取途径

951290c64e425dd40cc174a3997af867.png

2、ODDR原语接口和属性(文档129页)

  D1 和 D2 为数据输入,CE 为 ODDR 工作使能,C 为时钟,S 和 R 分别为置位和复位,Q为输出。

41035cca8d7a13a52f51afc4e643f9d9.png

e597e918083c3a64b2efebde6fc779b7.png

8be1b2301ac31fade660fde2d5adba3e.png

3、ODDR原语工作模式(文档126页)

  ODDR 有两种工作模式,模式配置如下图所示,分别为:OPPOSITE_EDGE、SAME_EDGE。

24e8e9d7e8ac6b84905117753b67719a.png

94f67635816c8e09d10419d429bddccb.png

  OPPOSITE_EDGE 模式下,双沿都用来捕捉数据,其中上升沿捕捉低位数据 D1,下降沿捕捉高位数据D2,输出 Q 在上升沿传输 D1,下降沿传输D2。SAME_EDGE 模式下,只采用上升沿来捕捉低位数据 D1 和高位数据 D2,然后在输出 Q 的下降传输 D1,上升沿传输 D2。此次传输我们采用 SAME_EDGE 模式。

4、ODDR双沿数据转单沿数据使用方式

  根据硬件电路图,PHY 传输给 FPGA 有一条时钟线、一条使能线和四条数据线,其中使能线和数据线都为双沿数据。一条使能线包含了 DV 和 ERR,其中上升沿传输的是 DV 信号(即数据有效信号),下降沿传输的为 ERR 信号(即数据出错信号,通常情况下不考虑 ERR,ERR 是由硬件引起,和软件无关);四条数据线包含了一个始终 8bit 数据,其中上升沿传输的是 8bit 数据中的[3:0],下降沿传输的是 8bit 数据中的[7:4]。 我们采用 ODDR 原语将单沿 8bit 数据转换为双沿 4bit 数据,同时将 4bit 数据对应有效 DV 信号提取。那么可以建立实现单沿转双沿功能的模块:oddr_ctrl,其中输入与输出关系图如下所示。最后 PHY 芯片传输出去的 phy_txd 和 phy_tx_ctl 都和 PHY 的时钟 phy_tx_clk 同步。图中 sclk_90 为输入的时钟信号是 FPGA 时钟相移90度后形成的,目的是转换 phy_tx_rxd 和 phy_tx_ctl 更稳定,tx_dat 和 tx_en 为该模块输入的单沿 8bit 数据和对应的数据有效使能。

73c8e50909513727a1f1ace9414f29ba.png

二、以太网复位信号

  前面博客《千兆以太网(1)》中说过 PHY 芯片初始化必须满足当上电后至少经过 4ms 以上才可以将 PHY 芯片复位引脚置高,因此输出端口不要忘记了 PHY 芯片的复位信号 phy_rst_n,该信号通过延时 4ms 即可产生,有了这个信号,PHY 芯片才能工作起来。

//延时 4ms 后启动 phy_rst_nalways @(posedge clk50) begin    if(rst) begin        phy_rst_cnt <= 'd0;    end    else if(phy_rst_cnt[18]==1'b0) begin        phy_rst_cnt <= phy_rst_cnt + 1'b1;    endendassign phy_rst_n = phy_rst_cnt[18];

三、Wireshark 抓包

  上板后打开点击电脑中的以太网设置 --- 更改适配器选项,即可看到网卡正常工作起来。

59b95dc40014da8f2d399c0f814cdbee.png

  打开 Wireshark 软件,可以看到 以太网 的波形正在变化。

411ada4db31fa6b494932cdfc34004dc.png

  双击进去,点击 编辑 --- 首选项,点击 NTP 即可看到我们的端口编号,本次设计的是 UDP,为什么看 NTP ?因为 NTP 其实包含了 UDP,可以看成是 UDP 的一种。

21416d6bb69c754d5b87a18a2745ce26.png

  点击 UDP 和 IPv4,将 UDP校验和 IP校验 打勾,待会就看得到是否校验成功了。

e1df10011f3730a65a14ff6785246915.png

78297fd11f5c66395b418d92c1d2f84a.png

  接着我们重新捕获以太网,可以看到很多蓝色的部分表示我们传输的心跳包,黑色则的不是我们的,它的 Source 和 Destination 都和我们设置的不同,那就不管它了。点击某个蓝色条纹,下方就出现了相关信息,这些信息是去除了包头(7个55、1个d5)后的,因此字节显示 106 ,和我们设计相吻合。此外可以看到头部校验分别显示 [correct] 和 [Good],表明我们这次的校验也是正确的。

879c41e7e156b4294ef5d18c0d677483.png

  上面的现象表明我们此次的千兆以太网发送项目成功实现,完结撒花!!如果您觉得文章对您有用,不妨点个【赞】【在看】支持一下,谢谢!

转自 ZYNQ,如侵删。

  • 16
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值