ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Bittware VV4 移植 Corundum:开源 100G 网卡 FPGA 实战

Bittware VV4 移植 Corundum:开源 100G 网卡 FPGA 实战

1. 为什么要把 Corundum 搬到 Bittware VV4 上

第一次拿到 Bittware VV4 这块卡的时候,我盯着板子上那颗 VU13P 发了会儿呆。这卡是典型的 PCIe 加速卡形态,单槽全高、被动散热,板载 4 路 QSFP28 接口,理论上一口气能跑 4 个 100G 口。但问题也很直接:Bittware 原厂给的参考设计基本围绕他们自家的 Shell 和 IP 生态转,想跑一个开源、可改、社区活跃的 100G 网卡逻辑,就得自己动手把 Corundum 这套东西塞进去。

Corundum 这个项目在开源网卡圈子里算是老熟人了。它的定位很明确——用 FPGA 实现一个高性能、可综合、参数化的 NIC 核心,支持多队列、PCIe DMA、精确的收发路径,而且整套 RTL 是开源的,你能从队列管理一直改到 MAC 层。对于做网络加速、智能网卡、甚至只是想搞明白 100G 线速收发包到底怎么落地的人来说,Corundum 是一条绕不开的路。

但“开源”和“能跑”之间隔着一条河。Corundum 官方主要维护的是基于 Xilinx 官方开发板(比如 Alveo 系列)的参考设计,板级相关的约束、时钟、PCIe 硬核配置、QSFP 引脚映射,这些都得针对具体板卡重新做。Bittware VV4 用的是 VU13P,PCIe Gen3 x16,QSFP28 光模块接口,和 Alveo 的板级资源分布不完全一样。所以这次移植的核心工作,就是把 Corundum 的 RTL 和 Bittware VV4 的物理资源对接起来,让它在真实硬件上跑通 100G 收发。

这篇文章适合谁看?如果你手上有 Bittware 的卡,或者任何一块 VU13P/VU9P 级别的板子,想跑开源 100G NIC,那这篇就是给你写的。如果你只是刚入门 FPGA,想看看一个真实的 100G 网卡项目长什么样,也能从里面摸到不少工程细节。我会把整个移植过程拆成几个阶段,从环境准备、板级适配、时钟约束,到上板调试和踩坑记录,尽量把每一步的“为什么”讲清楚。

2. 移植前的整体思路与方案选型

2.1 为什么选 Corundum 而不是自己从零写

自己从零写一个 100G NIC 不是不行,但代价太大。PCIe DMA 引擎、描述符管理、收发队列、流控、MAC 层对接,这些东西每一个单独拎出来都够写几个月。Corundum 的价值在于它已经把这些模块做成了参数化、可配置的 RTL,而且经过了多轮流片和实际部署验证。它的架构大致分几块:PCIe 子系统负责和主机通信,DMA 引擎搬数据,队列管理模块维护发送和接收队列,MAC 层对接以太网 PHY,还有一套控制寄存器接口供驱动读写。

选 Corundum 的另一个原因是它的可移植性设计。它的 RTL 里把板级相关的部分抽象成了几个接口,比如 PCIe 硬核的配置、时钟输入、GT 收发器的位置,这些在移植时只需要改对应的 wrapper 和约束文件,核心逻辑基本不用动。这比那种把板级资源写死在代码里的设计友好太多。

2.2 Bittware VV4 的板级资源盘点

Bittware VV4 的核心是 Xilinx Virtex UltraScale+ VU13P,这颗芯片的逻辑资源、DSP、BRAM 都很充裕,跑 Corundum 的 100G 配置绰绰有余。板载 4 个 QSFP28 笼子,每个支持 100G,通过 GTY 收发器连接到 FPGA。PCIe 接口是 Gen3 x16,直接连到主机的 PCIe 根复合体。时钟方面,板上有专门的时钟芯片产生参考时钟,QSFP 和 PCIe 各有自己的时钟域。

