十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

开源100G FPGA UDP协议栈移植与上板测试实战全记录

开源100G FPGA UDP协议栈移植与上板测试实战全记录 调试桌上那块带QSFP28光口的板子烧完bit之后最紧张的时刻就是等那个link状态寄存器翻转。第一次在100G链路上跑通UDP回环测试的时候PC端iperf3的输出从一堆乱码跳到稳定的80多Gbps实验室里几个人同时松了口气。这类“开源100G FPGA UDP协议栈移植上板测试”的活儿这几年在高速数据采集、网络测试仪、芯片验证平台里出现得越来越频繁。今天这篇就把整个移植和测试过程拆开揉碎从设计思路到实测步骤到踩坑记录一次性讲清楚。先说说这篇文章适合谁看。如果你手头正好有一块带100G光口的FPGA板卡想基于开源工程跑通一套UDP传输链路或者你已经在用Xilinx CMAC这类硬核但不知道怎么把整套UDP协议栈正确接到自己的用户逻辑上又或者你只是想了解100G以太网在FPGA上到底怎么测、怎么排查问题——那这篇应该能给你省下不少走弯路的时间。我自己也是从“明明改了几行代码上板怎么就不通”的状态过来的所以这篇会写得格外啰嗦把那些文档里不会写的细节都补上。1. 为什么要在FPGA上折腾100G UDP1.1 这个活儿到底解决什么问题先说需求来源。现在很多场景需要把大量数据从采集端搬到处理端或存储端比如高速ADC采样后的原始数据回传、雷达信号处理前端的数据分发、网络流量监控设备里的线速抓包。这些场景共同的特点是数据量特别大动辄几十Gbps起步而且对延迟敏感、对突发容忍度低。用CPU加普通网卡做这种转发不是不行但得面对一个现实问题操作系统协议栈处理UDP小包时中断开销、内存拷贝、系统调用占比极高。实测下来单核CPU处理万兆线速小包就已经很吃力100G线速更是连想都不用想。TCP有状态、有拥塞控制、有重传对CPU的压力更大。所以这类高速数据面天然适合用FPGA做协议卸载把以太网帧解析、IP校验、UDP端口匹配这些重复劳动全部下沉到硬件逻辑里CPU只负责高层的业务处理。1.2 UDP比TCP简单在哪又难在哪UDP比TCP简单这句话本身没错。无连接、无状态、没有三次握手也没有拥塞窗口在纯硬件逻辑里实现UDP收发本质上就是做字段匹配和安全过滤识别以太网类型字段是0x0800IP头协议字段是17端口号对上数据就收下来转发出去的时候再把这些字段按规则填回去顺手把校验和算了。整个状态机加起来也就几百行代码的事。但简单不代表没有坑。最大一个坑是校验和IPv4头校验和是必须正确计算的UDP校验和在IPv4下虽然可以填0但很多网卡和软件工具会因此误判或干脆悄悄丢弃。其次是ARPPC要往FPGA发数据第一步永远是发ARP请求问“谁有这个IP”如果FPGA不回ARP或者回得不正确后面一切都是白搭。还有个容易忽略的点是包长计算硬件逻辑里包长算错一位整包就会被对端判定为FCS错误直接丢掉且没有任何日志提示。1.3 为什么说“移植”比“从零写”更考验功夫开源工程的价值在于它把MAC、PCS、PMA这层最痛苦的东西都封装好了。自己从空工程开始写100G以太网要处理GT收发器配置、PCS对齐、FEC、时钟恢复光把这些跑通就够写一个月的调试日志。而开源方案里比如基于Xilinx CMAC和GT的工程已经把底层打通用户只需要面对UDP层和用户逻辑接口。麻烦的地方在于移植。每个板卡的GT位置不同、参考时钟频率不同、引脚约束不同、器件型号可能也不同这些底层差异全堆在工程适配里。我见过不少人卡在“代码逻辑一点没改就是GT起不来”的问题上问题根源往往是xdc约束里某个GT引脚的位置和实际板卡设计对不上或者参考时钟的频率配置差了0.01%。所以移植这件事表面上是在改顶层文件和约束实际上是在做硬件适配的解耦这比写协议栈本身更需要耐心。2. 移植前必须做好的三项准备2.1 先吃透开源工程的架构再动手拿到一个开源UDP工程别急着改代码先花半天时间把架构理清楚。一般这类工程的顶层结构可以分成三层物理层GT收发器、PCS/PMA、MAC核、协议层收发解析、ARP、ICMP、UDP端口匹配、用户接口层通常是一组AXI-Stream接口加若干控制寄存器。我常用的梳理办法是用编辑器自带的大纲功能把模块列表导出来然后照着数据流画一条线光纤进来、GT恢复数据、MAC剥离前导码和FCS、IP/UDP解析判断是不是发给本机的包、用户逻辑处理完再原路打包回去。画完之后你会发现真正需要改的模块其实只有三类硬件相关的IP核配置、地址参数、用户逻辑接口。如果工程里带testbench建议先跑一遍仿真确认在原始配置下收发波形正常再开始动刀这一步能帮你排除“工程本身有问题”的干扰。2.2 硬件平台的核对清单移植前先确认板卡能不能支撑100G链路我把核对项列成一个表建议逐项打钩核对项典型要求注意事项FPGA器件支持100G Ethernet带100G CMAC或足够GT资源芯片选型直接决定用软核还是硬核GT参考时钟100G常用156.25MHz或161.1328125MHz频率不对GT根本无法锁定参考时钟光模块/光口形态QSFP28四路25G或CFP2确认板卡上的连接器类型供电与散热100G光模块功耗不低FPGA逻辑利用率高时发热明显USB供电的板子基本不用考虑用户逻辑带宽若接口是512bit 250MHz理论带宽就是100G接口位宽与时钟频率的乘积必须大于线速如果板卡是QSFP28接口而开源工程写的是CFP2引脚这一步就需要把引脚关心映射全部重做。我遇到过一次板卡明明是QSFP28工程约束却写的CFP2结果GT的rx_eq参数全都对不上链路指示信号死活不翻转。2.3 需要修改的四类文件真正需要动手改的文件归纳起来就那么几类我按容易出错的程度排个序第一是物理约束文件xdc。这里面包括GT所在bank的位置、参考时钟引脚、复位按键、指示灯以及网口物理地址。第二是IP核配置。如果工程用的是Xilinx 100G Ethernet Subsystem需要确认器件型号和GT参考时钟频率是否与板卡实际一致不一致就得重新生成IP核。第三是地址参数表。MAC地址、IP地址、端口号、ARP表项数量这些通常在单独一个参数包文件里改起来不难但很容易漏改其中某一处导致微妙的故障。第四是用户接口逻辑。这块和你具体的业务有关但刚开始做回环测试时通常不需要大改直接用工程自带的回环FIFO即可。一个比较容易漏掉的点很多开源工程会把GT参考时钟的约束写死在xdc里如果板卡的时钟引脚和工程默认不一样Vivado综合起来经常报错在非常底层的地方提示信息也晦涩。这时候可以用get_pins去查GT的时钟输入网络逐级逆推是哪里的约束不匹配比盲目改代码高效得多。3. 上板测试全过程详解3.1 第一轮测试从空载到链路稳定上板测试的第一步不是直接打流量而是先确认物理链路是健康状态。烧录bit之后我习惯先看几个关键信号GT的resetdone是否拉高、CMAC的tx/rx link状态是否是up、rx clock是否稳定。工程里一般会有状态寄存器也可以通过ILA集成逻辑分析仪直接抓内部信号看。确认IP核链路正常之后做一次“软回环”测试。软回环的意思是让数据刚进入MAC层就直接折返不经过物理光纤。这时候如果PC端的网卡状态显示已连接说明PC和FPGA之间的光模块、光纤、链路协商都正常。接下来再试物理回环也就是用一根光纤把QSFP28的TX和RX短路或者让光模块自环。这个测试用来确认光模块本身和GT收发通路没问题。这轮测试常见的异常是link status一直在up和down之间跳变。最典型的原因是光模块没插到位其次是因为IDELAY或RX均衡参数不匹配。遇到跳变先别急着怀疑代码把光模块重新插拔一次、看看模块指示灯把这类物理因素先排除掉再上逻辑分析仪。3.2 第二轮测试PC端UDP小包验证通路链路通了之后下一步就是最基础的协议层连通性测试。把PC网卡IP设为192.168.50.1子网掩码255.255.255.0FPGA侧IP设为192.168.50.100。首先要做的不是发UDP而是先ping一下。ping走的是ICMP协议它能通说明三件事ARP请求和应答正常、IP层路由正常、FPGA的MAC帧收发正常。如果ping不通后面UDP肯定也通不了而且此时UDP测试会遇到搜热词里常看到的“packets received本机一共收到多少个UDP数据包、packets to unknown port receive”这类连蒙带猜的排查方式。正确做法是用Wireshark先抓包看ARP请求有没有回复如果有回复但ping不通再把过滤器切到icmp看ICMP echo request有没有到达FPGA、FPGA有没有发出echo reply。ping通之后用UDP网络调试工具测试小包回环。PC向FPGA的端口比如默认的5001发送一串自增数据FPGA收到后原封不动地回发到PCPC端能在同一个工具里收到完整回包说明UDP接收解析、端口匹配、发送回包路径全链路正常。这一步要注意的是如果FPGA工程里配置了“仅允许特定端口”而调试工具用的是另一个源端口回包会被硬件短截掉现象就是PC能收到FPGA发来的数据但回包数据全丢。3.3 第三轮测试iperf3 UDP打流压带宽小包回环通了之后才到了真正考验性能的阶段。iperf3是目前最常用的打流工具UDP模式下用它来测极限带宽非常方便。我常用的命令是这样的iperf3 -c 192.168.50.100 -u -b 80G -l 8k -t 60参数的含义是以UDP模式向192.168.50.100发送数据目标带宽80Gbps包长8KB巨帧模式持续60秒。对于100G链路建议先跑一个较低的带宽比如20G确认链路稳定再逐步往上加。我这里写80G是因为100G线速下UDP包本身还带着额外的以太网头、IP头、UDP头以及帧间隙应用层有效载荷通常达不到100G整。这里回答一下很多新手都会问的问题iperf3跑UDP时该看sender端还是receiver端答案是发送速率看sender端丢包和实际接收速率必须看receiver端。UDP没有确认机制sender端永远只会报告“我发出了多少”根本不知道对端有没有收到。真正反映链路质量的是receiver端最后一行汇总里面会明确显示“received XX MBytes/sec”和lost包数。有一回我在实验室测数据sender端显示80Gbps让我兴奋了半天切到receiver端一看丢包率35%整个过程白测了。还有一点必须提醒跑满100G UDP打流时PC端这一侧极容易成为瓶颈。Windows系统默认的UDP接收缓冲区非常小在大流量下接收端内核还没来得及把数据交到应用层缓冲区就满了然后开始疯狂丢包。Linux下可以临时调大UDP缓冲区sysctl -w net.core.rmem_max134217728 sysctl -w net.core.rmem_default134217728Windows下则需要在注册表里调整全局UDP系统缓存区。搜热词里有一条“修改window全局udp系统缓存区”就是这个坑。具体位置在HKLM\SYSTEM\CurrentControlSet\Services\AFD\Parameters把DefaultReceiveWindow和DefaultSendWindow调大然后重启。如果测下来PC端丢包率特别高而FPGA端带宽正常十有八九是操作系统缓存的问题而不是FPGA逻辑的问题。3.4 Wireshark抓包辅助定位问题性能测试中出现丢包或带宽不达标时Wireshark是排查利器。但我发现很多人用过滤器的姿势不对最常见的一个困惑是我已经设置了过滤条件为udp为什么还是抓到了ICMP包这个问题其实不复杂Wireshark的过滤条件只决定“显示”哪些包而不能在抓包环节把其他包完全排除。抓包文件里其实包含链路上所有的包ARP、ICMP也都存在只是过滤条件把它们隐藏了而已。所以当你设置udp过滤器之后看到ICMP或ARP时不要惊讶它们一直是存在的只是之前没显示出来。遇到丢包或异常的时候我习惯用的组合过滤器是udp or icmp or arp或者更精确一点只看特定IP的流量ip.addr 192.168.50.1 (udp or icmp or arp)抓包时还有一个操作细节不建议在打流的同时开Wireshark在PC上抓全量数据因为100G流量会把PC网卡和Wireshark的抓包引擎直接压垮反而制造假丢包。正确做法是在FPGA逻辑里专门设置一个采样口只把需要分析的部分包复制出来给到Wireshark比如每1024个包抽1个在硬件上做“抽头采样”这样才能在不干扰主链路的情况下看清楚问题。4. 实战踩坑记录常见问题与排查思路4.1 链路是up的ping却不通这是最邪门的一类问题物理链路一切正常GT、CMAC、link状态全部正常但PC就是ping不通FPGA。我的排查顺序一般是固定的先看Wireshark里ARP请求有没有被抓到有没有ARP应答。如果应答有了但ping还是不通再单独看ICMP echo request是否到达FPGA、FPGA是否发出了echo reply。实测中这个现象最常见的原因是ARP应答报文内部的IP地址和MAC地址填反或者填错了。比如开源工程默认参数是MAC地址11:22:33:44:55:66你只改了IP没改MAC而网络环境里恰好另一个设备也是这个MACPC的ARP缓存表就会陷入混乱。排查方法是先清空PC端ARP缓存arp -d再重新ping同时Wireshark盯住ARP应答里的MAC地址是不是FPGA预期的。另一个容易出问题的地方是巨型帧设置。如果FPGA侧配置了MTU 9000而PC网卡没开巨帧那么PC发出的标准MTU包FPGA能正常处理但FPGA回的大包PC网卡可能会直接丢弃表现为“小包通、大包断”。这种情况在ping测试里看得特别明显ping默认payload 32字节能通一旦增加ping大小到1473字节以上就不通那基本就是MTU协商问题。4.2 小包正常、大包打流时疯狂丢包小包回环测试通过之后一上iperf3大流量就露馅这类问题占我调试经历的一半以上。要先分清楚丢包发生在哪个环节在FPGA接收路径、FPGA发送路径、还是PC接收路径。最简单方法是FPGA内部做一个计数回读统计收到的包数和发回的包数再看receiver端的丢包数对不上哪一侧。如果FPGA接收侧丢包先查用户逻辑的FIFO深度是不是不够。100G线速下即使只有10微秒的阻塞也会积压大约125KB的数据。有的开源工程默认FIFO深度设计的跑40G没问题换到100G就扛不住突发这时就要把FIFO换成分布式RAM或BRAM的异步FIFO并注意跨时钟域的亚稳态问题。如果发送侧丢包更可能是发送接口的背压没处理好。AXI-Stream的tready信号在跨时钟域时如果打拍方式不对会导致上游一拍之内接收方已经反压但上游还在发送这个丢包是单拍的很难肉眼发现。建议在用户逻辑和协议栈之间的每个AXI-Stream接口上都挂一个计数寄存器通过后台寄存器读回来看丢包差值。4.3 ARP应答时灵时不灵ARP表项有老化时间所以ARP应答时灵时不灵通常表现为刚ping通一次再隔几分钟ping又不通了。排查时先清空PC的ARP缓存然后Wireshark连续抓包看FPGA是否在收到ARP请求后稳定回包。翻看抓包时我遇到过一个问题PC发出来的ARP请求是广播包目标MAC是全FFF:FF:FF:FF:FF:FF但FPGA在接收路径上对广播包的处理有bug把它当成普通单播包过滤掉了。这种情况在代码里看接收解析模块对目的MAC的判断条件是“等于本机MAC才接收”把广播包直接丢弃。修复方法很简单判断条件加上“或者目的MAC为全F就接收”。这类问题在以太网小包测试中不一定暴露出来因为有的调试工具在发UDP前会自动先发ARP请求ARP不通UDP发包工具会一直干等表现为“发出去一堆包但一个回包都没有”。4.4 与周边接口联调时的坑协议栈本身测通了一接到用户业务逻辑就开始出问题比如通过FMC接口和另一块板卡联调时数据对不上。这时候要意识到很多问题并不是UDP移植本身造成的而是跨板级联调时字节序、位宽对齐的差异被协议栈暴露了出来。这里有个很实用的技巧在FPGA逻辑里加一个“数据源模块”它可以产生固定的伪随机序列比如PRBS作为UDP负载。联调时先用PRBS模式互发数据对比两端解出的序列种子是否一致就能快速定位是不是字节序、位宽或对齐出错。如果PRBS通而真实业务不通再把锅甩给业务逻辑否则大概率是传输链路的基础配置没磨合好。还有个大家容易忽视的小工具串口打印。FPGA上留一个UART调试口把关键状态寄存器定期打印出来联调时不用每次改逻辑重新综合直接看串口输出就能判断当前状态机卡在哪一步。这个习惯帮我省了不少时间尤其是FMC联调时板卡分属两个工位来回跑不现实。5. 性能调优与后续扩展思路5.1 让100G UDP真正逼近线速的几个技巧把UDP跑通之后大家都会想再压一压极限性能。首先是巨帧把MTU从1500改到9000每包携带的有效载荷变大CPU中断次数和MAC层处理开销都会被摊薄CPU侧的接收性能会明显提升。FPGA侧逻辑不变但PC侧接收端性能通常能从30~40G提升到70~80G以上。其次是关注包率瓶颈问题。100G线速下如果全是64字节小包每秒需要处理的包数高达148.8Mpps这个数字对于FPGA内部的解析逻辑是巨大压力。而如果全是1518字节标准包包率只有8.1Mpps左右解析逻辑压力小一个数量级。所以打流测试时用MTU 9000的巨型帧通常最能逼近线速而用64字节小包打满线速是最严苛的考验。再说用户接口的位宽选择。100G以太网的用户接口通常是512bit跑250MHz就是128Gbps的理论带宽已经覆盖了线速。但如果你的用户逻辑接的是256bit接口就得跑500MHz时序收敛难度直线上升。所以能用高接口位宽就不要挤在那个临界频率上给时序多留一点余量板上测试会省心很多。5.2 移植之后的几个扩展方向UDP跑通后这个工程的扩展空间非常大。最常见的方向是加PCIe DMA接口把UDP收到的数据直接搬到主机内存这就变成了一个简易的100G智能网卡雏形。另一个方向是改成多通道UDP把不同业务流的端口区分开各自送到不同的用户逻辑模块这在多业务数据分发场景下非常实用。还可以往更高层协议栈扩展比如在UDP之上做RDMA风格的可靠传输。UDP本身不可靠但通过硬件实现类似重传机制可以在保证带宽的前提下获得接近TCP的可靠性这也是很多FPGA存储阵列的常见做法。这些方向各有各的坑但有了一个能稳定上板的100G UDP底座之后后续开发就是在这个底座上加积木的事。5.3 一点实操体会从这次移植上板测试里最深的感触是开源工程给了很好的起点但真正花时间的不是跑通而是理解工程里每一条数据路径和每一个握手信号的因果。跑仿真只能验证逻辑行为上板测试才是把行为映射到物理世界的终极检验。建议把IP地址、MAC地址这类参数在工程里做成宏定义而不是散落在代码各处方便Debug时快速切换。另一点是版本管理改任何一句逻辑之前先提交一个初始版本上板测试前记录git commit号因为当你同时改了协议栈和用户逻辑测试失败时需要确切知道哪一次改动导致了问题。工程日志写得越细排查越快这是所有FPGA项目通用的法则。
返回列表