%Warning-UNDRIVEN: Signal is not driven 排查:Verilator 信号未驱动告警速查
更新时间:2026-10-26。FAQ 速查页,属于 EDA 报错排查系列。
%Warning-UNDRIVEN: Signal is not driven是 Verilator 开-Wall后最常见的告警之一:信号声明了,也被读了,但整个设计里没有任何一处给它赋值。它和 %Warning-UNUSEDSIGNAL 是一对镜像问题——一个缺驱动,一个缺消费。位宽类告警见 Verilator WIDTH 告警。
首屏答案:Verilator 报 Signal is not driven 就是字面意思——这根 wire(或 reg)有读者、没写者。三个高频根因按命中率排序:① 赋值语句里信号名拼错,真正想驱动的那根线没人写;② 子模块输出端口没连(例化时漏了 .out(u) 或连了空);③ 驱动代码被 `ifdef / 条件编译整块吃掉。修法是给信号找到合法驱动源,或确认它确实该删;不要用 lint_off 一关了之——悬空输入在综合后会锁到固定电平,功能 bug 就埋在这。
本文要回答的问题
%Warning-UNDRIVEN: Signal is not driven报错串每个字段是什么意思?- 声明了、也用了,为什么还说没人驱动?
- 按什么顺序排查最快定位到缺失的驱动源?
- UNDRIVEN 和 UNUSED 是什么关系,能一起消吗?
一、报错原文与现象
最小复现只有四行:模块里声明了内部线 u,在表达式里读了它,却没有任何 assign 或端口给它驱动:
module n1_bad(input wire a, output wire y);
wire u; // 声明了但没有任何驱动
assign y = u & a;
endmodule对这个文件跑 verilator --lint-only -Wall,真实告警如下:
读这段输出抓三个字段:第一,%Warning-UNDRIVEN 后面的 n1_bad.v:2:8 指向的是声明位置(第 2 行第 8 列的 wire u),不是使用位置——Verilator 认为问题出在「这根线从声明起就没有驱动源」;第二,In instance n1_bad 给出层次上下文,大工程里靠它区分同名信号属于哪个实例;第三,末尾的 %Error: Exiting due to 1 warning(s) 表示 lint 模式下告警被提升为退出错误,CI 里这一条就会卡住构建。提示行里的 lint_off UNDRIVEN 是 Verilator 给的消音开关,用法与风险在第四节展开。
二、可能原因总览
| 告警形态 | 可能原因 | 处置 |
|---|---|---|
| 声明处有、赋值语句无 | 赋值里信号名拼错(u / v / u1) | 全文搜索信号名,核对拼写 |
| 子模块输出没连上 | 例化漏接端口 .o(u) | 补上端口连接,或显式接 () 并审视 |
| 驱动在条件编译里 | `ifdef 分支未命中,驱动整块消失 | 核对宏定义与编译开关 |
| 该由上游模块驱动 | 顶层端口少声明一个 input | 把信号提为端口(本例修法) |
| 确实废线 | 重构残留,读它的表达式也该改 | 连读者一起删 |
关键判断是:这个信号应不应该有值? 应该有——找缺失的驱动源;不应该有——连读它的表达式一起删掉,而不是只删声明。
三、逐条排查展开
先沿着驱动链三段式走查:声明 → 赋值 → 端口连接。 任何一根 wire 的值只能来自三处之一:assign 连续赋值、子模块输出端口、模块 input 端口。UNDRIVEN 的意思是这三处全查无此人。把走查画成一张决策链:
这张图按角色上色:红色是告警现象入口,蓝色三个节点是排查动作(按「赋值 → 端口 → 条件编译」的命中率顺序走),橙色是两个修复分支,绿色是修复后的干净通过态。实际项目里 80% 的 UNDRIVEN 死在第一关——赋值语句里名字拼错了一个字符,assign y = u & a; 想写的是 v,结果 u 被声明被读、永远没值;grep -n '\bu\b' rtl/ 一遍就能现形。
端口漏接是第二高发,而且报错行有迷惑性。 子模块定义了 output o,例化时写成 sub i0 (.a(a), .y(y)); 漏了 .o(u)——有些场景下 Verilator 对「例化时整个没出现的端口」报 PINMISSING 或 PINCONNECTEMPTY,但如果端口在例化表里有名字、右边括号是空的 .o(),驱动的线网就成了 UNDRIVEN。报错行指向 u 的声明处,根因却在几十行外的例化语句。对策:告警里的 In instance 字段给出层次,顺着实例名去找例化点,把端口表和子模块定义逐行对一遍。
条件编译吃掉驱动是最隐蔽的一种。 驱动写在 `ifdef DEBUG 块里,量产编译没定义这个宏,u 就成了无源之水;更糟的是 Debug 构建一切正常、Release 构建才告警。排查动作:找到信号名所有出现行,看每一处是否被 `ifdef/`endif 包裹,再核对编译命令行 +define+ 或 -D 的实际取值。修法按意图二选一:驱动本该常驻就把 `ifdef 去掉;确实是调试专用就连读它的逻辑一起包进同一个宏,别让「读」和「写」分属不同编译分支。
本例的正确修法是把 u 提为模块端口——它的值本来就该由上游给。修完的代码与 lint 结果:
module n1_fixed(input wire a, input wire u, output wire y);
assign y = u & a;
endmodule# command: verilator --lint-only -Wall /tmp/errlog-evidence-b4/src/n1_fixed.v
# date: 2026-10-26T01:42:58+08:00
# host-toolchain: Yosys 0.9 (git sha1 1979e0b) / Verilator 4.038 2020-07-11 rev v4.036-114-g0cd4a57ad / iverilog 11.0
---
---
# exit_code: 0修复后 -Wall 全量告警清零,两个 --- 之间没有任何输出,这就是干净状态。
四、UNDRIVEN 与 UNUSED:一对镜像告警
lint_off UNDRIVEN 能把告警压掉,但压掉不等于修好。UNDRIVEN 的信号在综合后会被工具锁到常量(通常是 0),读它的逻辑行为因此确定——但大概率不是你想要的确定。真正该做的是先判断它和 UNUSED 的关系:同一个信号同时报 UNDRIVEN 和 UNUSED(声明了、既没人写也没人正经读),说明它是重构残留的死代码,应该整根删除;只报 UNDRIVEN 说明有人读它——读者在等一个永远不来的值,这是功能 bug 的前兆,必须找到驱动源。
这张对比卡给出处置优先级:UNDRIVEN 比 UNUSED 危险。UNUSED 是「算了没人看」,浪费一点面积;UNDRIVEN 是「看了等于白看」,表达式拿常量参与运算,仿真和综合都可能静默通过,上板才暴露。对偶篇 %Warning-UNUSEDSIGNAL 有完整的 UNUSED 排查路径,两篇对照使用。
五、预防与纪律
- 声明与驱动成对写:每声明一根内部线,立刻写下它的
assign或端口连接,不留「回头补」。 - 例化端口全显式:禁止位置例化,端口表逐行对齐子模块定义;空接端口写
.o()要过评审。 - 读写同属一个编译分支:
`ifdef要么包住「驱动 + 使用」整体,要么都不包。 - lint_off 只用于豁免,不用于掩盖:确需保留的悬空线(如留给 ECO 的 spare net)用
/* verilator lint_off UNDRIVEN */局部包裹并写注释说明意图,禁止整文件级关闭。 - CI 里
-Wall零告警门禁:verilator --lint-only -Wall进流水线,告警计数非零即构建失败。
实验环境
- 机器/系统:云主机 / Ubuntu 22.04
- 工具版本:Verilator 4.038 2020-07-11 rev v4.036-114-g0cd4a57ad(照抄 log 头
host-toolchain行);Yosys 0.9、iverilog 11.0 同机备用 - 日期:2026-10-26
- 完整命令:
verilator --lint-only -Wall /tmp/errlog-evidence-b4/src/n1_bad.v(复现告警);verilator --lint-only -Wall /tmp/errlog-evidence-b4/src/n1_fixed.v(验证修复)
代码获取
复现用两份源码(n1_bad.v 复现版、n1_fixed.v 修复版)与两份 lint log 在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/ts-verilator-undriven-UNDRIVEN。装好 Verilator 后跑 verilator --lint-only -Wall n1_bad.v 复现告警,再对 n1_fixed.v 跑同一命令验证告警消除。
这一篇在知识体系里的位置
本篇是 EDA 报错排查系列 中 Verilator lint 告警簇的一员,与 %Warning-UNUSEDSIGNAL(缺读者的镜像问题)、Verilator WIDTH 告警(位宽类)互补:UNDRIVEN 管「驱动链完整性」,是三者里唯一几乎必然对应功能 bug 的告警,优先级最高。
一句话总结
%Warning-UNDRIVEN: Signal is not driven = 信号有读者无写者,按「赋值拼写 → 端口连接 → 条件编译」三段驱动链走查定位缺失的驱动源,该补驱动的补驱动(本例把 u 提为 input 端口)、该删死代码的整根删,绝不用 lint_off 掩盖——它比 UNUSED 危险,因为悬空信号综合后锁常量,功能静默出错。