UDP 网络通信
FPGA的工程应用离不开通信功能,当前电子学场景泛用的通信方式为以太网通信,大规模的工程化项目基本离不开组网场景。因此本文从以太网为引,展开介绍如何通过FPGA纯逻辑实现网络通信。
以太网是一种传输规则,收发双方必须遵循这些规则才能使数据有效传输。OSI(Open System Interconnection)七层模型和 TCP/IP 四层模型是常用的数据传输模型,其各个子层的功能如下表所示。

数据从上至下逐级封装,加入每层的头部信息,在物理层转换为比特流发送;接收端使用逆向顺序把数据逐级解封装,发送给应用层。
FPGA 的 UDP 通信只做到了传输层,物理层一般由外部 PHY 芯片提供,没有应用层、表示层和会话层。
物理层
以太网物理层是 OSI 模型中的第一层,它负责数据的编码、解码、数模转换和数据在物理介质上的传输等。在 10Mbps、100Mbps 和 1Gbps 速率的以太网中,通常采用 RJ45 网口和双绞线作为物理介质传输数据,RJ45连接器是一种 8 针连接器,提供 4 对提供信号输出。在 10Mbps./100Mbps 速率下,使用了其中 4 根线,在 1Gbps速率下使用了全部 8 根线。千兆以太网传输的数据由外部 PHY 芯片提供编解码和数模转换功能。千兆以太网的数据链路层和物理层之间一般采用RGMII 接口连接,其接口连接示意图如下:

这一层一般为FPGA芯片与外部PHY芯片的交接界面,这里提供开发板原理图的PHY芯片引脚作为参考。对应的信号线均会给到FPGA。

RGMII接口采用双沿采样的方案,相比 GMII 接口减少了 I/O 数量,从而减小了 PCB 的面积和布线难度,降低了成 本。RGMII 接口的控制信号被多路复用,CTL 信号复用了 ER 和 EN 信号,上升沿传输 EN 信号,下降沿传输 EN 信号与 ER 信号的异或结果。
万兆以太网的内部逻辑则通过 XGMII 接口与 FPGA 芯片内部的SERDES 连接。XGMII 接口的数据位宽为 32 位,SERDES 提供 PMA 和 PCS 子层,其结构包括锁相环、串并转换器、预加重电路、均衡器、时钟恢复电路等,万兆网的数据经过SERDES 进行 64/66B 的编码,通过 10.3125Gbps 的速率发出,经过光模块进行光电转换后,采用光纤作为物理介质进行传输。
本文着重介绍千兆以太网的 UDP 协议栈实现。
RGMII与XGMII*
RGMII 是 FPGA 中 MAC 与千兆 PHY 之间的低引脚数并行接口,通过 4 bit DDR + 125 MHz 实现 1Gbps;而XGMII 是高速以太网 MAC 与 PCS 之间的宽并行接口,常见于 10GbE,通过宽数据总线承载数据和控制字符,再由 PCS/SerDes 转换成高速串行信号。
如下给出两种不同协议的典型FPGA设计架构。


**RGMII:**使用 4 bit 数据总线 + DDR,在一个时钟周期的上升沿和下降沿各传输一次数据;
125 MHz × 4 bit × 2 edges = 1 Gbps;
**XGMII:**结合接口版本和 IP 架构来看,尤其是 FPGA 10G Ethernet IP 中常见的是 64-bit XGMII @ 156.25 MHz;
64 × 156.25 MHz ≈ 10 Gbps;
|-------------|---------------|-------------------|
| 项目 | RGMII | XGMII |
| 典型速率 | 10/100/1000M | 10G |
| 典型连接 | MAC ↔ PHY | MAC ↔ PCS |
| 数据宽度 | 4 bit DDR | 常见64 bit SDR |
| 千兆时钟 | 125 MHz | 156.25 MHz |
| 是否使用控制信息 | 有 | 有 |
| 典型 FPGA IO | 普通IO | GT + PCS相关逻辑 |
| 常见物理接口 | RJ45 | SFP+ |
| 是否需要 SERDES | PHY内部完成 | FPGA GT/PCS完成 |
| 设计难点 | RGMII skew | PCS/GT/64b66b/CDR |
本次UDP工程使用开发板外部RJ45接口进行1Gbps速率设计,因此对RGMII的传输时序进行具体展开:
RGMII_TX:


