ARTICLE DETAIL

资讯详情

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

FPGA移植开源UDP协议栈实现100G线速吞吐的实践与排坑指南

FPGA移植开源UDP协议栈实现100G线速吞吐的实践与排坑指南 一直在做高速数据采集和网络加速相关的项目之前最多也就用到10G光口数据通路稍微一复杂就得上多路绑定的方案麻烦不说时延和同步问题也够头疼。最近接了个活要在单端口上跑满100G的UDP吞吐还得是用FPGA实现评估了一圈现成方案之后决定把开源的UDP协议栈移植到FPGA上做上板实测。这篇就把整个移植和验证过程完整记录下来包括方案选型的理由、移植时踩过的坑、上板打流的数据以及几个典型的排查思路给后面要搞100G UDP的同行们做个参考。1. 项目背景与整体方案选型1.1 为什么选100G UDP而不是TCP或者私有协议先说项目背景需要把前端采集到的高速数据流打包后通过网络传输出去带宽要求是单口100Gbps数据包以小包和突发流量为主对时延极其敏感。这种场景下TCP的可靠传输和拥塞控制在FPGA里实现成本太高尤其在链路层要做到线速转发光TCP的窗口管理和重传缓存就能把BRAM吃掉一大半完全不现实。私有协议当然可以但后端的服务器和测试软件都得配套开发项目周期撑不住。UDP恰好是平衡点。协议简单开销极小头解析和校验和计算在FPGA里只是一小段组合逻辑加一个流水线。而且开源生态里有不少现成的UDP/IP核可以做100G这就能把精力集中在数据通路优化和应用层逻辑上不需要从零写MAC层和PCS层。说白了UDP是把能跑和跑得快同时兼顾的选择剩下可靠性问题交给应用层或者链路冗余来解决。1.2 开源协议栈怎么选对比几个常见方案开源FPGA以太网协议栈目前主要的选项有Alex Forencich维护的verilog-ethernet、OpenCores上的10G/25G MAC项目以及一些商业IP的评估版本。verilog-ethernet是我个人比较推荐的原因有三一是它提供的软件栈粒度很细MAC、PCS、UDP、ARP、Checksum都是独立模块可以按需裁剪二是它对Xilinx UltraScale系列有成熟的示例工程特别是CMAC硬核的集成方式写得清楚三是代码风格统一接口都是AXI4-Stream移植到不同FPGA平台时改动成本低。OpenCores上的一些项目其实也不差但问题在于维护时间普遍偏早很多还停在Xilinx 7系列时代的代码风格做100G时需要大改跨时钟域逻辑和位宽转换。商业IP虽然稳定性和支持都很好但授权费高而且锁定厂商不适合做开源方案验证和后续的产品化预研。综合考虑之后选了verilog-ethernet作为核心协议栈自己写应用层数据通路和寄存器配置管理。1.3 硬件平台与整体架构规划实测平台用的是Xilinx UltraScale VU3P系列板卡板载100G QSFP28光口FPGA内部集成了100G CMAC硬核。一开始想过用软核逻辑直接做PCS/PMA但100G的PCS层开销和时序收敛难度很大LUT利用率会变得非常紧张。后来确认UltraScale支持CMAC原生协议栈就把PCS/PMA和MAC层全部交硬核开源协议栈只负责MAC上层的UDP/IP处理这样既保证了线速性能又大幅简化了逻辑设计。系统架构大概是这样的光模块进来的数据先进入CMAC硬核完成64B/66B编解码和FEC之后以AXI4-Stream接口输出512位数据总线由开源的UDP/IP RX通路完成以太网帧解析、IP校验、UDP校验和分离用户逻辑从UDP payload里直接读取数据处理后按TX通路封装成UDP包再送回CMAC发送。整个环路分成RX和TX两条独立流水线中间用异步FIFO隔离时钟域。2. 开源UDP协议栈的移植细节2.1 模块裁剪与接口适配不是拿来主义动手改了不少开源代码拿到手并不能直接跑首先是需要裁剪掉不必要的模块。verilog-ethernet里包含ARP、ICMP、Checksum、Priority等一堆可选IP我们实际只需要UDP/IPv4收发和基础的ARP响应能力外加一个用于调试的ICMP回显。因此把多余的模块从工程中排除掉了只保留了eth_mac_rx、eth_mac_tx、udp_ip_rx、udp_ip_tx、arp_cache和checksum这几个核心模块。接口适配上最大的工作量是位宽转换。CMAC硬核的AXI4-Stream接口固定为512位而开源协议栈内部默认支持8位、64位和256位三种位宽。100G下每个时钟周期要处理512位数据用户逻辑和数据接口必须统一到512位对齐。我改写了udp_ip_rx和udp_ip_tx的内部数据总线把非对齐处理逻辑放到帧头解析阶段payload部分全部按512位对齐快速穿过。这样改完之后逻辑时钟频率可以稳定的跑到322MHz刚好满足100G吞吐需求。2.2 时钟域处理与复位同步要点100G系统最容易被忽视的就是跨时钟域和复位设计。CMAC恢复出来的RX时钟和Core参考时钟不是天然同源逻辑侧用的user_clk如果直接连到MAC恢复时钟会在时钟切换或链路重训练时出现毛刺。我的做法是系统内所有用户逻辑都在经过BUFG的user_clk时钟域工作RX方向用异步FIFO跨到user_clk域TX方向直接用user_clk输出到CMAC内部FIFO。复位同步也是一个重要细节。开源代码里自带的全局复位是异步复位同步释放这在单时钟域下没问题。但在100G的高时序压力下复位信号如果直接扇出到几百个触发器会造成严重的复位偏斜影响时序收敛。我改用Xilinx原语生成同步复位树分别给RX和TX方向做独立复位域而且必须在MAC配置完成、链路up之后才能释放复位。实测下来这样做不仅时序收敛更容易还减少了链路误码率复位释放时的毛刺问题也消失了。2.3 Checksum卸载与ARP缓存处理UDP的校验和计算在100G线速下是一个容易被低估的痛点。传统做法是收包后逐字节累加校验但100G下数据速率太高串行累加会成为时序瓶颈。开源代码里checksum模块是流水线式实现了RFC 1071标准算法把16位累加拆成多个并行部分和流水段来拼凑最终结果。不过默认的位宽是64位直接跑512位总线时会有兼容性问题我改成了对512位数据进行按16位切片分成32组并行累加再合并只在首尾包边界做特殊处理。ARP缓存方面由于应用场景是FPGA直连服务器IP/MAC对应关系基本固定不需要频繁的动态刷新。我把ARP老化时间和自动请求逻辑简化了只在系统启动时发送一次ARP请求解析服务器MAC然后固定写入一个小的CAM表。这样避免了运行时ARP洪泛引起的不必要中断也减少了RAM消耗。如果是要动态组网的场景还是建议保留完整的ARP状态机。3. 关键模块实测数据与资源占用3.1 上板实测环境搭建与工具链测试环境包括一块VU3P FPGA板卡一台配备100G网卡的服务器网卡型号是Mellanox ConnectX-5用DAC直连光口在一起。FPGA侧加载的bitstream包含UDP协议栈、用户数据发生器和回环逻辑。服务器的操作系统为Ubuntu 22.04测试工具使用iperf3进行UDP打流同时用自写的python脚本通过raw socket发定制包验证特定长度包的转发行为。上板前先用xilinx的IBERT IP测试了光口物理层误码率确保光模块和链路没有问题避免后续排查时分不清是协议栈问题还是物理层问题。IBERT测试跑10分钟误码率为0才敢把逻辑加载进去开始协议验证。3.2 吞吐量测试结果与瓶颈定位iperf3打流到64字节小包时UDP吞吐稳定在83Gbps左右距离满速100G还有不少差距这个现象符合预期因为小包场景下包头开销占比高CMAC每帧需要的IPG和前缀也会占用一部分带宽。改发1518字节的大包时吞吐跑到99.1Gbps基本打满了线速。之后又用自定义发包工具做了混合包长测试在64、128、512、1518字节混合流量下吞吐保持在91Gbps以上没有出现丢包。进一步抓内部计数器的数据发现配置模式下吞吐差异主要是用户RX侧的非对齐处理单元引入了部分气泡。因为并发小包时几乎每个包都要做非对齐字节搬移处理端到端的流水线占用率高了拉低了吞吐上限。后续优化方向是把非对齐搬移改成延迟写法直接在局部做字节使能拼接可以减少一拍的处理周期预计能恢复到95Gbps以上。3.3 资源利用率和时序收敛情况移植完成后综合资源情况如下LUT使用了整个芯片资源的23.7%FF占用了15.2%BRAM消耗了34个其中大部分BRAM用在了用户数据缓冲和UDP payload缓存上。整体资源占用还有很大余量后续可以在同样的芯片里再部署两套收发通路或者集成DDR控制逻辑。时序收敛过程比较顺利最关键的用户时钟达到322MHz建立时间裕量为0.142ns保持时间裕量0.287ns满足设计要求。CMAC时钟域和用户时钟域之间的异步FIFO没有出现时序违规。使用单独的时钟约束文件把CMAC tx/rx时钟和用户时钟异步group避免误报路径。这一点建议做100G工程的同行也这么设置不然vivado会把无关路径全部分析一遍很难收敛。4. 踩过的坑与常见问题排查实录4.1 光口链接正常但收不到上行数据FEC对齐坑刚开始上板时遇到一个很诡异的问题光模块信号正常link状态是up的也带内计数器看到CMAC没有报错但UDP层一个包都收不到。用chipscope抓CMAC的rx_axis_tvalid发现数据始终为低。后来查了CMAC的Standard mode配置发现FEC模式不匹配默认配置没打开RS-FEC实际链路是用RS-FEC编码的CMAC收下来帧全被标记为fec_uncorrectable_error然后直接丢弃了。解决办法也很简单在CMAC的配置寄存器里开启RS-FEC(RS_528_514)并把FEC电感设置为standalone模式。这个问题提醒我现在100G的光模块和交换机很多都默认开FEC如果CMAC不配置对应模式即便物理层link up了实际数据通路上也是出不来的。排查顺序上建议第一步就检查FEC模式与对端是否一致。4.2 UDP Checksum校验错误导致的丢包offset参数设错了还有一个印象深刻的坑是TX方向的checksum偏移量计算错误。开源代码的UDP TX模块需要指定IP header和UDP header在AXI总线字节流中的位置偏移我刚开始按照默认example设置没考虑到接入的是512位总线的第2拍才开始有帧头数据导致checksum计算时覆盖了错误的数据位置发出去的UDP包所有checksum都是错的。服务器端收到了包但校验不通过直接丢弃。排查的时候绕了很大弯路一开始以为是物理层或者MAC的问题反复用wireshark抓包也没有头绪因为抓到的包看起来结构是完整的。后来在FPGA内部用iflax的调试模块直接把原始AXI数据通过DDR记录下来对照包结构才发现checksum字段和数据内容不匹配。最终把偏移量按具体接入位置做了调整用变量参数传入测试通过。4.3 突发流量下丢包背压信号处理不正确在测试server发送大量突发小包给FPGA时出现了一种难以复现的偶发丢包每秒钟丢几十个包频率很低但累计起来不可接受。一开始怀疑是UDP RX解析逻辑bug观察了很长时间没有发现规律。后来定位到是TX方向的背压没处理好用户逻辑处理完数据后通过接口直接往TX方向塞数据但TX模块在MAC输出FIFO接近满时会拉高backpressure用户逻辑没有polling这个信号导致数据在FIFO写端口等待时被覆盖。修正方案是在用户逻辑和UDP TX模块之间增加一个握手状态机对每拍数据都检查backpressure信号backpressure为高时保持当前数据不动等空闲再发送。这个处理看似简单但确实容易在写原型代码时忽略因为仿真时阻塞条件不够充分看起来一切正常上板高负载才暴露问题。4.4 资源排查速查表整理了这次调试中遇到的主要问题以及对应的检查点方便后面遇到类似情况时快速定位。现象直接原因排查方法解决措施物理link正常但收不到数据FEC模式不匹配检查CMAC统计寄存器两端配置相同的FEC模式收到包但校验失败checksum偏移错误chipsope抓AXI数据对比包结构确认帧头在总线中的偏移位置突发流量偶发丢包背压未处理检查TX FIFO write当拍背压状态增加握手状态机高频时序不收敛跨时钟域路径未约束查看时序报告中的违例路径加异步group约束小包吞吐不达标非对齐处理气泡多内部计数器观察处理占用率优化非对齐搬运逻辑5. 移植完成后的一些个人体会整个100G FPGA UDP协议栈移植加测试的过程最深的感受是协议栈本身的开源代码已经很成熟真正的挑战其实在于系统集成和边界条件的处理。100G系统对时序、时钟域、背压、FEC这些细节的要求极为苛刻任何一个环节考虑不周都会在上板阶段以极难排查的偶发问题形式暴露出来。给后面做类似项目的同行几个具体的建议第一尽量使用CMAC硬核而不是软核实现MAC层节省资源稳定性更好时序也更容易收敛第二移植开源协议栈时先把系统时钟架构和复位方案想清楚再动手改代码千万不要先改逻辑后补时钟约束最后改起来非常痛苦第三上板之前务必把物理层IBERT测试跑一遍确认链路本身没有问题再进入协议调试否则排查问题会陷入协议和物理层互相混淆的泥潭。还有一个不算经验的经验在验证100G高速设计时用chipscope抓波形已经不够高效了数据量太大且触发条件难以精确控制。这次在里面加了一个轻量级的内部调试寄存器组把关键通路上的计数器、状态机状态和FIFO水线通过PCIe读回来配合一套简单的python解析脚本做长时间监控定位突发问题的效率比在线逻辑分析仪高很多。这个思路我认为对于后续要做连续长时间稳定性测试的项目非常值得采纳。
返回列表