Yosys multiple drivers to net 警告:多驱动根因二分法与单驱动修复
更新时间:2026-10-29。开源 EDA 排错系列。综合日志里看到
Warning: multiple conflicting drivers(很多团队口头上叫它 multiple drivers to net),意思是同一张网线网上挂着两个或更多驱动源——数字电路里除了显式声明的三态总线,这是明确的设计错误。同簇相邻问题:位宽类静默截断见 yosys Warning Width mismatch assign 位宽不匹配,全簇报错入口见 开源 EDA 报错排查总览。
答案块(赶时间只看这里)
- 警告典型形态(Yosys 0.33 实测措辞):
Warning: multiple conflicting drivers for <模块>.<信号> [位号]:,每个冲突位一条,后面缩进两行列出打架的两个驱动源。 - 根因二分法:九成是「同一个信号被两个 always / assign 块同时赋值」;剩下一成是「端口方向写错」——子模块端口该写 output 写成了 input,导致父模块和子模块都往这张网上灌。
- 修复:一个信号只留一个驱动块。两个 always 块想各管一种场景,就合并成一个块、块内用 if/sel 表达选择意图。
- 验证:修复后重跑
read_verilog; hierarchy -top; proc; check,看到Found and reported 0 problems.才算关单。
本文要回答的问题
- multiple conflicting drivers 这条警告到底在说什么,下面缩进的两行驱动源怎么读?
- 多驱动的两类根因(双 always 块 / 端口方向错)各长什么样,怎么快速区分?
- 为什么 yosys 只在
check这一步报,综合本身却不拦? - 修复的正确姿势是什么,改完怎么确认?
一、报错原文与驱动源读法
先用一份最小用例把警告逼出来。multi_drv_bad.v 里输出 y 被两个 always 块同时驱动:一个时序块想在时钟沿锁存 a,一个组合块想把 b 直通给 y:
module multi_drv_bad (
input wire clk,
input wire rst_n,
input wire [7:0] a,
input wire [7:0] b,
output reg [7:0] y
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) y <= 8'h00;
else y <= a;
end
always @(*) begin
y = b; // 第二个驱动源:与上面的时序块冲突
end
endmodule跑 yosys -p 'read_verilog multi_drv_bad.v; hierarchy -top multi_drv_bad; proc; check',CHECK pass 一段输出如下(真实日志摘录):
读这段输出抓住两点。第一,警告按位展开:y 是 8 位信号,8 个位各自一条 for multi_drv_bad.\b [7] 到 [0](proc 把组合块的 y = b 直连成了 y 与 b 的合并网,check 报告时沿用了网表上的名字 \b——名字不眼熟别慌,看下面的驱动源列表)。第二,每条警告下面缩进的五行才是干货:port Q[7] of cell $procdff$4 ($adff) 是时序 always 块综合出来的触发器输出,module input b[7] 是组合 always 块把输入 b 直挂到了这张网上。两个驱动源一个来自触发器、一个来自模块输入,电路层面就是输出短接——这正是「多驱动」三个字的物理含义。
二、根因二分法
看到这条警告,根因只有两大类,按概率排序:
这张漏斗的主线:警告本身不给源码行号,但缩进的驱动源列表给了足够线索。如果两行里出现 $procdff、$mux、$add 这类 $ 开头的内部 cell,说明驱动源是 always / assign 综合出来的逻辑——回源码 grep 这个信号名,看它出现在几个赋值左端;如果一行写着 module input,而那个信号在你的意图里明明是本模块产生的,就要怀疑是端口方向把内外接反了。
根因 A:两个过程块驱动同一信号。 本文复现用例就是典型:写代码时先写了时序逻辑的 y <= a;,后来调试时又补了一个组合块 y = b; 想强制输出,忘删旧的。Verilog 语言层面完全合法(仿真器里两个块按调度顺序互相覆盖,行为是「后写赢」),所以 iverilog 仿真能跑、甚至波形看着「差不多对」,但综合到物理电路就是两个输出怼在一起。这也是这类 bug 最阴的地方:仿真过、综合报、上板死。
根因 B:端口方向写错。 子模块里把驱动外部的端口误声明成 input,父模块又把这张网接了自己的驱动逻辑,于是父侧逻辑和子侧内部逻辑同时驱动。yosys 展平层次后,check 看到的同样是两个驱动源。区分方法:警告里驱动源列表出现 module input/module output 字样且信号名属于子模块端口时,优先去对子模块的端口声明。
还有一个值得知道的排查误区:不要在 read_verilog 之后立刻跑 check 就下结论。多驱动警告是 proc 把过程块翻译成网表之后才能看出来的——proc 之前信号还躺在 process 里,check 查的是 cell 与连线的驱动关系。本文命令序列里 hierarchy -top; proc; check 的顺序不能省,少了 proc,你可能看到 0 problems 的假清净,问题其实还在。同理,如果警告里出现的 cell 名(如 $procdff$4)你完全不认识,那是综合器自动编号的过程块翻译产物,顺藤摸瓜回源码找对应 always 块即可,不必纠结编号本身。
还有一种边缘情况顺带提一句:三态总线是合法多驱动。用 assign bus = oe ? data : 8'hzz; 显式表达的三态信号,多个这样的驱动源是设计本意,yosys 能识别 z 态不会报 conflicting。只有普通 0/1 驱动的叠加才是问题。
三、排查清单
- 抄出信号与位号:从警告行抄
<模块>.<信号>和位号,多位信号逐位报属正常。 - 读驱动源列表:警告下两行缩进,分别是谁?
$procdff打头 = 时序逻辑,module input/output= 端口直连,其他$cell = 组合逻辑。 - 回源码定位:
grep -n "<信号名>" rtl/*.v,数它出现在几个always/assign的赋值左端;多于一个即根因 A。 - 查端口方向:驱动源里出现 module input 且信号源自子模块,去对子模块端口声明的 input/output 方向,错则根因 B。
- 改完复跑 check:同一命令重跑,
Found and reported 0 problems.才算关单;只消了一半说明还有别的驱动路径没清。
四、修复:一个信号一个家
修复原则一句话:把「谁在什么条件下驱动它」写进同一个块。两个 always 块想分别表达「时钟沿锁存 a」和「调试时直通 b」,正确写法是合并成一个时序块、块内用选择信号表达意图:
module multi_drv_fixed (
input wire clk,
input wire rst_n,
input wire sel,
input wire [7:0] a,
input wire [7:0] b,
output reg [7:0] y
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) y <= 8'h00;
else if (sel) y <= b;
else y <= a;
end
endmodulesel 把原来「两个块打架」的隐式博弈变成「一个块里明确的多路选择」,综合出来就是一个干净的 DFF 加一级 mux。对修复版重跑同一条命令,CHECK pass 的输出摘录如下:
4. Executing CHECK pass (checking for obvious problems).
Checking module multi_drv_fixed...
Found and reported 0 problems.
Warnings: 0 unique messages, 0 totalFound and reported 0 problems. 是这类问题的唯一关单标准。顺手养成一个习惯:把 check 写进日常综合脚本(read_verilog; hierarchy -top; proc; check),多驱动、位宽、未连接端口这类结构性问题在流程最前端就会被拦下,而不是等到 P&R 甚至上板才暴露。更系统的 lint 防线思路(同样适用于开源流程)见 SpyGlass RTL Lint 实战。
实验环境
- 机器/系统:Mac mini M4 / macOS 15(docker 容器内运行)
- 工具版本:Yosys 0.33 (git sha1 2584903a060)(geek-eda:0.2 docker 镜像)
- 日期:2026-10-29
- 完整命令:
yosys -p 'read_verilog multi_drv_bad.v; hierarchy -top multi_drv_bad; proc; check'(复现 8 条警告)yosys -p 'read_verilog multi_drv_fixed.v; hierarchy -top multi_drv_fixed; proc; check'(修复后 0 problems)
代码获取:本实验的复现与修复对照工程(
multi_drv_bad.v、multi_drv_fixed.v与复现命令)见公开仓库 derekzhuo/geek-doc.cn 的experiments/yosys-warning-multiple-drivers/;装好 Yosys 后按 README 两条yosys -p命令即可复现警告与修复归零的完整过程。
这一篇在知识体系里的位置
本篇负责开源综合环节的结构性问题:同簇的位宽静默截断见 yosys Warning Width mismatch assign 位宽不匹配,Verilator 侧更严格的 lint 口径见 Verilator WIDTH 位宽不匹配,开源工具链整体版图见 开源 EDA 工具链,全簇报错入口汇总在 开源 EDA 报错排查总览。
总结
Yosys multiple drivers to net 警告(0.33 实测措辞 multiple conflicting drivers)的本质是同一张网挂了多个驱动源:根因二分法——两个 always/assign 块驱动同一信号(占九成)或子模块端口方向写反;警告下缩进的驱动源列表就是定位线索,回源码数赋值左端、对端口方向即可定性;修复永远是合并成单一块、块内用选择信号写清意图,重跑 proc; check 看到 Found and reported 0 problems. 才算关单。