资讯动态

100G FPGA UDP协议栈移植实战:从选型到iperf3打流全记录

发布时间:2026/9/9 1:25:13 来源:尧图企业网站定制
上个月我把一块吃灰很久的UltraScale板卡搬出来配好QSFP28光模块和100G网卡打算把开源的100G FPGA UDP协议栈移植上板。原本以为两天能搞定的事结果从选型、源码集成、CMAC例化到真正的iperf3打流前前后后折腾了近两周。这篇博文就是整个过程的完整记录怎么挑开源方案、移植时哪些接口最容易翻车、上板测试到底怎么打流、最终实测能跑到多少以及我踩过的几个坑。这篇文章适合手里正好有100G板卡想做UDP加速的工程师也想给那些在商用IP太贵和开源方案不敢用之间犹豫的人做个参考。1. 为什么非要折腾100G FPGA UDP软协议栈扛不住商用IP又太贵先说说背景。项目需求其实很简单需要一块能持续收100Gbps数据的板卡把实时数据流拆包后交给后端处理丢包率要求极低。一开始我们评估过两个方向用服务器自带的高性能网卡Mellanox ConnectX-5/6这种靠DPDK或AF_XDP收包。这个方案吞吐没问题但如果想在数据路径里做用户自定义处理比如按端口分流、特定报文特征过滤甚至在内联做加密灵活性就差了很多。网卡offload功能被绑定死很多定制需求实现起来非常费劲。用FPGA做网卡方案把网口控制权完全掌握在自己手里。数据怎么拆、往哪送、要不要过滤都是RTL说了算。可一旦涉及到100G MAC事情就不那么轻松了。商用100G MAC IP核价格不低而且授权流程长。授权周期和价格对个人或中小团队来说都是实实在在的门槛。幸运的是这几年开源FPGA生态发展得不错比如大家都知道的Alex Forencich那套verilog-ethernet已经能较完整地支持10G/25G/100G MAC和UDP协议栈这给了我们一个可行路径。那为什么是UDP而不是TCP做硬件的应该都清楚UDP无连接、无状态、不需要维护拥塞窗口和重传队列状态机非常简单非常适合FPGA逻辑实现。而TCP的状态机太复杂开源项目里虽然有做得好的但要做到100G线速运行的TCP offload engineTOE难度完全是另一个数量级。所以这次移植先把UDP打通是符合实际情况的第一步。100G UDP本身的实际应用场景其实很多高速数据采集后端示波器、探测器数据回传、网络流量发生器打流工具、流表转发设备SDN数据面、分布式存储节点间的数据传输等等。这些场景的共同特点是要么需要极低延迟要么需要极高的自定义程度。FPGA UDP的组合恰好够用。2. 开源方案选型100G UDP核挑来选去核心看这五件事开源100G UDP方案绕不开的项目其实就几个。核心是Alex Forencich的verilog-ethernet另有一些基于它的二次封装项目比如Corundum。我在选型时重点看了以下五点选型维度关注点我的判断License是否允许商业闭源使用MIT/BSD类优先GPL类直接排除模块完整性是否包含MAC、UDP、ARP、CRC等缺一不可否则又要自己写一堆胶水逻辑速率范围是否原生支持100G只支持10G/25G的项目强改到100G很痛苦活跃度最近是否有commit、issue回复情况长期不维护的项目遇到bug只能自己啃接口风格AXI4-Stream是否标准方便和DMA、FIFO等对接2.1 各家方案对比verilog-ethernet这个项目其实更像一个以太网组件库包含10G/25G/40G/100G的MAC以及UDP、ARP、PTP等上层模块。每个模块都是独立、参数化的可以自己拼装。License是MIT代码风格规范模块间基于AXI4-Stream接口。Corundum相当于在verilog-ethernet之上做了一整套100G网卡方案还带PCIe DMA、多个队列、流分类等数据中心网卡功能。功能非常强大适合想直接做成品网卡的人。不过它提供的是一整套系统如果你只想用其中一部分裁剪成本比直接用verilog-ethernet更高。其它还有像open-nic、or-tools之类但下载量和活跃度都不如上面两个遇到问题都没处问。2.2 为什么最终选了verilog-ethernet最后选了verilog-ethernet主要原因就是克制。它没有替你把整个网卡做成黑盒而是给出了标准、清晰的积木块MAC就是MACUDP就是UDP你要哪块拿哪块不需要的绝对不会被编译进来。这次移植需要的是100G MAC UDP收发正好是它覆盖得最扎实的部分。另外一点是它的代码风格很稳。寄存器命名规范时序边界清晰内部带有跨时钟域处理不是那种仿真能过、上板就挂的示例代码。我在移植时改动的部分很少主要精力花在板级集成上。2.3 硬件准备板和光模块移植前先确认硬件否则后面全是坑。我这里用的是Xilinx UltraScale系列的板卡自带的100G Ethernet Subsystem硬核也就是CMAC光口是QSFP28。如果你的板子上用的外部PHY方案比如Synopsys的DesignWare Eth QoS用外部PHY驱动方式会有所不同这篇文章的后半部分我会专门说一下差异。时钟方面需要注意100G Ethernet通常需要两个时钟域参考时钟给CMAC/PCS使用频率一般是161.1328125MHz或322.265625MHz。用户逻辑时钟CMAC输出到用户侧的时钟在512bit位宽下大约是322.265625MHz。这些时钟必须保证来源正确很多移植问题的根源都在参考时钟上。上板之前用频谱仪或者说至少用Vivado里的眼图监测看一下时钟状态能省掉后面几天的调试时间。3. 移植过程实录从克隆源码到第一个数据包正常上线这次移植的整体流程是先克隆代码然后在Vivado里建一个测试工程把MAC和UDP部分接起来先跑仿真验证逻辑正确性再上板。3.1 源码克隆与工程组织基于当前主分支版本移植把rtl/目录全部加入Vivado工程不需要的模块最终综合时会因为悬空被优化掉但为了后续维护方便我只加入了用到的文件。先大体说一下用到哪些模块eth_mac_100g100G MAC核心。负责以太网帧的封装、拆解处理前导码、FCS校验等。它适配Xilinx CMAC硬核或者在仿真的时候可以使用标准接口。udp_completeUDP收发完整模块含UDP校验和生成、验证。axis_fifoAXI4-Stream FIFO用于缓存。eth_axis_rx/eth_axis_tx在MAC和用户逻辑之间做以太网帧格式转换剥离/添加以太网头。由于100G接口位宽大MAC核心与用户侧的数据宽度一般设置为512bit。当线速100G时AXIS时钟约322.265625MHz。这个频率在UltraScale上综合起来本身没问题但如果用户逻辑在这条路径上写得太深时序就开始紧张了。3.2 顶层的例化流程顶层逻辑步骤大致如下例化CMAC或者说通过Xilinx IP Catalog生成100G Ethernet Subsystem然后在工程里用verilog-ethernet的eth_mac_100g作为适配层把它和CMAC连接起来。例化eth_axis_rx和eth_axis_tx把MAC侧的AXI-Stream转成内部更易处理的接口。例化udp_complete配置成只处理指定端口模式这样非目标端口的报文会被直接丢弃省去很多没用的处理。用户侧自己写一个简单的收发逻辑发送端定时构造UDP包接收端把收到的payload通过串口或者ILA导出先验证最基本的通路。一个典型顶层例化片段大概是这样的感觉// 顶层集成示意非完整代码 eth_mac_100g #( .DATA_WIDTH(512) ) u_mac ( .gt_rx_clk (gt_rx_clk), .gt_tx_clk (gt_tx_clk), // -- 与CMAC硬核/外部PHY的接口 -- // ... // -- 用户侧接收 -- .rx_axis_tdata (mac_rx_tdata), .rx_axis_tvalid(mac_rx_tvalid), .rx_axis_tlast (mac_rx_tlast), .rx_axis_tuser (mac_rx_tuser), // -- 用户侧发送 -- .tx_axis_tdata (mac_tx_tdata), // ... ); udp_complete #( .DATA_WIDTH(512), // 配置默认端口等 ) u_udp ( // ... );这段代码看起来简单实际上前前后后接了一整天因为100G的数据位宽带来的对齐问题确实让人头疼。尤其在MAC拆出/填充字节时tkeep信号的组合逻辑非常容易被忽略。你不能把一个40G的设计简单地把位宽翻倍就往100G上怼包边界的处理方式、CRC处理位置、对齐有限状态机的设计都是不一样的。3.3 100G设计中容易忽略的时序合法性问题在100G数据通路上有一个经常被仿真掩盖掉的问题tvalid和tready握手。仿真时大家都按理想情况来可100G线速下每拍512bit、频率接近322MHz时一旦某个模块在start_of_frame时没有准备好后续数据就没法连续影响吞吐。要保证线速能力整条数据通路必须做到每个周期都能接收新的有效数据不能有反压。这里我建议在移植阶段先不必追求完整协议的正确性。先把#我的第一步定成FPGA主动发包PC收包。PC端随便用Wireshark先看包结构确认MAC头、IP头、UDP头的字节内容是对的。然后再做PC发包给FPGAFPGA收到后通过ILA观察数据内容。以上全部通过后才轮到真正的iperf3打流。3.4 综合后的资源占用和频率第一次综合时资源占用大概是这样的水平具体取决于项目和附加逻辑100G MAC相关含CMACLUT约2万-3万FF约3万-4万。如果是用硬核这块的资源主要由PCS和适配逻辑贡献。UDP完整协议栈LUT约8000-1.5万。用户自定义的DMA/发送逻辑LUT约5000-10000。时序收敛一开始报了-0.3ns左右的slack主要卡在UDP校验和生成器和MAC发送接口之间。解决方式是把校验和路径拆成两段流水为checksum预计算在包头还未到达时就提前算好部分前缀和后缀最终才压到了接近收敛的状态。4. 上板打流测试标称100G实际跑多少怎么测才不算耍流氓移植后的第一步是通能收能发包内容完全正确。第二步才是快打满线速。很多项目卡在第一步但反而更常见的是第一步还好第二步怎么都上不去。4.1 搭建测试环境我的测试环境是这样FPGA板卡Xilinx UltraScaleQSFP28光模块按100G速率先在BIOS/板卡层面确认光模块识别正常。对端设备一台带100G网卡的服务器网卡是Mellanox ConnectX-5直连。测试软件iperf3记好版本iperf3 3.7以上对高带宽UDP支持更好另外准备Wireshark做包内容分析。链路使用单模光模块QSFP28直连确认光线长度在合适范围内。4.2 从低速率热身到逼近线速先别直接100G起跳那样出问题根本没法定位。建议先让FPGA以大约1Gbps的速率发包PC端用iperf3收看丢包率、吞吐。确认无误后再逐步提高。比如用iperf3测试UDP接收端套路# PC端先作为接收端起一个UDP测试端口5201 iperf3 -s # 然后调整FPGA发送端速率逐渐逼近线速如果需要纯接收FPGA作为一个静态接收端PC端用iperf3发UDP数据参数可以这么写iperf3 -c 192.168.1.10 -u -b 80G -l 1384 -t 30这里-b 80G是目标带宽-l是UDP负载长度推荐设置接近MTU范围的值通常是1384字节TCP/UDP/IP头占了多余字节实际最大负载看具体配置。小包测试是另一回事后面单说。4.3 100G的实测结果在纯UDP发送方向FPGA满载荷发包到PC端iperf3实测大概在94Gbps上下浮动。这个数据和理论线速基本吻合。很多人不理解为什么100G口跑不到100Gbps其实是因为以太网线上还有前导码、帧间隔IFG和帧校验序列FCS。按报文头IP头UDP头总共42字节开销、UDP载荷1384字节来算理论最大UDP吞吐约为ETH帧长不含前导码和IFG 42字节头部 1384字节UDP载荷 1426字节UDP有效载荷率 1384 / (1426 8字节前导码 12字节IFG) ≈ 95.7%然后和线速相乘约95.7Gbps实际跑到94Gbps以上已经相当接近上限。在有反压、有DMA、有软件协议栈参与的场景下能持续稳定跑90Gbps以上就算优秀。你要是看到有人晒100G UDP打满大概率是用了非常理想的小包/特殊帧结构或者在统计口径上有争议。4.4 用ILA和Wireshark联合定位问题上板后如果发现收发异常第一件事就是用Vivado的ILA去抓用户侧的AXI-Stream信号确认数据、valid、ready、last的时序是否存在异常。比如在我这次测试中PC端iperf3一旦报告丢包我就在FPGA内部把rx_axis_tvalid rx_axis_tready的有效接收计数和端口过滤后的计数做对比立刻就能定位丢包是发生在大卡MAC接口附近还是发生在UDP过滤之后。另一个有效手段是PC端同时用Wireshark抓包。如果Wireshark里面能看到大量CRC错误或者错包说明链路物理层有问题优先查光模块、时钟和信号完整性。如果Wireshark完全没包那问题大概率出在FPGA发包逻辑上比如源MAC地址/目的MAC填错了。5. 复盘这次移植踩过的坑整个移植过程踩了不少坑挑有代表性的几个记录一下这些坑在仿真阶段极难发现。5.1 时钟复位设计的坑100G这种高速设计复位信号的释放是可复位逻辑和硬核顺利初始化的关键。常见问题是一个全局异步复位直接接到所有模块上结果CMAC还没起来用户逻辑的复位已经在跑了两边出现认为对方在线实则没准备好的假象。解决办法是使用同步化的复位释放电路在CMAC的user clock域里先打几拍将复位信号与时钟对齐后再释放。特别要注意CMAC自己会有tx_reset_done/rx_reset_done信号你必须在这些信号拉高后再开始传输否则MAC发送的第一个包大概率被截断。5.2 校验和与CRC的重复计算问题100G网络中UDP报文在IP层的字段如果填了全零某些接收端会拒收。IPv4 UDP的校验和是可以为0的表示未计算但前提是整个IP头校验和正确如果你在硬件里自动计算了UDP校验和却忘了开启checksum offload模式那包到了PC端会直接被协议栈判为bad checksum并且丢弃。这个问题在verilog-ethernet中表现为UDP模块有一个开关控制是否计算校验和而MAC层又有一个控制是否自动添加/校验FCS的开关。两个开关如果设置不当就会出现看起来有个包但统计里全是错误的情况。我踩过一次MAC层同时被要求自动生成FCS和手动插入FCS导致每个帧结尾多了4字节冗余链路虽然能通但吞吐少了一截而且对端一直在报FCS错。5.3 跨时钟域用户逻辑时钟与MAC时钟的偏差100G设计里CMAC输出给用户侧的时钟并不是一个绝对固定值的时钟不同IP配置下接到的时钟可能不一样。比如有的配置用户侧是512bit宽时钟是322.265625MHz有的配置是640bit宽时钟是257.8125MHz。如果你在用户逻辑里默认512bit322MHz去处理时序但实际生成的CMAC Core却是640bit257.8MHz那么你的FIFO读写时钟完全对不上表现为随机丢包或偶发卡顿。这是文档不看仔细的典型代价。重新阅读CMAC IP的用户指南后在用户侧加了一个异步FIFO彻底把两边时钟域隔离问题才消失。5.4 光模块的假死问题QSFP28光模块上有很多控制引脚比如TX_DISABLE、Reset、LPMODE。上电默认状态如果不确认可能把光模块置于disabling状态或者低功耗状态此时链路完全无光。一开始我查了很久MAC寄存器后来才发现是TX_DISABLE引脚默认拉高了。大多数FPGA板卡会有EEPROM或者拨码开关控制但板卡上电顺序不同默认状态也会不同。调试顺序建议先ethtool eth0看对端是否检测到link再看FPGA侧CMAC的link状态最后才看数据通路。光模块的link up是基础中的基础。5.5 性能瓶颈你以为在MAC其实在DMA当测接收方向时FPGA接收100G UDP数据后如果只是简单丢弃或者通过UART打印那无瓶颈。但如果要让数据进入DDR或者通过PCIe送到主机瓶颈立刻转移到存储带宽和PCIe传输上。100G线速对应的数据量大约每秒12.5GB这个吞吐对PCIe Gen3 x16已经接近极限对DDR4控制器的带宽也是一场压力测试。所以如果你想做的是完整的100G数据采集或网卡功能我强烈建议在一开始就把DMA和缓冲区带宽纳入规划不要只盯着MAC和UDP。我这次测试接收方向实测只能稳定跑到62Gbps的吞吐瓶颈就出现在DMA写的page分配和cache一致性处理上和数据通路没有关系。6. 从100G UDP再往前扩展方向和更实际的建议如果你这次移植跑通了后面有几个方向可以继续推进。把UDP变成更完整的传输层能力比如支持多端口、支持ARP应答、支持可选校验和策略、支持VLAN标签处理。这些都是相对独立的小模块verilog-ethernet本身都有参考实现直接拿过来接上就能用。再进一步如果要做成真正的100G网卡那就绕不开PCIe DMA。这个方向可以直接参考Corundum项目的实现思路它的队列管理、描述符环、流分类在开源方案里算是相当完整的。但在扩展开工之前先把UDP链路的丢包定位方法论沉淀下来成为团队的通用调试手段比堆新功能更有价值。给正在上板的人三个最实用的建议拿着板卡先花一个晚上反复看100G Ethernet相关的用户指南尤其是时钟、复位、状态寄存器、用户接口时序。看懂了再动手能避免80%的初期问题。仿真能过的部分不要掉以轻心。时序收敛不等于上板正确上板正确不等于线速稳定。每一层都要做明确验证。打流测试前把Wireshark抓包、网卡性能参数、iperf3版本这些周边准备好再开始。因为到了上板打流阶段真正令人痛苦的往往不是FPGA逻辑本身而是你根本不确定对端的工具统计口经。这次100G UDP移植的成功让我更确认了一件事开源方案在高速网络方向已经完全可以作为入门和产品原型的起点。它省下来的授权费用和时间足够你踩完所有该踩的坑还能剩不少预算去吃顿好的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价