4、综合(Synthesis):Yosys与OpenROAD的集成、逻辑综合流程、综合策略与脚本编写、综合后的检查

综合这一步,说白了就是把咱们写的 RTL 代码,翻译成门级网表。你想想看,Verilog 里写的 always @(posedge clk) 和 assign,最终都要变成标准单元库里的与非门、触发器和连线。Yosys 就是干这个的,它是 OpenROAD 流程里的综合引擎。

我个人习惯把综合看作「翻译 + 优化」两个阶段。翻译保证功能正确,优化则为了满足时序和面积目标。很多新手只盯着时序,忽略了综合策略,结果后端跑得再辛苦也救不回来。

核心要点: 综合不是一次过的,它需要反复迭代。我见过不少团队,RTL 改一行就要重新跑全套综合,其实没必要。先搞清楚哪些是高频改动,哪些是结构性的,分开处理效率更高。

4.1 Yosys 与 OpenROAD 的集成方式

Yosys 是开源世界里最成熟的综合工具。OpenROAD 把它作为默认的综合引擎,集成得相当紧密。你不需要单独启动 Yosys,OpenROAD 的 flow 脚本会自动调用它。

集成路径其实很简单:

  • 输入: RTL 文件(.v / .sv)、约束文件(.sdc)、工艺库文件(.lib)
  • 输出: 综合后的门级网表(.v)、时序报告、面积报告
  • 中间产物: Yosys 的脚本文件(.ys)、综合日志

我记得第一次用 OpenROAD 时,以为要手动写 Yosys 脚本。后来发现 flow 已经封装好了,你只需要在配置里指定 synth 阶段开启就行。当然,如果你想精细控制,也可以自己写 .ys 脚本覆盖默认行为。

小技巧: 在 OpenROAD 的 config.mk 里设置 SYNTH_HIERARCHICAL = 1,可以启用层次化综合。对于大设计,这能显著减少内存占用和运行时间。我曾在 500 万门的设计上试过,内存从 32GB 降到了 12GB。

4.2 逻辑综合流程

综合流程可以拆成几个清晰的步骤。我习惯用一张图来理解它:

逻辑综合流程 RTL 读入 高层次优化 技术映射 逻辑优化 时序/面积评估 网表输出 不满足约束时迭代 说明:技术映射阶段将 RTL 逻辑映射到工艺库中的标准单元,是综合最核心的一步

流程看起来是线性的,但实际工作中经常要往回跳。比如时序评估发现 slack 为负,就得回到逻辑优化阶段重新调整。我曾经在一个高速接口的设计里,来回迭代了七八次才收敛。

4.3 综合策略与脚本编写

综合策略决定了你的网表质量。我总结了几种常用策略,用表格对比一下:

策略 适用场景 优点 缺点
面积优先 低成本芯片、IoT 设备 芯片面积小,成本低 时序可能紧张
时序优先 高性能 CPU、网络芯片 时序余量大,频率高 面积大,功耗高
功耗优先 可穿戴设备、电池供电 动态功耗低 面积和时序折中
平衡策略 大多数通用设计 各方面均衡 没有突出优势

写 Yosys 脚本其实不复杂。下面是一个典型的综合脚本片段,我加了注释:

# 读入 RTL 文件
read_verilog -sv src/top.sv
read_verilog -sv src/sub_module.v

# 设置顶层模块
hierarchy -top top

# 高层次优化(常量传播、资源共享)
proc; opt; fsm; opt; memory; opt

# 技术映射到工艺库
techmap; opt

# 映射到标准单元
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;strash;dch,-f;map,-M,1,-D,5000

# 清理并输出网表
clean
write_verilog -noexpr synth_output.v
stat -liberty lib/sky130_fd_sc_hd__tt_025C_1v80.lib
注意: abc 命令里的 -D,5000 是时序约束,单位是皮秒。我曾经因为写成了 -D,5(纳秒),结果综合出来的网表时序完全乱套。检查了三天才发现是单位问题。一定要确认你的约束单位跟工艺库一致。

在 OpenROAD 里,你不需要直接写这些。flow 脚本会自动生成。但如果你想自定义,可以在 scripts 目录下放一个 custom_synth.ys,然后在 config.mk 里指定:

SYNTH_SCRIPT = scripts/custom_synth.ys

4.4 综合后的检查

综合跑完不代表万事大吉。我见过太多人直接跳到布局布线,结果后面发现问题,回头改综合,浪费大量时间。综合后必须做以下几项检查:

  • 功能等价性检查: 综合后的网表跟原始 RTL 功能是否一致?用 yosys 自带的 equiv 命令或者 Formality 做形式验证。
  • 时序报告审查: 看 setup 和 hold 的 slack。如果 slack 为负,先别急着往后跑,先优化综合策略。
  • 面积报告核对: 综合出来的面积跟预期差多少?如果差太多,可能是 RTL 里写了不该写的代码。
  • 扇出/扇入检查: 高扇出的 net 会导致时序问题。我一般把扇出超过 30 的 net 标记出来,看看能不能插入 buffer。
  • 未连接端口检查: 综合日志里会报 warning,说某些端口没连上。别忽略,这可能是 RTL 的 bug。
我的习惯: 综合完成后,我会先看日志里的 Warning 数量。如果超过 50 个,我基本不会继续往下走。先花半小时把 warning 清掉,后面能省下好几天的 debug 时间。

举个例子,有一次我综合一个多时钟域的设计,日志里报了 30 多个 unresolved clock 的 warning。我没在意,直接跑布局布线。结果 CTS 阶段时钟树完全乱掉,花了整整一周才定位到问题——原来是一个时钟门控单元没例化对。如果当时在综合后检查一下,十分钟就能搞定。

嗯,综合这一步,说白了就是打好地基。地基不稳,上面盖的房子再漂亮也没用。花时间做好综合后的检查,是值得的。