移植前我先把板子的原理图和引脚约束翻了一遍,重点确认三件事:QSFP 的 GT 通道映射、PCIe 的参考时钟来源、以及系统复位和配置相关的引脚。这些信息决定了后面约束文件怎么写。Bittware 通常会提供一份板级约束模板,里面包含了引脚定义和基本的时序约束,这份模板是移植的起点。

2.3 移植路线的取舍

移植有两条路可以走。一条是直接用 Bittware 的 Shell,把 Corundum 的逻辑挂到 Shell 的 PCIe 和网络接口上。另一条是抛开 Shell,自己从 Corundum 的参考设计改,把板级相关的部分替换成 VV4 的。我选了第二条,原因是 Bittware 的 Shell 封装比较重,里面有不少他们自己的 IP 和授权限制,改起来反而束手束脚。Corundum 的参考设计结构清晰,板级适配层薄,改起来更可控。

当然这条路也有代价:PCIe 硬核的配置、时钟约束、GT 的复位序列,这些都得自己搞定。但好处是整条链路透明,出了问题能一路查到 RTL 层,不会被 Shell 的黑盒挡住。

3. 环境搭建与工程结构梳理

3.1 工具链版本选择

Xilinx 的工具链版本对 UltraScale+ 的支持很关键。我用的 Vivado 2021.2,这个版本对 VU13P 的支持成熟,而且 Corundum 的官方脚本在这个版本上验证过。太新的版本有时候会引入一些 IP 核的兼容性问题,太老的版本又可能缺少某些 UltraScale+ 的时序模型。2021.2 算是一个比较稳的平衡点。

除了 Vivado,还需要一台 Linux 主机来跑编译和驱动。Corundum 的驱动是内核模块,编译需要对应的内核头文件。我用的 Ubuntu 20.04,内核 5.15,编译驱动没遇到大问题。主机和板卡之间的 PCIe 通信依赖驱动加载后的字符设备,所以驱动编译通过是上板前的必要步骤。

3.2 Corundum 源码结构速览

Corundum 的仓库结构不算复杂,但第一次看容易迷路。核心目录大致分这几块:rtl下面是所有 RTL 源码,fpga下面是各板卡的参考设计,sw是驱动和工具,scripts是编译脚本。移植时主要动的是fpga目录下的板级工程,以及rtl里少数和板级相关的 wrapper。

rtl里的核心模块包括mqnic_core(主逻辑)、mqnic_pcie(PCIe 接口)、mqnic_eth(以太网接口)、mqnic_dma(DMA 引擎)。这些模块本身是板卡无关的,移植时不需要改。真正要改的是顶层 wrapper,它负责把核心模块的接口连到具体的 PCIe 硬核和 GT 收发器上。

3.3 建立 VV4 专属工程目录

我习惯在fpga目录下新建一个bittware_vv4文件夹,把参考设计的约束和脚本拷过来改。这样做的目的是保持和上游的隔离,后面 Corundum 更新时,只需要对比板级目录的差异,不会把核心逻辑的改动搞混。工程目录里放三样东西:约束文件(xdc)、编译脚本(tcl)、以及板级 wrapper 的 RTL。

约束文件是移植的重头戏,里面要定义时钟、引脚、时序例外。编译脚本负责调用 Vivado 的 IP 核生成、综合、实现、比特流生成。板级 wrapper 则是把 Corundum 的顶层接口和 VV4 的物理引脚对接起来。这三样东西配合好了,工程就能跑起来。

4. 板级适配的核心细节

4.1 PCIe 硬核配置与时钟约束

VV4 的 PCIe 是 Gen3 x16,用的是 UltraScale+ 的 PCIe 硬核。Corundum 的参考设计里 PCIe 配置是通过 IP 核生成的,移植时要改几个关键参数:链路宽度改成 x16,链路速度改成 Gen3,参考时钟频率改成板子实际提供的 100MHz。这些参数在 IP 核的配置界面里改,改完重新生成。

