Yosys stat area 0 速查:Number of cells 0 是逻辑被优化掉了
更新时间:2026-10-29。FAQ 速查页,属于开源 EDA 排错系列,总入口见 开源 EDA 报错排错中心。
yosys -p 'synth; stat'跑完了、没报任何错,stat 里Number of cells: 0、area 估算为 0——RTL 明明写了几十行 always 块,综合结果却是空的。注意这不是「找不到 top」那一类硬报错:Yosys error no top module 是流程直接报错误出,本篇讲的是 top 选对了、流程跑通了,但内部逻辑自己塌成 0 的情形。元凶几乎都是同一个:你的逻辑没有任何一条路径能到达输出端口,Yosys 的优化 pass 把它当作死代码整片删掉了。
本文要回答的问题
- stat 里
Number of cells: 0与 area 为 0 到底意味着什么? - 为什么
check命令报 0 problems,逻辑还是被删光了? - 怎么用 proc 前后的 stat 对比确认「逻辑进过网表、是被优化删掉的」?
- 最常见的三种塌法(无输出端口、输出悬空、常量折叠)怎么区分和修复?
一、现象:流程跑通但 stat 全空
最小复现:一个累加器模块,clk/rst_n/a/b 四个输入齐全,always 块写得好好的,唯一的问题是——忘了声明输出端口。
// bad_no_output.v:逻辑写全了,但没有输出端口
module top(
input wire clk,
input wire rst_n,
input wire [7:0] a,
input wire [7:0] b
);
reg [7:0] acc;
always @(posedge clk) begin
if (!rst_n)
acc <= 8'd0;
else
acc <= acc + a + b;
end
// 忘了把 acc 接到任何输出端口
endmodule跑 synth -top top; stat,日志里没有任何 ERROR,但统计表一片空白:
这张终端卡里最关键的两行:Number of cells: 0 且 Number of processes: 0——不仅门级 cell 没了,连过程块都没剩下,说明逻辑是在优化阶段被整片删除,而不是「没读进来」。wires 还剩 4 根(就是 4 个输入端口本身),这是「端口还在、内部被掏空」的典型形态。
area 为 0 只是 cells 为 0 的推论:stat 的 area 估算来自各 cell 映射到的标准单元面积累加,一个 cell 都没有,面积自然是 0。所以看到 area 0 不要单独当成一个问题去查工艺库映射——先解决 cells 为什么是 0,area 会跟着恢复。
二、排查流程
cells 为 0 的排查按「确认逻辑进过网表 → 找删除它的 pass → 补齐输出路径」四步走,下面这张流程卡给出顺序:
读这张卡抓住第二步这个分水岭:hierarchy -top top; stat; proc; stat 两次统计的对比能一刀切开两类问题——proc 前 cells 就大于 0,说明 RTL 编译进了网表、是后续优化删掉的(本篇主题);proc 前就是 0,那是读文件或层次就没对上,得回头查别的坑。
三、定位实锤:proc 前后 stat 对比
对坏例子跑 read_verilog bad_no_output.v; hierarchy -top top; stat; proc; stat,输出里两次统计形成对照:
读这张卡看三个数字:proc 前 Number of cells: 3(read_verilog 直接把加法器等映射成了粗粒度 cell),proc 后涨到 5(时序逻辑被 PROC_DFF 换成触发器)——逻辑确实存在过。那么它是在 proc 之后、synth 流程后半段的 opt_clean / opt_expr 一类优化 pass 里被删掉的:这些 pass 做死代码删除和常量折叠,判定标准就是「这个信号的值能不能影响到顶层输出」。acc 寄存器算得再热闹,没有任何输出端口引用它,整条逻辑锥(logic cone)就是死的,删了对电路外部行为零影响——Yosys 删得理直气壮。
为什么 check 帮不上忙?日志里 Found and reported 0 problems——check 查的是多驱动、未驱动这类「结构性问题」,而「寄存器没被任何人使用」在它的判定里不算错(未使用信号顶多算 warning 级别语义,且不影响一致性)。所以 cells=0 这类问题 check 全绿是常态,不能拿 check 的结果排除优化删除。
再往深看一层,删除动作其实分两步走。第一步在 proc 内部:日志里能看到 PROC_PRUNE pass (remove redundant assignments in processes)——如果 always 块里某些分支赋值最终不影响任何输出,proc 阶段就开始剪枝。第二步在 synth 流程后半段:opt_clean 把没有扇出的 cell 逐级往回删,opt_expr 做常量折叠把能算出定值的逻辑替换成常量。这两个 pass 的设计哲学是一致的:综合器对「外部可观察行为」负责,端口列表就是可观察行为的边界,边界之内的东西如果不能影响边界上的信号,就没有存在的必要。理解了这条哲学,cells=0 就从「灵异事件」变成了「必然结果」——你要做的不是阻止优化,而是把想保留的逻辑挂到边界上去。
还有一个辅助判据值得记下:stat 里剩下的 wires 数。本例删光后还剩 4 根 wire、18 个 wire bit,正好等于 4 个输入端口的位宽总和(1+1+8+8=18)——端口本身不会被删,剩下多少 wire 就是端口占多少。如果 stat 里 wires 数明显大于端口位宽,说明还有幸存逻辑,塌得不彻底,排查方向就要转向「哪部分被删了」而不是「为什么全没了」。
四、三种常见塌法与修复
| 塌法 | 典型现场 | 快速验证 | 修复 |
|---|---|---|---|
| 无输出端口 | 模块只有 input,内部逻辑再复杂也是死代码 | 看模块端口表有没有 output | 补 output 端口并 assign 内部信号 |
| 输出悬空/未驱动 | output 声明了但没 assign,或接错了信号名 | check 找 undriven;看 assign 右值 | 把内部信号正确接到 output |
| 常量折叠 | 复位值/参数把逻辑常量化,输出恒为定值 | stat 里 cell 极少且都是常量驱动 | 检查参数覆盖与复位逻辑是否把数据路径短掉了 |
修复动作(本例):给模块补上输出端口 output wire [7:0] sum,加一行 assign sum = acc;。改完复跑同一条 synth 命令:
这次 stat 给出 Number of cells: 80、73 根 wire——加法器、触发器、多路选择器全都映射成了真实 cell。同一份 always 块,唯一的差别就是输出路径通了。这也是这类问题最让人困惑的地方:综合器不认为它在「丢东西」,它认为自己在帮你省面积,报错通道里一个字都不会提。
常量折叠那种塌法稍微隐蔽一点:端口接好了,但参数或复位值让输出恒为常量(比如数据路径被 parameter WIDTH = 0 之类的配置短掉,或者控制信号被 tie 死导致多路选择器永远走同一支),stat 里 cell 数不为 0 但远小于预期、area 接近 0。验证手段同一张牌:proc 前 stat 细胞数正常、synth 后塌掉,就去看优化日志里的 OPT_EXPR pass (perform const folding) 段,常量传播会把折叠过程打在日志里。
「输出悬空」这种塌法还有一个常见变体值得单独说:端口声明了、assign 也写了,但 assign 的信号名拼错,接到一个不存在/打错的 wire 上——Verilog 默认允许隐式声明,拼错的名字会悄悄变成一根新的 1 位 wire,真正的内部信号照旧无人引用。这种现场 check 有时会报 undriven,有时全绿(取决于是否构成未驱动冲突),但 stat 塌法与无端口完全一样。防御办法是在 RTL 顶部加 `default_nettype none,让拼错的信号名在编译期就报错,把这类问题从综合阶段提前到读文件阶段解决——这是比任何排查脚本都便宜的拦截。
五、规范做法与预防
- stat 三字段一起看:cells、processes、wires 三个数组合判读。cells=0 且 processes=0 是优化删光;processes>0 说明 RTL 还没走完 proc,方向完全不同。
- proc 前后对比是标准实锤:
hierarchy -top top; stat; proc; stat三行脚本写进排查笔记,cells=0 先跑这条确认逻辑是否进过网表。 - 顶层端口表先自查:synth 之前肉眼过一遍 top 模块端口——每个 input 有消费者、每个 output 有驱动者,这是 cells=0 类问题最便宜的拦截点。
- 仿真先行:testbench 先跑过仿真再综合,仿真波形里输出恒为高阻/常量时,综合塌成 0 是必然结果,排错在仿真阶段就该完成。
实验环境
- 机器/系统:Mac mini M4 / macOS 15(docker 镜像 geek-eda:0.2 内跑 Yosys)
- 工具版本:Yosys 0.33(
yosys -V输出Yosys 0.33 (git sha1 ...)) - 日期:2026-10-29
- 完整命令:
# 现象
docker run --rm -v <实验目录>:/work -w /work geek-eda:0.2 \
yosys -p 'read_verilog bad_no_output.v; synth -top top; stat'
# 定位:proc 前后 stat 对比
docker run --rm -v <实验目录>:/work -w /work geek-eda:0.2 \
yosys -p 'read_verilog bad_no_output.v; hierarchy -top top; stat; proc; stat'
# 修复后复跑
docker run --rm -v <实验目录>:/work -w /work geek-eda:0.2 \
yosys -p 'read_verilog fixed_with_output.v; synth -top top; stat'代码获取:完整实验源码(坏例子、修复版、复现命令)在公开仓 geek-doc.cn/experiments/yosys-stat-area-zero,clone 后按 README 里的三条 yosys 命令即可复现「0 cells → 定位 → 80 cells」的完整对照。
这一篇在知识体系里的位置
本篇是开源 EDA 排错簇中「Yosys 综合结果异常」节点的 FAQ 页:流程直接报错的「找不到 top」见 Yosys error no top module,端口/位宽类的综合警告另有专篇;更多工具报错从 开源 EDA 报错排错中心 按入口分流。
总结
Yosys stat 出现 Number of cells 0 / area 0 且流程无报错,含义是 top 选对了但内部逻辑被优化 pass 当死代码整片删除,首因是逻辑没有任何路径到达顶层输出端口(无输出、输出悬空、被常量折叠)。排查实锤是 proc 前后 stat 对比:proc 前有 cells 就坐实优化删除,再补输出路径复跑 stat 确认 cells 恢复;check 报 0 problems 不能排除这类问题,因为它不查「未被使用」。