ARTICLE DETAIL

资讯详情

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

100G UDP协议栈FPGA移植实战:从CMAC配置到上板打流

100G UDP协议栈FPGA移植实战:从CMAC配置到上板打流 把100G UDP协议栈从开源工程移植到自己的FPGA板卡上再跑通上板测试整个过程比想象中要多花好几倍时间。物理层和MAC层靠Xilinx的CMAC硬核从MAC往上的UDP处理逻辑有开源代码可以参考理论上就是约束改一改、时钟配一配、上板看灯。真动起手来才发现从工程迁移到链路自检再到打流压测每一步都可能遇到“文档里不会告诉你”的坑。这篇文章把我这次移植的完整过程、关键参数选择、上板测试步骤以及排查方法整理出来给准备在FPGA上做100G UDP吞吐验证的朋友提供一个可以直接参考的路线图。1. 方案选型100G UDP在FPGA上怎么落地1.1 为什么直接选CMAC硬核而不是软核MAC先聊方案选择。很多从10G/25G过来的朋友习惯性地想“我用一个软核MAC外面包一层UDP逻辑不就行了”在100G这个速率下这个思路基本走不通。100G以太网的物理编码子层PCS、前向纠错FEC、通道绑定、时钟补偿这些工作如果用软逻辑实现资源消耗和时序收敛难度都非常大而且很难稳定跑满线速。Xilinx UltraScale系列内部的CMAC硬核把100G以太网物理层、MAC层以及PCS/PMA相关的大量逻辑固化在硅片上。用户侧看到的只是一个比较干净的AXI4-Stream接口发送侧往里塞数据帧接收侧往外吐数据帧CMAC自己负责和高速收发器对接、处理对齐、FEC、流控这些事情。这相当于把“以太网物理层”这个最麻烦的黑盒交给了硬核我们只需要关心用户侧逻辑。对于FPGA上的100G UDP实际的工作量主要集中在两个部分一是把CMAC配置正确二是把CMAC上方的UDP协议栈写对。CMAC本身用IP核生成UDP协议栈可以自己写或者参考开源实现。如果目标只是“移植上板测试”选择CMAC硬核几乎是唯一合理路径。1.2 开源协议栈怎么选Corundum、verilog-ethernet和官方IP怎么权衡选完CMAC下一步是上层UDP逻辑哪来。我这次对比过三种常见做法方案优点缺点适合场景Xilinx官方UDP Offload IP配置简单稳定支持校验和/ARP需要License是黑盒出问题不好排查做产品赶进度不在乎IP核成本Corundum开源网卡功能完整支持100G含ARP/UDP/TCP模块可裁剪工程庞大依赖PCIe DMA裁剪有学习成本想深入理解协议栈做高性能网卡原型自研精简UDP栈灵活逻辑清晰便于教学和二次开发需要自己处理ARP、校验和、过滤等细节只用UDP收发不需要TCP/DMA想完全掌控逻辑我这次的做法是参考Corundum里ARP和UDP模块的设计思路但没有直接整个工程搬过来而是基于CMAC硬核自己搭了一套精简的UDP收发通路。原因也很简单Corundum整个工程是为“开源网卡”设计的集成了一大套PCIe DMA框架我的场景只需要FPGA端把UDP包收进来、再能发出去不需要CPU参与搬运。如果强行移植整个工程反而会被DMA和Host接口的逻辑拖累光跑通框架就要耗掉不少时间。如果只是想做快速验证其实可以直接用Xilinx官方UDP Offload IP配完例化上板用iperf3打流半天就能看到效果。但如果你想搞清楚UDP协议栈在FPGA上到底是怎么解析和组包的参考开源代码自己搭一遍收获会大得多。2. 开源工程与硬件环境的准备2.1 硬件清单板卡、光模块、光缆和网卡移植100G UDP硬件环境比普通FPGA调试要“讲究”一些。我这次用的板卡是Xilinx VU9P级别的开发板板载QSFP28光口配了一块100G光模块。对端是一台带Mellanox ConnectX-5网卡的服务器系统是Ubuntu用iperf3和Wireshark做打流和抓包验证。这里有一个非常实用的建议如果只是做功能测试优先用QSFP28 DAC高速铜缆而不是光模块加光缆。DAC线缆不需要配置光模块寄存器插上就能识别而且信号链路简单排查问题会省很多事。光模块虽然也不复杂但涉及MDIO/I2C配置、光功率检查、收发方向确认第一次调的时候容易在这些细节上卡住。板卡另一端需要一台确认支持100G的网卡。Mellanox ConnectX-5、ConnectX-6或者Intel E810这些都可以。如果没有100G网卡也可以用两块FPGA板卡对打一块发一块收同样能验证链路和UDP通路的正确性。2.2 开源代码结构快速摸底拿到开源工程之后不要急着打开Vivado生成比特流先花半天把代码结构看一遍。我参考的开源工程主要包含这几块CMAC相关负责例化CMAC IP并连接到GT引脚AXI4-Stream处理数据通路上的位宽转换、字节序调整、跨时钟域FIFO接收引擎解析以太网头、IP头、UDP头做MAC/IP/端口过滤发送引擎组以太网头、IP头、UDP头计算校验和把用户数据打包发出ARP模块处理ARP请求和应答这步很关键主机要往FPGA发UDP第一步一定是ARP询问看代码的时候重点看接口定义和时钟域划分。很多开源工程针对特定板卡做了适配引脚名字、时钟频率、复位极性都写死在约束和例化代码里。移植的第一步是把这些“板级相关”的代码找出来改成自己板卡对应的值。2.3 移植过程中最关键的三个修改点动手改代码时我归纳出三个必改点也是初学者最容易出错的地方。第一是引脚约束。QSFP28的收发引脚、参考时钟引脚、复位按键、指示灯这些都要按照自己板卡的原理图重新约束。注意100G光口通常是多对高速收发器比如4对TX加4对RX每一对都有P/N差分引脚漏掉一对或者接反一对链路肯定起不来。第二是CMAC IP配置。IP核里需要选线速率100G、参考时钟频率通常156.25MHz、是否开启RS-FEC、流控方式等。这部分配置必须和你的板卡晶振频率、对端设备能力对齐。我这次因为参考时钟频率配置错误折腾了一个下午后面会专门说这个问题。第三是时钟方案。CMAC IP会生成用户侧时钟开源工程里有的用CMAC输出的user clock驱动后续逻辑有的要求外部提供独立时钟。如果没按原工程约定来做跨时钟域就会出现偶发丢包或者时序违例。建议先沿用原工程的时钟架构跑通基础链路后再做优化。下面是我这次板卡的引脚约束示例具体引脚以你的板卡原理图为准set_property PACKAGE_PIN AD12 [get_ports qsfp_tx_p[0]] set_property PACKAGE_PIN AD11 [get_ports qsfp_tx_n[0]] set_property PACKAGE_PIN AE12 [get_ports qsfp_rx_p[0]] set_property PACKAGE_PIN AE11 [get_ports qsfp_rx_n[0]] # 参考时钟156.25MHz create_clock -name qsfp_ref_clk -period 6.400 [get_ports qsfp_ref_clk_p]3. 移植中的关键细节与原理3.1 时钟与复位大多数移植失败的根源移植这类高速网络工程时钟和复位带来的问题远比逻辑本身多。100G CMAC的参考时钟一般使用156.25MHz周期正好是6.4ns。这个时钟从板载晶振进来经过缓冲后送到CMAC的GT参考时钟引脚。很多朋友第一次上板链路起不来第一反应是引脚接错或者代码写错实际上往往死在复位时序上。CMAC的复位有一套固定顺序上电后先等GT复位完成然后释放CMAC的复位最后再释放用户逻辑复位。这个顺序如果不对CMAC的状态机就可能卡在某个中间状态表现为TX/RX的reset_done信号拉不起来或者link状态一直在flapping。我建议在移植时把CMAC IP核生成的复位逻辑原封不动保留不要为了简化自己重写。IP核的复位模块已经考虑了上电时序用户逻辑的复位最好由它派生的user_reset_out来控制。另外用户逻辑里如果有跨时钟域的FIFOFIFO的复位信号也要和各自时钟域同步释放否则会出现FIFO内部指针错乱表现就是第一个包正常从第二个包开始数据错位。还有一个容易被忽略的点复位释放后CMAC内部需要一段时间才能完成PCS对齐和链路训练这段时间通常是毫秒级甚至更长。所以上板后看到link灯没亮不要马上断言“代码不对”先等几秒看状态寄存器。3.2 CMAC接口对接AXI4-Stream总线位宽与频率CMAC用户侧的AXI4-Stream接口在100G配置下通常是512bit位宽用户时钟约322.265625MHz。很多刚接触的人会对这个频率产生疑惑100G线速算下来512bit总线的理论最低频率只需要约195MHz为什么CMAC用了322MHz这是因为CMAC的用户接口带宽比线速高一些给数据通路留出了时序余量和控制开销。512bit乘以322.265625MHz约等于165Gbps远高于100G线速接口上会有空闲节拍。好处是用户逻辑在大部分情况下不用微秒级地精确计算反压只要保证FIFO深度合理就不会因为瞬时流量把数据通路堵死。对接时需要注意几个信号tdata、tkeep、tlast、tvalid、tready以及tuser。tuser里携带了错误标记比如CRC错误、帧截断等强烈建议把这个信号引出来接到统计寄存器或者ILA上。否则链路跑起来你只看到收包计数不对却完全不知道是物理层误码还是协议栈丢弃。字节序也是一个大坑。以太网报文在网络上是按字节流顺序传输的但映射到512bit总线上不同工程定义的位映射可能不一样。最常见的问题是CRC字段位置反了、MAC地址高低字节互换、甚至整个64字节的前导和以太网头都错位。判断方法也很简单用ILA抓一拍接收数据和Wireshark里抓到的包对比一眼就能看出映射关系对不对。3.3 UDP协议栈内部逻辑校验和、过滤与跨时钟域UDP协议栈的逻辑本身并不复杂但有几个细节决定了上板后能不能稳定跑。UDP校验和比较微妙。IPv4下UDP校验和允许为0表示发送端不计算校验和接收端可以不校验。很多开源工程为了节省逻辑在发送时直接填0接收时也不检查。如果你的对端程序对校验和要求严格或者数据经过交换机/网卡时被强制计算校验和就可能出现包被丢弃的情况。我这次在测试时遇到过FPGA发出来的UDP包服务器端用tcpdump能抓到但应用程序收不到排查一圈发现是网卡在RX路径上校验了UDP checksum发现为0后直接丢包。后来在发送引擎里补上了UDP校验和计算问题立刻消失。ARP模块不能省。主机要向FPGA发送UDP数据第一件事就是ARP请求问“192.168.1.100的MAC地址是多少”。FPGA端如果不应答主机的ARP表里就没有FPGA的MACUDP包根本不会发出来。这属于“不发包导致发包失败”的经典案例。检查方法很简单在服务器上ping一下FPGA的IP如果能通说明ARP链路正常。过滤逻辑也要做对。一个完整的接收引擎至少应该过滤MAC地址、IP地址、UDP端口不匹配的包直接丢弃。如果只过滤端口不过滤IP同一个局域网里其他设备发包也会被FPGA接收干扰统计结果。端口过滤建议做成可配置寄存器方便测试时切换目的端口。跨时钟域FIFO的深度计算也有讲究。假设最大包长是1518字节在512bit总线上大约是24个时钟周期存完一个包。考虑到CMAC用户时钟和用户逻辑时钟之间的频率差异以及反压可能让FIFO积压多个包深度256的FIFO基本上够用。但如果要支持jumbo frame9000字节一个包就需要约141拍深度至少要512起步。我这次为了保险收发各用了一个深度1024的异步FIFO实测在回环和打流场景下都稳定没有出现FIFO溢出。4. 上板测试过程实录4.1 第一阶段光模块与链路检测代码编译通过、比特流下载到板卡之后第一步不是急着发UDP包而是先确认物理链路。用ILA挂在CMAC的状态接口上观察以下几个关键信号TX和RX的reset_done是否拉高rx_status里的link状态是否变为up如果有误码统计确认误码计数器长时间不增加如果是光模块还要确认模块的光功率和LOS信号。用DAC线缆的话这一步会简单很多插上之后通常几秒内link就能up。我这个板子第一次上板时link一直起不来ILA里面reset_done信号始终是低。排查半天发现是参考时钟引脚约束到了错误的BANK156.25MHz信号根本没进到GT的时钟输入。把XDC改过来重新跑综合实现link立刻正常。链路检测通过后建议在CMAC的RX侧做一个简单的计数模块把收到的包数量统计出来。此时即使FPGA端还没写UDP处理逻辑从对端网卡发出的pause帧、ARP广播、LLDP等报文都会被CMAC接收计数会持续增加这能证明物理层和MAC层已经打通。4.2 第二阶段回环测试与包计数链路up说明PHY和MAC正常接下来要验证FPGA内部的UDP通路。最稳妥的办法是先做回环测试。回环有两种内部回环和外部回环。内部回环是指在FPGA内部把RX通路收到的数据直接搬到TX通路发出去这样包从服务器出去绕一圈回到服务器可以验证TX和RX整个数据通路。外部回环是用DAC线缆把同一个QSFP28端口的TX和RX短接FPGA发出的数据又从RX口回来这能同时验证高速收发器的收发方向。我这次先做的外部回环。服务器上连好网卡后给网卡配IP然后在服务器上ping FPGA端设置的IP。这里有个前提FPGA内部已经完成了UDP通路的逻辑并且能够正确处理ARP请求。ping通之后在服务器上执行tcpdump能看到FPGA回过来的ICMP echo reply报文这就说明以太网头、IP头、UDP头的组包解析链路都通了。回环测试还要做包计数校验。在FPGA逻辑里加一个计数器统计TX通路发出的包数和RX通路收到的包数。服务器端用ping发送1000个包FPGA端计数应该正好1000收、1000发。一旦出现收发不一致优先检查字节序和CRC字段大概率又是位映射问题。4.3 第三阶段iperf3/UDP打流与带宽验证回环通了之后还要回到“外部设备到FPGA”的单向打流验证UDP通路在持续流量下是否稳定。服务器端设置网卡IP和速率sudo ethtool enp3s0f0 # 确认Speed: 100000Mb/s sudo ip addr add 192.168.1.1/24 dev enp3s0f0 sudo ip link set enp3s0f0 up然后用iperf3往FPGA发UDP流iperf3 -u -c 192.168.1.100 -b 10G -l 1400 -t 30这里有一个实测心得iperf3单线程UDP很难跑满100G因为CPU和中断处理会成为瓶颈。我测试的时候单线程最多跑到40Gbps左右CPU占用已经接近100%。想要更接近线速有两种方式一是iperf3开多个客户端实例并行每个打10G二是用DPDK pktgen或者T-Rex这类专用发包工具。如果你只是想验证FPGA端能承受线速建议直接用pktgen效果最直观。带宽验证的同时重点观察两个指标丢包率和接收计数。FPGA端计数器收到的包数应该等于服务器端发出的包数减去路由器/交换机丢弃的包数。如果FPGA端有丢包一般能在计数上看到缺口同时也需要检查FPGA内部是不是有FIFO溢出这往往是因为用户逻辑处理速度跟不上100G线速需要用更深的FIFO或者优化反压逻辑。为了验证小包极限性能还可以测试64字节小包。100G线速下64字节包的包速率极限大约是1.49亿包每秒这个数值会远超大多数处理逻辑的包处理能力。如果FPGA端小包丢包严重而大包不丢说明瓶颈在包处理速率而非带宽。这时需要关注每一拍能处理多少个包如果512bit总线一拍只能处理一个包那小包场景下肯定无法达到线速。5. 常见问题与排查技巧5.1 现象、原因与排查思路速查表现象常见原因排查方向链路起不来reset_done为低参考时钟没进来、引脚约束错误、复位顺序问题用ILA看GT参考时钟和reset_done检查XDC引脚link up但ping不通ARP没响应、MAC地址过滤错误、字节序反了在服务器上抓ARP请求看FPGA是否有应答ping通但UDP收不到UDP校验和问题、端口过滤写错、对端网卡RX校验丢弃tcpdump确认包到了网卡再做UDP checksum检查大包不丢、小包丢包严重包处理速率瓶颈每拍只能处理一个包优化流水线或者在输入端做多包合并处理持续流量下偶发丢包异步FIFO溢出、反压逻辑不完善加大FIFO深度检查tready信号是否正常反压CRC错误计数持续增长光模块信号质量差、FEC配置不匹配检查光功率和误码尝试开启/关闭RS-FEC网卡协商速率只有10G/25GQSFP28线缆接触不良、对端网卡不支持100G更换线缆检查ethtool的speed设置5.2 两个典型的“坑”复盘这次移植中有两个问题最让我印象深刻值得单独拿出来说。第一个是RS-FEC不匹配导致的误码问题。最开始测试时服务器网卡强制开启了RS-FEC而FPGA端CMAC没有开结果链路虽然up了但交换统计里CRC错误很多吞吐上不去且偶发丢包。RS-FEC在100G链路上常用于长距离光模块它能纠正部分符号错误但前提是收发两端配置一致。排查方法是查看两端设备的FEC模式设置一边开一边关肯定不行。第二个是u-boot/Linux网卡中断导致iperf3打不满。这个问题不完全是FPGA端的问题但对端设备的稳定性直接决定了整个测试是否可信。我一开始单线程iperf3只能跑到20G以为是FPGA端UDP逻辑有瓶颈后来发现服务器网卡的队列中断只分配在一个CPU核上。把网卡多队列打开并用smp_affinity把中断分散到多个核之后单线程iperf3明显改善多实例测试也更能反映真实线速。遇到带宽上不去先看对端CPU使用率再看FPGA端计数不要一上来就怀疑逻辑写错了。5.3 最后的经验移植开源网络工程和完全自己写逻辑不太一样最大的挑战在于“你没有从头设计这套代码却要在它的框架下做裁剪和适配”。我自己的习惯是改代码之前先把原工程的时钟域、复位极性、字节序映射、接口时序记录下来形成一张自己的对照表每改一处就核对一次。很多问题不是复杂逻辑导致的而是“原工程用A顺序你改成了B顺序然后整个链路行为全变了”。如果时间允许建议在移植工具链上搭一个简单的仿真环境用脚本灌几个标准的以太网包验证UDP解析、组包、校验和计算是否正确。仿真能覆盖的问题越多上板调试的时间就越短。上板之后再用ILA做在线观测配合服务器的tcpdump、ethtool、iperf3基本就能把整个链路快速定位到具体模块。100G UDP移植物理层靠CMAC协议层靠开源代码真正考验人的是如何把别人的工程吃透再改造成适合自己的方案。希望这篇记录能让你少走一些弯路。
返回列表