恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Corundum开源FPGA网卡移植到Bittware VV4的完整实践
首页
资讯中心
/
Corundum开源FPGA网卡移植到Bittware VV4的完整实践
Corundum开源FPGA网卡移植到Bittware VV4的完整实践
发布时间:2026/9/26 2:56:42
搞网络硬件的人多多少少都听说过 Corundum。它是目前最有名、也最完整的开源 FPGA 网卡方案支持 10G/25G/40G/100G 以太网自己带 PCIe DMA 引擎、队列管理、MAC 和 PHY 接口等于把一块商用以太网卡的 IP 全部开放给你。但开源是一回事真正把它跑到某一块具体板卡上又是另一回事尤其是像 100G 这种高速接口任何一点引脚约束、时钟关系、GT 位置对不上都会导致整块 FPGA 起不来或者端口link不上。这次我做的事情就是要把 Corundum 移植到 Bittware VV4 这块板卡上。VV4 用的是 Xilinx Virtex UltraScale VU4P板上带双 QSFP28 光口、PCIe Gen3 x16、DDR4从硬件规格上看很适合做 100G 网卡验证。但它不在 Corundum 官方支持的平台列表里所以不能直接make一把梭需要自己补平台工程、改约束、调 IP。这篇是系列的第一篇重点讲移植思路、平台工程怎么搭、文件系统怎么组织以及我在实际操作中踩到的坑和总结出来的流程。适合有一定 FPGA 基础、想跑 Corundum 但手里板卡不是官方平台的朋友参考。1. 为什么要把 Corundum 搬到 Bittware VV4 上1.1 Corundum 到底是什么我简单介绍一下免得有人第一次听说这个项目。Corundum 是一个基于 FPGA 的 10G/25G/100G 以太网网卡实现整个工程用 Verilog 写模块化程度非常高。它不是那种只能跑 demo 的玩具而是带完整数据通路的设计PCIe 侧负责和主机通信内部有 DMA 引擎、发送/接收队列、中断控制网络侧则把 Ethernet MAC、PCS/PMA、以及和光模块对接的高速串行收发器全部串起来。它的架构在 GitHub 上可以直接拿到目录结构也清晰fpga/mqnic/下面按平台归好类官方支持了不少板卡比如 Xilinx 的 VCU118、Alveo 系列、部分 Bittware 板卡等。对想自己研究网卡内部实现的人来说这是很难得的参考设计对做实验、做验证、做数据面加速的人来说Corundum 又能直接当成一块 100G 网卡来用Linux 下都有配套驱动。说白了Corundum 解决了两个问题一是把网卡内部细节透明化二是让高速网络硬件不用非得买 ASIC 网卡才能玩。用一块 FPGA 开发板烧上这个工程就能做成一块 100G NIC。1.2 为什么选 Bittware VV4 这块板子Bittware VV4 这块板子我拿到的是配了双 QSFP28 的版本。QSFP28 一个口就是 4 路 25G可以拆成 4x25G也可以从 MAC 层聚合走 100G正好对应 Corundum 里 4 通道的 100G MAC 设计。板卡的主芯片是 Xilinx Virtex UltraScale VU4P逻辑资源不算最大但做 100G 网卡验证绰绰有余再加上 PCIe Gen3 x16、DDR4 和板载时钟方案基本把一块现代网卡需要的接口都集齐了。选它还有一层原因——它不在 Corundum 官方支持列表里。官方支持的板卡工程文件、约束、IP 配置都是现成的make一下就完事整个过程学不到什么东西。但 VV4 这种非官方板卡就需要你完全理解 Corundum 的平台依赖自己把缺的部分补上。这个迁移过程才是对一个人 FPGA 设计和 IP 集成能力的真正考验。而且这种板卡往往基于 UltraScale 系列和官方支持的 VCU118、Alveo 在底层资源上有很多相似之处移植路径是清晰的。如果你手上是其他型号的 Bittware 板卡或者类似规格的第三方 FPGA 板这篇里说的思路一样适用核心就是搞清楚 Corundum 对平台有哪些隐含要求然后在自己的工程里把这些要求逐条满足。1.3 移植这件事难在哪很多人以为移植就是把器件型号改一改编译下载就能跑实际上完全不是。Corundum 不是一段松散的 RTL它由多个 IP 和硬核协同工作PCIe 硬核有它固定的参考时钟和位置约束QSFP28 光口要接到指定的 GTY transceiver 上PCS/PMA 的时钟频率、复位时序、MDIO 接口都要和板载物理层对上。接口对了还有 DDR 的时钟和引脚以及整个设计的时序收敛问题。一句话总结难在两处第一板卡的硬件资源位置和 Corundum 默认假设不一致第二FPGA 工程的组织方式必须完全贴合 Corundum 的构建脚本否则后续make流程会把你逼疯。这篇文章后面给的方案主要就是围绕这两点来展开的。2. 移植前的架构拆解与方案取舍2.1 Corundum 的整体架构移植之前我先把 Corundum 在顶层是怎么分层的梳理了一遍这决定了我改哪些文件、不动哪些文件。Corundum 的网卡侧设计是围绕 MQNICModular Queue NIC这一套结构展开的。从主机 PCIe 进来的数据经过 XDMA 或者自定义 DMA 逻辑转成 AXI 总线格式然后进入一个内部互联网络再分发到多个以太网端口模块。每个以太网端口模块里包含一个可配置的 MAC、一个 PCS/PMA 层最后接到 GTY/GT 高速收发器上。发送和接收都有独立的 FIFO 和队列管理中断控制器负责告诉主机“数据到了”。这个架构对移植非常友好因为它把平台相关的东西约束在几个边界上。PCIe、DMA、队列、MAC 这些都是平台无关逻辑它们的接口是标准 AXI而 GT 位置、参考时钟输入、板卡上的复位按键、QSPI flash、JTAG、DDR 引脚、PCIe 硬核这些才是平台相关的东西。移植的时候只需要把平台相关的部分替换掉平台无关的核心逻辑可以保留。这就是“分层”的意义。如果不理解这一层你会看到一个顶层例化了一堆 IP根本不知道该动谁。2.2 移植需要满足的“接口契约”我习惯把这些平台相关的东西称为“接口契约”。移植前必须逐项确认有遗漏后面对时序和功能都会出问题。我列一下我每次移植都会核对的关键项第一时钟方案。Corundum 每个平台都有自己的时钟拓扑比如 PCIe 参考时钟、以太网 GT 参考时钟、DDR 参考时钟、系统复位后逻辑时钟。这些时钟的频率、来源、是否差分都必须按板卡原理图来。VV4 上有些时钟可能是同一个 PLL 出来的不同频率需要单独约束。第二复位逻辑。有的板卡上电后由电源监控芯片拉低复位有的需要 FPGA 内部逻辑去释放还有的板卡没有用户复位按键。Corundum 顶层一般会有一个全局复位模块你得把板卡实际复位信号接进去或者用逻辑自己产生复位。第三PCIe 硬核的位置和可变性。UltraScale 里 PCIe 硬核位于固定的 SLR/column 上而 Corundum 的 PCIe IP 配置必须和话术对得上否则综合出来的设计可能没法布线到硬核上。第四以太网 GT 位置。QSFP28 的 8 个 lane 或者两个口的 8 个 lane必须接到 FPGA 里支持 25G 的 GTY transceiver 上而且这些 GT 要在同一个 Quad 内或者按照时钟分布要求布局。VV4 的原理图里会把 QSFP28 差分对连到 FX 哪个 Quad这个要反查。第五DDR 接口。有的 Corundum 平台带 DDR用于更大的 DMA 缓冲有的不带。如果带DDR 的引脚约束也得补上并且 DDR 控制器 IP 的型号和带宽要和板卡匹配。这五项我都写进了一张检查表每次移植前先过一遍。别看它们不起眼90% 的问题都出在这些地方。2.3 平台工程方案怎么选在动手之前我评估了三条路。第一条路直接在 Corundum 里找一个最接近的官方平台比如 Bittware 的某个已支持型号复制一份目录改成 VV4 的名称然后把约束文件和器件型号全换掉。优点是快缺点是有时候官方平台和你的板卡差别很大改起来反而像在打地鼠。第二条路从零手写一个平台目录。就是自己新建fpga/mqnic/Bittware_VV4/目录新建create_project.tcl、Makefile、约束文件、IP 配置。优点是完全可控缺点是 Corundum 构建脚本对文件结构有依赖你如果不熟悉它的 TCL 流程花在脚本上的时间可能比 RTL 还多。第三条路用 Vivado 的 Block Design 重新搭一遍顶层再调用 Corundum 的模块。我没选这条路因为 Block Design 对某些高速 IP 的迭代非常慢而且 Corundum 本身的构建脚本是批处理化的绕开脚本反而失去了后续自动化编译的优势。我最后走的是第二条路但参考了第一条路的一些结构。先在fpga/mqnic/下建Bittware_VV4目录然后在里面组织一个最小可行的 Vivado 工程把 IP 生成、约束加进来让 Corundum 的make能直接驱动。这样后续每次改 RTL、改约束都能一条命令跑完整条链路效率最高。3. 核心实操细节与关键步骤3.1 第一步从原理图抠出来的“硬信息”我要强调一下任何移植都不要从样例工程开始要从板卡原理图和手册开始。我这次拿到 VV4 之后先用原理图查清楚几件事FPGA 具体型号和封装比如xcvu4p-flga2892-2L-e这种完整编号。同一个 VU4P 有不同速率等级和封装IP 配置和时序约束都要和它匹配。QSFP28 的 A/B 口分别接到了 FPGA 的哪一组 GTY。查这部分要看原理图上的网络标号比如MGT_REF_CLK、QSFP0_TX_P/N、QSFP1_RX_P/N每个引脚都要记录。有没有板载时钟芯片频率多少。VV4 上应该有一个可编程时钟发生器给 GT 参考时钟100G 模式下四路 25G 对应的参考时钟一般是 156.25MHz但这个必须和实际硬件确认不能想当然。PCIe 是 Gen3 x16 还是拆分成了多个 x8、x4参考时钟是从金手指引入的还是板载的这对 PCIe IP 的配置方式有直接影响。把这些信息整理成一张表格后面写 XDC 约束和调 IP 的时候对着这张表填数就能少走很多弯路。3.2 平台目录怎么建Corundum 的平台目录结构大概是这样的以官方某个平台为例fpga/mqnic/VCU118/ ├── Makefile ├── create_project.tcl ├── constraints/ ├── ip-defs/ ├── rtl/ ├── xdc/ └── scripts/我没有完全照搬这个结构因为 VV4 的资源分配和 VCU118 差很多。我创建了fpga/mqnic/Bittware_VV4/里面至少要有这几个东西Makefile调用 Vivado 批处理模式跑create_project.tcl并把 bit 文件输出到指定目录。create_project.tcl创建工程、设置 part、添加 RTL、添加约束、生成 IP。xdc/存放顶层 XDC 约束文件包含时钟、复位、PCIe、QSFP、DDR 等所有引脚和时序约束。ip/本项目需要生成的 IP 核比如 PCIe、以太网 PCS/PMA、DDR4有些可以直接放现成 xci有些用 TCL 动态生成。Corundum 的构建脚本通常会从create_project.tcl里读device和board参数所以我在 TCL 里把part写成 VV4 的实际型号其余保持不变。3.3 把原工程里的 IP 逐个换成板卡需要的配置这一步是最花耐心的。Corundum 官方平台用的是配套 IP 版本但换到 VV4 以后由于器件型号变了IP 需要重新生成。常见的几个PCIe IP官方平台可能是 PCIe Gen3 x16VV4 可能支持但位置不同。要打开 PCIE IP 配置确认 Lane 数、参考时钟频率、Endpoint/RC 模式然后确认AXI 接口宽度和频率。Corundum 对 PCIe AXI 接口宽度和时钟频率有具体要求这个直接关系 DMA 带宽和时序。100G Ethernet PCS/PMACorundum 100G 模块使用的是 Xilinx 100G 以太网 IP这个 IP 的接口很长要仔细对齐。特别是它的时钟是gt_rx_clk、gt_tx_clk还是coreclk每种模式不同。你还要留意这个 IP 的许可证有些 license 只绑定在特定器件上换型号后可能无法仿真或综合。DDR4 IP如果 VV4 带 DDR 并且你打算启用需要新建 DDR4 控制器 IP配置成板卡实际颗粒的容量、频率和位宽。这一步不能直接复用官方工程的 DDR4 IP因为引脚完全不一样。IP 换完以后在顶层 TCL 里重新生成 bit 之前先做一次elaboration它会把你所有例化模块的端口连接错误列出来尤其能发现 IP 接口名称不一致的问题。这一步比直接综合省时间太多。3.4 时钟约束别只写 create_clock写约束文件时很多新手只写一个create_clock然后觉得万事大吉。实际上对于 100G 接口你的约束至少要包含下面这几类主时钟比如 PCIe 参考时钟、GT 参考时钟、DDR 参考时钟虚拟时钟如果你用set_input_delay和set_output_delay的时候需要一个基准时钟生成时钟PLL/MMCM 产生的内部时钟如果 Vivado 没自动追踪到需要手动补充异步时钟域约束DDR 和以太网 PCS 的时钟往往和逻辑时钟是异步的要设置set_clock_groups -asynchronous避免 timing 引擎误报GT 相关的高速时钟通常由 IP 内部处理但如果你在约束里误写了这些时钟会导致一堆 hold 违规。我自己写 XDC 的习惯是把约束按“时钟 - 引脚 - 时序伪路径/时钟组”分成三段并尽量在每段前面写注释方便日后回查。一个典型的引脚约束片段长这样set_property PACKAGE_PIN AW27 [get_ports pcie_refclk_p] set_property IOSTANDARD LVDS [get_ports pcie_refclk_p] set_property PACKAGE_PIN AP36 [get_ports eth0_gt_rx_p[0]] set_property IOSTANDARD LVDS [get_ports eth0_gt_rx_p[0]]当然具体引脚编号要对着 VV4 原理图填我在这里只是给你示范写法和思路。3.5 复位和 MDIO 接口的坑把网络口和 PCIe 都接好之后还有一个容易被忽略的MDIO。QSFP28 的光模块管理比如读取模块温度、电压、los 信号都是走 MDIO 接口。Corundum 的以太网端口模块会通过 MDIO 访问 PCS/PMA 内部寄存器如果你的 MDIO 引脚没接对或者方向错了PCS 状态寄存器读不出正确值端口会一直显示 no link。还有个典型问题是复位。有的板卡把光模块的复位 pin 拉死为高导致模块一直处于复位状态这样无论怎么配置都不可能 link 起来。我这次在 VV4 上就遇到类似情况后来把 QSFP28 的复位引脚接成由 FPGA 控制在启动流程里先拉低再释放问题才解决。这部分属于板级细节移植的时候一定要回到原理图去确认信号方向。4. 综合实现与编译调优4.1 综合之前先解决“资源打架”问题Corundum 支持多端口配置。官方平台有的板卡带 8 个 SFP有的带 2 个 QSFP28所以 RTL 顶层有多个端口模块的例化。VV4 是 2 个 QSFP28也就是最多两个 100G 口。如果直接从某个多端口官方平台复制顶层可能例化了 8 个以太网端口这不仅浪费 LUT、FF 和 BRAM更重要的是会把 GT 资源消耗在根本不存在的光口位置上布局布线时必然报错。所以我在顶层做了裁剪保留两个端口。怎么裁剪找 Corundum 顶层里端口实例化的部分删掉多余的mqnic_eth_block例化同步调整 AXI 互联和总线的地址映射。这一步看起来简单实际牵涉到队列数量、中断分配、DMA 带宽所以不能乱删要把每个模块的base_addr和维护的端口号对应起来。4.2 Timing 收敛的实际操作第一次跑完综合实现以后大概率时序是不过的。我自己的经历是100G 接口的时序难点主要有两个第一个是 PCS/PMA 到 MAC 的接口时序。这个接口的频率很高比如 100G 分成 4 个 lane 后每个 lane 的线速率是 25G内部并行数据总线的时钟可以达到几百兆赫兹。如果约束文件里 GT 参考时钟写对了但内部的生成时钟约束没有配好timing 引擎就会在 PCS 到 MAC 的路径上报出很大的违规。解决办法通常是给相关路径打上set_multicycle_path或者放到异步时钟组前提是你确实理解了这条路径的真实时序关系不能为了调时序而盲目约束。第二个是 PCIe DMA 与用户逻辑之间的 AXI 时钟域跨越。DMA 模块的时钟频率和以太网 MAC 的时钟频率不一样中间的数据 FIFO 处理不好也会出现 hold 违规。Corundum 本身在这些接口上做了同步器但你的 XDC 如果错误地把这两个时钟设为同步的会导致报出大量伪路径。正确的做法是识别真实的时钟域边界设置异步时钟组或者正确的相互约束。我编译时习惯用 Vivado 的Implementation Strategies先从默认的Performance_Explore跑如果 critical path 在 GT 附近的 IPO 上再试Congestion相关的策略。还要留意每轮实现后的report_timing_summary看 WNS、TNS、WHS不要只看它是否通过要关注最短的 slack 是落在哪类路径上。只有知道瓶颈在哪才能决定是改 RTL 流水、改约束还是换实现策略。4.3 资源占用和布局检查我在 VV4 上做完两个 100G 口、带 PCIe 和 DDR 的配置后资源占用大概的情况是 LUT 和 FF 各用了百分之二三十BRAM 占得稍多一点但整体留有冗余。真正要关心的是 GT 位置和 PCIe 位置这些固定资源一旦放不下整个实现直接失败。跑完place_design之后我会打开 device 视图人工确认几个关键资源的位置PCIe 硬核是否在预期 columnQSFP28 的接收和发送 lane 是否落在同一个 GT QuadDDR 的引脚有没有被错误地扇出到了远处导致长走线。如果在 device 视图里发现 GT 走了很远的布线就要考虑是不是约束文件里引用的引脚不对或者是该 GT Quad 本身不满足参考时钟共享要求。这类问题不解决即使 bit 生成成功上板也大概率没有输出。4.4 第一次生成 bit 前的 checklist生成 bit 之前我会再过一遍下面这些检查顶层 RTL 里是否还有未连接的悬空端口尤其是一些error、status类信号所有 IP 的OUTPUT PRODUCTS本地方特别是 PCIe 和以太网的仿真模型避免最后跑车时才想去仿真XDC 文件里是否有 FPGA pin 冲突同一个物理引脚被约束到两个逻辑端口顶层是否有残留的虚拟模块或未使用的用户逻辑导致综合面积虚高bit 文件路径是否被 Makefile 正确输出到 release 目录方便后面烧写。检查完再跑整条make流程这台机器如果 CPU 不强第一次从综合到生成 bit 可能要一个多小时。中间可以把报告导到同一个子目录里方便对比。5. 常见问题与排查技巧实录5.1 从官方平台复制工程时最常见的五个问题我这次移植前后改了很多遍下面这几个问题我遇到一次就记一次列成速查表希望你能避开现象根因解决思路综合报错找不到 IP 的某个端口官方平台 IP 版本和当前 Vivado 版本不匹配重新生成对应 IP并手动检查接口名变化布局时报 GT 位置冲突QSFP28 的线被约束到了不支持 25G 的 GT 上对照原理图重新选 GT Quad 并改写 XDCPCIe 枚举不到设备PCIe 参考时钟频率或 Lane 数配置错误查原理图确认参考时钟来源重新配置 PCIe IP端口 link 不起来QSFP28 复位引脚被拉死或 MDIO 未接对在 XDC 和顶层 RTL 中修正复位控制以及 MDIO 方向时序 WNS 为负且集中在 GT 附近的 IO 路径输入输出延时约束缺失或错误补set_input_delay/set_output_delay必要时用虚拟时钟5.2 有些问题不是逻辑问题是构建脚本问题Corundum 的构建脚本很长里面有一些路径变量是从平台名推导出来的。如果你新建一个平台目录但Makefile里的BOARD、PROJECT变量没有改成Bittware_VV4Vivado 会跑到默认路径去加载约束然后报一堆找不到文件的错误。这种问题不好发现因为它不会直接告诉你“变量没改”而是报一个“file not found”让你去怀疑文件路径。我的建议是新建平台目录后先不要改任何 RTL只复制一个最小可综合的工程验证 Makefile 和 TCL 流程能跑通再加 Corundum 的源码。这样可以把“脚本问题”和“RTL 问题”分开排查不然所有错误混在一起非常痛苦。5.3 实测过程中的一个细节先回环再上交换机bit 生成之后我习惯先做“内部回环测试”。这个回环不是把光口短接而是把 PCS/PMA 的 TX 和 RX 在 FPGA 内部做一个回环看看 MAC 层的收发通道是否正常。Corundum 的 PCS/PMA 模块一般有回环模式下探针可以在 Vivado 的硬件管理器里临时置位。如果内部回环都通不了问题一定在 FPGA 内部而不是光模块或线缆如果内部回环通了但外部不通那就要查光模块的 MDIO 配置、复位状态和线缆了。Vivado 的 ILA 在这里非常重要我会在生产 bit 里预先留一个 ILA只抓链路状态相关的信号比如tx_axis_tvalid、rx_axis_tvalid、pcs_rx_status这样上板后能直接看到数据到底卡在哪里。另一个实践细节是不要把整个 bit 烧进去以后再慢慢 hw_manager 里翻寄存器最好先写好一个小的寄存器读写脚本对 Corundum 里 NIC 的基地址做基本的 read/write 验证再往数据通路走。5.4 关于 Vivado 版本和许可证我有话说Corundum 这种项目对 Vivado 版本比较敏感因为 Xilinx IP 的接口在版本之间会变。比如某些百兆以太网 IP 在 2020.2 和 2022.1 里端口名都不一样直接升级会导致 RTL 例化端口对不上。我的建议是先确认你手里的 Corundum 版本是基于哪个 Vivado 验证过的然后固定用那个版本不要追新。另外100G 以太网 IP 通常需要许可证。如果用正版 Vivado它会根据你的许可证类型决定 IP 是否可以生成。如果卡在 license 上工程会停在生成 IP 的步骤。这里我没办法帮你绕过但因为你是做研究和移植验证可以去 Xilinx 评估页面申请对应 IP 的评估 license流程上不难。记得提前申请别等编译到一半发现 IP 不能生成。5.5 其他容易忽略的小点我还想在结尾多说几个容易忽略的点。Corundum 顶层通常有参数化的端口数量如果你从多端口平台改到双口记得要把AXI总线宽度、内部 FIFO 深度这些参数一起调整不然即使编译过了实际带宽也会不对。引脚约束里的IOSTANDARD必须和板卡上电平标准一致QSFP28 是接 GTY 的这部分的 IOSTANDARD 一般由收发器 IP 帮你做了但 PCIe 金手指和 DDR 这些还是要显式约束。如果你用 LiteX、OpenCPI 等其他开源工具链会发现它们的板卡抽象和 Corundum 不太一样。Corundum 更像是一个为网卡量身定制的框架约束文件和 RTL 高度耦合。所以移植的时候不要想着“只要把引脚填上就能用”你还要理解它内部的总线拓扑尤其是地址映射表。我个人的经验是移植这种高度依赖板级资源的开源项目最大的价值并不是最后跑通那一刻而是中间把整个系统打通的过程。你会被迫把 PCIe、DMA、GT、以太网 MAC、时钟树和复位逻辑在脑子里整体过一遍这对任何 FPGA 工程师来说都是难得的历练。VV4 这次移植到目前只完成了平台工程搭建和初步 bit 生成驱动侧的工作我还在继续验证。下一篇我计划重点写 Linux 驱动加载、端口识别和 iperf 实测带宽这部分到时候把实际吞吐数据和踩坑记录一起放出来。