Verilator NEEDTIMINGOPT:--timing 与 --no-timing 怎么选
Verilator NEEDTIMINGOPT 常见于把带 #10 的 testbench 或仿真模型直接交给 Verilator 5.x 检查时。它不是说 Verilator 完全没有延时能力,而是要求你在编译前明确选择:保留延时语义,还是忽略延时。这个分叉决定了该加 --timing,还是接受 --no-timing 的行为变化。旧版直接报告 STMTDLY 的表现见 Verilator NEEDTIMINGOPT 之前的延时告警说明。
本文要回答的问题
- 为什么同一份
#delay源码在未加选项时直接报NEEDTIMINGOPT? --timing与--no-timing分别改变了什么,而不只是改变告警?- 为什么
--no-timing已明确给出,进程仍可能退出 1? - 怎样根据设计对时间语义的依赖选修复路径并复跑?
这张图的关键不是两个选项谁更高级,而是第二步不能跳过:工具不知道 #10 是否影响测试结论。需要等待十纳秒后再驱动或检查,就必须保留;只有确认它与功能无关,才有理由选择忽略。
一、完整报错:默认调用在井号延时处停止
最小用例只有一个 initial 块:等待 10ns,打印一句话并结束仿真。timescale 把延时单位定义为纳秒,因此 #10 不是可随意删掉的注释,而是这条激励序列的一部分。
`timescale 1ns/1ps
module needtimingopt_broken;
initial begin
#10;
$display("delayed stimulus reached");
$finish;
end
endmodule执行 verilator --lint-only -Wall needtimingopt_broken.sv,Verilator 5.020 给出完整诊断。插入符指向 #10,主行明确要求用两个选项之一指定延时处理方式;末尾退出码为 1。
%Error-NEEDTIMINGOPT: needtimingopt_broken.sv:5:9: Use --timing or --no-timing to specify how delays should be handled
: ... note: In instance 'needtimingopt_broken'
5 | #10;
| ^
... For error description see https://verilator.org/warn/NEEDTIMINGOPT?v=5.020
%Error: Exiting due to 1 error(s)
default exit=1这里不是语法解析失败,initial 和 #10 都是合法 SystemVerilog。失败点位于编译策略选择:源码需要一种延时处理规则,而命令行没有提供。也不要把 %Error-NEEDTIMINGOPT 当成建议加 -Wno-fatal 的普通告警;它是错误专码,压低告警级别不会补上延时模型。
二、工具版本:5.020 把策略选择显式化
本例使用 Verilator 5.020。与旧版遇到延时便报告“不支持并忽略”相比,这里默认先报 NEEDTIMINGOPT,让调用者决定是否启用时间调度。下面的同次运行依次覆盖默认、--timing 和 --no-timing,所以差异来自选项,而不是源码或版本变化。
记录中默认路径退出 1,--timing 无诊断且退出 0;--no-timing 则产生 STMTDLY,明确写着因该选项而忽略延时,并在本次 -Wall 调用中退出 1。末尾 PASS 表示脚本已逐项核对专码与进程状态,不是只看某一条命令是否打印了错误。
三、根因:两个选项对应两种不同仿真语义
--timing 启用 Verilator 的时序仿真支持,让 #delay 等时间相关构造进入事件调度。对本例而言,打印动作被安排在 10ns 之后,源码的时间关系得到保留。--no-timing 则选择另一条路:工具继续处理设计,但把延时忽略,并用 STMTDLY 告知语义已被改变。
| 调用方式 | 5.020 实测结果 | 延时语义 | 适用判断 |
|---|---|---|---|
| 不指定策略 | NEEDTIMINGOPT,退出 1 | 尚未决定 | 不能作为最终配置 |
--timing | 无诊断,退出 0 | 保留并调度 | 激励、检查或协议依赖时间 |
--no-timing | STMTDLY,本例退出 1 | 明确忽略 | 已证明延时丢弃不影响目的 |
因此,--no-timing 后仍失败不代表选项没生效。恰恰相反,STMTDLY 证明它已经生效并忽略了 #10;退出 1 来自该告警在当前告警致错策略下被提升为致命结果。即使另行调整告警策略让进程通过,被忽略的时间也不会恢复。
还要区分 lint 通过与仿真正确。--timing 让这份源码干净通过 lint,说明构造已被所选模型接纳;它并不自动证明整个 testbench 的等待、竞争和结束条件都正确。真正的仿真项目仍应检查关键事件是否发生在预期时刻。
时间单位也不能替你做选择。timescale 只负责把 #10 解释为时间量,不负责启用调度器;反过来,--timing 也不会修正错误的单位或精度。若原意是等待 10ns,源码却使用了别的时间单位,lint 通过仍可能得到错误激励。迁移老工程时,应同时核对时间单位、延时数值和仿真精度,不能把新增选项当作唯一需要审查的变更。
另一个常见误判是把 --timing 理解成 testbench 文件名专用开关。它描述的是本次编译对时间构造的处理方式,并不判断文件叫 tb 还是 RTL。只要纳入编译的源码包含需要调度的延时,策略就要一致;可综合 RTL 本身通常不依赖 #delay,不要为了消除 testbench 的报错去删改真实硬件逻辑。
四、排查:先问延时是否影响结论,再看退出码
拿到报错后,沿报错行阅读延时前后的动作。若 #10 用来等待复位释放、满足接口建立时间、错开竞争或推迟断言检查,它属于功能语义,应直接走 --timing。若只是临时打印节流或可删除的仿真占位,才可能选择 --no-timing,而且需要由测试结论证明忽略无害。
持续集成里还应把“命令是否成功”和“采用了什么语义”分开检查。仅看最终退出码,可能漏掉被人为降级的 STMTDLY;只搜是否出现 --timing,又可能漏掉某条旁路脚本仍在使用旧参数。稳妥做法是让所有入口复用同一份构建变量,并保留一个含已知延时的最小用例:默认路径应失败,目标路径应按预期通过或给出明确诊断。这样升级工具后,策略变化会直接暴露在小型对照中,而不是混进大型设计的数千行日志。
延时源码 -> 是否必须保留延时
是否必须保留延时 -> 使用--timing: 是,时间影响结果
是否必须保留延时 -> 使用--no-timing: 否,明确接受忽略
使用--timing -> 复跑lint
使用--no-timing -> 复跑lint
延时源码.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
是否必须保留延时.style: { fill: "#FDECEC"; stroke: "#D9534F" }
使用--timing.style: { fill: "#E9F8F0"; stroke: "#2E9E6B" }
使用--no-timing.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
复跑lint.style: { fill: "#F1ECFD"; stroke: "#7A5BD4" }读图时把绿色视为保留语义的常规修复,橙色视为附带行为变化的显式取舍,紫色则是两条路都不能省略的复跑。红色判定只问设计意图,不问哪个选项更容易让终端安静;这样能避免把“编译通过”误当成“延时仍然有效”。
对于第三方仿真模型,还要把处理范围划清。若模型依赖精确延时,只对顶层加 --no-timing 仍会改变整个编译单元的语义;若工程本来就采用无时延周期模型,则应确认所有相关源文件都遵循同一约定,而不是逐个压制 STMTDLY。
五、修复与复跑:本例保留十纳秒等待
本例的打印必须以延时到达为前提,所以源码不变,在原有 lint 命令中加入 --timing。另跑一次 --no-timing 作为反面对照,确认它不是等价替代。
verilator --lint-only -Wall --timing needtimingopt_broken.sv
verilator --lint-only -Wall --no-timing needtimingopt_broken.sv复跑结果已经收录在上面的同一份记录:--timing diagnostics: none、--timing exit=0;反面对照出现 Ignoring delay on this statement due to --no-timing,随后 --no-timing exit=1。修复因此包含两层证据:目标路径零诊断、零退出,同时另一路径仍暴露延时丢弃告警。若团队选择 --timing,还应把它写进统一的构建配置,避免不同脚本有的带选项、有的继续触发 NEEDTIMINGOPT。
实验环境
- 机器/系统:Intel x86_64 Mac / macOS 26.5.2,实验在
geek-eda:0.2容器中运行 - 工具版本:Verilator 5.020 2024-01-01 rev (Debian 5.020-1)
- 日期:2026-10-30
- 完整命令:
docker run --rm -v "$PWD":/work -w /work geek-eda:0.2 make run;脚本内部对同一源码依次执行默认、--timing、--no-timing三种verilator --lint-only -Wall调用,并核对专码和退出码。
代码获取
最小源码、复现脚本、Makefile 与原始输出均在公开仓 experiments/ts-verilator-warn-needtimingopt。进入目录后执行 make run,或使用上面的容器命令,即可重复三种策略对照。
这一篇在知识体系里的位置
本篇属于 EDA 报错排查系列 的 Verilator 5.x 延时策略节点,重点是 NEEDTIMINGOPT 与两个编译选项的分叉。旧版 4.038 的 STMTDLY、可综合改写和 C++ 激励迁移见 Verilator NEEDTIMINGOPT 相关的旧版延时排查;本篇不重复那些改写方案,只解决新版本选项选择与退出行为判断。
总结
Verilator NEEDTIMINGOPT 的根因是源码含延时而命令行未选择处理策略。Verilator 5.020 实测中,默认调用报错并退出 1,--timing 保留时间语义且无诊断通过,--no-timing 则报告 STMTDLY、明确忽略延时,并在本例 -Wall 下退出 1。先判断延时是否影响结果,再选选项并复跑,不能把忽略延时当作无声修复。