首页 / 资讯中心 / 文章详情

FPGA实现100G ROCE V2 RDMA系统:从CMAC配置到AXI Lite控制链路的实战指南

FPGA实现100G ROCE V2 RDMA系统:从CMAC配置到AXI Lite控制链路的实战指南 ★ FEATURED ARTICLE
做FPGA高速接口的工程师这两年大概率都绕不开ROCE V2这个方向。我最近正好把一个基于Xilinx UltraScale FPGA的100G RDMA通信系统从方案设计跑到了实测用的是AXI Lite做控制面配置数据面走ROCE V2协议。整个过程踩了不少坑也积累了一些直接能用的经验这篇就把整个搭建过程、关键设计和调试心得整理出来希望能帮到正在纠结“FPGA怎么上100G RDMA”的朋友。这篇内容主要覆盖方案为什么这么选、CMAC和XDMA这些核心IP怎么配、AXI Lite控制链路怎么搭、ROCE V2协议栈在FPGA侧的落地方式以及我实际调试中遇到的典型问题和排查思路。不管你是刚开始看RDMA的FPGA新手还是想从网卡方案切到FPGA方案的老手这篇应该都能给你一些参考。1. 方案选型与整体架构设计1.1 为什么用FPGA来做100G ROCE V2先说结论FPGA做ROCE V2不是要替代商用RDMA网卡而是填补网卡方案覆盖不到的空间。商用网卡虽然能跑到100G甚至200G但它的协议栈是固化的你想在数据路径里加自定义处理逻辑、做特殊报文识别、定制拥塞控制策略几乎不可能。FPGA的价值在于数据面完全可编程你可以把ROCE V2的绝大部分逻辑下到硬件里同时保留CPU无论是硬核还是软核做控制面这样既能线速转发又能灵活定制。另外一个关键因素是时延。ROCE V2虽然走UDP/IP封装但RDMA语义要求很高的响应实时性比如ACK报文、重传决策这类操作如果都靠CPU软件来做时延会非常高。FPGA把这些逻辑做成硬件状态机一个时钟周期内就能完成判决这在HPC和高频交易场景里差别非常大。还有一点容易被忽略FPGA方案可以精确控制拥塞管理。ROCE V2在无损网络中依赖PFC优先级流控制和ECN显式拥塞通知商用网卡虽然支持这两个机制但buffer调度、阈值设置都不开放。FPGA上你可以自己定义每个VP的buffer水位线也可以决定什么时候打ECN标记这种粒度是网卡给不了的。1.2 整体架构三个平面分开设计我这次的设计把系统分成三个平面数据面、控制面和管理面。这个划分是整个项目最核心的设计决策后面所有IP选型和代码组织都围绕它展开。数据面负责ROCE V2报文的线速收发。物理层用Xilinx 100G Ethernet子系统也就是常说的CMAC它内部集成了PCS和PMA也支持RS-FEC。MAC层之上我自己写了一个精简的ROCE V2硬件协议栈负责解析UDP/RDMA头、维护QP状态、生成ACK和NAK、处理重传。这个协议栈是整个系统里工作量最大的一块也是FPGA方案和网卡方案差异最大的地方。控制面负责配置和状态查询。我用MicroBlaze软核在UltraScale上也可以直接挂CIPS的硬核处理器通过AXI Lite总线去访问所有寄存器的地址空间。这里包括CMAC的配置寄存器、XDMA的控制寄存器、我自己写的ROCE V2引擎的配置寄存器。AXI Lite的优点是逻辑简单、时序容易收敛对寄存器读写的场景足够用。管理面走PCIE通过XDMA IP和主机通信。RDMA的QP创建、内存注册MR、WQE下发都是主机驱动通过PCIE写到FPGA的寄存器队列里然后FPGA硬件主动去取出来执行。这三个平面物理上是分开的总线和时钟域逻辑上也做了严格隔离调试时特别好定位问题。数据面出了吞吐问题不会影响到控制面配置控制面卡死了数据面还能继续转发——这在实测联调时帮了大忙。1.3 配置总线为什么锁死AXI Lite有朋友问我为什么配置总线不用AXI Full这里有一个很实际的原因控制面配置的是寄存器不是大数据传输。AXI Full的优势在于批量读写和高带宽burst比如一次DMA搬运一大块内存但寄存器配置场景每笔事务就几个字节burst反而带来额外的握手开销和等待周期。AXI Lite在协议级别就简化掉了Burst和独占访问握手时序非常简洁在FPGA里实现出来占用的LUT逻辑大概只有AXI Full的几分之一。更关键的是AXI Lite对跨时钟域处理更友好。控制面用的通常是100MHz或150MHz的慢时钟数据面是322MHz甚至更高频率的线速时钟。AXI Lite的单个读写事务很短你只需要在接缝处做两级同步和简单的状态机握手就行不像AXI Full那样要考虑burst中间被打断、多个outstanding请求乱序返回这些头疼问题。我在这个项目里把MicroBlaze的AXI Lite主接口和所有IP的从接口挂到一个总线上通过Xilinx Interconnect IP做地址译码。地址空间规划如下模块地址空间大小说明100G CMAC0x44A0_000064KBMAC/PCS/FEC配置与状态XDMA0x44A1_000064KBPCIE DMA控制、中断、门铃ROCE V2引擎0x44A2_0000512KBQP上下文、doorbell、统计计数系统控制0x44B0_00001MB复位管理、版本号、LED、时序控制这个表格里的地址规划后来基本没改过因为一开始就留够了地址对齐空间建议你们在做的时候也把地址间隔拉开一些否则后续加功能会发现地址不够用改地址带来的联调成本非常高。2. 核心IP配置与控制链路搭建2.1 100G Ethernet子系统的配置细节CMAC是100G链路的基础配置不好后面RDMA根本跑不起来。这里分享几个我在实际配置中认为最关键的点。复位顺序是最容易犯错的。CMAC IP的手册里给了推荐的复位序列先复位gt_reset等GT的TX/RX复位完成信号拉起来之后再复位mac_rx_reset和mac_tx_reset最后等rx_reset_done和tx_reset_done。我刚开始图省事把所有这些复位信号绑在一起用一个全局复位打过去结果GT链路起来一半PCS状态机卡在alignment状态link up信号死活拉不起来。后来老老实实按IP手册推荐的序列加了状态机自动等待链路才稳定起来。CMAC的TX和RX需要分别配置。尤其注意RS-FECReed-Solomon前向纠错的协商。100G场景下如果链路对面是交换机端口双方必须协商一致使用相同的FEC模式否则会有大量CRC错误表现为link能up但ping不通。CMAC IP支持通过AXI Lite寄存器动态查询实际的FEC状态我的做法是在启动时读回当前协商结果并打印出来方便排查。CMAC的AXI Lite寄存器接口也很值得留意。它的寄存器地址偏移是固定的比如MAC控制、PCS状态、FEC纠错统计各占一段。配置时要注意有些寄存器是自清零的写入之后硬件会自动清掉你读回来是0不代表没写进去。我有一次排查误以为寄存器写失败其实是芯片内部已经处理完了。另外提醒一下CMAC的统计寄存器非常有用。比如rx_crc_error总数、rx_ltc_code_error、tx_link_fault这些在链路联调阶段比示波器还直观。我的习惯是在每轮测试后把统计寄存器全部dump出来和预期值比对能快速定位问题出在物理层、链路层还是协议层。2.2 XDMA配置与AXI Lite寄存器映射XDMA承担PCIE数据搬运任务。这个IP看着简单但有几个配置项需要特别注意。第一是PCIE硬核的DMA通道数量和方向。我这边只用了H2CHost to Card和C2HCard to Host各一个通道但XDMA IP在配置界面会问你要不要开PCIE Bridge、要不要开AXI4 Streaming接口。如果只做寄存器配置和DMA搬运选AXI4 Memory Map接口就够了不要开Streaming否则后面接用户逻辑时要多做一层转换。第二是中断配置。RDMA场景下WQE处理完需要通知主机CQE写完要上报这都依赖中断。XDMA支持MSI-X中断每个引擎可以映射到独立的中断号。我在FPGA侧做了一个中断控制器把ROCE引擎的各类事件汇聚后映射到XDMA的MSI-X表主机侧驱动注册对应的IRQ handler。AXI Lite映射这部分重点是地址空间。中端把MicroBlaze挂到总线上然后把XDMA配置寄存器映射到上面表格里的0x44A1_0000段。MicroBlaze通过这组寄存器做DMA描述符的配置和启动。这里有个推荐的做法把XDMA内部的寄存器偏移用宏定义到C代码里比如#define XDMA_IRQ_ENABLE 0x2088 #define XDMA_IRQ_REQUEST 0x2090 #define XDMA_H2C_CHANNEL_ID 0x0000 #define XDMA_H2C_CONTROL 0x0004这样软件人员不会因为偏移量写错而踩坑代码也更容易维护。不要直接用魔法数字在代码里裸写偏移后面调试非常痛苦。2.3 ROCE V2引擎的寄存器设计ROCE V2引擎是整个系统里我投入工作量最大的自研模块。它的外部接口很简单——一组AXI Lite从接口接收主机的控制命令一组AXI4主接口去读写DDR里的队列和上下文一组AXI4-Stream接口接CMAC的数据通路。引擎内部的核心数据结构包括QP上下文表存储每个QP的状态、目的IP、目的QPN、PSN、重传队列指针WQE队列主机侧通过DMA写入引擎硬件轮询取出执行CQE队列引擎执行完WQE后写回主机Doorbell寄存器区域主机写入告诉引擎“新WQE已就绪”这里有一个设计取舍QP上下文是放在Block RAM还是DDR里。如果QP数量少且性能要求高放BRAM最合适访问延迟固定逻辑简单。但一个QP的上下文如果做完整可能有几百字节BRAM消耗会很大。我的做法是做一个两级结构BRAM里放热QP的上下文缓存DDR里放全部QP的上下文Cache缺失时自动到DDR搬运。这个设计踩了不少坑后面专题细讲。AXI Lite配置引擎的时候建议加一个“软件复位寄存器”。有时候协议栈状态机因为异常报文卡住了如果没有软件复位只能断电重启联调效率极低。有了这个寄存器我可以快速把引擎内部所有状态机拉回初始态重新开始。2.4 复位设计与亚稳态问题这个项目里复位设计的重要性不亚于时钟设计。FPGA里最常见的错误之一就是把异步复位直接打进所有触发器。AXI Lite总线和高速数据通路之间没有做异步复位同步释放很容易出现亚稳态表现就是系统偶发跑飞、寄存器读回随机值。我的做法是生成每根复位信号时都先用目标时钟域打两拍再做异步释放同步。更严格一点的说法是“异步置位、同步释放”这样做既能保证复位立即生效又能保证释放时不会产生亚稳态。GT的复位尤其敏感。你注意看Xilinx GT IP的复位信号它有gt_reset、rx_reset、tx_reset每个复位都有对应的done信号。正确的打开方式是先给gt_reset等gt_reset_done拉高然后再给tx_reset和rx_reset等tx_reset_done和rx_reset_done拉高。如果中间复位间隔太短会直接导致GT初始化失败。还有一个小经验不要把复位信号和普通逻辑信号混在一个always块里如果你写Verilog的话。虽然仿真时看不出问题但综合后很容易产生不必要的复位网络导致布线拥塞和时序变差。把同步逻辑和复位逻辑分开写时序会好很多。3. 搭建过程与关键参数记录3.1 环境准备我用的开发环境是Vivado 2023.1板卡是一块UltraScale VCU118的兼容板板载QSFP28光模块接口通过100G光模块连到对端交换机。主机侧用的是一台带PCIe 3.0 x16插槽的服务器。这里提醒一个很常见的坑Xilinx Platform Cable USB下载器在Windows下经常出现“无法加载设备的驱动程序”问题设备管理器里显示一个未知设备。实际上这不是硬件坏了而是驱动版本和Vivado自带的驱动不匹配。解决办法是安装Vivado安装目录下data/xicom/cable_drivers/nt64里的驱动并且断开重插一下USB线通常能解决。如果还不行检查一下是不是用了第三方的兼容下载器某些非官方线缆的PID/VID识别有问题需要手动指定驱动。光模块的选择也要注意。100G SR4模块和100G LR4模块的光口定义不一样如果你用的是SR4模块而交换机侧是LR4端口link灯可能亮不起来。这个不是FPGA逻辑能解决的属于物理层匹配问题。我现在调试时会常备一个光功率计快速确认模块发出来的光功率是否在正常范围。3.2 Vivado工程搭建步骤整个工程搭建流程我梳理成下面这几步每一步都直接影响后续能不能跑通第一步创建RTL工程选择目标FPGA芯片。UltraScale的芯片在Vivado器件列表里能找到对应的封装型号选中之后会自动生成默认约束。第二步例化100G CMAC IP。Configuration界面的Line Rate选择100GReference Clock选156.25MHzFEC模式建议配置成RS-FECClause 91这样兼容性最好。接口类型选AXI4-Stream数据位宽512bit因为100G在322MHz时钟下512bit正好保证线速。第三步例化XDMA IP。Mode选择AdvancedDMA Interface选AXI4 Memory MapPCIE链路宽度选x16速率选Gen3这样理论带宽能覆盖100G的数据流。中断选MSI-X。第四步例化MicroBlaze或CIPS。我这次为了调试方便用的是MicroBlaze挂在AXI Lite总线上。但要提醒一下UltraScale里用CIPS硬核处理器性能和可靠性会更好但软件工具链要复杂一些。如果只是做控制面配置MicroBlaze完全够用。第五步连线。将CMAC的AXI4-Stream接口接到ROCE引擎的MAC接口把XDMA的AXI4接口接到同一个DDR控制器的不同端口把MicroBlaze的AXI Lite接到各模块的配置端口。第六步写约束。最关键的是GT参考时钟引脚和复位按键的引脚约束还有QSFP28模块的I2C管理接口引脚约束以及PCIE的差分对约束。用户约束文件XDC里GT参考时钟要特别标注get_ports {refclk_p}并分配在专用时钟引脚上不要分配到普通IO上。第七步综合、布局布线生成bitstream。第一次跑可能会遇到时序违例优先检查GT相关路径和AXI Lite跨时钟路径。3.3 寄存器配置冒烟测试硬件跑起来之后第一步不是直接对RDMA而是先做寄存器读写冒烟测试。我在MicroBlaze里跑了一个简单的裸机程序依次对每个模块的已知寄存器写一个固定值再读回来比如写0x5A5A5A5A读回来一致就说明AXI Lite通路是通的。这里分享个小技巧写一个自动遍历的脚本按照地址表把所有寄存器做一遍回环测试。测试结果直接打印出来哪些地址能读、哪些地址返回错误一目了然。比手动翻各个寄存器要省太多时间。CMAC的回环测试也建议做。CMAC IP内部提供了近端回环和远端回环模式回环模式开启后TX数据会直接环回到RX。先跑通内部回环确认MAC和PCS链路正常再关掉回环连接外部光模块看物理链路。这样一层一层往上验证问题定位会非常清晰。3.4 首次线速测试与有效带宽验证ROCE引擎调通后我用主机侧的工具做了一次带宽实测。工具用的是RDMA社区常见的perftest套件跑ib_write_bw测试验证写带宽。第一次跑的时候结果差点让我以为硬件是坏的带宽只有40Gbps整整差了一半。后来排查了半天发现问题出在主机侧内存注册MR的大小上。RDMA要求主机侧内存注册后物理地址连续如果注册的buffer跨了多个非连续的物理页网卡一侧的DMA就会频繁发生页表查找吞吐上不去。解决办法是调整主机侧的内存分配策略使用hugepage我改成2MB的hugepage之后带宽立刻上到94Gbps左右。这个经历想说明一个问题FPGA侧跑到100G线速之后瓶颈往往在主机侧而不是FPGA侧。PCIE带宽、内存分配、中断频率、驱动轮询机制任何一个环节卡住成绩都上不去。4. ROCE V2协议栈落地的关键实现4.1 RDMA语义在FPGA侧的拆解很多同学对RDMA的理解是“绕过CPU直接访问内存”这个说法没错但到了FPGA实现层面你会发现协议栈远没那么简单。RDMA是一套完整的语义体系包括QPQueue Pair状态机、WRWork Request与WQEWork Queue Element、CQECompletion Queue Element、内存注册MR、地址向量Address Vector以及ACK/重传机制。FPGA里做ROCE V2本质上是把这些语义翻译成硬件状态机。我的实现方式是QP状态机用FSM实现每个QP在BRAM里保存一份状态WQE解析器从主机DMA写入的队列里读取请求解析出操作类型READ/WRITE/SEND/ATOMIC、本地地址、远端地址、长度等字段然后进入发送逻辑。CQE的生成同样在硬件里做。WQE执行完成后引擎自动写CQE到主机指定的CQ队列并通过MSI-X中断通知主机。整个过程不需要CPU参与这正是RDMA低时延的来源。但要注意一个关键问题FPGA硬件实现的ROCE协议栈复杂度有限不要试图一开始就实现完整的RDMA语义。我的建议是先实现RC可靠连接的SEND和WRITE操作。READ操作涉及复杂的内存保护检查和远端读响应的乱序处理ATOMIC操作更是涉及缓存一致性这些放到第二期再做。联调时先用SEND和WRITE把链路打通之后再逐渐加功能。4.2 RC与UD模式怎么选ROCE V2定义了两种传输模式RCReliable Connection和UDUnreliable Datagram。RC模式下每个QP对端是一对一连接软件保证可靠传输硬件负责ACK、重传、包序。这需要对每个QP维护PSN包序号还要维护一个重传队列复杂度高但语义完整。UD模式下一个QP可以发给任意多个目的QP每个包都是独立的不需要维护连接状态和重传硬件逻辑简单得多但它不可靠。我的建议是第一个版本先做UD。UD模式下不需要处理ACK超时重传这些逻辑整个协议栈的状态机数量能减少一半以上联调周期大幅缩短。等UD跑通了第二个版本再实现RC你会发现有了UD的基础RC只是在它之上加一个可靠传输的封装层工作量比从头做要少很多。还有一点RDMA的地址向量问题ROCE V2报文头里带的是目的GID和目的QPNGID本质上是IP加端口的一种表达。在FPGA侧做地址解析时需要一张MAC表把目的IP映射到目的MAC。这个表可以通过ARP或静态配置填充。我的做法是先用静态配置把对端MAC、IP、QPN写死调试通了之后再接动态学习逻辑。4.3 PFC与ECN的流控机制ROCE V2被设计在无损以太网上运行无损的核心依靠PFCPriority Flow Control机制。PFC允许交换机对某一个优先级暂停发送保证这一优先级不丢包。RDMA流量通常映射到高优先级队列如果交换机buffer吃紧就发送PFC pause帧让源端口暂停发送。FPGA实现PFC时最直接的问题是要不要支持答案是必须支持而且要能正确响应对端的Pause帧否则对面交换机buffer溢出时会直接丢包ROCE的可靠传输就会触发严重重传风暴。我在CMAC和ROCE引擎之间加了一层流控状态机收到Pause帧时暂停TX端的数据发送并记录Pause计时器计时器到期后自动恢复。同时监控本地RX的buffer水位超过阈值时主动向对端发Pause帧。ECN方面ROCE V2的拥塞控制依赖IP头里的ECT和CE标记位。交换机检测到拥塞时会在IP头上打CE标记接收端收到CE标记后通过NACK反馈给发送端发送端降速。FPGA实现ECN标记容易只要在入方向检查IP头ECN字段并判断队列深度打上CE标记即可但完整的拥塞控制算法比如DCQCN则需要一个rate limiter模块做配合。我建议第一版先做固定速率限速拥塞算法迭代优化放到后面再做不要一开始就贪多。5. 调试经验与问题排查记录5.1 一张能救命的排查速查表这一节我整理了一份排查速查表遇到问题直接对照着看能省出大量盲试时间。现象优先检查排查思路link up不亮GT参考时钟、光模块先确认refclk频率再用ibert眼图扫描看信号质量link up但收发带宽为0PCS状态、FEC协商读CMAC PCS状态寄存器确认alignment和FEC锁定ping不通MAC地址、IP配置确认静态MAC表填写是否正确抓包看ARP是否正常CRC错误计数暴涨FEC协商不一致、光模块信号劣化关掉FEC测试对比错误率变化AXI Lite读写超时地址译码、跨时钟域桥确认地址空间与Interconnect配置一致检查时钟域同步RDMA建链失败QP状态、PSN不一致抓取握手报文检查QP初始化序列和状态迁移带宽上不去主机内存注册、PCIE中断频率用hugepage检查XDMA带宽计数偶发丢包PFC配置、buffer水位开启PFC并在交换机侧监控pause帧计数这个表里的每一项我都实际踩过。最典型的教训是FEC协商不一致当时接了不同厂商的交换机两边自动协商结果一个开了FEC一个没开结果链路虽然up了但长时间传输时CRC错误隔几秒就冒几个定位了很久才发现问题。5.2 GT复位与POWER_DOWN的时序处理GT复位和POWER_DOWN的时序这个必须单独说因为太多人在这里翻车。Aurora、CMAC这些高速IPGT_RESET的时序要求是严格的。如果你看过IP手册里的时序图会发现gt_reset必须保持有效若干个时钟周期并且在gt_reset_done拉高之后才能开始下一个复位步骤。很多博客里也提到过“5) gt_reset、reset、power_down”这个系列讲的就是这个坑。POWER_DOWN信号是控制GT进入省电模式的正常情况下应该拉低让GT处于工作状态。我调试时遇到过一个很奇怪的现象每次系统跑一段时间GT自动掉链后来发现是控制POWER_DOWN的逻辑被某个错误状态机误拉高了。GT进入低功耗模式后链路立即断开而且因为复位时序不满足重新拉起链路非常困难。建议在代码里把POWER_DOWN固定拉低除非你有明确的功耗管理需求否则不要让它受控于复杂逻辑。GT的复位序列状态机一定要严格按照手册写不要自己“优化”时序。还有一个隐藏坑GT复位状态的参考时钟必须稳定。如果refclk是从可编程时钟芯片出来的要确保时钟芯片配置完成后再释放GT复位。否则GT在参考时钟不稳的情况下初始化锁相环可能锁定到错误频率表现为链路极不稳定。5.3 性能验证心得性能验证不能只看带宽数字要关注三件事有效带宽、时延、稳定性。有效带宽指的是扣除协议开销后的实际数据吞吐。ROCE V2有报文头、有ACK、有CQE处理开销100G线速是12.5GB/s但实际有效负载带宽能做到11GB/s以上就算不错。ib_write_bw的输出里会直接给出有效带宽你要关注的是这个值而不是线速值。时延测试建议用ib_send_lat小包时延能反映整个路径的处理效率。FPGA方案的小包时延通常能做到1微秒左右不含网络传输如果这个值明显偏大说明协议栈处理WQE的流水线有停顿需要查代码里有没有不必要的等待周期。稳定性测试是最容易被忽视的。我建议做72小时长稳测试使用固定负载持续跑同时监控FPGA侧的丢包计数、重传计数、CRC错误计数、PCIE错误计数。长稳测试能暴露的问题是短测完全发现不了的比如内存泄漏在FPGA里表现为某块RAM被耗尽、状态机在特定条件下死锁、DDR刷新与数据搬运冲突等。最后分享一个经验整个项目做下来我最深的体会是FPGA做ROCE V2真正复杂的不是写RTL而是把控“硬件能力和软件需求”的边界。哪些逻辑放硬件、哪些放CPU、哪些第一版不做这比任何一行代码都重要。如果你正要开始类似的项目我建议第一版的交付目标一定不要定得太满。能先把链路通、能建QP、能跑通一个大包WRITE就已经是胜利了。别急着在第一个版本里上ATOMIC操作、乱序支持和完整拥塞控制那会让你陷入无休止的调试泥潭。还有一个后期的优化方向你可以关注把现有的MicroBlaze软核方案换成硬核处理器配Linux内核和RDMA驱动这样可以完美复用现有协议栈开发效率更高。但这个属于第二个版本的规划了等这一版跑稳再说先把手里的100G ROCE V2系统跑扎实这比什么都重要。
阅读完成 · 觉得有帮助?
咨询建站