时钟约束是容易出问题的地方。PCIe 的参考时钟从板子的时钟芯片来,经过 IBUFDSGTE 进入硬核。约束里要定义这个时钟的周期和抖动范围,还要定义硬核输出的用户时钟。Corundum 的逻辑跑在用户时钟域,如果时钟约束不对,综合出来的时序会报一堆违例,上板后链路可能根本起不来。

提示:PCIe 参考时钟的约束一定要和板子实际提供的频率一致。VV4 的 PCIe 参考时钟是 100MHz,如果你按 125MHz 约束,链路训练阶段就会失败,而且报错信息往往很模糊,不容易定位。

4.2 QSFP28 的 GT 通道映射

VV4 有 4 个 QSFP28 笼子,每个笼子对应 4 对 GT 收发器。移植时要把 Corundum 的以太网接口连到正确的 GT 通道上。这需要查板子的原理图,确认每个 QSFP 的 TX/RX 对连到 FPGA 的哪些 GT bank 和通道。VV4 的 GT 通道分布不是连续的,有些 QSFP 的通道跨了 bank,约束时要特别小心。

我一开始只跑一个 QSFP 口,选了 QSFP0,对应 GT bank 的 4 个通道。约束里定义了这 4 对收发器的引脚位置和参考时钟。Corundum 的 MAC 层支持多口,但第一次移植建议先跑单口,把链路跑通再扩展。单口跑通后,加第二个口只需要复制约束和例化逻辑,工作量不大。

4.3 复位与配置引脚的处理

VV4 的复位和配置引脚在板级约束里有定义,但 Corundum 的参考设计用的是自己的复位逻辑。移植时要把板子的全局复位信号接到 Corundum 的复位输入上,确保上电后逻辑能正确初始化。配置引脚主要是 JTAG 和配置模式相关的,这些在约束里声明一下就行,不影响逻辑功能。

复位逻辑里有个细节:PCIe 硬核的复位和用户逻辑的复位是分开的。硬核复位由板子的 PERST 信号控制,用户逻辑复位由硬核输出的复位信号控制。Corundum 的复位模块要接对这两个信号,否则会出现硬核起来了但用户逻辑没复位的情况,表现为链路能训练但驱动读不到设备。

5. 编译、上板与调试实录

5.1 综合与实现的参数调整

第一次综合的时候,Vivado 报了不少时序违例,主要集中在 PCIe 用户时钟域和 GT 的收发路径上。分析下来有两个原因:一是时钟约束不够紧,二是布局布线时 GT 通道的位置不理想。调整方法是把 PCIe 用户时钟的约束收紧,同时在实现策略里选择性能优先的布局布线选项。

综合选项里有个-flatten_hierarchy参数,我设成了rebuilt,这样 Vivado 能跨层次优化逻辑,对时序有帮助。实现阶段用了Performance_ExplorePostRoutePhysOpt策略,这个策略在布线后会做物理优化,对高速路径的时序收敛效果不错。调整后时序违例基本清掉了,只有少数路径有很小的负 slack,上板后不影响功能。

5.2 比特流下载与链路训练

比特流生成后,用 Vivado 的硬件管理器下载到 VV4。下载完成后,先看 PCIe 链路有没有训练成功。在 Vivado 的硬件管理器里能看到 PCIe 硬核的状态寄存器,如果链路宽度显示 x16、速度显示 Gen3,说明训练成功。如果显示 x1 或 Gen1,说明链路有问题,通常是参考时钟或复位的问题。

链路训练成功后,主机端应该能枚举到设备。用lspci命令能看到 Xilinx 的设备 ID。如果看不到,先检查驱动有没有加载,再检查 PCIe 配置空间有没有被正确映射。我遇到过一次枚举不到设备的情况,最后发现是板子的 PERST 信号没接对,硬核一直处于复位状态。

5.3 驱动加载与回环测试