一个 8 bit 数据:Data = D7:0;通过:TXD3:0会分两次发送:
TXC ↑ → D3:0
TXC ↓ → D7:4
例如:Data = 8'b1010_0110
上升沿:
TXD = 0110
下降沿:
TXD = 1010
由于采用了DDR编码,RGMII 中的TX_CTL可以同时承担:TX_EN 及 TX_ER
TXC ↑:
TX_CTL = TX_EN
TXC ↓:
TX_CTL = TX_EN XOR TX_ER
RGMII_RX:
RXC ↑ → RXD3:0 = 低4位
RXC ↓ → RXD3:0 = 高4位
在上下沿接受数据后重新组合为:RX_DATA7:0
由于DDR在时钟上下沿均会对数据信号进行采样,在实际 FPGA 设计中,RGMII 最容易出问题的地方不是数据宽度,而是Clock 和 Data 的相位关系。因为 FPGA 和 PHY 之间存在:PCB 走线延迟、FPGA IO 延迟、PHY IO 延迟、时钟偏移、setup/hold 时间等不确定因素。
如果数据变化太靠近时钟边沿,就可能产生:setup violation;hold violation
进而导致:偶发 CRC 错误;丢包;UDP 数据异常;TCP 重传;网络不稳定
因此 RGMII 通常需要约束好 clock skew / clock delay。
以太网帧格式
数据在以太网中传输时需逐层封装或拆解,以添加或过滤上各层协议对应的首部。UDP 数据帧的结构和封装顺序如图所示,用户数据经过 UDP 层、IP 层、MAC 层,分别添加了 8 字节 UDP 头部、20 字节 IP 头部、22 字节 MAC头部和 4 字节 CRC 校验结果,再经过 PHY 层传出完整的一帧数据。

在了解了物理层后,继续向上介绍数据链路层。
数据链路层
以太网 MAC 层在网络通信中起着至关重要的作用,它负责数据的封装与拆包、传输控制、错误检测与纠正功能,确保数据在网络中正确传输和有效处理。MAC 数据帧格式如下图所示,其包头包含前导码、SFD、目的 MAC 地址、源 MAC 地址、类型/长度,最后 4 个字节为 CRC 校验位。下图为典型的 Ethernet II Frame格式。

****前导码(7Byte):****用于调整时钟使收发节点的时钟同步,接收端通过识别前导码来确定数据帧的边界,以确保接收端能够准确地提取出完整的数据帧。内容为连续 7 字节的 8'h55。
帧起始定界符 SFD(1Byte):用于区分前导段和数据段,确保后续数据的正确解析。内容为 8'hd5。
DST/SRC ****MAC 地址(6Byte):****MAC 地址由 48bit 数据组成,它是网卡的物理地址,一般固化在网卡的 ROM 中。在以太网传输的最底层就是根据 MAC 地址来发送数据,同一个网络里不能有两个相同的 MAC 地址。MAC 地址的前 3个字节是组织唯一标识符,用于标识网络硬件制造商,后 3 个字节是制造商为网卡分配的序列号。
类型/长度(2Byte):该区域可以用来表示 MAC 数据包下一层的类型,也可以用来描述 MAC 数据包数据段的长度。该值小于 1536 表示长度,大于 1536 表示类型,IPv4 协议对应的数值为 0x0800,ARP 协议为 0x0806。
****FCS(4Byte):****FCS(错误检测码)是用于检测数据帧在传输过程中是否发生错误的一个字段,它通过 CRC(循环冗余校验)算法计算得出,并附加在数据帧的尾部。发送数据时,会根据数据内容生成简短的校验和,并将其与数据一起发送。接收数据时,将再次生成的校验和并将其与发送的校验和进行比较。如果二者相等,则数据没有损坏。
每一帧传输完成后,都必须等待 96bit 数据传输的时间,即最小帧间隔(IFG),才可以进行下一次以太网帧的传输。由于以太网每个时钟周期发送 8bit 数据,所以最小帧间隔为 12 个时钟周期。
PAUSE流量控制帧*
在任何通信形式的两端,发送端的发送速率如果大于接受端的接受处理速率,都会导致通信异常。因此在通信架构下设计反压机制十分重要,在以太网下也存在这样的设计,即流量控制帧。

目的 MAC(6Byte):PAUSE 帧的目的为保留的组播地址,其值为固定的 48'h01_80_c2_00_00_01。
TYPE(2Byte):固定值为 16'h8808,表示帧类型为 MAC 控制帧。
OPCODE(2Byte):操作码,值为 16'h0001,表示 MAC 控制帧中的 PAUSE 帧。
TIME(2Byte):时间参数,它的值表示以太网以当前速率传输 512bit 数据的时间,接收方实际暂停的时间为该字段数值左移 6 位得到的值乘时钟周期。
补充数据:有效信息后补 0,补齐 MAC 帧所需要的最小数据数量。
PAUSE帧仅用于全双工以太网链路。在半双工模式下,流量控制通过强制碰撞等机制实现。同时PAUSE帧作为链路层协议,不会被路由器转发,只在直连设备间生效。标准PAUSE帧会暂停链路上的所有流量,无论其优先级高低。
Tri Mode Ethernet IP
对于数据链路层这种通用性极强的FPGA设计,Xilinx也提供了封装好的IP。

