Verilator 报 Unsupported: Ignoring delay on this delayed statement:#delay 写法速查
更新时间:2026-10-24。FAQ 速查页,属于 Verilator 排错系列。把一段在仿真器(VCS/XSim/Icarus)里跑得好好的 Verilog 丢给 Verilator 做 lint 或编译,迎面就是一句
Unsupported: Ignoring delay on this delayed statement——这不是代码写错了,而是#10这类延时语句本来就不在 Verilator 支持的子集里。同簇的其它 Verilator 报错见 Verilator 报 WIDTH 位宽不匹配 与 Verilator 报 Cannot find file containing module。
答案块(赶时间先看这里)
- 报错原文:
%Warning-STMTDLY: blinky_delay.v:10:10: Unsupported: Ignoring delay on this delayed statement. - 环境:Verilator 4.038,
verilator --lint-only -Wall检查一段带#10延时的 RTL - 根因:
#delay是基于仿真时间的语句,Verilator 是「可综合子集 + 周期精确」的仿真器,不维护全局事件队列里的绝对时间延时,直接忽略并告警;-Wall下告警升级为 error 退出 - 修复:RTL 里删掉
#delay,时序用时钟沿表达;真要延时激励,挪到 C++ testbench 里做;确认无害的地方用/* verilator lint_off STMTDLY */局部豁免 - 验证:改完再跑同一条 lint 命令,
exit_code=0即通过
本文要回答的问题
- 为什么
#10 led = 1'b1;这种写法 Verilator 不肯接受? - 报错说是 Warning,为什么进程退出码却是 1?
- 我的延时逻辑(上电延时、脉冲宽度、testbench 激励)该改写成什么样?
lint_off STMTDLY豁免什么时候能用、什么时候是掩耳盗铃?
一、报错原文与现象
典型的踩坑现场:一个 LED 闪烁模块,想在 initial 块里「上电 10ns 后把 led 拉高」,代码在事件驱动仿真器下完全合法:
initial begin
led = 1'b0;
#10 led = 1'b1; // 上电 10ns 后拉高
end丢给 Verilator 做 lint,实录如下:
关键信息有四层:第一,%Warning-STMTDLY 是这条诊断的编号,STMTDLY 即 statement delay;第二,报错位置精确到 blinky_delay.v:10:10 那个 #10;第三,Verilator 明确说 Ignoring——它不会模拟这 10ns,而是当延时不存在继续编译;第四,%Error: Exiting due to 1 warning(s),-Wall 把 warning 抬成致命错误,进程以 exit_code=1 退出,CI 流水线在这一步就断了。
容易误判的点:很多人看到 Warning 就以为「只是提醒,不影响」,结果 make 整条编译链中止才回头查。记住一条:Verilator 里 STMTDLY 的字面意思是「延时被忽略」,你的电路行为和原设计已经不一样了,哪怕退出码是 0 也不能当没看见。
二、根因:Verilator 的可综合子集边界
Verilator 的定位不是通用事件驱动仿真器。它把 Verilog 的可综合子集翻译成 C++,按周期精确(cycle-accurate)的方式求值,目标是大规模设计的极速仿真与 lint。这个定位决定了它手里没有「绝对仿真时间轴上的事件队列」这种基础设施——#10 需要一个调度器在 10ns 之后唤醒一条语句,Verilator 的两状态、周期驱动模型里根本没有这个概念。
所以边界很清晰:时钟沿(@(posedge clk))、组合逻辑、有限状态机——进子集,支持;#delay、事件驱动的任意时间调度、fork/join 这类仿真专属构造——出子集,报 Unsupported 或被忽略。
怎么读这张图:蓝色是排查动作(对照源码判定落点),绿色是落在子集内的写法与最终结果,红色是出子集的写法与它触发的告警,橙色是修复动作。主线一句话——Verilator 只接可综合子集,#delay 在红线那边,想过去只能改写,没有开关能让它真的模拟这 10ns。这也解释了为什么同一份代码在 Icarus/XSim 里不报错:那些是全功能事件驱动仿真器,子集边界宽得多。
三、三条修法对照
报错只是表象,你真正要回答的是「这个延时原本想干什么」。按意图分三条路:
这张卡读法:绿区是治本——把「时间」翻译成「拍数」,代码从此在任何工具里都过;橙区是把仿真专属的东西搬出 Verilog,让 Verilator 只管被测设计、延时激励由 C++ 侧掌控,这是带复杂 testbench 时最干净的架构;紫区是局部豁免,只在你确认延时被忽略也不影响结果时用(比如纯仿真的打印节流),千万别拿它去压量产 RTL 的告警。
橙区再展开一句,因为它的迁移成本常被高估:C++ testbench 里的延时不需要任何调度器,Verilator 的主循环本来就是「翻转时钟 → eval → 推进时间戳」的手动循环,「等 10ns」就是在 clk 为低的相位里多循环一拍、把时间戳加 10 而已。原来写在 Verilog 里的激励序列(上电复位拉多少纳秒、多久后灌第一个数据),逐条翻译成 C++ 循环里的计数判断,功能完全等价,还多了一个好处——激励出错时能用 gdb 单步,而不是对着波形猜。
四、病因速查表
同一句 Unsupported: Ignoring delay,背后的来历不止一种:
| 报错现象 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
initial 块里 #N 赋值 | 上电延时初始化 | 看报错行是否在 initial 内 | 改 rst_n 复位或 C++ 激励 |
always 块里 #N 延时赋值 | 想模拟门级延迟 | 报错行在 always 内 | 删掉,门延不属于功能仿真 |
testbench 里 #N 驱动激励 | 激励按纳秒写 | 文件是否是被测设计以外 | 激励逻辑整体挪进 C++ 主循环 |
#0 用于竞争消除 | 老代码 trick | 报错行是 #0 | 改用非阻塞赋值 <= 消除竞争 |
| 大量 STMTDLY 刷屏 | 第三方 IP 带仿真模型 | 报错路径指向 IP 目录 | 给 IP 文件单独加 lint_off 或换仿真模型 |
| 只有 Warning 但编译中断 | 开了 -Wall 或 -Werror | 看命令行参数 | 先修代码,别先想着降告警级别 |
五、修复示例与验证
以本文开头的 blinky 为例,改法:去掉 initial 里的 #10,引入异步复位 rst_n,初始化语义由复位沿表达,LED 翻转逻辑全部留在时钟沿上:
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 24'd0;
led <= 1'b0;
end else if (cnt == 24'd12000000) begin
cnt <= 24'd0;
led <= ~led;
end else begin
cnt <= cnt + 24'd1;
end
end同一条 lint 命令再跑一遍,实录输出只剩头部信息与 exit_code=0,零告警通过(原始记录见下方实验环境)。这一步同时验证了两件事:STMTDLY 消失,且 -Wall 下没有引入新的位宽/未用信号等告警——改写是干净的。
防复发只有一条纪律:往 Verilator 工程里引入任何 Verilog 文件之前,先过一次 verilator --lint-only -Wall,把 STMTDLY、WIDTH、CASEINCOMPLETE 这批子集类告警在合并进主干前清掉,而不是等全量编译失败再回头翻。
实验环境
- 机器/系统:云主机 / CentOS 7(Verilator 4.038 源码编译)
- 工具版本:Verilator 4.038 2020-07-11
- 日期:2026-10-24
- 完整命令:
verilator --lint-only -Wall blinky_delay.v(复现报错)、verilator --lint-only -Wall blinky_fixed.v(验证修复)
代码获取:复现源码(报错版/修复版两个模块 + 一键脚本)在公开仓 experiments/ts-verilator-timing-delay-unsupported,本机装有 Verilator 4.x 时 sh run.sh 一键复现,或用 README 里的 docker 命令。
这一篇在知识体系里的位置
本篇是 Verilator 排错簇中「子集边界」节点的 FAQ 页:同簇的 Verilator 报 WIDTH 位宽不匹配 处理另一大类 -Wall 告警,Verilator 报 CASEINCOMPLETE 与 Verilator 报 UNOPTFLAT 组合环 分别对应 case 完备性与组合逻辑环;告警豁免(lint_off/waiver)的体系化基线管理见 RTL Lint 基线与豁免管理,环境类排错的方法论参照 docker permission denied 速查——同一套「先看报错原文、再定位子集/权限边界」的路子。
一句话总结
Unsupported: Ignoring delay on this delayed statement(STMTDLY)的本质是 #delay 落在 Verilator 可综合子集之外——它没有事件时间队列,只能把延时忽略并告警,-Wall 下直接 error 退出;治本是把时间翻译成拍数(时钟沿/计数器/复位),仿真激励挪进 C++ testbench,确认无害才局部 lint_off STMTDLY 豁免;改完重跑同一条 verilator --lint-only -Wall,exit_code=0 即修复。