UDP网口(2)逻辑组包(上)

前言

本文实现概述中提到的方案一,即使用FPGA组包实现MAC层及以上的层+外部的PHY芯片实现物理层。这种方案FPGA只需要按照与PHY芯片一致的通信接口,按照不同层的通信要求传输数据即可。这类通信接口常见的有RGMII、GMII等(二者都适配)千兆以太网口。这种方案灵活性高,可根据自己的需求定制满足特定场景需求,也不需要购买额外的IP核,成本相对较低。但需要对MAC层的协议和实现有深入的了解,相对的开发难度大,调试成本较高。

目录

前言

1.简介

2.硬件原理

3.PHY芯片的配置

4.RGMII接口介绍

5.ARP原理

6.CRC校验代码

7.ip校验和的计算

8.传送门


1.简介

本文将要创建的应用是上位机发送数据,下位机接收到数据之后按照指定端口返回。应用搭建的场景是,上位机发送数据,首先发起arp请求,随后下位机给出arp应答响应,上位机get到下位机的mac地址之后,将发送框中的数据打包成udp数据包下发到下位机中,下位机收到udp包,将有效数据保存到fifo中,然后在接收完毕之后,随即发起一次长度与接收长度一致的数据发送操作,在操作无误的情况下,上位机将收到下发的数据。

2.硬件原理

如下图所示,典型应用的连接是,PC机通过网线与FPGA板卡上的与phy芯片相连的RJ45接口相连,PHY芯片用来实现OSI模型物理层,主要功能是用来发送和接收以太网的数据帧,我使用的是realtek公司的RTL8211芯片,phy芯片与FPGA之间可以用多种接口相连,例如MII,GMII,RGMII,具体的接口要根据芯片的支持、系统设计综合考量来决定,三者之间逻辑上实现的差别不大,此处我用RGMII。可以清晰的看到,链路层网络层传输层数据的打包都要由FPGA实现,物理的传输通过PHY芯片实现,FPGA只需关注RGMII接口的时序即可,同时也要关注PHY芯片的配置。

3.PHY芯片的配置

实现以太网PHY功能的芯片有很多,不同的芯片的数据接口,支持的特性,配置都略有不同,但相差不大。通常可以通过MDIO接口对PHY芯片内部的寄存器进行配置,MDIO 接口也称为 SMI 接口(Serial Management Interface,串行管理接口)。它是由一个时钟信号线和一根数据线组成,与IIC接口的时序相差不大。RTL8211这款芯片可以不做任何配置直接使用,因此对这款芯片的配置、寄存器的用法未做深入研究。通常PHY芯片都有两种工作模式,延时模式和正常模式。其区别如下:

RGMII接口发送端口,左边是正常模式,数据在TCK的中心位置对齐,TCK周期为8ns,即数据提前2ns;右边是延时模式,数据与TCK对齐。对于TX_CTL信号也是一样的。

 RGMII接口接收端口,左边是正常模式随路时钟与数据边沿对齐,右边是延时模式,数据先于随路时钟2ns处在数据中心位置到来。由此可以看到,采用正常模式接收数据容易出现亚稳态现象。

在芯片手册中,提到了设置发送和接收延时模式的发放,即将TXDLY和RXDLY端口做上拉处理。原文的描述为Pull up to add 2ns delay to TXC for TXD latching;Pull up to add 2ns delay to RXC for RXD latching。 而在我的硬件环境中, pin24 TXDLY和pin25 RXDLY均未做上拉处理,因此芯片工作在正常模式。结合上面的分析,处于正常模式的芯片应该在接收时进行数据对齐。在发送模式时应该让发送的随路时钟与数据和数据有效信号之间有90°的时钟延迟,以此让芯片满足接收时序。

4.RGMII接口介绍

