iverilog timescale 继承警告:Found both default and `timescale based delays 排查
更新时间:2026-10-29。FAQ 速查页,属于 EDA 报错排查系列。
iverilog timescale 继承警告(warning: Found both default and \timescale based delays)是 Icarus Verilog 在「一部分文件写了 ``timescale ``、另一部分没写」混编时给出的提示,常和 iverilog unknown module type 一样在多文件拼工程时才冒出来;开源工具报错总入口见 开源 EDA 报错排查导航。
首屏答案:这条警告的意思是编译单元里同时存在「带 `timescale 的模块」和「不带 `timescale 的模块」,后者的 #delay 会按默认时间单位(iverilog 里是 1s/1s)解释,和前者差几个数量级,仿真结果悄悄错位——功能错误但不报错。定位用 iverilog -Wtimescale 重编一次,它会逐个列出缺声明的模块名和文件行号;修复是给每个源文件顶部补同一行 `timescale 1ns/1ps(或全工程统一值),重编零警告、仿真数值回归预期才算闭环。只改一部分文件没用——只要还有一个模块缺声明,警告和错位都在。
本文要回答的问题
Found both default andtimescale based delays` 这条警告到底在说什么?- 为什么少写一行
`timescale会让仿真功能直接错? iverilog -Wtimescale的输出怎么读?- 修复后复跑,拿什么证据确认闭环?
一、警告原文与现象
最小复现只要两个文件:测试台 tb.v 写了 `timescale 1ns/1ps,被测模块 dut.v 故意不写,里面各有一组 # 延时:
// tb.v
`timescale 1ns/1ps
module tb;
reg clk = 0; reg en = 0; wire y;
always #5 clk = ~clk; // 意图:半周期 5ns
dut u_dut (.clk(clk), .en(en), .y(y));
initial begin
#12 en = 1;
#40 $display("time=%0t y=%b", $time, y);
#20 $finish;
end
endmodule// dut.v —— 没有 `timescale
module dut(input wire clk, input wire en, output reg y = 0);
reg en_d = 0;
always @(posedge clk) begin
en_d <= #2 en; // 意图:2ns 的建模小延时
y <= #2 en_d;
end
endmodule把不带声明的文件放前面编译再仿真,完整实录如下:
读这段实录抓四点。第一,编译期只有一句提示 Found both default and timescale based delays,**进程退出码仍是 0**,CI 不设防就直接放行了。第二,vvp跑出来的结果是time=52000 y=0——en拉高两个时钟后y 还是 0,功能已经错了,但仿真照常跑完。第三,-Wtimescale 重编后工具直接把肇事者钉出来:module dut declared here: dut.v:2,不用自己猜是哪个文件。第四,给 dut.v补上 ``timescale `` 重编(sim_ok 一段),编译一行警告都没有,仿真结果变成 y=1,功能回归正确。中间 time=52000 是因为 tb 按 1ps 精度计数,52ns 显示成 52000,属正常。
二、工具与版本
本篇所有输出来自 Icarus Verilog 13.0(stable,v13_0),iverilog -V 首行为 Icarus Verilog version 13.0 (stable) (v13_0)。相关开关只有两个:-Wtimescale 把「哪些模块没写 `timescale」逐个列出来;默认不开时只有那句汇总提示。该行为在 Icarus 11/12/13 上一致,与 Verilator 对同类问题的 -Wall 提示措辞不同、根因相同,排查思路可以互搬。
三、根本原因
`timescale 1ns/1ps 是一条编译指令(compiler directive),不是模块属性:它只对「编译顺序上位于它之后的模块」生效,遇到文件结束也不会自动带入下一个文件。iverilog 编译多个文件时按命令行顺序逐个处理,每个文件开头若没有自己的 `timescale,其中模块就落到默认时间单位 1s/1s 上。
于是本例里 dut 的 #2 被解释成 2 秒,而不是意图中的 2ns。tb 的时钟 10ns 翻一次、en 在 12ns 拉高,可在 dut 的时间尺度里这些事件瞬间就过去了,en_d 的 2 秒延时永远等不到,输出 y 自然一直是 0。编译顺序反过来(先 tb.v 后 dut.v)时 dut 会「继承」到 1ns/1ps,碰巧结果正确——这就是这个坑最阴的地方:同一份代码,换一下文件顺序结果就变,警告却始终只有那一句不痛不痒的提示。
再把 `timescale 1ns/1ps 这两个数拆开看:斜杠前是时间单位(time unit),#1 延时就按它计;斜杠后是时间精度(time precision),仿真器内部时间轴的最小刻度,延时值会先按精度四舍五入再调度。混编时各模块以各自的单位换算到全局时间轴上,单位错一档,延时就差一个数量级。三种组合的实际效果对照如下:
| 文件组合 | dut 的 #2 实际延时 | 仿真结果 | 编译输出 |
|---|---|---|---|
| tb 有声明,dut 无声明,dut 先编 | 2 秒(默认 1s) | y 恒为 0,功能错 | Found both default 警告 |
| tb 有声明,dut 无声明,tb 先编 | 2ns(继承 tb 的 1ns) | y 正常出 1 | 同样报警告 |
两个文件都写 1ns/1ps | 2ns(自己的声明) | y 正常出 1 | 零警告 |
这张表也解释了为什么很多人「明明没写 `timescale 也一直好好的」:只要命令行里带声明的文件排在前面,后面的文件碰巧继承到正确的值,问题就被掩盖了。但文件顺序是 Makefile、脚本、IDE 工程文件里最容易被重构改动的东西,依赖编译顺序的正确性等于埋雷。
四、排查清单
拿到这条警告后的收敛路径很短,一张流程卡看全:
这张卡的关键在第四步:timescale 类问题修完后警告消失不等于功能对了,一定要拿一个可观测的信号值做对拍——本例就是 time=52000 y=1 这个数。如果修复前后数值相同,先别庆幸,回头检查是不是两个文件「碰巧继承了同一个单位」的掩盖情形,把文件顺序倒过来再编一次,两次结果一致才是真的稳。团队层面防复发有两条:所有源文件模板里把 `timescale 写死成第一行,code review 看到新文件第一行不是它就打回;CI 里对 iverilog 输出做 warning 关键字扫描,因为这类警告不改退出码,靠退出码拦不住。还有一个排查提示:如果警告里列出的模块来自第三方 IP 或库文件,不要直接去改库——在编译自己的文件时保证都有声明,库的缺声明单独评估其 # 延时是否参与关键路径,多数纯行为模型库里根本没有 # 延时,确认后可以有选择地忽略。
五、修复代码与复跑确认
修复只需要在 dut.v 顶部加一行,与测试台保持一致(修复版存为 dut_fixed.v):
// dut_fixed.v
`timescale 1ns/1ps
module dut(input wire clk, input wire en, output reg y = 0);
reg en_d = 0;
always @(posedge clk) begin
en_d <= #2 en;
y <= #2 en_d;
end
endmodule复跑命令与结果见第一节实录的最后一段:iverilog -o sim_ok tb.v dut_fixed.v 编译零警告,vvp sim_ok 输出 time=52000 y=1,与「en 拉高后两拍出 1」的设计意图一致,闭环。
两个工程化建议。其一,全工程统一写 `timescale 1ns/1ps 是目前最通用的组合:1ns 单位让 # 数值好读,1ps 精度足够覆盖门级建模的小数延时;如果工程里混入了第三方 IP 的 `timescale 1ns/10ps 之类声明,不必强行改成一致——精度可以不同,但每个文件都必须有自己的声明,这才是这条警告要守护的边界。其二,除了逐个文件补声明,iverilog 也支持用命令行 -tnull 之外的编译单位控制,但对多文件工程最稳的做法永远是「声明写在源文件里、不依赖命令行顺序」——这样换仿真器(Verilator、VCS、Xcelium)时语义也不会漂。
实验环境
- 机器/系统:Mac(Apple Silicon)/ macOS
- 工具版本:Icarus Verilog version 13.0 (stable) (v13_0)
- 日期:2026-10-29
- 完整命令:
iverilog -o sim_bad dut.v tb.v && vvp sim_bad(复现警告与错误结果);iverilog -Wtimescale -o sim_bad dut.v tb.v(定位缺声明模块);iverilog -o sim_ok tb.v dut_fixed.v && vvp sim_ok(修复后复验)
代码获取
复现用源码(tb.v、dut.v 报错版、dut_fixed.v 修复版)与完整实测记录在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/iverilog-timescale-inherit。装好 iverilog 后按 README 的顺序跑三条命令即可复现警告、定位与修复确认的全过程。
这一篇在知识体系里的位置
本篇是 EDA 报错排查系列中 iverilog 编译期警告簇的一员:模块找不到看 iverilog unknown module type;隐式网线声明看 iverilog implicit wire definition;语法错误定位看 iverilog syntax near token。整个系列的目录在 EDA 报错排查系列。
总结
iverilog timescale 继承警告 的本质是多文件混编时部分模块没写 `timescale,其 #delay 落到默认 1s/1s 上,与意图单位差几个数量级,仿真功能悄悄出错而退出码仍是 0;iverilog -Wtimescale 能逐个钉出缺声明的模块,修复就是给每个源文件补统一的时间单位声明,重编零警告、复跑信号值回归预期才算闭环,CI 里要盯 warning 文本而不是退出码。