驱动编译通过后,用insmod加载。加载成功后,dmesg里会打印设备初始化信息,包括队列数量、DMA 通道等。然后可以用 Corundum 自带的测试工具做回环测试。回环测试是把一个 QSFP 口的 TX 和 RX 用光纤跳线连起来,发一批数据看能不能正确收到。

第一次回环测试没通过,收发包计数对不上。排查后发现是 GT 的复位序列有问题,收发器没完全初始化。Corundum 的 GT 复位逻辑需要根据板子的参考时钟稳定时间来调整,VV4 的时钟芯片稳定时间比参考设计里的长,所以复位等待时间要加长。改完复位计数器后,回环测试通过,100G 线速下丢包率为零。

5.4 常见问题速查表

问题现象可能原因排查方法
PCIe 链路宽度只有 x1参考时钟频率不对或复位未释放检查时钟约束和 PERST 信号
主机枚举不到设备驱动未加载或配置空间映射失败用 lspci 和 dmesg 查看
回环测试丢包GT 复位序列不完整加长复位等待时间
时序违例集中在 GT 路径布局布线策略不优换用性能优先的实现策略
驱动加载后设备无响应用户逻辑复位未释放检查硬核输出的复位信号

6. 移植过程中的经验与避坑

6.1 时钟约束宁紧勿松

时钟约束是移植里最容易埋雷的地方。我一开始为了省事,把 PCIe 用户时钟的约束设得比较宽松,综合能过,但上板后链路偶尔会掉。后来把约束收紧到实际时钟的 90% 周期,时序虽然难收敛,但上板后链路稳定了很多。经验是:时钟约束宁可紧一点,让工具去努力收敛,也不要松,松了上板出问题更难查。

6.2 GT 复位序列要按板子调

Corundum 参考设计里的 GT 复位序列是针对 Alveo 板子调的,VV4 的时钟芯片和电源时序不一样,直接照搬会出问题。我的做法是把复位计数器加长,同时增加一个超时保护,如果复位在预期时间内没完成,就重新触发。这个改动不大,但解决了回环测试丢包的问题。

6.3 先单口后多口

VV4 有 4 个 QSFP 口,但第一次移植千万别一上来就搞 4 个口。单口跑通后,多口的扩展只是复制约束和例化,工作量小而且风险低。我见过有人一上来就配 4 个口,结果链路训练和时序问题混在一起,排查了半个月。先跑通一个口,把时钟、复位、GT 这些都验证过,再扩展就顺了。

6.4 驱动和 RTL 要版本对齐

Corundum 的驱动和 RTL 是配套的,寄存器定义和队列格式在两边要一致。移植时如果只更新了 RTL 没更新驱动,或者反过来,会出现驱动读到的寄存器和 RTL 实际行为对不上的情况。我的做法是把 RTL 和驱动放在同一个 git 分支里管理,每次改 RTL 都同步检查驱动有没有对应的改动。

6.5 保留调试接口

上板调试时,Vivado 的 ILA 是救命稻草。我在 PCIe 用户接口和 GT 收发路径上都插了 ILA,抓取链路训练和收发包的关键信号。ILA 会占用一些 BRAM 资源,但对调试的帮助远大于资源开销。等链路稳定后,再把 ILA 去掉,重新综合出最终版本。

7. 后续可以继续做的方向

单口 100G 跑通只是第一步。接下来可以做的包括:把 4 个 QSFP 口都配起来,跑多口并发;优化 DMA 引擎的队列深度,提升小包性能;对接 DPDK 或者 XDP,让主机侧的应用能直接走 Corundum 的收发路径;还可以在 Corundum 的逻辑里加自定义的包处理模块,做硬件卸载。

我个人在实际操作中的体会是,移植这件事最花时间的不是写代码,而是查板子手册和调时序。Corundum 的 RTL 本身质量很高,板级适配层也不厚,只要把时钟、复位、GT 这三样搞对,剩下的就是耐心调试。VV4 这块卡资源足、接口多,跑通之后能做的事情很多,值得花时间把它吃透。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进