从 RTL PASS 到门级网表不取指:一次 CV32E40P + Nangate45 + Xcelium 的完整排查记录
今天基本花了一整天追一个门级仿真问题。最开始看到的现象非常简单同一个 CRC32 workload在 RTL 仿真中正常通过但换成综合后的 mapped netlist 后处理器始终无法完成程序。结果看上去只是一个普通的 timeout但实际问题经历了好几层testbench 不兼容、顶层输入疑似悬空、处理器不取指、内部 X 状态传播最后才定位到 Nangate 标准单元仿真模型。这篇记录按照我实际排查的顺序整理整个过程。实验背景实验环境大致如下处理器CV32E40PWorkloadMibench CRC32RTL simulatorCadence XceliumSynthesis outputGenus 生成的 mapped Verilog netlistStandard-cell libraryNangate Open Cell Library 45 nmv2010_12Netlist simulation modezero-delay functional simulation主要仿真选项delay_mode_zero -notimingchecks排查的基本原则是首先证明mapped netlist 能够运行与 RTL 完全相同的 workload。第一步先确认 RTL baseline首先运行 CRC32 的 RTL simulation。RTL 仿真成功完成并得到CRC32 PASS: vector cbf43926 signature 2d6352b3 last 5650ac83 EXIT SUCCESS也就是说RTL 环境能够正常完成以下过程加载 firmware释放 reset启动 instruction fetch执行 CRC32 workload写出预期 signature正常退出仿真。仓库中的 RTL 结果也明确标记为PASS这一结果非常重要因为它基本排除了以下问题CRC32 C workload 本身错误firmware/HEX 生成错误memory initialization 错误RTL testbench 的基本控制流程错误reset 和 fetch-enable 的基本时序错误CRC32 signature 检查逻辑错误。因此后续 netlist 失败时我可以把 RTL simulation 当作 golden reference而不是同时怀疑 workload、testbench 和处理器设计。第二步直接替换成 mapped netlist最初的想法很直接保持同一套 firmware保持同一个顶层 testbench保持同一套 memory model只将 CV32E40P RTL sources 替换为 mapped netlist 和 Nangate cell model。理论上这应该是最干净的 RTL-to-netlist 对照实验。但是第一次 netlist 仿真甚至没有进入 simulation。结果是stageelaboration resultERROR记录下来的原因是RTL testbench supplied parameters to the parameterless mapped cv32e40p_top第一个问题RTL testbench 和综合网表顶层不兼容RTL 版本的cv32e40p_top是参数化模块。原来的 RTL testbench subsystem 在实例化处理器时会向cv32e40p_top传递若干 parameter。但是经过综合后mapped netlist 中的顶层已经变成具体实现。原本用于生成 RTL 结构的 parameter 已经被 elaboration 和 synthesis 消化掉了。换句话说cv32e40p_top #( .SOME_PARAMETER(...) ) dut (...);这种 RTL 实例化方式不能直接用于已经失去这些 parameter 的 mapped netlist。修复方法我创建了一个 netlist-specific 的cv32e40p_tb_subsystem这个 adapter 保留原 testbench 与处理器之间的端口连接但不再向 mappedcv32e40p_top传递 RTL-only parameter。修复之后compilation 通过elaboration 通过仿真能够真正开始运行。这一步解决的是testbench 与综合后顶层接口形式不一致还不是后面真正导致处理器不运行的问题。第三步netlist 可以运行但一直 timeout解决 elaboration 后我重新运行 netlist simulation。这次没有 compile error也没有 elaboration error但仿真始终无法完成 CRC32 workload。最终结果是stagesimulation resultTIMEOUT maxcycles5000000 rtl_reference_cyclesapproximately_940800RTL 大约在 94 万个周期左右完成而 netlist 即使运行到 500 万周期仍然没有出现预期 signature。这时需要特别注意“仿真能启动”并不代表“处理器在正常执行”。Xcelium 没有报 fatal error只能证明 event simulation 仍在推进不能证明 CPU 已经开始取指更不能证明 software 正在执行。最开始的怀疑是不是门级网表有输入悬空RTL 中的一些可选功能可以由 parameter 在 elaboration 阶段关闭。但是综合后的网表中这些功能对应的逻辑可能已经固定下来或者部分端口仍然存在。如果 testbench 没有显式驱动这些输入它们在门级仿真中就可能变成X或Z。最开始我怀疑的是COREV_CLUSTER0相关的 inactive inputs特别是进入sleep_unit_i的部分信号。怀疑路径大致是未连接输入 ↓ 输入变成 X ↓ sleep / wake-up logic 受到影响 ↓ core_sleep_o 或内部时钟状态异常 ↓ CPU 无法开始执行这个假设是合理的因为 RTL simulation 和 gate-level simulation 对未连接信号、常量传播和 X 状态的表现可能非常不同。排查方法为可疑输入加入明确 tie-off为了验证这个假设我没有一次性修改很多逻辑而是只针对可疑的 inactive cluster 和 sleep-related inputs 加入明确的常量 tie-off。也就是说把可能悬空的信号显式连接为确定的0或1避免它们保持X/Z。然后重新运行 netlist simulation。但结果仍然是TIMEOUT这一步非常重要因为它说明未连接的可选输入可能是一个需要修复的问题但它不是当前 CRC32 无法执行的根本原因。如果没有做这个对照实验很容易一直围绕 testbench tie-off 修改最后加入越来越多未经验证的常量连接却仍然接近不了真正的问题。为什么不能继续简单增加 maxcycles在看到 timeout 后一个很自然的反应是继续增加maxcycles但这时继续增加仿真周期已经没有太大意义。因为如果只是门级仿真速度比 RTL 慢那么应该仍然能够看到PC 持续变化instruction request 持续发出instruction response 持续返回pipeline 正常推进workload 只是完成得更晚。但如果处理器根本没有开始取指那么2,000,000 cycles 5,000,000 cycles 20,000,000 cycles得到的只会是更晚的 timeout。所以排查思路从“为什么程序还没跑完”改成“处理器到底有没有开始运行”第四步分别生成 RTL 和 netlist 的短 VCD完整 CRC32 仿真较长直接保存完整门级 VCD 会非常大也不利于快速定位。因此我分别创建了RTL short-VCD run netlist short-VCD run只观察 reset release 前后和启动阶段的少量周期。这种方法的核心是不需要等到 CRC32 最终 signature 出错最早发生分歧的位置通常更接近根本原因。我重点对比了以下信号clk_i rst_ni fetch_enable core_sleep_o instr_req_o instr_gnt_i instr_rvalid_i instr_addr_o pc_if pc_idRTL 启动行为在 RTL short VCD 中启动过程基本正常reset 最初为低reset 随后释放fetch enable 有效内部时钟正常翻转instruction request 开始产生PC 开始推进CPU 进入正常执行状态。这与完整 RTL simulation 最终PASS的结果一致。Netlist 启动行为在 netlist short VCD 中问题很早就出现了。观察到的主要现象包括core_sleep_o在启动阶段出现X部分内部 clock 从X状态开始pc_if和pc_id出现X或Zinstr_req_o无法形成正常、稳定的取指请求instr_rvalid_i始终没有进入正常的 instruction-fetch 交互。因此最终没有产生 CRC32 signature 只是表面结果。真正的问题发生在reset release ↓ sequential state initialization ↓ clock/sleep control ↓ PC initialization ↓ instruction fetch也就是说程序甚至还没有真正开始执行。仓库中保留了独立的 RTL 和 netlist short-VCD run正是为了比较这一启动分歧。重新定义问题不是 workload 错了而是 X 从哪里来的到这一步排查范围已经可以明显缩小。已知同一个 workload 在 RTL 中通过同一个 firmware 在 RTL 中通过mapped netlist 能够 compile 和 elaboratetie-off 可疑输入后仍然失败CPU 在启动阶段就出现 X问题发生在真正执行第一批指令之前。因此此时继续调试 CRC32 算法、memory contents 或程序结束条件已经没有意义。更合理的问题是门级网表中的第一个 X 是从哪里产生的门级设计中的 X 通常可能来自未初始化的 testbench 输入未连接端口register 没有正确 resetclock-gating cell 输入为 Xtiming violation notifierstandard-cell model 的 UDP 行为仿真模型与当前 simulator 的语义不兼容网表中的 power/ground pin 没有正确处理。前两项已经通过 RTL 对照和 tie-off 实验进行了检查因此注意力开始转向 sequential standard cells。第五步不要继续调整个 CPU先隔离一个标准单元完整 CV32E40P mapped netlist 包含大量标准单元。如果一直在完整 CPU 波形中追踪 X层次会非常深而且一个寄存器的 X 可能迅速扩散到大量组合逻辑很难判断真正的起点。因此我把问题缩小到一个最小可复现实验单独实例化一个 Nangate 带 reset 的 flip-flop检查 reset 是否能够将输出设置为已知值。选取的单元是DFFR_X1测试逻辑非常简单初始时 assert reset观察Qrelease reset在时钟边沿写入数据再次观察Q。理论上在异步 reset 生效时Q应该立即进入确定状态。最小 cell smoke test 暴露了真正的问题使用 Nangate v2010_12 默认 Verilog cell model 时最小实验出现了异常即使 reset 已经 assertedDFFR_X1的Q仍然保持为X。这与完整 CPU 中观察到的现象是吻合的。如果最基本的 reset flip-flop 都不能在仿真开始时进入确定状态那么结果会是DFF output X ↓ architectural state X ↓ sleep / clock gating control X ↓ internal clock X ↓ PC X ↓ instruction address/request X ↓ CPU 无法取指 ↓ workload timeout这一步非常关键因为它把问题从一个复杂 RISC-V CPU 为什么不运行缩小成了一个 Nangate DFF 为什么在 reset 时仍然输出 X一旦问题可以在单个 cell 中复现就可以排除CV32E40P pipeline 逻辑CRC32 workloadmemory system指令接口大部分 testbenchGenus 生成的复杂逻辑连接。第六步测试 Nangate 模型中的TETRAMAX分支继续检查 Nangate 标准单元 Verilog model 后我注意到模型中存在针对TETRAMAX的条件编译分支。于是重新编译 cell model并加入defineTETRAMAX在这个模式下单元通常使用更偏向 functional/ATPG 的行为模型而不是默认的完整 timing-oriented model 路径。重新运行同一个DFFR_X1smoke test 后reset 能够将Q设置为确定状态Q不再从 reset 阶段持续保持Xsequential cell 的基本功能行为恢复正常。这说明问题并不在 mapped netlist 的布尔连接本身而主要出现在Nangate v2010_12 默认 sequential-cell Verilog simulation model 与当前 Xcelium zero-delay functional simulation 流程之间。更准确地说当前证据表明默认模型路径下部分 sequential cell 的 reset/X-state 行为不符合本次 functional GLS 的需要TETRAMAX模型路径绕过或替换了引发问题的默认建模逻辑问题在单个标准单元上可独立复现。目前还不足以把它直接称为“Xcelium bug”或者“Nangate bug”。更谨慎的表述应该是旧版 Nangate v2010_12 默认标准单元 Verilog 模型与当前 Xcelium 的 zero-delay functional gate-level simulation 配置存在兼容性问题。最终的根因链条整个问题可以整理为下面这条链Nangate v2010_12 默认 sequential-cell model 在当前 Xcelium zero-delay functional simulation 中 未能让部分 resettable flip-flop 正确退出 X 状态 ↓ 综合网表中的 sequential state 保持 X ↓ X 传播到 sleep 和 clock-control logic ↓ 部分内部 clock 和 core_sleep_o 异常 ↓ pipeline PC 无法得到有效初始状态 ↓ instruction request 无法正常启动 ↓ CPU 没有真正开始执行 CRC32 ↓ 仿真最终 TIMEOUT因此最终 timeout 并不是因为CRC32 workload 太复杂simulation cycles 设置得太少CPU 执行速度变慢firmware 加载失败RTL 和 netlist 的功能不同单纯缺少某一个 tie-off。它是一个从 standard-cell simulation model 开始的 X-propagation 问题。当前解决方案对于当前阶段需要的zero-delay functional gate-level simulation可行的 workaround 是defineTETRAMAX也就是使用 Nangate 模型中面向 functional/ATPG 场景的条件编译路径。但这个方案需要明确限定使用范围。可以用于验证 mapped netlist 的基本逻辑功能运行 C workload检查故障是否改变程序行为或 signature。不能直接假设可用于timing-accurate gate-level simulationSDF back-annotationsetup/hold violation 仿真propagation delay 精确建模post-layout signoff simulation。TETRAMAX分支解决的是 functional behavior 问题不应该未经验证就被当成完整 timing model。是否应该更换 PDK这里需要区分三个概念PDK standard-cell library standard-cell Verilog simulation model本次直接出现问题的并不是已经证明 Nangate 的Liberty timing dataLEF physical datatransistor modelsynthesis mapping版图规则。目前直接暴露问题的是standard-cell Verilog simulation model所以现阶段不一定需要立刻更换整个 PDK。更合理的处理顺序是方案 1继续使用当前 Nangate library并使用 functional model workaround也就是加入defineTETRAMAX先完成当前的 functional golden-netlist 和 fault-injection 实验。这是成本最低的方案。方案 2寻找同一个 Nangate library 的更新或修正版 Verilog model如果能够找到与当前 Xcelium 验证过的 cell model就不需要重新综合。这通常比更换整个 PDK简单。方案 3更换 standard-cell library如果后续发现更多 sequential cells 仍然异常clock-gating cells 行为不可靠TETRAMAX模型不足以支持实验timing/SDF simulation 无法建立那么可以考虑换到一个更新、维护更完整的 standard-cell library。方案 4更换 PDK 并重新综合这是成本最高的方案。因为它可能需要重新进行synthesistiming analysisnetlist validation可能的 place and routefault target selection后续所有 workload regression。因此不应该只因为一个 Verilog cell model 问题就立即更换完整 PDK。这次排查中最重要的几个经验1. RTL PASS 是门级调试的前提必须先固定 workload、firmware、testbench 和 signature。否则 netlist 出错时会同时怀疑软件和硬件排查空间会迅速失控。2. Compile PASS 不等于功能正常门级网表能够 compile 和 elaborate只能证明语法和层次连接基本成立。CPU 是否真正执行必须观察resetclockPCinstruction requestmemory handshake。3. Timeout 只是结果不是原因看到 timeout 后不应该立刻增加 maxcycles。应该先确认处理器是否已经开始取指。4. 比较最早的 RTL/netlist 分歧最终 signature 可能在一百万周期以后才出现但根本问题可能在 reset release 后几个周期内就已经发生。短 VCD 通常比完整波形更适合定位启动问题。5. 一个假设只改一个变量怀疑输入悬空时只增加 tie-off然后重新运行。tie-off 后仍然失败就可以排除这个假设而不是继续无边界地修改 testbench。6. 从完整 SoC 缩小到单个 cell如果怀疑 standard-cell model最有效的方法不是继续在 CPU 波形中追 X而是为一个 DFF 写最小 smoke test。复杂问题一旦能够在单个 cell 上复现定位速度会快很多。7. 对旧 PDK/library 的仿真模型保持警惕综合成功并不意味着 Verilog cell model 一定适合当前 simulator。旧 library 可能仍然能用于 mapping 和 STA但其 simulation model 未必与现代工具和当前仿真选项完全兼容。