Yosys synth 未指定 top 模块:不报错但逻辑被悄悄优化光的排查与修复
跑 yosys 综合时很多人图省事写 synth 不带 -top,命令跑完没有任何 ERROR、stat 也打出了数字,看起来一切正常——直到把网表交给后端或拿去仿真,才发现设计规模小得离谱。这是 Yosys synth 未指定 top 模块这一类问题最阴险的地方:它不是报错,而是静默地综合出一个不是你想要的东西。同族「-top 名字写错直接报 Module is not part of the design」的硬报错见 yosys ERROR Module is not part of the design 修复,本篇专讲不报错的那一半:top 没指定、或 top 指错了一个存在的模块。更多开源 EDA 报错入口见 开源 EDA 报错排错中心。
本文要回答的问题
synth不带-top时 yosys 到底拿谁当顶层?-top指错成子模块时,Removing unused module删掉了什么?- 怎么用
stat的 cell 数快速判断顶层锚定是否正确? -flatten在顶层问题排查里起什么作用?
一、现象:不报错,但顶层只剩 4 个 cell
复现工程是一个有真实层次的小设计:真顶层 chip_top 例化了 uart_tx、pwm_led、debounce 三个子模块,外加一个故意留下的「孤儿」模块 glitch_filter(早期版本遗留,不被任何人例化)。这是真实工程里最常见的形态——源文件目录里总躺着几个没人用的旧模块。
下面这张流程卡概括了从踩坑到修复的完整路径,正文后面逐段展开:
先看不带 -top 时发生了什么。下面这张终端卡把四种跑法的关键输出录在了一起,重点看前两段:
读这张卡抓三个异常。第一,全程没有任何 ERROR,甚至没有任何 Warning——不带 -top 的 synth 是「安静通过」的。第二,stat 把五个模块各自打印了一份统计,每个模块都被当成了独立顶层综合,chip_top 名下只有 4 个 cell(3 个子模块例化加 1 个与门),完全没有「整个设计一共多少逻辑」的总计行。第三,孤儿模块 glitch_filter 也被完整综合出来占 10 个 cell——它根本不在设计的调用链上,只是因为没有显式顶层,yosys 无法判断谁是垃圾。
第二段更刺眼:把 -top 错指成子模块 uart_tx 后,hierarchy pass 连续打印 Removing unused module —— 删除名单里赫然包括真顶层 chip_top 自己。整个设计最终只剩 142 个 cell,而完整的层次设计应该是 281 个。也就是说,-top 指错不是「综合了一个子模块」这么简单,而是从 uart_tx 的视角把够不着的逻辑全部当垃圾回收了,uart_tx 还少了 start 信号上游的那个与门,从 143 掉到 142。
二、根因:hierarchy 锚定顶层决定了谁活谁死
yosys 综合的第一步永远是 hierarchy pass,它的职责是按 -top 锚定设计入口,然后从入口出发做可达性分析:凡是顶层够不着的模块,一律视为 unused 删除。这个机制本身是对的——综合结果里不该带垃圾——问题出在顶层锚定的来源:
- 不带
-top:yosys 0.33 的行为是把「没有被任何人例化的模块」全部各自当顶层,有多少个候选就综合多少份,互不隶属。于是chip_top和孤儿glitch_filter平起平坐,层次关系名存实亡,stat自然没有 design hierarchy 总计行。 -top指错成存在的模块:不报错(模块确实存在),但可达性分析的根错了,真顶层连同其他子树一起进 Removing 名单。-top指错成不存在的模块:这才是硬报错Module is not part of the design,属于同族另一篇的覆盖范围。
一句话:synth 的 -top 不是可选项,是可达性分析的根。根错了,后面所有优化 pass 都在错误的设计上兢兢业业地工作,而且不吭声。
三种形态的快速区分不用翻源码,看日志里的两个特征就够:
| 形态 | Removing unused module 名单 | stat 有无 design hierarchy 总计 | 顶层名下 cell 数 |
|---|---|---|---|
不带 -top(有孤儿模块) | 无(没有删除动作) | 无,每模块各打一份 | 只有例化壳,本例 4 个 |
-top 指错成存在的子模块 | 含真顶层 chip_top | 无,只剩被指的那一个模块 | 真顶层已消失 |
-top 正确 | 只有真正的孤儿模块 | 有,且等于各模块之和 | 壳 + 完整子树 |
第一行和第二行是「静默故障」,第三行是「健康基准」。日常巡检脚本里把这两条断言写进去——Removing 名单不含真顶层、stat 存在 design hierarchy 行——比肉眼看日志可靠得多。
三、修复与验证:stat 的三组数字
修复就是把真顶层显式写进命令,再用 stat 对照验证。模块名大小写敏感,必须与源码逐字符一致:
yosys -Q -T -p 'read_verilog chip_top.v uart_tx.v pwm_led.v debounce.v glitch_filter.v; synth -top chip_top; stat'修复后的输出里有两个标志性变化(完整对照见终端卡第三段):其一,Removing unused module 名单只剩 glitch_filter 一行——孤儿模块被正确识别并回收,真顶层安然无恙;其二,stat 末尾出现了 === design hierarchy === 总计行,Number of cells: 281,即 4(顶层胶合)+ 79(debounce)+ 58(pwm_led)+ 143(uart_tx)- 3(例化边界去重)。看到这个总计行,才算整个设计被当成一个整体综合了。
如果后端流程或形式验证工具要求单模块扁平网表,在修复的基础上加 -flatten:
yosys -Q -T -p 'read_verilog chip_top.v uart_tx.v pwm_led.v debounce.v glitch_filter.v; synth -top chip_top -flatten; stat'打平后 chip_top 单模块 285 个 cell,比层次总计的 281 多 4 个——这是跨层次边界优化(端口常量传播、边界缓冲合并)的正常结果,量级一致即说明 flatten 前没有丢逻辑。反过来,如果你 flatten 之后 cell 数和层次总计差了一个数量级,先回头查顶层锚定,不要怀疑 flatten 本身。
排查时还有一个顺手的习惯:在 synth 之前先单独跑 hierarchy -check -top chip_top,把顶层锚定与缺模块检查提前到综合之前,问题暴露得更早、日志更短。脚本化流程里建议把顶层名抽成变量,read_verilog、synth -top、后端约束全部引用同一个变量,杜绝手敲不一致。
最后补一个容易混淆的点:不带 -top 时 yosys 之所以「不报警」,是因为它无法区分「孤儿模块是垃圾」还是「用户想分别综合多个独立模块」——后者在 IP 核批量 lint 的场景里是合法用法。工具把判断权留给了用户,代价就是忘了给 -top 时不会有人提醒你。这也是为什么正规流程的 Makefile 里 synth -top $(TOP) 永远是显式写法,把「谁是顶层」从工具的猜测变成人的声明。
代码获取:本实验的完整复现工程(五个源文件与一键
run.sh,四段对照实验)见公开仓库 derekzhuo/geek-doc.cn 的experiments/yosys-synth-top-not-set/;装好 yosys 或用 geek-eda docker 镜像后sh run.sh即可复现全部四段输出。
实验环境
机器/系统:Mac mini M4 / macOS 15,docker 镜像 geek-eda:0.2
工具版本:Yosys 0.33 (git sha1 2584903a060)(照抄
yosys -V输出)日期:2026-10-29
完整命令:
bashdocker run --rm -v "$PWD":/work -w /work geek-eda:0.2 sh run.sh # run.sh 内四段:synth(不带 -top)/ synth -top uart_tx / synth -top chip_top / synth -top chip_top -flatten # 每段均为:yosys -Q -T -p 'read_verilog chip_top.v uart_tx.v pwm_led.v debounce.v glitch_filter.v; synth ...; stat'
这一篇在知识体系里的位置
本篇是开源 EDA 报错排查簇里「综合入口」节点的静默故障页:同族硬报错(顶层名不存在 / 文件没读入)见 yosys ERROR Module is not part of the design 修复,两篇合起来覆盖了 synth -top 的全部坑位;按综合/仿真/后端分诊的报错总入口见 开源 EDA 报错排错中心。
总结
Yosys synth 未指定 top 模块不会报错,而是把每个候选模块各当顶层综合,真顶层只剩空壳、孤儿模块白占资源;-top 指错成存在的子模块时,hierarchy 会把真顶层打印进 Removing unused module 名单并删光够不着的逻辑。判据看两处:Removing 名单里有没有真顶层、stat 有没有 design hierarchy 总计行;修复就是 synth -top <真顶层> 显式锚定,需要扁平网表再加 -flatten,cell 数量级一致即验证通过。