ARTICLE DETAIL

资讯详情

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

FPGA实现100G UDP协议栈:从开源选型到上板调通全流程解析

FPGA实现100G UDP协议栈:从开源选型到上板调通全流程解析 100G UDP在FPGA上跑通这事听起来挺硬核但只要找对开源方案、捋清移植思路其实比很多人想象中要接地气得多。我前阵子刚把一个开源的UDP协议栈搬上自己的板子从100G光口到DMA再到上位机打流整个链路调通之后那种感觉确实很舒服。这篇文章就把我从选型到上板测试的完整过程拆开揉碎了讲适合正在搞FPGA网络加速、想做高速数据传输、或者单纯想看看100G UDP到底怎么落地的朋友。1. 项目整体设计与开源方案选型1.1 为什么用FPGA做100G UDP先聊一个最基本的认知问题为什么高速UDP传输场景里FPGA几乎是绕不开的选择CPU跑100G UDP不是不行但代价太高。一台通用服务器跑满100G线速的UDP收发CPU占用率基本会被打到接近极限而且延迟抖动很大数据包处理路径上的每一次内存拷贝、中断处理、协议栈解析都会吃掉大量性能。FPGA不一样它的数据通路是硬件逻辑包进来之后走的是流水线不需要操作系统调度一个包从光口到应用接口的延迟能做到微秒级甚至亚微秒级。UDP本身又是传输层协议里最适合硬件实现的无连接、无状态、没有重传机制和拥塞控制逻辑上就是“封装IP头校验和”就发出去收到包就解头、校验、把Payload丢给上层。这套逻辑在FPGA里展开之后几百兆赫兹的时钟域里每一个时钟周期都能处理一个数据字配合100G以太网的MAC和PCS层理论上就能跑满线速。这也是为什么很多金融高频交易、数据中心无损网络、科研数据采集系统都在用FPGA做UDP卸载和加速。1.2 怎么选一个靠谱的开源UDP协议栈开源FPGA UDP方案在GitHub上其实不少但质量参差不齐。我这趟踩了一圈之后筛选标准基本可以总结成四条一看MAC层是否完整二看接口是否标准三看时序是否收敛四看有没有上板验证记录。MAC层完整意味着你不需要自己写PCS/PMA的适配接口标准意味着能挂到Xilinx或Intel的原生IP上时序收敛意味着不是在仿真里能跑、上板就崩的玩具。综合下来我选型时重点看了两个方向。一个方向是基于Verilog的轻量级UDP offload引擎这类方案一般自带MAC和UDP封装逻辑代码结构清晰适合做定制修改。另一个方向是完整的开源NIC方案比如Corundum这个项目它把DMA、队列管理、MAC、UDP/TCP卸载全部做进去了功能完整但复杂度高移植周期长。考虑到我是要做100G UDP的定向验证最终选了前一类方案做基础再对DMA和时钟模块做定制修改。如果你还没定方向建议先把自己手头的FPGA型号列出来再根据已有的PCIe硬核、GT收发器资源去反推选型这样能少走很多弯路。1.3 整体架构梳理整个系统从上到下大概是这么个结构QSFP28光模块接收到的光信号进入GTY收发器经过PCS/PMA层完成SerDes到并行数据的转换然后是100G MAC核做以太网帧的解析和封装接着进入UDP offload模块它会检查IP头和UDP头把有效的Payload提取出来写入FIFO或DMA描述符指向的内存区域CPU那边通过PCIe总线把数据读走。发送方向就是反过来CPU写数据到内存DMA把数据搬到FPGA侧UDP offload模块负责填充以太网头、IP头、UDP头并计算校验和然后经过MAC送到光模块发出去。这里面UDP offload模块是核心也是最需要花精力理解的地方。它本质上是一个状态机加一组校验算法状态机分为收包解析和发包封装两个方向校验算法则要处理IP头校验和与UDP校验和。很多人觉得校验和计算很简单求和取反而已但放在100G速率下每一个时钟周期要处理512bit的数据校验和的累加逻辑就得拆成多级流水线否则时序直接崩掉。后面我会详细讲这块的实现细节。2. 移植到FPGA的关键步骤与细节处理2.1 从开源仓库到本地工程的搬运技巧开源代码拿到手第一件事不是急着综合而是先把工程结构与自己的开发环境对齐。100G UDP相关的开源项目往往会依赖特定版本的Vivado或Quartus IP核直接打开工程大概率会报IP版本不匹配所以我的习惯是自己新建工程把RTL源码、约束文件、IP配置脚本分开导入。这样能确保后续无论是换芯片型号还是换工具版本都不会被原工程的残留配置绑架。导入RTL源码的时候有一点要特别注意很多开源项目里会混着仿真专用的文件、测试平台、脚本它们引用的库在综合环境里并不存在。我通常的做法是先只添加rtl目录下的文件仿真文件单独放在另一个目录等到做仿真测试时再引用。如果项目里带了.xci或.qip这种IP配置文件先在原工程里确认一下IP的版本和参数再在新建工程里按同配置重新生成这样能避开IP核锁版本的问题。2.2 时序约束与时钟域处理100G UDP移植过程中最容易让新手心态崩掉的就是时序收敛问题。100G以太网MAC层的用户接口通常是512bit位宽对应逻辑时钟是322.265625MHz这是一个非常高的频率。再加上DMA模块可能需要跑在250MHz或更高多个时钟域之间的数据交互就成了约束的重灾区。我的做法是先把所有时钟域理清楚画出时钟树GT参考时钟进来之后经过QPLL或FPLLL产生SerDes的并行时钟MAC核在此基础上产生用户接口时钟rx_clk和tx_clkDMA和用户逻辑则跑在独立的axi_clk上。跨时钟域的数据通路全部用异步FIFO隔离绝不写跨时钟域的直接赋值。时序约束方面create_clock把每个时钟都定义清楚set_clock_groups -asynchronous声明无关时钟域让工具在布局布线时有更明确的优化目标。如果你做完这些之后时序还是不过优先检查GT收发器的时钟配置特别是rx_clk和tx_clk是不是来自GT的rxoutclk和txoutclk而不是由MMCM凭空产生的。很多人在这里图省事用MMCM分频出322MHz结果时钟抖动和相位噪声不达标时序报告里就是一片红。这个坑我当年也踩过一定要用GT输出的时钟走BUFG再进全局时钟网络。2.3 UDP校验和模块的实现与调优UDP校验和的计算范围包括伪IP头、UDP头和Payload伪IP头里有源IP、目的IP、协议号、UDP长度这些字段。硬件计算校验和的经典思路是16位补码求和但100G吞吐下数据是一个时钟周期512bit进来的这个求和就必须拆成多级加法树。我当时实现时是这样处理的把512bit按16bit拆成32个部分和第一级用16个加法器两两相加得到16个16bit结果第二级8个加法器第三级4个第四级2个第五级1个五级流水下来延迟大约5个时钟周期最高能跑到400MHz以上。关键点在于每一级的进位都要回卷加到最低位不然补码求和的结果就是错的。这个回卷逻辑如果写成纯组合逻辑很容易成为时序路径上的关键瓶颈所以我把回卷单独拆了一拍来做。仿真的时候特别构造了全0xFFFF的边界用例和奇数长度包确保回卷逻辑没有漏。2.4 DMA与PCIe接口对接的注意事项如果你的100G UDP方案需要和CPU交换数据那DMA就是绕不开的一环。我用的方案是把开源UDP栈的用户接口接到自研的DMA写读引擎上再通过Xilinx的XDMA IP挂到PCIe总线上。这里最容易被忽视的是描述符的格式和内存屏障问题。描述符里包含了地址、长度、状态、标志位这些字段CPU和FPGA之间通过描述符通信但编译器和CPU缓存可能会对描述符的读写顺序做优化导致FPGA看到的数据不一致。我的做法是在描述符的头部和尾部各加一个valid标志位CPU写完描述符的最后一步才置位validFPGA轮询到valid为1才开始读描述符内容处理完成之后再清掉valid。这其实就是最简的硬件内存屏障。还有一个实际问题是DMA地址对齐。100G UDP包的Payload长度不固定但DMA传输如果按512bit对齐来搬运效率会高很多。所以我做了一层简单的组包逻辑DMA传输总是按对齐地址发起Payload数据在FPGA内部做一个短FIFO缓冲达到对齐长度后再触发DMA最后的残余字节单独处理。这个方法牺牲了一点延迟但换来了DMA效率的显著提升实测下来非常划算。3. 上板测试的完整流程与测试工具3.1 测试环境搭建上板测试之前环境准备决定了你能多快定位问题。我的测试环境其实不复杂一块带QSFP28接口的FPGA板卡一台有100G网卡的服务器一根直连光缆或者一对光模块加光纤。如果服务器端暂时没有100G网卡也可以用两块FPGA板卡背靠背对测但那样定位问题会麻烦一些因为两侧都是黑盒。如果条件允许我强烈建议你准备一个支持100G的交换机哪怕只有两个口也行。有了交换机之后FPGA板卡和服务器都接到交换机上这样就能在链路上插入抓包工具无论是用交换机的端口镜像功能还是外接一个分光器接网络测试仪都能极大提升排查效率。没有交换机的直连模式也能跑通基本功能但一旦出现丢包或者错包你只能用链路另一端的统计信息来反推定位问题会很吃力。3.2 板级调试的启动流程拿到板卡之后别急着直接跑UDP打流先把底层链路验证通了再说。我的启动流程分四步先跑IBERT测试GT收发器的信号完整性确保光模块到FPGA引脚的物理链路没有误码然后跑一个简单的Loopback测试把MAC核配成内部环回模式发什么收什么验证MAC层逻辑没有问题接着是外部光口环回用一根光纤把TX和RX短接验证光模块的收发链路正常最后才是让UDP模块真正工作在FPGA内部自发包自收包确认协议栈逻辑没问题之后再和服务器对接。这四步看起来繁琐但每一步都能筛掉一大类问题。尤其是IBERT如果GT通道的信号完整性不过关后面所有调试都会非常痛苦。IBERT测试的时候重点看眼图和误码率100G速率下误码率至少要低于1e-15才算正常如果高于这个量级检查光模块的型号、光纤的洁净度、GT的TX驱动电流和接收均衡参数这几个地方是高频问题区。3.3 用iperf3和Wireshark做UDP打流验证服务器端测试工具我首推iperf3它支持UDP模式可以指定带宽、包长、时长和并发数是验证网络性能和丢包率的利器。测试的时候我一般这样操作服务端先启动iperf3 -s -p 5201客户端再发起UDP打流iperf3 -c 服务器IP -u -b 100G -l 1400 -t 60。这里-b 100G是目标带宽-l 1400是UDP Payload大小-t 60是持续60秒。iperf3跑完之后会输出接收端的吞吐量和丢包统计比如lost/total这个数字如果很大说明链路有丢包jitter如果很大说明抖动严重可能跟DMA的批处理策略或者时钟稳定性有关。如果你不想用iperf3也可以用专门的UDP测试工具发包但注意很多工具在高速率下会因为CPU瓶颈而发不满带宽导致测试结果失真。我建议至少用两个不同的工具做交叉验证避免误判。Wireshark在100G链路上直接抓包比较吃力因为抓包本身会丢包但可以拿它来验证低速率的控制面流量比如ARP、ICMP或者用udp.portxxxx的过滤条件抓一小段时间的小流量包做协议分析。真正的高速率性能测试还是得看网卡侧和FPGA侧的统计计数器两边数据对上了才说明链路是健康的。3.4 结果解读与性能数据我这次上板测试在没有优化和开启DMA批量搬运的情况下UDP收发速率大概是97Gbps左右线速100G的利用率约97%丢包率在长时间打流下是0。这个数值其实还有提升空间主要体现在两个方面一是接收方向如果每次来包都触发一次中断通知CPU中断频率太高会消耗大量CPU资源改成中断聚合比如每收到N个包或每累计M微秒才发一次中断能明显降低CPU占用二是DMA描述符如果支持多队列并行处理可以把吞吐进一步往上推。为了验证这个判断我把中断聚合时间从0调到100微秒后重测CPU占用率下降了很多而吞吐量几乎没有变化因为数据本来就是突发性的聚合中断不会增加明显延迟。如果你测试时发现吞吐量上不去优先检查DMA burst长度和中断频率这两个点是最常见的瓶颈所在。4. 常见问题与排查技巧实录4.1 上电后光口link不起来光模块的link状态是很多人的第一个坎。插上光纤之后FPGA侧显示link down服务器网卡也显示无连接。这类问题九成出在物理层配置上先看GT参考时钟是否稳定、光模块的电源供电是否足够、复位时序是否满足要求。另一个经常踩的坑是GT收发器的TX端接电阻配置不对导致信号摆幅太小对端识别不到。用IBERT跑一遍眼图测试立刻就能看出来是信号质量问题还是配置问题。还有一类情况是对端是交换机或者服务器网卡它们的自协商机制可能和FPGA侧的强制模式不兼容。100G以太网有RS-FEC和Fire Code FEC等几种模式两端如果不一致link就会反复up/down。解决办法是在FPGA侧固定FEC模式同时在对端网卡上做相同配置禁止自协商。4.2 校验和不对导致收包异常收包异常但链路是通的Wireshark也抓到包了但Payload数据错乱甚至被系统直接丢弃这种问题优先查UDP校验和。以太网帧里IP头和UDP头都有校验字段任何一个算错了接收端的协议栈都可能把包丢掉。硬件计算校验和的时候最容易出错的是补码回卷逻辑和伪IP头字节序问题。我排查这类问题时习惯先在FPGA侧把收到的原始帧完整存下来再和服务器端抓包的结果逐字节比对确认FPGA发出的帧格式完全正确。如果发现伪IP头里的源/目的IP写反了或者长度字段没有包含UDP头本身的8字节很快就能定位。建议在代码里把这些字段做成寄存器软件可以动态配置这样调试时不用反复编译工程。4.3 丢包率居高不下怎么定位如果链路通的但长时间打流丢包率很高问题往往不在PHY层而在缓冲和流量控制环节。先看接收路径MAC收到包之后是不是瞬间写入FIFOFIFO满了之后后续包有没有被丢弃的策略如果是DMA搬运速度跟不上那就是DMA的吞吐瓶颈需要优化burst长度和描述符的预取深度。再看发送路径CPU发数据的速率是不是超过了对端处理能力UDP没有流控机制只能靠应用层自己限速如果上层应用不管不顾地猛发丢包其实是正常现象这时候更应该怀疑测试方法而不是FPGA逻辑。散个经验之谈排查丢包的时候把FPGA内部的计数器全部引出来例如接收总帧数、CRC错误数、校验和错误数、FIFO溢出数这些数据通过ILA或者串口打印出来配合服务器端的统计信息做交叉比对很快就能把责任定位到具体模块。靠猜靠看是绝对不行的。4.4 高速信号完整性导致的间歇性错误100G信号速率高信号完整性问题是间歇性错误的常见根源。表现是短时间测试全过长时间测试偶尔出现一个CRC错误包或者偶发误码导致链路重同步。这类问题排查起来比较耗时但方向很明确检查光模块接收光功率是否在正常范围检查QSFP28连接器的焊接和阻抗匹配检查GT收发器的均衡参数是否配置得当。我遇到过一种非常隐蔽的情况是电源纹波过大导致GT的时钟抖动超标表现也是偶发误码。后来在电源输出端加了滤波电容和磁珠问题就消失了。所以高速调试的时候示波器除了看眼图也别忘看看电源的纹波往往能救你一命。5. 性能调优与后续扩展方向5.1 让数据通路再快一点当UDP链路稳定跑通后性能调优就是一个持续迭代的过程。吞吐量上不去先查数据通路上有没有多余的时钟周期浪费。我一般会打开综合报告里的时序利用率看数据通路上的关键路径在哪里然后针对性地优化。比如UDP校验和的加法树、MAC层的CRC计算、FIFO的读写指针逻辑这些都是容易成为关键路径的地方。还有一个容易被忽略的优化点是FIFO深度。100G速率下一个乒乓FIFO就算只有几KB也只能缓存几微秒的数据如果DMA或上层应用的响应延迟超过这个时间就会出现因为FIFO满而丢包的情况。所以调优的时候先把FIFO深度酌情加大把丢包率降到零再逐步调小看极限在哪里这样能找到性能和资源的最佳平衡点。5.2 从UDP到更复杂的协议栈UDP跑通之后很多场景会要求再加一层可靠传输机制毕竟UDP本身不保证可靠交付。你可以考虑在FPGA里实现选择性重传机制也就是发送端为每个包编序号接收端检测到丢包之后向发送端请求重传缺失的包。这个逻辑在UDP之上封装一层轻量协议既保留了FPGA线速处理的优势又能达到类似TCP的可靠传输效果。如果想进一步扩展也可以把TCP的接收卸载和发送卸载加到FPGA里。TCP比UDP复杂得多核心难点在连接状态管理和reorder处理上但如果你能把UDP这套架构吃透TCP offload的移植也只是工作量问题。另外RoCEv2这种基于UDP的RDMA协议也是很好的扩展方向它在数据中心场景里应用非常广泛如果手头的FPGA资源够用很值得深入研究。5.3 谈谈开源社区对FPGA网络开发的影响做这个项目的过程中我越来越觉得开源社区对FPGA网络开发的影响其实在悄悄改变整个行业的协作方式。过去很多团队做高速UDP传输都是闭门造车每个人都要从零写一遍MAC适配、校验和逻辑、DMA驱动进度慢而且重复造轮子严重。现在有了优秀的开源项目做地基个人或者小团队也能快速搭建出一个接近工业级的100G UDP传输方案。当然这并不是说用了开源方案就可以直接躺平。恰恰相反正因为开源代码可以自由修改你更需要深刻理解每一行逻辑的来龙去脉才能在出问题的时候快速定位。我在移植过程中几乎把校验和模块、MAC接口的时序图、DMA描述符的处理流程都重新画了一遍这些功夫没有白费后面遇到问题都能靠脑中的图景快速缩小范围。如果你也准备做类似的项目我的建议是先把最小功能跑通再追求性能最后再考虑协议扩展。别一上来就想着把所有特性都堆上去FPGA的调试链路比软件长得多每增加一个特性都可能引入新的时序问题或者资源瓶颈。稳扎稳打把每一步都验证扎实了100G UDP对你来说就只是个起点而不是终点。
返回列表