RGMII(Reduced Gigabit Medium Independent Interface)即吉比特介质独立接口,数据位宽为 4 位,在 1000Mbps 传输速率下,时钟频率为 125Mhz,在时钟的上下沿同时采样数据。在 100Mbps 和 10Mbps 通信速率下,为单个时钟沿采样。RGMII 接口的优势是同时适用于10M/100M/1000Mbps 通信速率,同时占用的引脚数较少。但 RGMII 接口也有其缺点,就是在 PCB 布线时需要尽可能对时钟、控制和数据线进行等长处理,且时序约束相对也更为严格。RGMII接口如下图所示,(关于RGMII接口的时序在2.1节所示的工作模式图中可直观看到)

  • ETH_RXC:接收数据参考时钟,1000Mbps 速率下,时钟频率为 125MHz,时钟为上下沿同时采样;100Mbps 速率下,时钟频率为 25MHz;10Mbps 速率下,时钟频率为 2.5MHz,ETH_RXC 由 PHY 侧提供。由此可以看出数据是双沿DDR模式发送的。
  • ETH_RXCTL(ETH_RX_DV):接收数据控制信号。
  • ETH_RXD:四位并行的接收数据线。
  • ETH_TXC:发送参考时钟,1000Mbps 速率下,时钟频率为 125MHz,时钟为上下沿同时采样;100Mbps速率下,时钟频率为 25MHz;10Mbps 速率下,时钟频率为 2.5MHz,ETH_TXC 由 MAC 侧提供。
  • ETH_TXCTL(ETH_TXEN):发送数据控制信号。
  • ETH_TXD:四位并行的发送数据线。
  • ETH_RESET_N:芯片复位信号,低电平有效。
  • ETH_MDC:数据管理时钟(Management Data Clock),该引脚对 ETH_MDIO 信号提供了一个同步的时钟。
  • ETH_MDIO:数据输入/输出管理(Management Data Input/Output),该引脚提供了一个双向信号用于传递管理信息。ETH_MDC和ETH_MDIO信号共同组成了MDIO接口,用于配置PHY芯片的寄存器。通常芯片的默认配置可使芯片正常工作。

5.ARP原理

地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。ARP 协议的基本功能是通过目的设备的 IP 地址,查询目的设备的 MAC 地址,以保证通信的顺利进行。MAC 地址在网络中表示网卡的 ID,每个网卡都需要并有且仅有一个 MAC 地址。在获取到目的 MAC 地址之后,将目的 MAC 地址更新至 ARP 缓存表中,称为 ARP 映射,下次通信时,可以直接从 ARP 缓存表中获取,而不用重新通过 ARP 获取 MAC 地址。但一般 ARP 缓存表会有过期时间,过期后需要重新通过 ARP协议进行获取。ARP 协议分为 ARP 请求和 ARP 应答,源主机发起查询目的 MAC 地址的报文称为 ARP 请求目的主机响应源主机并发送包含本地 MAC 地址的报文称为 ARP 应答。当主机需要找出这个网络中的另一个主机的物理地址时,它就可以发送一个 ARP 请求报文,这个报文包含了发送方的 MAC 地址和 IP 地址以及接收方的 IP 地址。因为发送方不知道接收方的物理地址,所以这个查询分组会在网络层中进行广播,即 ARP 请求时发送的接收方物理地址为广播地址,用48’hff_ff_ff_ff_ff_ff 表示。

关于ARP数据包的格式如下图所示,关于各字段的含义以及如何填充,网上资源丰富。图一为以太网帧格式,图二为ARP数据包格式,图三为具体的ARP字段。

6.CRC校验代码

生成的代码(有生成工具)里面仅为一个计算 CRC32 的一个函数 function。CRC 的计算与除了与生成多项式相关外,还与 CRC 的初始值,输入输出数据是否位反向以及计算结果是否取反等操作相关。上面生成的 CRC 计算的 Verilog 代码仅仅是与多项式有关的计算过程,未涉及到初始值等因素,所以要实现符合以太网要求的CRC32 的完整计算需要添加一些计算操作。例如, 算法开始时,CRC 的初始化预置值(十六进制表示);待测数据的每个字节是否按位反转;在计算后之后,异或输出之前,整个数据是否按位反转;计算结果与此参数异或后得到最终的 CRC 值;等等,这些规则都应与以太网的校验规则一致才行。调整好的代码如下所示,这个代码输入8bit数据和数据同步信号,在一个周期后才能出现32bit的crc校验值。