网络层
MAC 数据帧经过数据链路层传输到网络层时,前导码、SFD、MAC 地址、类型/长度以及校验字节均被过滤,IP 数据包传入了网络层。该数据包也不完全是有效数据,其还包含 20 字节的 IP 头部,具体见下图。IP 头部各区域功能如下:

****版本(4bit):****定义 IP 协议版本,IPV4 为 4'h4。
****首部长度(4bit):****定义 IP 数据包头部长度,表示具有 32 位字长数据的数量。最小值为 5,最大值为 15。
****服务类型(8bit):****用于分配优先级、延迟、吞吐量及可靠性,一般为 8'h00。
****总长度(16bit):****定义整个 IP 数据包长度。
****标识(8bit):****发完一包数据自动加 1。
****标记(3bit):****最高位保留为 0;中间位是否开启分段,0 不开启,1 开启;最低位表示是否存在下一个分段,0表示为最后一个分段,1 表示还存在下一个分段。一般默认为 3'b010。
****分段偏移(13bit):****表示分段数据在源数据报中的相对位置。
****生存时间(8bit):****表示以太网数据包可以中转进过多少个路由器,每进过一个路由器,该值就会减少 1,直到该值变成 0,丢包该包。WIN 系统默认为 8'h80。
****协议(8bit):****指出 IP 处理过程完成后,传输层的协议。UDP 为 8'd17,TCP 为 8'd6,ICMP 为 8'd1。
****首部校验和(16bit):****该区域确保 IP 协议头部的完整性。将头部每 16 位划分为一部分,将各部分相加取得的结果再取反码,即为该区域数据。
****源地址(32bit):****源主机 IP 地址。
****目的地址(32bit):****目的主机 IP 地址。
IP TX


IP RX


ARP (Address Resolution Protocol)*
既然从数据链路层再上一级就是网络层了,那么ARP又是做什么的?ARP的主要功能是管理IP地址-MAC地址的映射。
例如:FPGA(192.168.1.100)想要给目标IP:192.168.1.10 发送UDP;首先FPGA会查询本地的ARP Cache,如果在本地的ARP Table中检索到MAC映射信息,则直接组包发送;如果没有查到192.168.1.10,则会对外发送ARP Request(在Wireshark中抓包可以看到"Who has 192.168.1.10?Tell 192.168.1.100"这种报文);PC在收到该报文后会回复"192.168.1.10 is xx:xx:xx:xx:xx:xx";FPGA在收到后会更新本地的ARP Cache,然后才会进行正式发送。

当然实际工程中 ARP 和 IPv4 通常不是严格的串联关系,而是共同连接到 Ethernet MAC。

****硬件类型(2Byte):****硬件地址的类型,以太网的硬件类型为 1。
****协议类型(2Byte):****上层协议类型,IP 的协议类型为 16'h0800。
****硬件地址长度(1Byte):****硬件地址(MAC 地址)的长度,以字节为单位,其值固定为 8'h06。
****协议地址长度(1Byte):****IP 地址的长度,以字节为单位,固定值 8'h04。
****OPCODE(2Byte):****操作码,值为 16'h0001 表示 ARP 请求,值为 16'h0002 表示 ARP 应答。
****源 MAC 地址(6Byte):****发送方的 MAC 地址。
****源 IP 地址(4Byte):****发送方的 IP 地址。
****目的 MAC 地址(6Byte):****发送 ARP 请求时为广播地址 48'hff_ff_ff_ff_ff_ff,发送 ARP 应答时为接收方的 MAC地址。
****目的 IP 地址(4Byte):****接收端的 IP 地址。
在PC上 cmd 指令 arp -a 所查看的即是 ARP Cache。

ICMP( Internet Control Message Protocol )*
ICMP协议主要用于IP网络中的控制、错误报告和诊断,即常用的ping指令。
在IPv4下,ICMP的Protocol = 1,UDP为17,TCP为6。

当网络设备能ping通时,即可证明以下通信路径正常工作:PHY 基本正常;RGMII/XGMII 基本正常;MAC RX/TX 基本正常;MAC 地址基本正常;ARP 基本正常;IPv4 RX/TX 基本正常;ICMP RX/TX 基本正常。

因此,Ping 是非常好的 FPGA Ethernet 基础功能验证手段。
FPGA 网络协议栈总览
在了解了以太网相关的知识后,即可有明确的FPGA设计框架

这篇文章属于临时起意,从工程角度出发,不能通信的设备肯定不是好设备,所以先准备打通上位机到开发板之间的通信链路吧。这一篇内容已经比较多了,下一篇再介绍代码落地吧。
Fin