Vortex 3.0 + xcvu19p CMD_MEM_WRITE 调试
-
- 一、背景
- 二、调试过程
-
- [第一阶段:人工排查 --- 怀疑 Block Design](#第一阶段:人工排查 — 怀疑 Block Design)
- [第二阶段:思路转变 --- 从「找自己的错」到「找工具的错」](#第二阶段:思路转变 — 从「找自己的错」到「找工具的错」)
- [第三阶段:引入 AI 协助 --- 综合后仿真定位根因](#第三阶段:引入 AI 协助 — 综合后仿真定位根因)
-
- [3.1 综合后仿真初探](#3.1 综合后仿真初探)
- [3.2 逐步缩小范围](#3.2 逐步缩小范围)
- [3.3 确认根因](#3.3 确认根因)
- [3.4 最小化修复](#3.4 最小化修复)
- 三、根因总结
- 四、心得与启示
-
- [1. 「同一套 RTL,不同芯片不同结果」是最有价值的线索](#1. 「同一套 RTL,不同芯片不同结果」是最有价值的线索)
- [2. 行为仿真通过 ≠ 硬件没问题](#2. 行为仿真通过 ≠ 硬件没问题)
- [3. 综合属性不是万能的](#3. 综合属性不是万能的)
- [4. 警惕硬件中的「成功假象」](#4. 警惕硬件中的「成功假象」)
- [5. 保守使用语言特性的「边缘组合」](#5. 保守使用语言特性的「边缘组合」)
- [6. 最小化修复,精准打击](#6. 最小化修复,精准打击)
- [7. 善用 AI 加速调试闭环](#7. 善用 AI 加速调试闭环)
- 五、调试方法论
一、背景
1、项目上下文
事情开始于将 Vortex 3.0 GPU 软核移植到 xcvu19p-fsva3824-2-e 芯片。在此之前,已有两条成熟的基线:
| 基线 | 芯片 | 状态 |
|---|---|---|
| Vortex 3.0 + xcvu440-flga2892-1-c | xcvu440 | 正常运行 |
| Vortex 2.3 + xcvu19p-fsva3824-2-e | xcvu19p | 正常运行 |
本次任务需要在 xcvu19p 上同时运行 Vortex 3.0 和 PCIe Bridge。这意味着不能直接把 xcvu440 的工程搬过来------两款芯片的 PCIe IP 不同(xcvu440 和 xcvu19p 分别使用不同的 PCIe 硬核 IP),工程需要重新搭建。
幸运的是,xcvu19p 上已经有一个跑通了 PCIe Bridge 功能的 demo 工程,可以作为起点。
2、问题现象
在 xcvu19p 的 PCIe Bridge demo 基础上,加入 DDR 控制器和 Vortex 3.0 IP 后,测试发现:
CMD_MEM_WRITE和CMD_MEM_READ命令执行后,DDR 上数据没有任何变化- 通过 ILA 抓取
m_axi_mem接口,发现没有任何 AXI 请求发出 - 但 Vortex 状态寄存器报告命令执行成功(无错误标志位置位)
环境:Vivado 2020.2,目标器件 xcvu19p-fsva3824-2-e。
二、调试过程
第一阶段:人工排查 --- 怀疑 Block Design
最开始,思路集中在「工程搭建是否正确」上。毕竟 xcvu440 上的 Vortex 3.0 工作正常,问题大概率出在 xcvu19p 工程本身。
尝试 1:照着 xcvu440 的 Block Design 重建
以 xcvu440 上正常工作的 Block Design 为蓝本,只替换 PCIe IP 为 xcvu19p 对应的版本,其余互联结构尽量保持一致。结果:问题依旧。
尝试 2:尝试多种 AXI 互联方案
怀疑是 AXI Interconnect / SmartConnect 的配置或连接方式有问题,反复调整 Block Design 中的互联拓扑。结果:均以失败告终。
尝试 3:RTL 行为仿真
既然硬件上不行,回头看仿真。跑了一遍 RTL 级行为仿真,结果完全正确------这也符合预期,毕竟同一套 RTL 在 xcvu440 上实机运行正常。行为仿真只能告诉你「RTL 逻辑没错」,但无法暴露综合/实现阶段的问题。
阶段性结论:RTL 逻辑正确,工程搭建无明显错误,问题可能出在综合或实现阶段。
第二阶段:思路转变 --- 从「找自己的错」到「找工具的错」
多条路走不通之后,一个关键事实开始浮现:
同一套 RTL,xcvu440 正常,xcvu19p 异常。这不可能仅仅是我工程搭建的问题。
这个认知将排查方向从「我哪里连错了」转向了「工具对两个芯片的处理有什么不同」。
尝试 4:寻找 LEC(逻辑等价性检查)工具
最直接的思路是:对两个芯片分别跑综合,然后用 LEC 工具对比生成的网表,看行为是否一致。如果能找到差异点,就能定位问题。
现实很骨感------没找到可用的 LEC 工具下载链接,这条路走不通。
第三阶段:引入 AI 协助 --- 综合后仿真定位根因
既然人工排查 + 传统工具都遇到了瓶颈,决定让 AI 介入,对 Vortex 3.0 IP 的 CMD_MEM_WRITE 数据路径做系统性的综合后仿真分析。
关键决策:不做 RTL 仿真(已经确认正确),直接跑综合后仿真,对比 xcvu440 和 xcvu19p 的综合网表行为。
3.1 综合后仿真初探
基于 Vivado 2020.2 环境,AI 首先对 xcvu440 跑了综合后仿真。出乎意料的是------xcvu440 的综合后仿真结果也不对。这说明即使是 xcvu440,在 Vivado 2020.2 下综合出的网表也存在问题,只是在该芯片上表现不同(可能恰好不影响实测结果,或者此前 xcvu440 用的是不同版本的 Vivado)。
分析网表后发现,DMA 相关的连线在综合后丢失 ,导致 m_axi_mem 无法发出 AXI 请求。这解释了一切:DMA 数据路径断了,写请求根本到不了 DDR。
3.2 逐步缩小范围
AI 对两个芯片的综合后仿真结果进行了系统对比,排查过程如下:
- 排除 FPU 嫌疑:xcvu19p 上 FPU 模块可能有问题?但 FPU 与 DMA 数据路径无关,排除。
- 排除 DMA 状态机本身:DMA 的逻辑在两个芯片上综合结果应一致,问题更可能在输入侧。
- 聚焦
VX_cp_unpack.sv:这是命令解包模块,负责从 AXI 数据中提取cmd.arg0(DDR 目标地址)、cmd.arg1(Host 源地址)、cmd.arg2(DMA 传输字节数)。如果解包结果错了,DMA 收到的就是错误的命令参数。
尝试过的修复手段(均无效):
| 手段 | 结果 |
|---|---|
在 VX_cp_unpack 及其相关模块上加 KEEP_HIERARCHY + DONT_TOUCH 属性 |
无效 |
综合选项 -keep_equivalent_registers -no_lc -resource_sharing off |
无效 |
(* keep = "true" *) 内部属性保护关键寄存器 |
无效 |
set_property DONT_TOUCH 通过约束文件施加 |
无效 |
综合工具铁了心要把这段逻辑「优化」掉,任何阻止它的尝试都失败了。
3.3 确认根因
最终定位到 VX_cp_unpack.sv 中使用了 Variable Part Select 语法的三个函数:
cl_byte--- 单字节提取read64--- 8 字节拼接为 64 位read_hdr--- 命令头解析
问题代码模式:
根因 :Vivado 2020.2 在 xcvu19p (UltraScale+) 上,对 function automatic + int 参数 + 运行时 +: part-select + for 循环 + packed struct 返回值 这一组合的综合优化存在 bug,导致函数返回值被错误优化。具体表现为 cmd.arg2(DMA 传输长度)综合后恒为 0。
verilog
function automatic logic [63:0] read64(input int off);
logic [63:0] result;
for (int i = 0; i < 8; i++) begin
result[i*8 +: 8] = cl_data[(off + i)*8 +: 8]; // 运行时变量 part-select
end
return result;
endfunction
cmd.arg2 = read64(off + 4 + 16); // DMA 长度 → 综合后恒为 0
这完美解释了所有症状:
arg2 = 0
→ DMA 认为没有数据要搬
→ 直接跳到 S_DONE 状态
→ 完成回写照常执行(所以状态寄存器报告「成功」)
→ m_axi_mem 从未收到任何写请求
→ DDR 数据不变
m_axi_host有 ILA 波形 ✓ (命令抓取和完成回写仍通过 host 接口执行)- 状态寄存器显示成功 ✓ (DMA 走了
S_DONE,q_error硬编码为 0) m_axi_mem无请求 ✓ (从未进入S_REQ_AW/S_WRITE状态)- DDR 数据不变 ✓
- xcvu440 行为不同 ✓ (UltraScale 器件的综合优化策略不同,未触发此 bug)
3.4 最小化修复
AI 给出的修复方案极为克制------只需修改 VX_cp_unpack.sv 一个文件 ,将 cl_byte、read64、read_hdr 三个使用 Variable Part Select 的函数,改为 always_comb 显式逐字段赋值:
verilog
// 修复前:function + 循环 + 运行时 part-select(触发综合 bug)
// function automatic logic [63:0] read64(input int off);
// for (int i = 0; i < 8; i++)
// result[i*8 +: 8] = cl_data[(off + i)*8 +: 8];
// return result;
// endfunction
// cmd.arg2 = read64(off + 16);
// 修复后:always_comb 显式赋值(综合器无歧义)
always_comb begin
cmd.arg0 = cl_data[off*8 +: 64];
cmd.arg1 = cl_data[(off+8)*8 +: 64];
cmd.arg2 = cl_data[(off+16)*8 +: 64];
// ...
end
修复后,xcvu19p 综合后仿真日志中出现 DEV AW/W,DMA 正常发起写事务,仿真 PASS。
三、根因总结
一句话 :Vivado 2020.2 在 xcvu19p (UltraScale+) 上对 function automatic + 运行时 +: variable part select 的综合优化存在 bug,导致 Vortex 命令解包模块中的 DMA 长度字段被错误优化为 0,DMA 控制器因此跳过了实际的内存写入操作。
受影响的 SystemVerilog 代码模式:
verilog
function automatic logic [W-1:0] extract(input int idx);
for (int i = 0; i < W/8; i++)
result[i*8 +: 8] = data[(idx + i)*8 +: 8];
return result;
endfunction
为什么只影响 xcvu19p 而不影响 xcvu440:两款芯片架构不同(UltraScale+ vs UltraScale),Vivado 综合引擎使用不同的器件映射和优化策略。xcvu19p 的优化路径触发了这个 bug,而 xcvu440 没有。
四、心得与启示
1. 「同一套 RTL,不同芯片不同结果」是最有价值的线索
这个信息直接将排查方向从「RTL 逻辑错误」扭转为「工具行为差异」。如果两个芯片都失败,可能会在 RTL 层面浪费大量时间。当问题呈现器件相关性时,首先怀疑综合/实现工具链,而非自己的 RTL。
2. 行为仿真通过 ≠ 硬件没问题
RTL 行为仿真只能验证逻辑的正确性,无法暴露综合器的 bug。综合后仿真(post-synthesis simulation)是桥接 RTL 与硬件的关键验证步骤------它能发现综合工具对 RTL 的「误解」。在这个案例中,RTL 仿真完全正确,xcvu440 实机也正常,唯独 xcvu19p 的综合出了问题。如果没有综合后仿真,这个 bug 可能需要更长时间才能定位。
3. 综合属性不是万能的
DONT_TOUCH、KEEP_HIERARCHY、(* keep = "true" *) 等属性在本案例中全部失灵。工具 bug 不会因为你说「请保留这段逻辑」就正确地保留它------综合器根本不知道自己在做错误的优化。当综合属性无效时,重构代码往往比与工具对抗更高效。
4. 警惕硬件中的「成功假象」
本案例中,状态寄存器报告命令执行成功,但实际数据完全没有写入 DDR。原因有三:
q_error硬编码为32'd0,根本不反映实际错误状态cp_error未从子模块(DMA、fetch 等)正确聚合- DMA 即使跳过了数据搬运,仍然走到了
S_DONE状态并发送了完成回写
教训:不要信任未经充分验证的状态寄存器。芯片设计时,错误报告路径和正常数据路径同样重要。
5. 保守使用语言特性的「边缘组合」
function automatic + int 参数 + 循环中的 +: part-select + 返回 packed struct,这个组合在仿真中完全正确,在大多数综合器中也正确,但它恰好踩中了 Vivado 2020.2 在特定器件上的 bug。
对于数据路径上的关键逻辑,优先使用更「保守」的写法。 always_comb + 静态 part-select 的语义没有任何歧义,综合器几乎不可能出错。减少对工具优化正确性的依赖,本质上是降低不确定性。
6. 最小化修复,精准打击
最终的修复只修改了一个文件(VX_cp_unpack.sv),只改动了真正触发 bug 的三个函数。在大型 FPGA 工程中,每增加一行改动就多一分引入新问题的风险。找到根因后,用最小的改动解决问题。这不只是优雅,更是工程纪律。
7. 善用 AI 加速调试闭环
如果没有 AI 协助,手动对比两个芯片的综合后仿真结果、逐模块排查网表差异,将是一个极其耗时且容易遗漏的过程。AI 在以下环节发挥了关键作用:
- 穷举式代码审查:通读全部 ~100 个源文件,不遗漏任何可疑点
- 自动化综合后仿真:对两个芯片分别跑综合→仿真→日志分析,形成闭环
- 网表差异分析:从海量网表信号中快速定位 DMA 连线丢失
- 最小化修复:在理解整个数据路径的基础上,给出只触及根因的修改方案
AI 不是替代工程师的判断,而是放大了工程师的分析带宽。
五、调试方法论
回顾整个调试过程,可以归纳为以下推进节奏:
观察现象 (DDR无变化, ILA无信号)
→ 复现确认 (CMD_MEM_WRITE/READ 均异常)
→ 排除工程问题 (Block Design重建, 多种互联方案)
→ 排除RTL逻辑 (行为仿真通过)
→ 思路转变 (芯片差异 → 怀疑工具)
→ 寻找对比手段 (LEC未果 → 转向综合后仿真)
→ 系统对比 (xcvu440 vs xcvu19p综合网表)
→ 定位模块 (DMA连线丢失 → VX_cp_unpack)
→ 确认根因 (Variable Part Select触发综合bug)
→ 最小化修复 (改一个文件)
→ 验证通过 (综合后仿真 PASS)
核心理念:调试不是靠一次想对,而是靠系统性地排除错的。 每一步排除都在缩小搜索空间。从 Block Design → RTL 逻辑 → 综合工具 → 具体模块 → 具体语法,每个阶段的结论都在为下一阶段导航。最终剩下的那个解释,无论多么意外(谁会想到是综合器在特定器件上对特定语法的 bug?),就是答案