FPGA实现100G UDP数据通路:开源核移植与上板测试全记录 📅 发布时间:2026/9/8 8:43:22 👁 浏览次数: 去年接了个挺挠头的需求要在FPGA上打通一条100G的UDP数据通路把采集前端的高速数据实时送给后端服务器。一开始大家第一反应都是“买张100G网卡不就行了”但仔细把需求捋完之后发现完全不是那么回事——数据源不是PCIe而是ADC阵列的并行数据线中间必须要有FPGA做数据整形、预过滤和按帧打包然后再进网络。这条路走下来其实就是一个典型的“开源100G FPGA UDP移植上板测试”项目。这篇文章把我从选型、移植、仿真到真机验证的完整过程写出来重点说说那些只在跑实机时才会暴露的问题。文章适合手里有UltraScale级别板卡、想快速打通100G UDP的FPGA工程师参考也适合正在评估“自研FPGA网卡还是买网卡”的技术负责人可以先看看这套方案的坑和成本再下决定。1. 为什么要在FPGA上自己做100G UDP通路1.1 什么样子的项目才会提这种需求很多人会觉得“100G网络”是服务器和交换机的领域FPGA在里面顶多当个PHY芯片用。但实际项目中有一些场景是绕不开在FPGA内部实现UDP打包的。第一种是数据源头不是PCIe的场景。比如高速ADC采集、软件无线电的IQ数据、工业视觉的Camera Link数据它们都是以并行总线或者JESD204B之类的接口进来的必须先经过FPGA做拼帧、对齐、格式转换然后才能进网络。第二类是端到端延迟要求极其苛刻的场景比如网络化测控或者分布式系统的同步控制这类场景不允许数据经过操作系统协议栈因为中断调度和上下文切换带来的抖动是毫秒级的而FPGA内做UDP打包转发延迟可以稳定在微秒量级。第三类是需要在收包路径上做处理的场景比如对进来的UDP载荷做加解密、压缩、特征匹配如果每包都上CPU100G速率下CPU根本扛不住。所以能不能在FPGA里直接搞定100G UDP是很多高速数据采集和处理项目绕不开的技术决策点。1.2 网卡方案和FPGA方案怎么权衡先放一张我当时画的对比表这个表直接决定了我们最后往哪个方向走。维度服务器100G网卡FPGA 开源UDP核FPGA 商用UDP核开发成本低驱动装好即用中需要自己接逻辑高需要采购授权端到端延迟高软件栈抖动大低固定流水线延迟低官方优化较好灵活性低只能标准UDP高可自定义帧格式/加解密中受IP核接口限制与采集前端集成差需要额外数据通路好数据直接在FPGA内部流转好但依赖商业IP生态长期维护依赖厂商驱动完全可控依赖IP供应商选型的时候我们认真考虑过直接用100G网卡但最后发现最痛的点是采集前端到网卡之间仍然需要一块FPGA做数据搬运那与其多加一块FPGA和一条PCIe链路不如把网络功能直接收敛进同一片FPGA里省掉一级缓冲和一次PCIe传输延迟更低系统更简单。1.3 风险与成本判断100G和10G相比协议栈本身并没有变难多少难的是物理层和时序收敛。如果从零写100G MAC加上UDP协议栈工作量非常可观而且调试周期会很长。我们的做法是把风险拆成三块来降低难度物理层与PCS/PMA这块完全交给Xilinx的100G CMAC IP这是成熟方案厂家把GT、编解码、FEC都封装好了。链路层MAC和IP/UDP协议栈采用开源RTL核只做适配和验证。用户应用逻辑这是项目差异化的核心由自己实现。这样拆完之后每一块都能独立验证协议栈如果出问题开源代码可读性高自己也能改。综合下来我们认为这个方案的投入产出比最高。2. 开源方案盘点与选型思路2.1 三个方向先分清自己在哪个位置开源FPGA网络方案其实不少但思路差异很大。我当时主要看了三个方向第一个是纯RTL开源核代表性的是Alex Forencich维护的verilog-ethernet仓库。它的特点是模块化做得极好里面有各个速率档位的MAC也有ARP、ICMP、UDP等协议层的处理模块代码是纯Verilog不绑定任何厂家的FPGA。第二个是开源完整网卡实现代表是Corundum项目。它更像一整套NIC方案集成了PCIe、DMA队列、描述符管理、100G MAC目标是做一个可用的开源智能网卡。但它的体量明显更大如果我只想打通一条UDP数据通路把Corundum完整移植过来有点“杀鸡用牛刀”调试链路也会更长。第三个方向是“官方IP加自研逻辑”就是用Xilinx CMAC IP做MAC和PHYUDP协议逻辑自己写。这种做法的优点是所有东西都在自己掌控下但缺点是自己写UDP offload引擎容易在ARP、校验和、分片、错误帧过滤上踩坑而这些开源核通常已经处理过了。最终我们选了第一个方向原因也很现实我们不是在做网卡而是在做数据采集系统里的网络通路需要的是一个“能发能收的UDP端点”而不是一整套DMA驱动框架。2.2 最终选择verilog-ethernet的三个理由第一是模块解耦做得好。MAC层和协议层是两个层面的东西开源仓库把它们分开这意味着我可以先只跑MAC回环确认PHY链路通再接上UDP核确认协议栈正常。这种分层验证顺序在排错时极其重要。第二是不绑定特定FPGA。它只是纯RTL逻辑适配到Intel或者国产FPGA平台时只需要把底层的PMA/PCS换掉上面的MAC和UDP协议逻辑可以原样保留。这个灵活性在供应链不确定的当下非常值钱至少不用怕被单一厂商IP锁死。第三是接口标准统一。核心模块对外都是AXI4-Stream接口用户侧数据也是标准总线我们自己的数据整形模块和采集逻辑非常容易对接不需要花精力去理解私有总线协议。2.3 100G协议栈的任务切分在移植之前必须先把100G通信在FPGA里到底分成哪几块搞清楚。我习惯这样切分第一层是物理层和PMA/PCS负责把并行数据变成高速串行光信号包括编解码、时钟恢复、FEC。在Xilinx平台上这层由CMAC IP承担。第二层是MAC和网络协议层负责以太网帧的封装与解析、CRC校验、IPv4头解析、ARP响应、ICMP回显、UDP端口匹配和校验和计算。这一层正是开源RTL核所实现的功能。第三层是用户应用层处理实际要传输的数据比如把ADC采样数据打包成UDP payload或者把收到的控制指令解析出来。这个切分决定了后面移植和调试的思路任何一条100G链路出了问题先判断它属于哪一层再决定去看GT状态、MAC统计还是协议栈的逻辑波形。没有这个分层意识上板之后很容易像无头苍蝇一样到处乱试。3. 移植前必须理解的核心原理3.1 100G以太网在FPGA里的分层模型100G以太网在FPGA里通常不是一根线而是一组高速串行通道。以Xilinx UltraScale平台为例100G一般通过CAUI-4拆成4路25G高速通道每一路内部又通过64B/66B编码在物理线路上跑约25.78Gbps的速率四路合计线速率约103.125Gbps。因为加入了编码开销实际有效载荷速率正好是100Gbps。这个差异直接解释了为什么FPGA内部用户接口位宽会那么大100G的有效数据率实在太高了。如果接口宽度只有64位工作时钟需要超过1.5GHz这在FPGA里根本不现实。所以CMAC IP的用户侧接口通常是512位宽在这个位宽下时钟大约是195MHz这是一个FPGA比较容易跑到并留有余量的频率。理解了这个模型之后你就会明白上板测试为什么第一步永远是看GT链路而不是看UDP逻辑。GT链路没锁定的话后面所有协议状态都是假的。3.2 UDP offload引擎到底替你做了什么很多人以为“UDP offload”就是把UDP头加上去然后发出去其实没那么简单。一个可用的UDP端点至少要处理六件事收方向要有MAC解帧和FCS校验把坏帧丢掉要有IPv4头解析和IP头校验和验证要能匹配目的端口不匹配的包直接丢弃要解析并应答ARP请求否则对端在数据链路层根本找不到你要能响应ICMP回显请求方便你用Ping来确认三层链路是通的发方向要能够正确填充MAC地址、IP头、UDP头并且计算UDP校验和。这些功能如果全部自己用状态机写短期内也能跑通但边界情况非常多比如ARP请求里带VLAN tag、IPv4头的IHL不是最小值、UDP分片这些边界情况如果没有处理干净真机联调时就会莫名其妙地丢包。开源核的优势在于它已经被很多项目验证过边界情况处理得比较全。3.3 位宽与时钟速算公式我在设计用户逻辑时经常用一个速算方法数据位宽乘以工作时钟频率要大于等于接口速率同时留出20%左右的时序余量。以100G为例100Gbps除以512位大约是195MHz这意味着用户逻辑最高运行频率必须能承受195MHz。如果你的用户逻辑里有一段级联很深的组合逻辑布线后只能跑到150MHz那数据通路就会成为瓶颈表现出来就是上游数据反压、FIFO溢出、丢包。所以在代码设计阶段就要提前把用户逻辑按流水线切开尤其是校验和计算这类有长加法链的逻辑一定要做流水处理。这是移植前最容易忽略、最后最容易爆雷的点。3.4 缓冲策略保留FIFO比省资源重要开源核的收发数据通路上通常有可选的FIFO我当时第一版图省资源把它关掉了结果把自己坑得很惨。原因很简单用户逻辑的时钟和CMAC用户侧接口时钟不一定完全同源即便都是195MHz附近也存在相位和频率偏差数据跨时钟域总得有个FIFO兜底。后来我把收发路径上的FIFO全部使能并且把FIFO深度调到足够容纳最大帧问题立刻缓解。做100G这种速率级别的设计不要为了省几个BRAM引入时钟域风险这是原则问题。100G一旦丢包排查成本远高于几个BRAM的资源成本。4. 移植实操从Git仓库到可综合工程4.1 环境与板卡准备我使用的工具是Vivado 2023.1板卡是Xilinx VCU118板载的GT参考时钟和100G光口管脚都比较齐全。如果你手里是VU3P、AU200或者其他UltraScale平台整体流程是一样的只是约束文件里的参考时钟管脚不同。另外一个建议是准备好100G DAC线或者光模块加光纤。DAC线在短距离调试时省事不用考虑光功率和模块兼容问题。PC端需要一张100G网卡如果暂时没有也可以用另一块FPGA板做对端不过用PC做对端能极大简化测试工具的编写所以我强烈建议先搞一张100G网卡。4.2 获取源码并整理依赖文件源码获取很简单git clone https://github.com/alexforencich/verilog-ethernet.git仓库里RTL文件非常多包含各种速率档位的MAC和协议模块。建议不要手动挑选文件直接用一个文件列表脚本把rtl目录下需要的文件包含到工程里让综合工具自己解析依赖关系。我自己用Tcl脚本批量添加效率比在GUI里一个文件一个文件地加高得多。这里有个小技巧先用Vivado的read_verilog把所有RTL文件读入再运行report_design_analysis看有没有缺失的模块引用。通常缺的是一些基础库比如LFSR或CRC模块这些在仓库里都有加进来即可。4.3 例化Xilinx 100G CMAC IPCMAC IP配置时要注意几个关键选项。线速率选100G接口模式选CAUI-4用户接口位宽选512位参考时钟频率按板卡实际给的频率填通常是156.25MHz。CMAC IP生成之后在它的用户侧会出现一组大的AXI4-Stream收发总线以及状态信号例如RX链路状态、TX链路状态、阻塞锁定、FEC状态等。这些状态信号建议全部引到顶层接到ILA上上板之后第一时间就能看到物理链路状态不用猜。这里还要提一个大家容易忽略的点CMAC IP自带的测试报文生成功能。在Vivado的IP配置界面里可以打开测试流量生成器它能产生固定格式的测试帧也能校验接收到的测试帧是否正确。上板第一步测试时先不接UDP逻辑直接利用CMAC自测模式把GT物理层先跑通会省很多事。4.4 顶层对接与AXI-Stream接口映射把CMAC和开源UDP核对接本质上就是总线映射。关键信号包括tdata、tvalid、tready、tlast和tkeep。CMAC出来的接收总线和UDP核的接收总线位宽保持一致tdata直接赋过去tvalid和tready做握手tlast表示帧尾tkeep必须按字节有效位置1。tkeep是最容易出问题的信号如果接收方向tkeep拉得不对UDP核会把padding字节当成有效载荷处理导致解析出来的包长度错误。顶层对接的伪代码示意如下// 收发通路对接示意 assign udp_core_rx_tdata cmac_rx_axis_tdata; assign udp_core_rx_tvalid cmac_rx_axis_tvalid; assign cmac_rx_axis_tready udp_core_rx_tready; assign udp_core_rx_tlast cmac_rx_axis_tlast; assign udp_core_rx_tkeep cmac_rx_axis_tkeep;发送方向则反过来把UDP核的发送总线和CMAC的发送总线相连同时要处理CMAC可能反压的情况。如果UDP核的发送侧没有FIFOtready不满足时数据就会丢这里的处理方案就是前面说的把UDP核自带的收发FIFO打开。4.5 综合时序与资源观察综合后第一步看时序报告。100G设计的关键路径通常出现在校验和计算、帧长度计数和FIFO读写指针逻辑上。如果出现时序违例优先考虑对校验和路径做流水处理不要轻易降低时钟频率去迁就时序因为这会牺牲整个数据通路的带宽。资源方面CMAC IP加上开源UDP核以及必要的FIFO在VCU118上大约占用几万个LUT和几百个BRAM整体利用率不算高大概20%到30%。如果你发现资源占用异常偏大多半是文件列表里加进了不需要的高速MAC核检查一下模块引用只保留100G相关和UDP相关即可。5. 上板测试从Link Up到打满100G5.1 测试环境怎么搭我当时的测试环境是FPGA板卡与服务器100G网卡通过DAC线直连没有经过交换机。直连的好处是少一层网络设备排错时只需要关注两头。PC端操作系统是Ubuntu用iperf3做打流工具配合Wireshark抓包做协议分析。FPGA内部在ILA上挂了几个关键信号CMAC的链路状态、UDP收发包计数、包错误计数、ARP请求接收计数。上板之后先把ILA的触发条件设好比如触发条件设为“收到第一个UDP包”这样能最快定位问题。5.2 第一关GT链路和CMAC状态接好DAC线后上电加载bitstream。第一步不是Ping而是看CMAC的链路状态。如果链路状态没有变为up先检查参考时钟有没有锁定、GT重启次数是否为0、光模块或DAC线是否插牢固。我这边的现象是一次通过很快看到链路状态变为up。如果你的链路状态一直不稳定大概率是参考时钟配置不对要回到底层去查GT参考时钟的输入管脚和频率设置。只有确认物理层稳定之后才开始进行上层协议测试。5.3 第二关ARP和ICMP物理层通了之后给FPGA的IP接口配一个固定IP比如192.168.50.10MAC地址用板卡丝印地址不要随手乱写。然后在PC上Ping这个IP。如果Ping不通优先看ILA里有没有抓到ARP请求。如果收到了ARP请求但没有回复问题在开源核的ARP模块配置如果没收到ARP请求问题可能出在PC的ARP缓存或者IP配置。我当时遇到一个问题换了测试网段后Ping不通原因是PC的ARP缓存里还留着旧MAC地址。执行arp -d清掉缓存后立刻通了这虽然不是FPGA端的故障但很容易让人误判为FPGA逻辑问题值得提一嘴。5.4 第三关UDP打流与统计校准Ping通之后用iperf3的UDP模式打流测试iperf3 -u -c 192.168.50.10 -b 100G -l 1400 -t 10这里的参数含义是按100Gbps的速率包长1400字节持续10秒发包。FPGA内部统计收到的UDP包数量、载荷字节数以及匹配指定目的端口的包数然后把计数通过ILA读取出来和PC端iperf3报告的数据做对比。我当时实测的结果是PC端发出的包数和FPGA接收侧统计到的包数完全一致连续跑了几轮都没有丢包。这个结果说明整条数据通路已经具备稳定传输能力。注意在测试过程中要把Wireshark关掉因为Wireshark抓包本身会消耗大量CPU资源干扰高带宽测试结果。5.5 长稳与反向回环压测打流跑到确认无误之后还要做长稳测试。我跑了一个12小时以上的持续打流同时定期记录CMAC状态和UDP包计数观察是否有任何递增的错误计数。长稳测试最重要的观察项是CRC错误计数和FEC纠正计数如果这些计数持续上升说明光模块或链路存在隐患而不仅仅是UDP逻辑问题。反向方向也就是FPGA向PC打流我用一个简单的测试模块在FPGA内部生成UDP包通过开源UDP核发到PC。PC端用iperf3的服务器模式接收再对比丢包率。这个方向同样验证通过说明收发通路都具备全速转发能力。6. 踩坑记录真机上才会遇到的那些麻烦6.1 参考时钟管脚配错的典型现象如果你的GT参考时钟管脚选错CMAC的link状态会一直无法稳定现象是GT的复位计数不断增长参考时钟锁定信号始终拉不起来。这个问题的排查思路是回到板卡原理图确认100G接口对应的参考时钟管脚然后在XDC里固定下来不要依赖时钟向导自动推断。我第一次做类似设计时以为只要配置了“参考时钟频率156.25MHz”就行结果实际板卡上对应管脚接的是一个普通IO最终链路起不来。把管脚约束修正后问题立刻消失。6.2 ARP缓存导致的不通换过FPGA工程后如果FPGA的MAC地址变了PC的ARP缓存不会立刻更新会一直往旧MAC地址发包结果就是Ping不通。这个坑特别容易出现在重新配置FPGA之后。排查手段很简单先在PC上执行arp -d清空ARP缓存再重新Ping。更长效的做法是把FPGA的MAC地址固定为板子上的丝印地址不要每次编译都随机改。这样即便工程更新PC端ARP缓存也不需要强制刷新。6.3 错误帧过滤不能省CMAC在接收方向会输出带FCS校验结果的信息如果接收到了CRC错误的帧不会主动帮你丢弃而是会打上错误标记送出来。如果在MAC层不做过滤UDP核会尝试解析这些坏帧导致统计计数出现莫名其妙的异常值。开源UDP核自带错误帧过滤逻辑但前提是你必须把对应的使能打开并且在MAC和UDP核之间保证错误标记信号正确传递。如果你跳过了MAC模块直接把CMAC接到UDP核就必须自己处理FCS错误帧的丢弃逻辑。6.4 小包打流的假象100G线速下64字节小包的包速率接近1.48亿包/秒。这个量级下FPGA可以处理但PC端软件协议栈很难完整接住iperf3的统计会严重掉包。我当时第一次测试用了小包丢包率惨不忍睹第一反应以为FPGA逻辑有问题。后来改用1400字节的包丢包率立刻变成0。这个经验告诉我评估高速UDP通路能力时必须明确包长配置不要用64B小包验证大带宽吞吐不然会得出完全错误的结论。真正需要小包高吞吐的场景要考虑专门的硬件加速方案而不是依靠软件打流工具。6.5 回环测试中的带宽翻倍问题在FPGA内部做自发自收回环测试时如果同时开启收包发送FPGA会一边接收PC发来的流量一边把同一份流量再发回PC。这样一来PC到FPGA的物理链路是100GFPGA返回到PC的链路也是100G但PC网卡看到的实际入站速率是两路之和如果PC端处理能力不足就会表现出“回环全速运行时大量丢包”的假象。所以设计回环测试时一定要明确测试目的是验证PCB链路还是验证协议栈。如果是验证协议栈最好做单向打流不要在FPGA内部同时做收发转发。最后再分享一个我个人的经验上板之前把能仿真的场景尽量在仿真里跑一遍特别是两个极端场景一个是包长刚好不是数据位宽整数倍的包边界情况另一个是ARP请求和UDP数据帧连续紧挨着的时序。仿真里多花半天时间上板之后能少熬三个晚上。100G UDP很吸引人但它的调试链路也比10G长不少把每一层都验证扎实才能真正做到一次上板跑通。希望这篇记录对正在做类似移植的朋友有帮助遇到具体问题也欢迎来交流。