ARTICLE DETAIL

资讯详情

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

开源100G FPGA UDP移植与上板测试全流程详解

开源100G FPGA UDP移植与上板测试全流程详解 最近项目里需要把一套高速数据采集板卡的通信通路从开发板环境迁移到自研板卡上主控是UltraScale系列的FPGA光口走QSFP28模块上位机通过光纤直连或者交换机收发UDP包。整个方案的底层不是从零写的GitHub上能找到不少开源的100G UDP工程但别人的板卡上跑得顺的工程搬到自家板卡完全就是另一回事。这篇博文把整套“开源100G FPGA UDP移植上板测试”的过程从头梳理一遍内容包括工程结构分析、器件替换、约束适配、物理层验证、协议层调试以及最后打流测试中踩过的一些坑希望能给正在做类似移植的朋友一些参考。这个主题适合有一定FPGA基础、至少碰过10G或者40G以太网、现在想把100G UDP通路跑起来的开发者阅读。如果你只是想了解100G以太网在FPGA上怎么实现这篇文章也能帮你建立一个整体认知100G链路不只是一根光纤插上就能通里面涉及GT高速收发器、PCS/FEC、MAC、UDP逻辑、主机接口等多层内容。下面直接进正题。1. 项目概述与移植目标1.1 这个项目在做什么所谓“开源100G FPGA UDP”本质上就是用FPGA的GT高速串行收发器连接光模块在物理层之上完成以太网帧的接收和发送再把UDP报文载荷交给用户逻辑处理。FPGA端的典型架构是QSFP28光模块 → GTY/GTM收发器 → 100G MAC/PCS通常是厂商IP Cores→ UDP协议栈逻辑 → DMA/FIFO接口 → DDR或PCIe。这套方案解决的核心问题是“高速数据怎么从光纤进到FPGA逻辑里再以UDP包的形式发出去”。应用场景很典型高速数据采集卡示波器、频谱仪、软件无线电设备、网络测试仪、存储网关、数据中心里的可编程网卡等。项目里要处理的往往是几百Gbps级别的数据流通用CPU直接处理这个量级的网络包不太现实FPGA加UDP offload就成了一个性价比很高的选择。1.2 为什么选开源方案而不是自己从零写刚开始接触这个项目时团队内部也讨论过要不要完全自研后来评估之后果断放弃了。原因很直接100G以太网的底层复杂度远超10G/40G。光是在PCS层64B/66B编码、多通道绑定、对齐标记、RS-FEC编解码如果开FEC的话这些模块自己写RTL并验证到可以商用级别工作量非常大而且出问题的概率极高。商用IP比如Xilinx的100G Ethernet Subsystem虽然功能完整但授权费用不低而且对于很多学习型项目和早期原型验证来说用开源工程把上层的UDP通路跑通再用厂商IP替换底层是更平滑的路径。开源社区里像verilog-ethernet、Corundum这类项目已经把MAC层到UDP层的逻辑写得比较成熟特别是Corundum支持100G自带DMA引擎和UDP offload非常适合作移植基础。这里要给第一次做这类移植的读者提醒一个关键认知开源工程不等于完全开源。绝大多数100G UDP开源工程依赖Xilinx或Intel的GT收发器和CMAC IP所谓“开源”的部分其实是UDP协议栈和用户逻辑。真正从物理层到MAC层全部用纯RTL写并开放出来的项目很少就算有也几乎没有直接用在高速板卡上的成熟案例。所以拿到一个开源工程后先看它依赖了哪些厂商IP再决定移植策略这比上来就改代码重要得多。要理解移植工作到底要做什么得先把100G UDP通路拆开看。很多朋友第一次接触100G会以为它跟千兆网卡一样只要把PHY芯片接上就能跑实际上完全不是这个思路。2. 移植前必须理解100G UDP链路的组成2.1 100G不是一条道而是四条道并行100G以太网在物理层并不是单路100Gbps而是4条25.78125Gbps的通道并行组成这个接口形态叫CAUI-4。FPGA侧的四路GT lane分别连接到QSFP28光模块的四路电气接口上光模块再把电信号转换成四路光信号通过光纤传输。QSFP28模块之所以叫“28”就是因为单路速率是28Gbps级别正好覆盖25.78125Gbps的100G BASE-R线速率。移植时最直观的问题就出在这里不同板卡的QSFP28模块接在FPGA的不同GT Bank上。以Xilinx UltraScale器件为例同一个器件的不同封装引脚分布差别很大有的板卡把QSFP28放在MGTY Bank 128有的放在Bank 130GT Lane的编号也不一样。开源工程里的引脚约束文件XDC针对的是原作者那块开发板到了自己的板卡上如果不改GT位置和参考时钟引脚上电后光口是绝对不可能link up的。还有一个容易忽略的点是参考时钟。100G BASE-R的GT参考时钟一般是156.25MHz但有些板卡为了兼容其他协议会用可编程时钟芯片如Si5344输出不同频率例如161.1328125MHz给某些FEC模式或者50G/100G组合模式用。如果移植时没核对参考时钟频率即使GT Lane位置改对了link也起不来或者起来后误码率非常高。所以拿到一块新板卡第一件事是查原理图把QSFP28对应的GT Bank、GT Lane、GTREFCLK引脚和时钟频率全部列出来再和开源工程的约束文件对比做一张映射表后面所有工作都基于这张表展开。2.2 厂商CMAC IP和开源UDP逻辑的分工前面提到100G UDP链路是分层实现的。最底下是GT收发器负责把并行数据变成高速串行信号以及接收端的时钟恢复GT上面是PCS层做64B/66B编解码、lane对齐、FEC等再往上是MAC层负责以太网帧的定界、FCS校验、帧过滤MAC层之上才是UDP协议栈。在实际工程中PCS和MAC层基本都是直接用厂商IP完成的。以Xilinx平台为例就是100G Ethernet MAC以下简称CMACIP它把GT控制、PCS、FEC、MAC全部封装在一个核里用户不需要关心底层的对齐和编解码细节。开源工程里给你的是什么是MAC层以上的UDP协议处理逻辑包括ARP请求和应答、ICMP echo应答、UDP端口匹配、校验和计算、收发通道的AXI-Stream接口控制。这一部分用Verilog实现起来相对可控开源社区也维护得比较成熟这也就是“开源100G UDP”这个名称的由来。从自研、开源、厂商IP这几个选择来看可以做一个简单的对比方案底层物理层移植难度可靠性适用场景完全自研手写GTPCSMAC极高中低学习研究、特殊定制开源工程厂商CMAC厂商IP中等中高项目原型、学习实践商用IP定制逻辑厂商IP低高商用产品、量产设备这个表格的意思是移植工作真正要动的其实是“开源UDP逻辑”和“厂商CMAC”之间的那层接口以及整个工程的时序、引脚、时钟约束。理解了这一点就不会被工程里庞大的源码吓到也不会在错误的方向上浪费大量时间。3. 移植实操从开源工程到目标板卡的完整步骤移植过程听起来抽象实际操作起来可以分为四个阶段重建工程、适配约束、适配接口逻辑、上板验证。下面按顺序展开。3.1 第一步拿开源工程换到你的板卡先重建工程很多新手拿到一个Vivado工程第一反应是直接打开、修改器件型号然后点Generate Bitstream这个做法在100G项目里基本不会成功。原因在于原工程的IP核配置、引脚约束、时序约束全部跟原板卡绑定直接换器件型号会让IP核和约束文件全部报错即使勉强综合过上板也跑不起来。我的建议是在Vivado里新建工程选择自己板卡的FPGA型号然后把开源工程的RTL源码文件加入工程重新创建和配置所有IP核。以Corundum为例它的工程里有一个mcap/FPGA的IP core描述文件但实际用的时候CMAC、DDR控制器、PCIe控制器如果板卡支持PCIe都要根据自己的器件重新配置。这个阶段别嫌麻烦也不要想着把原工程里的IP核“迁移”过来在Vivado里旧版本的IP核换个器件重生成生命周期管理会非常乱。3.2 第二步重写XDC约束文件GT位置是重灾区XDC约束是上板前最关键的步骤。我把一份最小可用的100G UDP工程约束拆成几类引脚位置约束LOC、时钟约束create_clock、差分对约束、电平和端接约束。其中最容易出问题的就是GT位置和参考时钟。在UltraScale器件上GTY lane的名字格式是“MGTY_X0Y4”不同封装下同一个bank的GT坐标是固定的QSFP28模块接在哪个bank就得用对应的坐标。这个信息只能从自己板卡的原理图和Xilinx封装文件ug578等中确认。实际操作时我会在XDC里写清楚每个GT lane的约束例如set_property PACKAGE_PIN AY10 [get_ports gt_rxp_in[0]] set_property PACKAGE_PIN AY9 [get_ports gt_rxn_in[0]] set_property PACKAGE_PIN BA10 [get_ports gt_rxp_in[1]] # 其余lane依此类推参考时钟约束则要确认时钟源是板载晶振还是可编程时钟芯片频率是多少。如果板卡上用了一个可以输出多种频率的时钟芯片上电默认频率可能不是100G需要的156.25MHz而是别的值。这种情况需要在FPGA逻辑里通过I2C或SPI接口先把时钟芯片配好再释放GT的复位。很多板卡设计里这个配置时序非常关键配置慢了或者配置错了GT就一直起不来。3.3 第三步UDP逻辑和CMAC之间的接口时序适配约束文件做完之后接下来要处理的是逻辑层面的适配。开源UDP逻辑和CMAC之间通常是AXI-Stream接口位宽一般是512bit时钟频率约322.265625MHz100G线速率、64B/66B编码后的实际有效数据速率。时序上要求tvalid、tready、tlast、tkeep这几个信号之间的握手关系必须正确。很多工程在别的板卡上能跑移植之后出现丢第一个包或者最后一个包的问题十有八九是AXI-Stream接口在跨时钟域处理上有BUG。我在移植的时候习惯先把CMAC用户侧接口的关键信号用Vivado的ILA抓出来看。注意ILA本身也会占用逻辑资源而且在100G数据率下如果触发条件设置不当很容易采样丢失。一个更稳妥的做法是先在UDP逻辑和CMAC之间插入一个简单的数据计数器模块统计发送帧数和接收帧数用板卡上的LED或者UART打印出来。上板后先不发业务数据只发一个内部生成的固定包看计数是否正常确认接口握手无误后再接真实业务数据。这里顺便提一个容易踩的坑CMAC IP复位之后需要等待它内部的PCS和MAC完成初始化这个时间通常有几百微秒到几毫秒不同配置下不一样。开源工程里一般有一个wait_for_cmac_ready的流程如果复位时序处理得不对会出现CMAC永远无法进入ready状态。移植时不要精简这一步务必在用户逻辑里检测CMAC的user_rx_reset和user_tx_reset信号释放之后再开始发送数据。3.4 第四步和用户业务接口的对接别把字节序搞错UDP逻辑适配完之后还要考虑它怎么和你的业务数据对接。如果板卡上有DDR通常需要一个DMA或者FIFO把DDR里的数据搬到UDP发送通道如果有PCIe则要涉及PCIe DMA的描述符逻辑。这部分是移植中最容易出隐蔽问题的地方因为不同工程的接口格式千差万别。以字节序为例。CMAC和UDP逻辑之间传输的是以太网帧以太网帧头是“目的MAC、源MAC、类型/长度、IP头、UDP头、载荷、FCS”每一段都是大端序。开源工程内部已经把这个处理好了但当你把自己的数据接入UDP payload时你的数据是大端还是小端就完全取决于你的业务逻辑。如果处理不当上位机收到的payload是反着或者错位的字节序这不会引起任何错误告警但解析出来的数据完全是乱的。我的经验是在业务接口处先发一组特定的字节序列比如0x00、0x01、0x02...0xFF在上位机收包后检查字节顺序确认无误后再接真实数据。4. 上板测试从物理层到协议层的四级验证这一部分是整个移植工作的重头戏。很多朋友上板之后填bit然后直接把网线插上芯片烧录完就急着ping、打流结果发现不通又不知道问题出在哪一层。正确做法是分层验证每一层确认无误后再往上走。4.1 物理层验证用IBERT测GT的误码率上板测试第一步不是跑UDP工程而是先跑IBERTIntegrated Bit Error Ratio Tester。IBERT是Xilinx FPGA内部的一个自检IP它通过在GT收发器上生成PRBS码型并回环校验直接检测物理链路的误码率。这一步的目的就是把“GT到光模块再到对端”这一段物理通道单独验证干净。创建一个IBERT IP选择对应的GTY quad线速率设置为25.78125Gbps然后综合、实现、上板。之后在Vivado的IBERT GUI或者通过JTAG调试窗口里观察每条lane的误码统计。正常情况下四条lane应该全部link up并且误码率为0或者至少在长时间测试中没有新增误码。如果某条lane误码高先检查光模块是否插好、光纤端面是否干净、QSFP28的参考时钟是否正常再用示波器看GT的TX差分信号质量比如眼图、抖动。这里有一个实用技巧不要只在实验室常温下测几分钟就完事。100G链路的误码分布有时是偶发的建议至少跑一个小时的PRBS31伪随机二进制序列测试同时观察FPGA的温度变化。有些板卡散热不好温度升高后GT的误码率会明显上升这种情况在IBERT阶段就能发现比在协议测试阶段排查容易得多。IBERT是通过JTAG访问的不占用你设计的逻辑资源但要注意它和你的业务工程是两套bit流。也就是说IBERT验证是“物理层专项测试”的一种方式测完再下业务bit流。因此每次插拔光纤或者更换光模块之后建议重新跑一遍IBERT确保物理层没有退化。4.2 MAC层验证CMAC内部回环测试IBERT跑通之后物理通道已经没有大问题了。接下来验证CMAC的PCS/MAC层最直接的方法是打开CMAC IP的自带回环功能。Xilinx CMAC IP内部有两种回环模式Near-end PMA loopback和Far-end PMA loopback。Near-end是把GT的TX数据直接环回到RX路径不经过光模块和光纤主要验证FPGA内部的GT和PCS逻辑Far-end则需要外部环回通常是通过一个回环光模块或者对端设备把TX信号发回RX端验证包括光模块在内的整个链路。建议先测Near-end确认FPGA内部逻辑没问题再测Far-end。在回环测试时不需要把整个UDP逻辑跑起来可以写一个很简单的测试逻辑发送端产生固定pattern数据比如0xBCBCBCBC通过CMAC TX接口发出RX接口收到数据后做一个比对不一致就计数并置位错误标志。这样能非常清晰地验证CMAC的配置是否正常FEC是否使能、PCS是否对齐、MAC是否正常识别帧边界。特别注意如果CMAC配置里开了RS-FEC回环测试时两端必须都配置成同样的FEC模式。有些工程默认关FEC有些默认开移植的时候一定要检查IP配置。我在调试过程中就遇到过一次板卡A的工程开了FEC板卡B上重新生成的CMAC默认配置关了FEC结果两端link up了但误码率一直在报错排查了很久才发现是FEC配置不一致。4.3 UDP协议层验证从ARP到第一个UDP包物理层和MAC层都通了接下来进入UDP协议层的验证。这一步最直观的做法就是用PC直连FPGA的光口先ping一下通了之后再发UDP包。先说ping。要ping通FPGAFPGA端必须能正确响应ARP请求和ICMP echo请求。ARM处理逻辑是否完善很大程度上取决于开源工程做了什么程度的实现。常见的简化型工程只实现了“固定ARP表项”也就是FPGA只应答预先写死的某几个IP地址完整一点的工程才会在每次收到ARP请求时动态应答。如果发现ping不通优先排查ARP。在上位机打开命令行用arp -a看看有没有学习到FPGA的MAC地址。如果没有说明FPGA没有响应ARP请求这时候可以用Wireshark或者tcpdump抓包确认。抓包时会发现一个有意思的现象如果你的主机网卡开启了Offload功能Wireshark里的ARP包可能是由主机驱动程序合成的看起来发出去了但实际上没有到物理链路。遇到这种情况可以先关掉网卡的offload比如在Linux下用ethtool -K eth0 tx off rx off再重新抓包。ping通了之后再用UDP测试工具收发几个包。这里有个小陷阱很多开源UDP工程出于简化处理UDP校验和checksum没有正确计算或者直接填0。上位机软件如果开启了UDP checksum校验就可能把这些包丢弃或者上报错误。在实际测试时可以先用Python脚本在FPGA端发一个已知载荷的UDP包PC端接收并解析确认载荷正确然后再让PC发UDP包给FPGA看FPGA内部计数是否增加。这里给一段最简单的UDP收发Python脚本方便临时验证# 上位机接收FPGA发来的UDP包 import socket s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((0.0.0.0, 5000)) data, addr s.recvfrom(2048) print(data.hex())4.4 100G吞吐打流测试别急着用iperf3单线程UDP包收发都正常后接着就是大家最关心的性能测试也就是用工具打流。很多朋友直接打开iperf3一条命令就开始打流结果发现怎么都打不满100G于是开始怀疑FPGA逻辑有问题。实际上100G打流这个事瓶颈往往不在FPGA而在测试工具和上位机的协议栈。iperf3本身是多线程设计但UDP模式下单线程在100G链路上很难跑满。原因是CPU要处理系统调用、协议栈、中断100Gbps相当于每秒约1.48亿个最小以太网帧按64字节算这个中断和拷贝开销单核CPU根本扛不住。如果只是想验证“FPGA能不能发出100G的数据”更好的办法是FPGA内部自发自收FPGA启动一个计数器把计数器的值作为UDP载荷持续发给外部回环光模块再从光模块收到的数据里解析并比对内容。这种方式完全绕开了PC的协议栈能够直接验证FPGA内部逻辑在满速情况下的稳定性。如果需要用服务器做真实打流测试建议这样操作在服务器上用DPDK数据平面开发套件写一个简单的收包程序绕开内核协议栈而不是用通用socket。如果暂时不想碰DPDK那就用多线程iperf3例如 iperf3 -c FPGA_IP -u -b 0 -P 8多线程分散负载。观察吞吐时不要只看iperf3打印的带宽数值也要关注丢包率。iperf3 UDP模式的带宽计算基于发送端发送速率接收端接收速率可能因为系统缓存溢出而变化。检查接收端是否丢包可以用 netstat -su 查看UDP层丢包统计。关于上位机的UDP统计Linux下可以直接看/proc/net/snmp文件中的UDP段有UdpInDatagrams、UdpNoPorts、UdpRcvbufErrors几个字段。UdpRcvbufErrors如果一直在增长说明接收缓冲区不够需要调大socket缓冲sysctl -w net.core.rmem_max134217728 sysctl -w net.core.rmem_default16777216Windows下也需要在注册表里修改UDP动态端口范围和缓冲区大小方法类似但路径不同。调试网络工具的问题时如果看到“packets received 本机一共收到多少个udp数据包”这类统计可以结合这些字段做判断。5. 常见问题与排查技巧移植过程中我整理了一些高频问题的排查顺序希望对读者有帮助。5.1 问题一光口link up了但误码率时不时的增加这个问题的排查思路很简单先从IBERT测起。如果IBERT跑PRBS31有误码说明物理层就没干净协议层怎么调都没用。重点检查参考时钟质量、GT供电电源纹波、光模块端面清洁度、光纤弯曲半径。另外值得注意的是QSFP28模块对静电非常敏感拿取模块时要做好防静电措施否则模块内部可能已经损坏表面看是link up的但误码率高到无法使用。如果IBERT干净但业务数据时有误码那就不是物理层问题而是逻辑问题。比较常见的是跨时钟域处理不严谨导致偶发数据采样错误。可以用Vivado的CDCClock Domain Crossing分析工具检查设计中的跨时钟域路径。5.2 问题二ping不通但抓包发现FPGA有发出ARP响应这种情况通常是主机网卡没有正确更新ARP表。可以先手动添加静态ARP表项试试arp -s 192.168.100.10 00:11:22:33:44:55如果加入静态ARP后ping通了说明FPGA的ARP响应包格式正确只是主机接收后没有处理。这往往和网卡的Offload设置有关关闭网卡Offload后重新测试看问题是否消失。另外一个容易忽略的点是FPGA和PC必须在同一个子网内。有的FPGA工程默认IP是192.168.1.10而PC的网卡IP可能是192.168.100.10不在同一个网段ARP请求根本不会发出来。建议把PC网卡IP改成和FPGA同一子网并且用直连线连接避免交换机配置问题干扰。5.3 问题三长时间跑流之后吞吐掉到零FPGA侧统计还在发这通常是上位机侧的接收瓶颈导致的反压问题。FPGA作为一个UDP发送端是没有拥塞控制机制的。如果上位机来不及接收系统socket缓冲区满了之后数据包会被丢弃但FPGA并不知道它还在继续发送。这样就会出现“FPGA发得快PC收得少”的现象如果PC长期无法接收外部设备可能会通过某种流控机制影响整条链路。解决思路有几个层面首先如果是用通用socket收包考虑用内存映射的方式比如PF_RING、AF_XDP提高收包效率或者用DPDK。其次增大socket缓冲区也能缓解治标不治本。最后如果业务允许在FPGA端做一个简单的流控上位机周期性反馈接收计数FPGA如果发现计数停止增长主动暂停发送等对方恢复后再继续。很多商用网卡都有这种流量控制机制纯FPGA方案就需要自己实现一个类似的小逻辑。5.4 问题四Wireshark抓包过滤器填了udp却抓到ICMP这个问题在网络调试里经常遇到。Wireshark有两套过滤机制Capture Filter捕获过滤器和Display Filter显示过滤器。如果你在显示过滤器栏填了“udp”它只是把已经抓到的包里非UDP的隐藏了但文件里仍然有ICMP等非UDP包。如果真正想要在抓包时直接丢弃非UDP包需要在Capture Filter栏填过滤表达式。很多人只改显示过滤器就会产生“我加了udp过滤但还能看到icmp”的错觉。在FPGA调试场景下还有一个特殊原因有些FPGA发出的非UDP包比如ARP、ICMP占流量比例很小Wireshark的显示过滤器“udp”通常已经把它们隐藏了但如果你看到的是和UDP无关的协议报文比如TCP或者其他奇怪协议的包大概率是你PC上的其他进程在发包而不是FPGA发的。5.5 常见问题速查表现象优先排查次要排查光口link up但误码高参考时钟频率、光模块端面GT供电、光纤弯曲半径link up后CMAC一直报reset检查CMAC复位时序和参考时钟配置检查FEC配置是否一致ping不通但UDP直连可用ARP响应逻辑是否完整网卡Offload、静态ARP表小包正常但大包丢包严重MTU、IP分片处理UDP载荷长度配置上位机收包统计增长但业务解析乱字节序、UDP payload偏移数据包头字段拼接iperf3带宽上不去换多线程、关闭CPU节能检查socket缓冲区大小抓包看到非UDP协议显示过滤器和捕获过滤器混淆PC本机其他进程发包6. 移植完成后的最后一个小技巧这套100G UDP移植和上板测试做下来我最大的体会是分层验证真的不能跳步。以前为了赶进度IBERT测了几分钟就急着上UDP逻辑结果光口偶发误码的问题拖到协议测试阶段才发现排查起来非常痛苦既要怀疑逻辑问题又要怀疑物理层问题。后来老老实实把IBERT跑足一个小时再往后反而顺利很多。另外一个非常容易忽略的点就是光模块和光纤的日常维护。100G光模块的端面非常小灰尘、指纹都能明显影响信号质量。上板测试过程中凡是遇到莫名其妙的误码和丢包先把光纤拔下来用光纤清洁笔或者专用的清洁棒擦一下端面再重新插上。很多时候问题就解决了。这个习惯能帮你省掉一半的排查时间。最后再说一点关于后续扩展的建议如果现在的FPGA板卡还带PCIe接口可以考虑在开源工程的基础上把DMA引擎加上做成一个完整的100G智能网卡方案。这样不仅能用网络收发数据还能让CPU通过PCIe直接访问FPGA的DDR整个系统的灵活性会提升一个台阶。不过这一步涉及PCIe的枚举、DMA描述符管理、中断处理等复杂逻辑建议先在这个UDP通路稳定运行一段时间后再逐步扩展。
返回列表