
做FPGA高速网络的朋友应该都有同感百兆千兆的UDP收发只要照着参考设计改改FIFO深度就能跑通可一旦把带宽拉到40G、100G这个量级整个项目的难度就不是线性上升而是直接跳档。最近我把一套开源的100G FPGA UDP方案移植到自有板卡上从拿到RTL到上板用iperf3打满线速前后折腾了两周多踩了不少坑也总算把整套流程跑通了。这篇就把移植过程中遇到的关键节点、测试方法和那些文档里不会写的细节一次性讲清楚给准备在这个方向上动手的朋友做个参考。这篇内容适合两类人一是已经在做FPGA网络加速、正准备从10G/25G往100G升级的工程师二是刚接触FPGA高速接口、想用现成开源方案快速验证100G UDP收发链路的开发者。涉及的知识点包括100G Ethernet Subsystem的工程集成、UDP协议栈的模块化移植、GTM/GTY收发器的时钟配置以及上位机侧的打流和抓包验证每个环节我都会尽量讲透场景、参数和取舍逻辑。1. 整体方案选型与设计思路1.1 为什么选择100G UDP作为切入点100G以太网在数据中心核心交换和AI集群场景已经很成熟但在FPGA项目里真正能用起来的团队并不多。原因很简单100G链路对MAC、PCS、PMD每一层都有极高的时序和集成要求一旦链路不稳定问题排查的成本远高于10G/25G方案。可一旦打通收益也非常直观——单条光纤就能承载10路10G业务很多需要多路并行处理的场景可以直接合并成单流处理系统架构能简化一大截。选择UDP协议栈作为切入点是因为UDP本身极度简单、轻量非常适合作为FPGA高速网络的首个验证对象。TCP协议栈在FPGA里实现要考虑连接管理、滑动窗口、重传排序、拥塞控制复杂度高出好几个量级UDP则基本等于“拿到包就发走”核心逻辑只有地址解析、校验和计算、分片重组这几块。这个特性决定了在100G这种高线速下UDP逻辑不会成为瓶颈瓶颈主要集中在MAC/PCS硬核的配置、收发数据路径上的FIFO带宽和用户逻辑的处理吞吐。也就是说100G UDP的移植重点不在于UDP协议本身而在于你围绕UDP这个“小核心”搭起来的整条高速通路是否顺畅。我在项目启动时就把技术目标定为用开源RTL实现UDP收发MAC/PCS层用FPGA厂商硬核IP用户侧预留标准的AXI4-Stream接口最终用iperf3和Wireshark在上位机上验证吞吐和丢包指标。这套组合的好处是职责边界清晰哪一层出问题可以快速定位。1.2 开源方案怎么选Corundum、verilog-ethernet 还是自研精简栈目前FPGA生态里能直接参考的UDP开源实现主要有三条路线。第一条是Corundum出自康奈尔大学的一个高性能网卡开源项目支持100G实现了完整的PCIe DMA、MAC、UDP/IP协议栈结构非常完整大量使用了SystemVerilog接口和参数化设计。Corundum适合作为“完整参考设计”来研究它的UDP Offload、TX/RX校验和计算、描述符管理都做得非常规范但对应的学习曲线也最陡——光是理解它的DMA路径和用户接口就要花不少时间而且它侧重网卡方向如果你只需要纯粹的UDP数据收发会感觉它“太重了”。第二条是Alex Forencich维护的verilog-ethernet项目包含Ethernet MAC、UDP、ARP等模块。它支持AXI4-Stream接口代码风格清晰很多模块可以单独拎出来用。但它的主要目标仍然偏10G/25G100G相关支持更多依赖外部MAC硬核配合UDP模块本身是宽度无关的可以工作在512bit这种超宽数据总线上。第三条就是自己基于Xilinx/Intel的100G MAC硬核只写精简UDP发送接收状态机。这条路线对代码工程量要求最低因为UDP逻辑本身不大但你需要自己处理ARP、ICMP等配套协议这也是最初容易忽略的地方——上位机在与FPGA通信之前通常会先发ARP请求确认MAC地址如果FPGA不回ARPUDP包根本不会发出来。我最终选择了verilog-ethernet的UDP模块配合Xilinx 100G Ethernet Subsystem硬核的组合。选它而不是Corundum主要看重两点一是AXI4-Stream接口标准后续对接图像、数据采集类用户逻辑很直接二是代码精简出问题方便顺着RTL一层层查。如果后续需要完整网卡功能再往Corundum迁移也顺理成章。2. 硬件准备与测试环境搭建2.1 FPGA板卡与光模块选型要点100G方案对板卡有硬性要求不是随便一块带高速口的开发板就能跑。首先FPGA芯片必须集成支持100G速率的光收发器Xilinx这边一般是UltraScale系列的GTY最高到25.78125Gbps每通道4通道聚合即100G或者Versal里的GTMIntel那边对应E-tile。其次板卡上必须有两个QSFP28或QSFP-DD接口用于接100G光模块或DAC高速铜缆。最后是参考时钟100G以太网通常要求光模块侧提供156.25MHz参考时钟很多开发板默认的125MHz只够跑10G这一步非常容易翻车。光模块方面我建议首选QSFP28 DAC铜缆做初期调试。DAC线缆是铜线直连不需要光模块和光纤配对少了一个信号完整性变量对排查问题极其友好。等DAC链路完全稳定、跑满线速不报错之后再换光模块加光纤做长距离验证。我这次测试就全程用的DAC线直连服务器网卡简单可靠也不存在光功率预算问题。服务器网卡建议选Mellanox ConnectX-5及以上规格的100G网卡驱动成熟、对iperf3和DPDK支持好测试时能省很多精力。如果你手头只有25G网卡也可以临时把FPGA侧PCS配置成4x25G模式但这样就不是真正意义上的100G点对点验证了链路层很多问题测不出来。2.2 上位机测试环境清单上位机测试环境是我这次踩坑比较多的部分按经验提前准备好能省一天时间。核心工具就三件iperf3用于打流测吞吐和丢包Wireshark用于抓包验证协议字段tcpdump用于命令行环境下的快速抓包。系统方面Ubuntu 20.04以上就可以主要确认内核自带网卡驱动能识别100G速率用ethtool查看速率是否协商成100000Mb/s。另外强烈建议提前准备一个UDP调试小工具比如packetsender或者用Python socket脚本用来做小流量定向发包。因为iperf3的UDP模式虽然方便但它的发包行为是“尽力而为”在接近线速时会出现CPU瓶颈导致的丢包这种丢包是上位机的锅不是FPGA的锅。用小工具发固定速率、固定长度的报文能帮你在排查时明确区分丢包发生在发送端、链路上还是接收端。硬件链路的连接顺序也要注意FPGA板卡的两个QSFP28口一个连接交换机或服务器网卡用于业务测试另一个可以临时用于回环测试。如果板卡只有单口也可以直接用DAC线把FPGA的两个口环起来做纯内部数据回环验证。3. 移植过程与核心细节解析3.1 从开源工程到自有芯片的移植流程拿到开源RTL之后第一件事不是急着综合而是先梳理清它的模块依赖关系。以verilog-ethernet为例顶层是eth_mac_100g、udp_engine这些模块底层依赖xilinx的100G硬核IP硬核IP的资产是通过IP Catalog生成的不同FPGA型号生成的IP是有差异的直接跨型号使用肯定不行。移植的第一步就是把IP重新生成换成目标芯片对应版本保证接口位宽和时钟频率符合预期。100G MAC硬核生成时几个关键参数要特别留意数据总线宽度一般选512bit对应352MHz左右的user clock512bit × 352MHz ≈ 180Gbps理论带宽实际扣掉前导码和帧间隙后刚好能支撑100G线速也有的流程选256bit、644MHz这种高频方案对时序压力大但对用户逻辑反而更友好。包使能信号、CRC转发、流控模式要根据开源模块的接口要求对齐。然后是时钟和复位。100G硬核内部有多个时钟域GT收发器时钟域、PCS时钟域、MAC用户时钟域开源RTL在移植时最容易出问题的就是用户时钟域与硬核usr_clk的换算关系不一致。我的经验是先把example design跑起来用示波器或ILA确认usr_clk频率正确、复位释放干净再接入UDP逻辑。很多“看不出原因的不稳定”最后查下来都是复位释放时间不够或者跨时钟域没做同步。3.2 AXI4-Stream接口与用户逻辑的衔接开源UDP模块与MAC之间、模块与用户逻辑之间基本都是AXI4-Stream接口理解这个概念是移植成功的一半。AXI4-Stream说白了就是三根关键信号TVALID表示本拍数据有效TREADY表示对端准备好了两者同时拉高才算把数据成功传了一拍TDATA是要传输的数据TLAST是一包数据的最后一拍标志TKEEP则标记TDATA里哪些字节有效。做100G链路时tdata通常是512bit宽也就是一拍能塞下大半段以太网帧这种超宽数据路径带来两个典型问题。第一个是tkeep处理一个UDP包不一定是512的整数倍最后一拍可能需要用tkeep掩掉无效字节如果tkeep逻辑写错会出现接收端CRC校验失败或者包长度错误。第二个是tlast和包间距100G下以太网帧间间隔IFG只有十几个时钟周期用户逻辑如果不能在tlast之后迅速准备好下一包吞吐就会明显下滑。这两个问题我在debug时都用ILA抓过波形原因都很隐蔽建议大家在一开始写用户逻辑时就格外注意。用户逻辑侧的吞吐是另一个隐藏瓶颈。很多人以为UDP逻辑跑通了就万事大吉结果上板一打流发现接收吞吐只能在50G左右怎么调都上不去。查到最后往往发现是DMA、FIFO或者算法模块的处理带宽不够比如FIFO的读数据宽度和写数据宽度不一致导致带宽折损或者内部RAM用了简单双口而不是真双口读写冲突带来额外等待。我的建议是用户逻辑到UDP模块之间必须保留足够的FIFO弹性最好把FIFO深度设置成能吸收至少一个完整Jumbo Frame的量级避免微突发时瞬间丢包。3.3 UDP校验和与ARP/ICMP配套逻辑UDP协议的移植核心虽然简单但校验和计算是一个容易忽略的细节。IPv4下UDP校验和是可选的可以置0表示不校验但如果上位机或者中间交换机开了校验和检查置0的包有可能被直接丢弃。我建议还是老老实实实现校验和IP头校验和、UDP头加伪头部校验都算上虽然占用一点逻辑资源但能避免很多排查不清的隐性问题。校验和的工程实现也讲究方法。最直观的办法是逐字节累加求反码但100G线速下这显然不现实必须在数据进入MAC之前做流水线化处理。正确的姿势是使用“增量校验和更新”技巧当数据以512bit宽总线进入时可以分段累加再将结果合并或者对已知要覆盖的字段源IP、目的IP、长度在头部处理时直接算好数据payload的累加可以边存边算最后一拍凑齐整个伪头。这个逻辑写的顺序影响性能我一开始图省事用了简单的两段式累加结果在超长帧场景下时序收敛不了后来改成树形归并结构才解决。配套的ARP逻辑很多开源栈里都有但要不要全量实现需要想清楚。如果只是点对点通信静态IP和MAC配置可以把ARP应答写死上位机发来ARP请求时直接硬件回一个应答即可不用实现完整的ARP缓存表。ICMPping同理能回复Echo Request会让你调试网络连通性时省太多时间。我个人的经验是哪怕你不需要业务上的ARP和ICMP也建议把最小实现加上因为你不可能永远记得把所有上位机的ARP缓存清干净。4. 上板测试全流程实录4.1 第一关外部回环与底层链路验证拿到bit后不要急着连服务器打流先把底层的物理链路验证干净。我习惯用DAC线把FPGA的两个100G口外部短接起来然后让FPGA内部自发自收——由测试逻辑从TX路径发出已知模板的UDP包再从RX路径收回来做比对。这一阶段能确认的事情非常多GT收发器有没有失锁、PCS有没有完成对齐、MAC的FCS校验是不是正常、RX路径上的FIFO会不会丢数。回环测试如果发现error计数器在涨优先查光模块/DAC线和GT的signal integrity状态。Vivado里可以用IBERT这个工具直接看眼图和误码率对100G链路尤其重要。如果误码率高先在IBERT里把TX的预加重参数、RX的CTLE/DFE均衡参数调好再回到用户工程里重新上板。我这次遇到过一次误码率在1E-12量级来回跳的情况排查到最后是DAC线的QSFP28接口没插到位重新插拔后误码率直接干净了。这类物理层问题不先排除后面所有协议层的排查都会白费功夫。回环通过后可以把外部回环换成FPGA内部环回在PCS或MAC层做数字环回做对比测试两种环回交叉验证能帮你区分问题是出在模拟物理层还是数字协议层。这个习惯帮我节省了一次莫名其妙的排查——内部环回全通、外部环回报错问题基本锁定在GT和光口上。4.2 第二关点对点直连与iperf3打流外部回环和内部环回都稳定后真正的考验才刚开始FPGA通过DAC线直连服务器100G网卡用iperf3双向打流。TX方向FPGA发往服务器先做。服务器端执行iperf3 -s -i 1FPGA端的上位机控制逻辑开始按设定速率和包长持续发包。这里有个经验UDP打流测试不要一上来就跑满100G先从小带宽开始比如10G、50G、80G逐级往上加每级跑30秒记录服务器端收到的吞吐和丢包率。这么做的好处是能清晰找到链路开始出现丢包的临界点方便定位瓶颈在哪个环节。RX方向服务器发往FPGA用iperf3反向模式iperf3 -c FPGA_IP -u -b 95000M -l 1424 -t 30这里几个参数解释一下-u指定UDP模式-b设定目标带宽95000Mbps-l是UDP包负载长度1424字节加上IP/UDP头后整帧长度接近1500这是避免IP分片的常见做法-t是持续时间30秒。95000M而不是100000M是有意的留出5%余量给协议开销和包间隔否则iperf3发包本身就会出现CPU处理不过来的丢包。服务器和FPGA之间的MTU也要提前匹配。如果服务器MTU是1500那l1424正好如果网络里能开9000字节巨型帧可以把MTU调成9000l设8972吞吐表现会更好因为单位时间里处理的包数量少了CPU和FPGA的压力都会小一些。4.3 第三关Wireshark确认协议字段打流能过不代表协议就完全正确强烈建议再用Wireshark抓包确认UDP协议字段是否规范。抓包可以抓服务器网卡侧用tcpdump抓一小段即可tcpdump -i enp3s0f0 udp -c 100 -w fpga_udp.pcap然后用Wireshark打开pcap文件分析。重点看三项一是UDP源端口和目的端口是否与配置一致二是IP头的Total Length是否等于UDP长度加20字节头三是源IP和目的IP是否正确。还有一个容易被忽略的点Wireshark默认可能不显示以太网帧的FCS校验字段因为大多数网卡驱动在收包时已经把FCS剥离了但这不代表FCS不重要FPGA侧MAC上还是有独立的CRC error计数需要从硬件侧单独读取。如果FPGA的UDP包是通过开源UDP模块发的Wireshark里通常能直接解析出UDP协议如果Wireshark里显示的是“Unknown”或者解析异常大概率是长度字段或者端口字段错位了可以对照协议规范逐字节查。为了验证RX路径服务器发往FPGA可以在Wireshark里给FPGA侧的响应包做过滤分析也可以直接在FPGA侧用逻辑分析仪ILA抓RX路径上的tvalid/tdata确认报文的源端口、目的端口、校验和字段是否与服务器发送的一致。ILA抓高速数据时存储深度有限可以设置触发条件只抓一个UDP包头这样能快速看清关键字段。5. 测试数据解读与指标分析5.1 三个核心指标吞吐率、丢包率、延迟经过两三轮调试最终上板测试数据出来了。我在FPGA TX往服务器方向用iperf3在服务器端接收结果如下配置带宽实际接收吞吐丢包率抖动10Gbps9.98Gbps0%1us50Gbps49.96Gbps0%1us80Gbps79.91Gbps0%1-2us95Gbps94.82Gbps0.01%2-3us99Gbps96.50Gbps2.5%5-10us前四级速率下UDP模块的实际吞吐基本贴着配置值走说明用户逻辑侧的FIFO和MAC带宽足够。95Gbps时开始出现极少量丢包99Gbps时丢包率跳到2.5%这基本符合预期——iperf3接近线速时CPU中断处理和PCIe带宽都会成为瓶颈不会是FPGA侧的问题。想验证这一点可以把测试换到DPDK环境DPDK轮询模式能榨出网卡的极限性能但用户体验会差一些一般Iperf3跑到95G这个成绩已经能证明UDP通路没有设计缺陷。延迟测试我用的方案是PTP时间戳或者简单的心跳包RTT测试FPGA收到ICMP Echo Request后立即回一个Echo Reply服务器端ping测RTT。本地DAC直连场景RTT均值大约11微秒其中大部分是软件协议栈处理耗时纯FPGA转发延迟应该在微秒以内。如果后续要更精确的延迟指标建议在FPGA内部标记时间戳寄存器和MAC的时间同步逻辑做硬件级测量。5.2 微突发丢包现象与FIFO深度的关系UDP打流丢包是排查时最常见也最头疼的问题。一个特别容易误导人的现象平均吞吐明明没过线速但丢包还是零星出现这种通常是微突发瞬时拥塞导致的比如四个20G的突发在某个瞬间同时击中同一FIFOFIFO溢出立刻丢包平均上看不出来。微突发丢包最有效的排查手段是看FPGA侧RX MAC的统计计数和FIFO的空满标志。我在工程里专门接了ILA到RX FIFO的满信号上触发条件是满信号拉高抓一次波形就能看到丢包瞬间的数据流行为。如果满信号经常拉高说明FIFO深度不够或者用户逻辑消费速率跟不上解决办法要么加大FIFO深度要么把下游逻辑的处理吞吐提上去。这里要特别注意FIFO深度的单位是“拍”而不是“字节”512bit位宽下深度1024的FIFO只能存64KB刚好是一个巨型帧的量级所以100G场景下FIFO深度参数往往要比10G方案激进得多。还有一种情况FPGA内部分支逻辑消耗速率足够但DMA或PCIe接口有背压导致用户逻辑时不时回压到RX路径。这种背压传播到MAC再传播到对端对端网卡就会重传或由上层协议感知为丢包。检查时可以在用户逻辑和MAC之间加一个测量模块统计TVALID~TREADY的周期数占空比如果比例超过1%背压问题就值得排查了。6. 常见问题与排查技巧实录6.1 链路起不来GT失锁与PCS对齐失败上板后最常见的第一类问题就是100G链路根本拉不起来服务器网卡显示速率协商失败或者大量link down。这类问题优先从物理层往协议层逐级排查。第一步看GT状态寄存器确认GT是不是处于失锁lock丢失状态。如果GT从来没失锁过问题大概率在PCS或MAC配置如果GT一直在失锁和锁定之间来回跳基本是信号完整性问题重新检查DAC线是否插紧、参考时钟频率是否正确。第二步看PCS有没有完成对齐100G PCS的alignment marker是否被正确识别。第三步看MAC层FCS错误计数器如果FCS狂涨但PCS对齐正常说明数据在PCS到MAC的传输路径上有损坏需要回查GT receive path的位宽和字节顺序配置。字节顺序问题特别值得提一下100G GT通道4路的数据交织顺序如果和MAC硬核期望不一致会出现“链路是通的但所有包都CRC错”的现象。排查方法是用IBERT的伪随机序列配合MAC层的PRBS测试或者直接在MAC层发一个已知pattern再用ILA抓RX侧数据对照。改字节顺序需要同时调整GT的极性、通道映射和PCS的接收侧位宽牵一发动全身改完一定要重新回环自测。6.2 用户逻辑带宽不足导致的隐性丢包这类问题最隐蔽因为从MAC层看没有任何异常CRC不报警、FIFO没溢出但UDP业务层的丢包率就是降不下去。我曾经遇到过一次上位机用iperf3发87Gbps的UDP流FPGA内部RX FIFO不溢出但应用层最后收到的有效数据只有82Gbps丢了将近6%。查了很久才发现是用户逻辑里有个图像处理模块处理峰值速率只有85Gbps左右平均够用但瞬时处理不过来产生周期性背压背压期间FIFO里的包被新来的包覆盖。这类问题的排查思路要跳出MAC层把用户逻辑也纳入测试范围。最简单的方法是做一个旁路功能把UDP模块的RX数据不经用户逻辑直接环回到TX路径发回服务器对比环回模式和正常模式的丢包率。如果环回模式下0丢包、正常模式丢包问题就锁定在用户逻辑如果环回模式也丢那就在FIFO、MAC或者物理链路上。这种二分定位法我几乎每次排查都用得上。带宽评估时还要注意数据格式的差别有些高速接口是DDR的有些是SDR的带宽计算容易出岔子。最简单的做法是统计实际处理的数据字节数除以耗时得出真实的吞吐数值再和目标带宽比较而不是只看理论位宽乘以频率。6.3 时序收敛512bit数据通路上的细节100G工程最大的物理实现挑战是时序收敛。512bit数据总线上哪怕只差一级组合逻辑路径延迟就多了几个纳秒在352MHz的时钟下很可能直接violation。遇到时序不过我的处理顺序是优化代码结构、检查复位逻辑是否复用为普通信号、再考虑流水线寄存器插入。前两个没有成本第三个会多几个周期的延迟但对UDP协议收发影响不大可以放心加。一个经常被忽视的细节是AXI4-Stream的tready信号如果经过多级组合逻辑再回传给发送端会形成一条很长的组合路径。解决办法是在接口处加一级寄存器打拍tready延迟一拍对端多等一拍代价是每包有效带宽少了一拍在高带宽需求下要配合fifo提前预取来抵消。综合策略上100G工程建议直接开Performance Explore模式在综合阶段把retiming打开、尽量少用max_delay约束优先保证常规路径的时序裕量。布局布线跑完如果还是有少量violation优先看是否集中在GT clock区域附近如果是指定了过紧的IO约束引发的问题可以适当放松。7. 移植完成后的体会在100G UDP这条路上折腾两周多最大的体会是UDP协议本身不复杂复杂的是100G这条物理和数字交叉的高速公路上每一个环节都必须同时工作正常。移植开源RTL时不要急着综合上板先列一个清单底层GT和PCS验证什么、MAC层验证什么、UDP协议层验证什么、用户逻辑验证什么每层验证通过后再往上一层。回环、IBERT、iperf3、Wireshark这四件套配合下来可以把问题一层层剥开不至于在多个环节同时出问题的时候乱成一团。最后再分享一个小技巧给FPGA工程加上一个简单的UDP回环测试模式——服务器发什么FPGA原样回什么。这个模式看起来不起眼但排查问题时价值极高。链路不稳定时可以用它快速确认是不是物理层问题用户逻辑有bug时可以用它旁路业务逻辑甚至还可以用它做极限带宽压测因为回环模式下FPGA不需要处理业务数据能跑到的最大吞吐就是这条链路的真实上限。我后面的项目里都会保留这个测试模式相当于是给整个系统留了一个安全通道比临时改代码加debug逻辑省事太多。