module crc32_d8
(
  input           clk         ,
  input           reset_n     ,

  input    [7:0]  data        ,
  input           crc_init    ,
  input           crc_en      ,
  output   [31:0] crc_result
);
wire   [7:0]   data_i;
reg    [31:0]  crc_result_o;
assign data_i = {data[ 0],data[ 1],data[ 2],data[ 3], data[ 4],data[ 5],data[ 6],data[ 7]};
assign crc_result = ~{crc_result_o[00],crc_result_o[01],crc_result_o[02],crc_result_o[03],crc_result_o[04],crc_result_o[05],crc_result_o[06],crc_result_o[07],
                      crc_result_o[08],crc_result_o[09],crc_result_o[10],crc_result_o[11],crc_result_o[12],crc_result_o[13],crc_result_o[14],crc_result_o[15],
                      crc_result_o[16],crc_result_o[17],crc_result_o[18],crc_result_o[19],crc_result_o[20],crc_result_o[21],crc_result_o[22],crc_result_o[23],
                      crc_result_o[24],crc_result_o[25],crc_result_o[26],crc_result_o[27],crc_result_o[28],crc_result_o[29],crc_result_o[30],crc_result_o[31]};
always @(posedge clk or negedge reset_n)
begin
  if(!reset_n)
    crc_result_o <= 32'hffff_ffff;
  else if(crc_init)
    crc_result_o <= 32'hffff_ffff;
  else if(crc_en)
    crc_result_o <= nextCRC32_D8( data_i, crc_result_o);
  else
    crc_result_o <= crc_result_o;
end
  // polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
  // data width: 8
  // convention: the first serial bit is D[7]
  function [31:0] nextCRC32_D8;
    input [7:0] Data;
    input [31:0] crc;
    reg [7:0] d;
    reg [31:0] c;
    reg [31:0] newcrc;
  begin
    d = Data;
    c = crc;
    newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];
    newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];
    newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31];
    newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];
    newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];
    newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31];
    newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31];
    newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31];
    newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28];
    newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29];
    newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29];
    newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];
    newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30];
    newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31];
    newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31];
    newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31];
    newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29];
    newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30];
    newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31];
    newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31];
    newcrc[20] = d[4] ^ c[12] ^ c[28];
    newcrc[21] = d[5] ^ c[13] ^ c[29];
    newcrc[22] = d[0] ^ c[14] ^ c[24];
    newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30];
    newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31];
    newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];
    newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30];
    newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31];
    newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30];
    newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31];
    newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31];
    newcrc[31] = d[5] ^ c[23] ^ c[29];
    nextCRC32_D8 = newcrc;
  end
  endfunction
endmodule

7.ip校验和的计算

UDP的字段中含有UDP校验和,但在大多数使用场景中接收端并不检测 UDP 校验和,因此这里不做过多介绍。16 位ip首部校验和(Header Checksum),该字段只校验ip数据报的首部,不包含数据部分,校验 IP 数据报头部是否被破坏、篡改和丢失等。其计算步骤如下:①将 16 位检验和字段置为 0,然后将 IP 首部按照 16 位分成多个单元;②对各个单元采用反码加法运算(即高位溢出位会加到低位,通常的补码运算是直接丢掉溢出的高位);③此时仍然可能出现进位的情况,将得到的和再次分成高 16 位和低 16 位进行累加;④最后将得到的和的反码填入校验和字段。注意,这种校验方法称为16bit累加和校验算法,在自定义协议实现的时候常常用到,可以对照代码理解记忆应用。

8.传送门

网口调试工具wireshark

网口调试助手netassist

手动ARP应答工程

UDP工程链接

我的主页 

FPGA通信接口汇总导航

UDP网口(1)概述​​​​​​​
                                                                         END                                                                         


🔈文章原创,首发于CSDN论坛。 
🔈欢迎点赞❤❤收藏⭐⭐打赏💴💴!
🔈欢迎评论区或私信指出错误❌,提出宝贵意见或疑问❓。


  • 1
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FPGA小油条

原创不易,请多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值