尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

FPGA 100G UDP协议栈移植与上板调试全流程实战

发布时间:2026/9/5 12:37:47

资讯中心
01
ARTICLE

FPGA 100G UDP协议栈移植与上板调试全流程实战

FPGA 100G UDP协议栈移植与上板调试全流程实战
100G UDP在FPGA上跑通是很多搞高速数据采集和处理的朋友绕不开的一道坎。软件协议栈在CPU上处理10G都已经吃紧到了100G这个量级想靠通用处理器做线速UDP收发基本是痴人说梦所以把UDP协议栈卸载到FPGA上就成了非常自然的选择。这篇文章我结合自己做的一个开源移植项目完整记录从拿到代码到最终上板打满带宽的整个过程包括方案选型、模块裁剪、时序约束、上板调试、iperf3打流实测以及一堆文档里根本不会写的坑。这个项目适合正在做FPGA高速网络传输、想用100G UDP替代部分RDMA场景、或者纯粹想在自家板卡上把开源协议栈跑起来的朋友。无论你是刚接触UltraScale的高速串行收发器还是已经在用Xilinx 100G CMAC硬核但被用户侧接口折腾得头疼这篇文章都能给你一些可以直接抄作业的参考。1. 项目整体设计与技术选型1.1 为什么是UDP而不是TCP先聊一个基本问题做100G传输为什么选UDP而不是TCP很多人第一反应是TCP更可靠但FPGA上实现TCP的成本远超想象。TCP有连接状态机、序列号管理、滑动窗口、重传超时、拥塞控制这套东西在CPU上跑很成熟但在FPGA里实现逻辑资源消耗巨大时序收敛困难而且100G线速下的重传缓存需要极大的片外存储带宽。更重要的是很多实时传输场景根本不需要TCP那套可靠性保障——数据是流式的丢了下一帧还有重传旧数据反而会让接收端处理逻辑变得复杂。UDP就简单直接得多定长或不定长的数据报加上源端口、目的端口、长度、校验和完事了。FPGA里实现UDP协议栈本质上就是拼以太网头、IP头、UDP头再加上发送侧的校验和计算和接收侧的校验和校验。硬件逻辑做这件事几乎是白送的资源开销却能省掉CPU所有协议处理的开销把全部带宽留给数据本身。我这次选择的场景是典型的流式数据传输FPGA采集端产生高速数据流通过100G UDP打包发送给上位机。上位机只需要收不需要应答所以UDP天然适配。1.2 硬件平台与核心器件选型平台选的是Xilinx UltraScale家族的VU9P板卡是常见的VCU118。VU9P这颗芯片在高速接口方面的资源非常充沛有GTY收发器支持100GE的线速。关键的一点是UltraScale自带100G Ethernet MAC/PCS硬核简称CMAC这意味着100G的PCS层、FEC、MAC层都可以直接用硬核不需要在可编程逻辑里烧LUT去实现省下的资源全部留给用户逻辑。这里有个选型认知要纠正很多人以为100G就必须上GTY甚至GTM其实VU9P的GTY跑100G4路25.78125Gbps NRZ是完全没问题的到了200G/400G才需要GTM或者PAM4。如果只做单口100GVU9P性价比其实不错。时钟方案上100G CMAC的参考时钟是156.25MHz内部用户接口时钟是322.265625MHz。如果走512bit数据位宽线速下的用户逻辑时钟就是322.265625MHz。这意味着你的用户逻辑、FIFO、DDR控制器全部要在322MHz下稳定工作这对时序设计要求不低。1.3 开源协议栈选型对比市面上开源的FPGA以太网协议栈不少我调研了一圈真正能支持100G且维护活跃的也就那么两三个。Xilinx官方其实有CMAC AXI Ethernet的参考设计但是那一坨代码封装太深用户侧接口是AXI4-Stream没错但内部实现黑盒程度很高出问题根本不知道去哪查。对比之后我选了Alex Forencich维护的verilog-ethernet项目直接说几个选它的理由第一纯MIT协议商用无压力。第二架构极其清晰以太网MAC层、ARP、IP、UDP、TCP是分模块独立的你要UDP就只拿UDP的模块要裁剪非常容易。第三它直接支持100G CMAC硬核的接口适配内部模块用的是独立的时钟域交叉设计每个子系统都可以单独复位和单独时钟这对多时钟域的工程是巨大的友好。第四作者对Xilinx系的适配做得非常到位包括UltraScale的CMAC例化模板都给你写好了。对比下来verilog-ethernet的决定性优势是可读。出了问题你能打开RTL一层一层追而不是对着Xilinx的黑盒IP干瞪眼。这在实际调试中的价值谁调过谁知道。2. 移植适配过程与关键模块改造2.1 工程骨架与IP配置移植的第一步不是写代码而是把工程骨架搭对。Vivado版本我用的是2023.1器件选的xcvu9p-flga2104-2-i。IP方面需要例化以下几样东西100G Ethernet CMAC硬核IPQSFP28的GTY参考时钟如果需要缓存还要例化DDR4控制器AXI4-Stream Data Width Converter有时候CMAC是512bit用户逻辑想用256bit就需要它CMAC IP的配置有几个关键的坑。首先Clock Modes默认是独立时钟模式TX和RX使用各自的时钟配置界面里线速率选择100G参考时钟选156.25MHz。然后要勾选FEC100G LR4光模块一般用RS-FECReed-Solomon也就是CL91这个不勾的话长距离传输出错率会明显上升。另外Receive Flow Control和Priority-based Flow Control我建议先全部关掉等基本通路打通了再开否则调试阶段收到流控帧会产生各种奇奇怪怪的行为。RTL工程里顶层模块建议自己写把CMAC IP、verilog-ethernet里的UDP协议栈、你自己的用户逻辑、以及时钟和复位管理单元整合在一起。CMAC IP的例化模板可以从Vivado生成的例化文件里复制但需要手动改掉一部分引脚连接把tx_axis_*和rx_axis_*信号暴露出来接给协议栈。2.2 核心模块的裁剪与连接verilog-ethernet仓库拿出来里面的rtl目录结构大概是这样的rtl/ ├── eth_mac_10g/ # 10G MAC逻辑 ├── eth_mac_100g/ # 100G CMAC适配层 ├── eth_udp_complete/ # UDP完整协议栈包含ARP和ICMP ├── eth_ip/ # IPv4模块 ├── eth_arp/ # ARP模块 ├── lib/ # 公共库我的做法是只保留eth_mac_100g和eth_udp_complete这两个顶层文件然后手动把内部依赖的模块选出来。eth_udp_complete实际上是包含UDP、IP、ARP、ICMP的一套组合如果你像我一样只需要UDP收发其实可以更激进一些直接用eth_udp模块eth_ip模块eth_arp模块自己拼一个精简栈。但考虑到稳定性和排错成本第一次移植我建议还是用完整的eth_udp_complete后面确认通路没问题了再拆。端口连接方面eth_udp_complete对外暴露的是两组AXI4-Stream接口一组是给用户逻辑发送UDP数据用的input_axis一组是接收UDP数据给用户逻辑用的output_axis再加上一组独立的AXI4-Stream接口用来上报接收到的ARP/ICMP等非UDP报文。用户接口的位宽是512bit和CMAC的位宽对齐不需要额外的位宽转换。这里有一个特别容易被忽略的点eth_udp_complete内部是有独立的发送和接收FIFO的还有基于RAM的ARP缓存表。这些缓存用的都是Block RAM综合后资源大概占VU9P不到百分之几但如果你在资源极紧张的芯片上做可能要考虑把不用的功能裁掉。2.3 字节序与校验和的隐藏坑这个部分必须单独拿出来说因为90%的移植问题都出在这里。以太网协议栈在网络字节序大端下工作而FPGA内部处理数据时习惯用主机字节序小端到了AXI4-Stream这种数据流接口上就涉及一个非常容易晕的位序问题。从CMAC出来的数据tdata[511:0]对应的是连续64字节其中tdata[511:504]是第一个字节tdata[7:0]是最后一个字节。如果你把寄存器数组和tdata直接做位拼接顺序一定是反的。我在第一次上板测试时用Wireshark抓包看到PC发过来的UDP包目的端口是0x0B3D即十进制的2877结果FPGA解析出来端口变成了0x3D0B整个字节序倒过来了。后来发现是FPGA侧对AXI4-Stream的位映射规则理解偏了Verilog里数组用[0:63]声明而tdata是[511:0]直接assign tdata {array[0], array[1], ..., array[63]}这种写法会得到完全相反的顺序。正确做法是// 正确每个字节内的bit顺序不变字节间按顺序排列 genvar i; generate for (i 0; i 64; i i 1) begin : byte_swap assign tdata[i*8 : 8] array[i]; end endgenerate校验和是第二个大坑。UDP校验和计算覆盖伪头部伪IP头UDP头数据伪头部里的源IP、目的IP、协议号、UDP长度都是参与计算的。verilog-ethernet的UDP发送模块里自带校验和计算逻辑但它默认接受外部传入的源IP地址和目的IP地址参数使用时必须确保这两个值和实际组包时的IP头一致。我在验证时遇到过一种情况IP头的源IP地址是动态配置的而UDP校验和模块的tx_eth_ip_src端口忘了跟着改导致PC端Wireshark看到源IP正确但校验和报错全部UDP包被判为checksum incorrect。这个问题在loopback测试中完全暴露不出来只有真实跨设备传输时才会发现。2.4 复位与时钟域的正确管理UltraScale的CMAC硬核对复位时序有严格要求。CMAC IP的复位信号不能简单用全局复位手册里规定了gt_rxp_in和gt_rxn_in的稳定时序、gt_rxrecclkout的建立时间等一堆约束。实际移植时我直接用CMAC IP自带的cmen_rst_*信号和rx_axis_arstn、tx_axis_arstn并且在复位释放后至少等待10微秒再开始发送数据。verilog-ethernet的所有模块都有独立的clk和rst输入这就允许你把用户逻辑、UDP协议栈、CMAC接口分别放在不同的时钟域里。我采用了最简单稳妥的方案全工程强制使用CMAC的用户时钟tx_clk和rx_clk不额外做时钟域转换。当发送和接收使用同源时钟时FIFO的时钟域问题就不存在了整个数据通道的时序验证压力小很多。如果你的应用需要异步收发那必须在协议栈的FIFO接口处处理时钟域交叉不要试图让数据通路跨两个毫无关系的时钟域工作。3. 上板调试与实测过程3.1 第一步回环验证先别接光模块上板调试最重要的一条原则先回环再对打先低速链路再满速满载。不要一上电就插光模块对端打流否则问题混在一起根本无从排查。首先在Vivado里配置ILA探针挂在CMAC的tx_axis和rx_axis上。然后把CMAC的RX侧配置成内部回环模式串行收发器自己发自己收不经过光模块。具体方法是在CMAC IP配置界面把RX Loopback设置为1b1内部PMA回环或者通过AXI-Lite接口动态写寄存器。上板后从PC发UDP包如果回环通路正常ILA上能看到tx_axis和rx_axis数据完全一致。这个阶段的目的是验证三件事第一CMAC硬核是否正常初始化GTY的复位和时钟是否稳定第二verilog-ethernet的接收通道能把数据从AXI4-Stream里正确解析出来第三ILA的采样深度够不够别等你想抓数据的时候发现被触发条件错过。我实测下来回环阶段最常见的故障是rx_axis_tvalid一直拉不高或者rx_axis_tlast的位置不对。TLAST是AXI4-Stream的帧结束标志对于UDP包它应该出现在以太网帧的最后一个BEAT。如果你的FIFO配置成512bit宽度一个64字节的最小以太网帧只占一个BEATTLAST位置在beat内第几个字节由tkeep决定需要仔细检查。3.2 第二步打通FPGA到PC的物理链路回环正常之后接上QSFP28光模块和100G光缆或者DAC高速铜缆对端接PC的100G网卡。网卡我用的是Mellanox ConnectX-5驱动用的MLNX_OFED。这一步要确认的是物理层和链路层能正常建链ethtool ethX能看到Speed: 100000Mb/sLink detected: yes。这时候可以先不跑业务流量直接在PC上起tcpdump抓包同时在FPGA侧用简单的计数器循环发UDP包。从最简单的固定目的IP/端口发包开始每包长度1500字节每秒发几千个包观察PC端tcpdump是否能看到预期流量。如果tcpdump里什么都看不到先别急着怀疑协议栈优先查光模块链路。有个快速排查方法ethtool -S ethX | grep -E rx_error|rx_crc|rx_fcs|phy_link_status如果rx_crc_errors在涨说明物理层有误码或者FEC没有正常工作检查CMAC配置里的FEC是否开启。我踩过的一个典型坑是光模块和板卡兼容性问题。某品牌的100G光模块在VCU118上死活link不起来换了一根DAC线缆立刻就好了。后来查datasheet才发现那颗模块是PAM4的DR4光模块而VU9P的GTY只支持NRZ根本不对。所以选光模块之前一定要确认模块类型和板卡支持的编码方式一致。3.3 第三步PC到FPGA方向的数据接收上板验证两个方向都要测。FPGA发到PC只是验证了发送通路PC发到FPGA则是验证接收解析。接收方向最容易出问题的是MAC地址过滤verilog-ethernet的接收模块默认带目的MAC过滤只接收匹配的MAC地址。如果你没有把PC网卡的MAC地址加到FPGA的过滤表里那么PC发来的包在MAC层就被丢了UDP栈根本看不到。检查方法很简单——上板后在ILA里看rx_axis_tvalid有没有任何脉冲。如果一点都没有去查eth_rx_mac模块的过滤逻辑。我当时把这个过滤功能直接裁掉了因为单点100G应用场景里根本不在乎进来的是哪个MAC地址全都收。PC端发UDP包我用的是开源工具packetgen它比iperf3灵活可以自定义MAC地址、IP、端口、负载内容和发包速率。快速验证接收通路时我会在FPGA里放一个计数器模块对收到的UDP包数累加同时把最后一个收到的UDP负载内容送到ILA或者串口打印出来。PC不断发编号递增的数据包FPGA侧看编号是否连续从而判断有没有丢包。3.4 第四步跑iperf3 UDP打流看真实的线速带宽当PC到FPGA和FPGA到PC两个方向都单通后直接上iperf3 UDP打流测极限性能。iperf3的UDP模式没那么复杂先UP到PC方向# 在PC上作为接收端 iperf3 -s -p 5001 # 在FPGA侧通过串口控制台触发内部发包模块 # 或者用PC上的iperf3客户端直连FPGA发的目的地址但iperf3的UDP打流有个问题它默认不走线速。iperf3的UDP是固定速率模式需要指定-b参数。比如iperf3 -c 192.168.10.10 -u -b 100G -l 1400 -t 10 -p 5001-b 100G告诉iperf3要打满100G线速-l 1400设置UDP负载大小超过MTU会被分片所以一般是1400或者更小。实际测试中iperf3在100G下经常成为瓶颈——单线程很难打满100G需要多线程或者换用专门的高速打流工具。我自己测试时用单个iperf3线程能跑到大约60Gbps开4个线程才能接近95Gbps以上多出来的零头被协议栈和驱动吃掉了。值得注意的是iperf3客户端发送方向打流服务器接收方向如果服务器性能不行会丢包这不一定是FPGA的问题。更好用的方案是让FPGA侧作为源PC作为宿FPGA侧用内部计数器恒速发流这样带宽完全由FPGA的时钟决定可以精确测出99.99%线速下的丢包率。4. 性能测试与极限调优4.1 带宽和丢包率的真实边界FPGA做UDP协议栈的极大优势是可以做到确定性低延迟和零丢包——因为所有逻辑都固定了不会像CPU那样因为cache miss、中断调度、内存竞争导致处理延迟抖动。实测下来我的发送方向在连续满载发包的情况下PC接收端丢包率稳定在0.00%前提是负载长度不要接近MTU上限内的极限小包。小包情况比较考验性能64字节的最小以太网帧100G线速对应的包速率大约148.8Mpps每秒1亿4880万包换算成512bit位宽接口时钟就是每两个时钟周期要处理一个包。这个速率下包间隔很短任何处理延迟都可能导致FIFO溢出所以小包场景必须极致优化。我这个工程在小包场景下用固定流量测过丢包率还能控制在0.02%以内但如果你需要100%不丢任何数据建议把应用层的包大小控制在256字节以上给协议栈留一点余量。4.2 缓存、反压与流控设计取舍UDP协议栈本身的FIFO深度决定了它能承受多长的突发数据。verilog-ethernet里发送FIFO的深度可以在参数里配置我默认配成4096个512bit条目也就是大约2MB的缓存空间。看似很大但在100G线速下2MB缓存只够撑大约160微秒。如果你的上游数据模块持续阻塞超过160微秒发送FIFO就会溢出丢包。上游反压的问题是很多FPGA工程师容易忽略的UDP协议栈输出接口的tready信号是反压信号当FIFO满时它会拉低但上游模块必须能在几个时钟周期内响应反压否则照样丢数据。我在用户的DDR缓存管理模块里实现了基于水线的长度反馈当发送FIFO剩余空间低于阈值时提前停止DDR的读操作而不是等到FIFO满了才处理。流控方面CMAC自带的IEEE 802.3x pause帧流控在有的场景下很有用。如果你希望PC端能主动让FPGA暂停发送可以开启CMAC的接收流控功能FPGA收到pause帧后会自动暂停发送。不过这个功能默认不推荐开它会引入额外的延迟而且pause帧的精度在跨网段传输中无法保证。4.3 减法调优从96G到99.99%线速第一次打流测试发送方向只能跑到96Gbps多一点离线速总是差一口气。我通过ILA统计发现每个UDP包之间平均有大约70ns的空隙这个空隙来自协议栈组包时状态机的跳转开销。优化方法比较土但很有效在发送FIFO的读侧做到深度流水线寄存器插入让FIFO的读数据和协议栈的AXI4-Stream写入对齐状态机在TLAST之后立刻进入下一个包的组包状态不要有额外的等待周期。核心优化点如下把UDP头部校验和的计算流水化用组合逻辑在同一周期算完而不是等IP头全部构建好再算对tuser字段做了裁剪让数据路径的每个周期不需要处理无关的调试信号查表法预计算以太网类型字段和IP版本号减少每个包处理周期内的case分支开销。一通优化下来读侧的空隙从70ns压缩到30ns左右吞吐率提升到99.9%。后面还是觉得不够极致进一步把整个发送数据通路改成了完全的无气泡流水线设计实测线速下发送侧tvalid一直保持拉高几乎没有因为协议栈本身导致的节流。5. 常见问题与排查技巧实录5.1 PC收不到FPGA发的UDP包这个现象是最常见的排错顺序建议固定为物理链路 - MAC层 - IP层 - UDP层。物理链路ethtool ethX看speed和link状态ethtool -S看rx_errors和rx_crc_errors。如果是rx_errors在涨基本是物理层问题。MAC层检查FPGA发的目的MAC是否为PC网卡实际MAC地址可以在PC上ifconfig确认MAC然后让FPGA侧ARP先跑通。IP层检查目的IP是否是PC网卡所在子网的正确IP以及PC防火墙是否禁ping和禁UDP。UDP层确认端口号一致、校验和正确。Wireshark能看到包但标红说明校验和错优先查字节序。注意Windows防火墙很坑尤其是普通桌面版Windows的防火墙默认拦截所有入站UDP。建议调试阶段先用Linux系统或者直接在Windows上彻底关闭防火墙再测。5.2 FPGA收到PC发的UDP包但解析出来的数据不对优先怀疑字节序。我用ILA抓数据时发现从PC发来的UDP包目的IP地址是c0 a8 00 0a192.168.0.10但协议栈解析出来变成了IP地址字段完全错乱后来查出来是我在ILA上的字节映射反了。ILA显示的信号是按照Verilog vector的bit顺序排列的tdata[511:504]对应的是第一个字节而不是最后一个字节容易看晕。另一个高发问题是Checksum校验不过。如果PC是Windows系统默认UDP校验和可能没有开启FPGA需要对校验和为0的UDP包做兼容处理。verilog-ethernet的接收模块里默认检测校验和如果发现错误会把包丢弃我实测发现Windows下某些驱动发出的UDP包校验和确实可能为0所以要么强制开启网卡的UDP校验和卸载功能要么在FPGA侧跳过校验和的验证。5.3 100G link up了但收不到任何数据Link up说明物理层没问题问题在MAC层或者协议栈。依次排查看CMAC的tx_axis和rx_axis是否有数据活动。检查CMAC是否开了flow control收到pause帧后RX通路会被挂起。检查MAC地址过滤。verilog-ethernet的MAC层默认过滤目的MAC配置成promiscuous模式最简单。检查RX侧fcs错误计数。如果rx_fcs_errors一直增长说明物理层有误码可能是FEC没开或者光模块信号太差。我遇到过一次特别诡异的情况FPGA发出去的包PC tcpdump完全抓不到。折腾半天发现是PC网卡的SRIOV虚拟功能被启用了物理网卡的接收队列被VF占掉导致抓包接口不对。关掉SRIOV后恢复。这种跟FPGA本身没关系的问题排查起来最费时间。5.4 小包性能上不去或丢包严重小包性能瓶颈来自包处理速率。100G线速下64字节包大约148.8Mpps对FPGA来说每个包即使只处理几十纳秒也需要并行度。排查建议先用ILA统计每两个TLAST之间的周期数算出实际每包处理耗时跟理论值对比。如果单包处理耗时超过两个时钟周期约6.4ns就要考虑在状态机里做流水化改造。检查用户逻辑侧是否对每个包都有额外的握手开销比如每次都要等待DDR的仲裁授权。5.5 上板测试时序不收敛VU9P达到322MHz的用户时钟如果综合后WNSWorst Negative Slack为负首先检查CMAC的例化位置是否与用户逻辑相距过远导致布线长度过长。Vivado里可以使用Pblock约束强制把UDP协议栈和用户逻辑放在CMAC附近。另外verilog-ethernet库的代码本身就对时序做了充分优化尽量不要在它的内部模块上做改动。有时为了加调试信号增加的额外逻辑反而成为长路径瓶颈。ILA的插入也会显著影响时序调试完成后记得把ILA摘掉再重新综合做最终测试。6. 一些想补充的实操心得6.1 改动最小化原则开源代码移植最忌讳大改特改。verilog-ethernet的模块边界设计非常干净我建议你第一版移植时尽量不改任何内部RTL只做顶层连线。功能通了你再慢慢往里塞自己的需求。任何对协议栈内部的修改都会让后续跟踪官方仓库更新变得很痛苦。6.2 调试观测点要前置ILA的采样深度和触发条件要提前想好。100G线速下数据速率太高ILA默认深度的窗口只有几微秒很容易错过需要观察的偶发问题。我的习惯是在用户逻辑和协议栈的交接处各挂一级计数器把累计的包数、字节数、错误数寄存到一组AXI-Lite寄存器里然后通过UART或者PCIe把计数读到上位机这样比纯粹靠ILA抓波形高效得多。6.3 后续扩展空间这次移植跑通之后我在两个方向上做了扩展。一个是多通道汇聚四路25G输入汇聚成一路100G UDP输出这需要在协议栈前加一个轮询仲裁器。另一个是动态配置把源IP、目的IP、MAC地址、端口号全部做成寄存器可配置这样同一套固件可以适配不同网段的测试环境不需要每次改RTL重新综合。如果你也要做类似的事情我的建议是第一步先确保应用层的包格式是确定的BDF业务数据格式设计得越简单后面的调试就越省心。就拿这次的100G UDP项目来说真正写业务逻辑的时间可能只占三成其余七成全是在解决协议栈对接和上板调试的问题但这些坑踩过之后你会对整个系统有更深的理解至少下次换一块板子重新移植一天就能跑通。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。