3. RTL 综合入门:Yosys 综合流程

综合,说白了就是把我们写的 RTL 代码,翻译成芯片厂给的 standard cells 能认识的门级网表。这一步做不好,后面布局布线全是白搭。我刚开始用 OpenROAD 的时候,总觉得综合就是个黑盒子,脚本一跑就完事。后来吃过亏才明白——综合策略选不对,时序收敛就是做梦。

今天我们就来啃这块硬骨头。我会带着你,把 Yosys 综合的每一步都走一遍。

3.1 Yosys 是什么?

Yosys 是 OpenROAD 流程里的综合引擎。它是个开源工具,但能力一点也不弱。我个人习惯把它看作一个「RTL 到网表的翻译官」——你给它 Verilog,它给你 mapped netlist。

它的工作流程大致分三步:

  1. 读取设计文件(读 RTL)
  2. 高层次优化(逻辑化简、资源共享)
  3. 技术映射(把逻辑映射到标准单元库)

嗯,这里要注意:Yosys 不是直接一股脑把 RTL 变成门级网表的。它中间会做很多「小动作」,比如常量传播、死逻辑消除、状态机编码优化等等。这些优化做得好不好,直接影响最终面积和时序。

核心观点: Yosys 综合的本质是「逻辑优化 + 工艺映射」两步走。优化是通用的,映射是工艺相关的。

3.2 读取设计文件

先看一个最基本的 Yosys Tcl 脚本。我一般习惯把脚本分成几个清晰的段落,方便调试。

# 1. 读取设计文件
read_verilog -sv ./src/top.sv
read_verilog -sv ./src/sub_module_a.v
read_verilog -sv ./src/sub_module_b.v

# 2. 设置顶层模块
hierarchy -top top

# 3. 高层次优化
proc; opt; fsm; opt; memory; opt

# 4. 技术映射
dfflibmap -liberty ./lib/sky130_fd_sc_hd__tt_025C_1v80.lib
abc -liberty ./lib/sky130_fd_sc_hd__tt_025C_1v80.lib -script +strash;scorr;ifraig;retime,{D};strash;dch,-f;map,-M,1,{D}

# 5. 输出网表
write_verilog ./outputs/top_synth.v
write_verilog -noattr ./outputs/top_synth_clean.v

这段脚本里,read_verilog 就是读文件。我遇到过不少新手,文件路径写错或者漏读某个模块,结果综合出来一堆黑盒(blackbox)。

避坑指南: 我曾经因为文件名大小写不一致,在 Linux 上折腾了半小时。Yosys 对文件名大小写敏感,建议统一用小写。

读文件时,有几点经验分享:

  • SystemVerilog 支持:加 -sv 开关才能读 .sv 文件。不加的话,很多语法会报错。
  • 文件顺序:被例化的模块要放在前面读,或者用 -D 定义宏来避免依赖问题。
  • 黑盒检查:读完后用 hierarchy -check 检查有没有未解析的模块。

3.3 标准单元库映射

技术映射(tech mapping)是综合里最核心的一步。Yosys 用 abc 命令调用 ABC 工具来做这件事。

标准单元库长什么样?说白了就是一个 Liberty 格式的文件(.lib)。里面描述了每个 cell 的功能、面积、时序、功耗等信息。Yosys 需要这个文件才能知道「你的设计里有哪些门可以用」。

映射过程可以拆成几个子步骤:

步骤 命令 作用
1. 提取逻辑 abc -script +strash 把电路转成 AIG(And-Inverter Graph)
2. 逻辑优化 scorr; ifraig; retime 消除冗余、重定时优化
3. 技术映射 map -M 1,{D} 把 AIG 节点映射到标准单元

这里 -M 1,{D} 的意思是:面积优先,同时考虑延迟(D)。你可以改成 -M 0,{D} 变成延迟优先。

我的经验: 对于时序紧张的设计,我一般先用 -M 0,{D} 跑一遍,看看能不能满足时序。如果面积超标,再调成 -M 1,{D} 做面积优化。没有一招鲜吃遍天的策略。

3.4 综合策略选择

综合策略说白了就是「在面积、时序、功耗之间做取舍」。Yosys 给了我们几个旋钮可以拧:

  • 面积优先:map -M 1,逻辑共享多,但路径可能变长。
  • 时序优先:map -M 0,用更大的 cell 来驱动长线,面积会涨。
  • 功耗优先:配合 dfflibmap 选择低功耗触发器,但速度会慢。

你想想看,实际项目里很少有「既要又要」的情况。我做过一个 MCU 项目,面积预算卡得很死,我就用了面积优先策略,结果时序差了 0.3ns。后来在布局布线阶段用插 buffer 的方式修回来的——但那是后话了。

下面这张图,是我自己总结的 Yosys 综合流程,你可以对照着看:

Yosys 综合流程框架图 RTL 设计文件 read_verilog + hierarchy proc → opt → fsm → memory dfflibmap + abc 映射 门级网表 (Netlist) 关键输入 • RTL 代码 (.v/.sv) • 标准单元库 (.lib) • 约束(可选) 关键输出 • 门级网表 (.v) • 综合统计报告 • 未映射黑盒列表

3.5 综合后的检查清单

综合跑完,别急着往下走。我每次都会做这几件事:

  1. 检查黑盒:hierarchy -check 看有没有未解析的模块。
  2. 检查面积:Yosys 会打印 cell 数量,跟预期对比一下。
  3. 检查连线:用 stat 命令看网表统计信息。
  4. 检查扇出:高扇出网络(比如时钟、复位)在综合阶段就要标记出来。
避坑指南: 我曾经综合完直接跑布局布线,结果发现网表里有个模块是空的——因为 RTL 里例化了但没写逻辑。Yosys 不会报错,只会默默把它优化掉。所以一定要用 stat 确认面积不为零。

3.6 实战小贴士

最后分享几个我在项目中积累的实用技巧:

  • 增量综合:如果只改了某个子模块,可以用 read_verilog -sv -D SYNTHESIS 配合 hierarchy -top 只综合改动的部分,节省时间。
  • 保留中间结果:在 abc 之前加 write_verilog ./outputs/pre_abc.v,方便对比映射前后的逻辑变化。
  • 多库支持:如果工艺库有多个 PVT corner,可以分别综合,然后选最差 corner 的网表做时序分析。
  • 脚本化:把综合脚本写成 Makefile 或 Python 脚本,每次改参数不用手敲命令。

嗯,综合这一步说难不难,说简单也不简单。关键是要理解 Yosys 每一步在做什么,以及怎么根据设计需求调整策略。你把这章的内容吃透了,后面的布局布线就会顺畅很多。