4、综合(Yosys + ABC):Yosys 逻辑综合流程、ABC 技术映射、面积与时序优化策略、综合脚本编写

综合,说白了就是把 RTL 代码翻译成门级网表。这一步做不好,后面 P&R 再怎么折腾也救不回来。我见过不少新手,RTL 仿真跑得欢,一综合就崩,其实就是没搞懂 Yosys 和 ABC 到底在干什么。

这一章,咱们就聊聊 OpenROAD 里的综合流程。我会把 Yosys 的逻辑综合、ABC 的技术映射,以及面积和时序的平衡策略,掰开揉碎了讲清楚。

4.1 Yosys 逻辑综合流程

Yosys 是 OpenROAD 的前端综合工具。它的核心任务就两个:读入 RTL 和 做逻辑优化。

我个人习惯把 Yosys 的流程分成三步:

  1. 读入设计:把 Verilog 或 VHDL 吃进来,解析成内部的数据结构。
  2. 高层次优化:做常量传播、资源共享、状态机编码等操作。
  3. 逻辑综合:把行为级描述转换成布尔逻辑表达式。

这里有个关键点:Yosys 默认不会做太多面积优化。它更擅长把 RTL 翻译成 通用逻辑,然后交给 ABC 去做技术映射。所以,别指望 Yosys 一步到位给你最优网表。

我的经验: 在 Yosys 阶段,我通常会多跑几遍 proc 和 opt 命令。特别是 opt_clean -purge,能帮你干掉那些没用的线网和模块。有一次我接手一个老项目,综合出来的网表比预期大了 30%,一查发现是遗留的 debug 逻辑没清理干净。跑一遍 opt_clean,面积直接降下来了。

4.2 ABC 技术映射

Yosys 做完逻辑综合后,会生成一个 与或图。这个图是抽象的,跟具体的工艺库没关系。ABC 的作用,就是把这个抽象图,映射到你的目标工艺库上。

ABC 的核心是 标准单元库。它会把 Yosys 生成的逻辑节点,拆成一个个标准单元,比如 AND、OR、NAND、NOR、MUX 等等。这个过程叫 技术映射。

我刚开始用 ABC 时,总觉得它是个黑盒子。后来踩过几次坑,才摸清楚它的脾气。ABC 的优化策略,说白了就三个方向:

优化方向 ABC 命令 效果
面积优先 strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf -K 6; &put 减少标准单元数量,但可能牺牲时序
时序优先 strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf -K 8; &put 优化关键路径延迟,但面积会变大
平衡模式 strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf -K 6; &put -m 在面积和时序之间取折中

你看,命令里有个 -K 参数,它控制的是 LUT 大小。K 值越大,ABC 越倾向于用更复杂的逻辑门,时序会更好,但面积会更大。K 值越小,逻辑门越简单,面积小,但路径延迟可能变长。

注意: ABC 的优化不是万能的。我曾经在一个项目中,为了追求极致时序,把 K 值设到了 10。结果综合出来的网表里,出现了大量 8 输入 NAND 门。这种门在标准单元库里根本不存在,最后 ABC 只能把它拆成多个小门,反而更慢了。所以,K 值一般不要超过 8,6 是比较稳妥的选择。

4.3 面积与时序优化策略

综合阶段的优化,说白了就是 面积和时序的博弈。你不可能既要马儿跑,又要马儿不吃草。但我们可以通过一些策略,找到那个平衡点。

我总结了几条实战经验:

  • 先跑一遍面积优化:用 synth -top <module> 做一次基础综合,看看面积基线是多少。
  • 再跑一遍时序优化:用 synth -top <module> -timing,看看时序瓶颈在哪。
  • 对比两次结果:如果面积差在 10% 以内,但时序改善了 20%,那时序优化就是值得的。
  • 手动干预关键路径:对于特别关键的路径,可以在 RTL 里手动实例化一些特殊单元,比如时钟门控、多路选择器,让 Yosys 和 ABC 少做决策。

为什么会这样?因为自动综合工具再聪明,也猜不到你的设计意图。你告诉它“这里要快”,它只能通过增加逻辑门来缩短路径。但如果你在 RTL 里直接写清楚“这里用 MUX 而不是 if-else”,它就能少走弯路。

避坑指南: 我曾经在一个高速接口的设计中,发现综合后的 setup 违例总是修不掉。折腾了两天,最后发现是 RTL 里写了一个 case 语句,综合工具把它优化成了一个巨大的优先级编码器。改成 parallel_case 后,面积没变,时序直接达标。所以,写 RTL 时就要想着综合工具的感受。

4.4 综合脚本编写

在 OpenROAD 里,综合脚本通常用 Tcl 写。我习惯把脚本分成几个部分:读入设计、设置约束、运行综合、输出结果。

下面是一个我常用的脚本模板:

# 读入设计
read_verilog rtl/top.v
read_verilog rtl/sub_module.v

# 设置顶层模块
synth -top top

# 设置时序约束
create_clock -name clk -period 10 [get_ports clk]
set_input_delay -clock clk 2 [all_inputs]
set_output_delay -clock clk 2 [all_outputs]

# 运行综合
synth -top top -timing

# 输出网表
write_verilog -noexpr outputs/top_synth.v

# 输出时序报告
report_timing -max_paths 10 > outputs/timing.rpt

# 输出面积报告
report_area > outputs/area.rpt

这个脚本看起来简单,但有几个细节要注意:

  • 时钟约束一定要准确:周期设错了,后面所有优化都是白费。
  • 输入输出延迟要合理:设得太紧,综合工具会拼命优化,面积爆炸;设得太松,时序可能不达标。
  • 多跑几次报告:综合完成后,一定要看时序报告和面积报告。别偷懒,这一步能帮你发现很多问题。

嗯,这里还要提一句。OpenROAD 的 Yosys 和 ABC 是深度集成的,你不需要手动调用 ABC。但如果你想精细控制,可以在脚本里加 abc -script <script> 命令,指定 ABC 的优化脚本。不过,对于大多数项目,默认配置已经够用了。

最后,我建议你把综合脚本和 RTL 代码一起纳入版本管理。这样,每次综合的结果都能复现,出了问题也能快速回滚。

Yosys + ABC 综合流程图 RTL 设计 Yosys 逻辑综合 读入 RTL → 高层次优化 → 逻辑综合 与或图 (AIG) ABC 技术映射 面积优化 / 时序优化 / 平衡模式 门级网表 关键参数 -K: LUT 大小控制 -timing: 时序优先 -m: 平衡模式 常见问题 K 值过大 → 单元不匹配 约束不准 → 优化失效 RTL 风格 → 影响综合