3. RTL到网表的综合过程:综合工具、时序约束、面积优化

说实话,综合这一步是RTL走向物理实现的“第一道鬼门关”。你写的代码再漂亮,综合工具不认,那也白搭。我个人习惯把综合看作“翻译+优化”——把你用Verilog/SV写的行为描述,翻译成跟工艺库绑定的门级网表,同时还要满足时序、面积、功耗这几个互相打架的需求。

3.1 综合工具:各家有各家的脾气

市面上主流的综合工具就那么几个,Synopsys Design Compiler(DC)用的人最多,Cadence Genus也有不少拥趸,Yosys则常见于开源/FPGA流程。我在项目中主要用DC,但有些小团队用Genus也跑得挺顺。

  • Design Compiler(DC):行业标杆,支持最全的SDC约束,优化算法非常成熟。我遇到的大部分复杂ASIC都是用DC过的。
  • Genus:相比DC,它更强调“并行综合”,对大设计跑得稍快一些,但脚本语法跟DC略有差异。
  • Yosys:开源里最活跃的,配合arachne-pnr或nextpnr做FPGA布局布线。我实习时用过一次,写RTL时注意不要用太花哨的语法。

不管你用哪款,核心流程都差不多:读入RTL → 指定工艺库 → 添加时序约束 → 运行综合 → 输出网表+SDF。下面这张图能让你快速理解整体脉络。

RTL到网表综合流程框架 RTL设计文件 .v / .sv 时序约束(SDC) 工艺库(.lib) 综合引擎 转换 → 优化 → 映射 时序优化 面积优化 功耗优化 门级网表 .v / .vg + SDF 布局布线(后端) 综合结果质量直接决定后端 能否顺利收敛

3.2 时序约束:你给工具画个“框”

综合工具如果没约束,它就会乱来——面积给你往死里做,时序可能完全不管。我见过最惨的一次,同事忘了设输入延迟,结果综合出来等效频率比目标低了一半……后来花了三天重新跑。

时序约束用SDC(Synopsys Design Constraints)格式,核心就那么几条:

# 定义时钟
create_clock -name clk -period 10 [get_ports clk]

# 输入延迟
set_input_delay -clock clk -max 2.0 [get_ports data_in]

# 输出延迟
set_output_delay -clock clk -max 1.5 [get_ports data_out]

# 伪路径(跨时钟域等)
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

# 时钟组(异步时钟域)
set_clock_groups -asynchronous -group {clk_a} -group {clk_b}

这里我要强调一个坑:约束要尽量准确。我曾经为了偷懒把输入延迟从2ns改成3ns,觉得“多留点余量”,结果综合工具因为太保守,把逻辑深压到了一个极其奇怪的形状,修都修不回来。所以别乱加margin,合理就好。

3.3 时序优化的三大狠招

综合工具做时序优化主要靠三种手段,你必须理解它们,才知道怎么写RTL能让工具“舒服”。

优化手段原理适用场景
逻辑重组改变运算树的拓扑结构,缩短关键路径多级加法器/乘法器
寄存器重定时移动寄存器位置平衡延迟流水线级数不均匀时
门尺寸调整使用驱动能力更大的标准单元长互连线负载过大

嗯,举个例子:你写了个a + b + c + d,综合工具会把它重构成树形加法器,而不是链式加法。这样关键路径就从3个加法器延迟降到了2个。你想想看,是不是比你手写pipeline省事多了?

3.4 面积优化:别让芯片“膨胀”

咱们做芯片,成本就是面积。工具默认倾向于时序优先,如果你不干预,面积经常超标。我常用的面积优化指令:

# 设置面积优化力度
set_max_area 0
compile_ultra -area_high_effort_script

# 禁止自动插入大量缓冲
set_dont_use [get_lib_cells *BUF*]
set_fix_hold [all_clocks]

注意,set_max_area 0不是让面积真的为0,而是让工具尽量去压缩。配合compile_ultra里的面积优先选项,效果很明显。

我的小技巧: 面积和时序本来就是互斥的。我一般在第一轮综合时只要时序稍微达标(slack在0附近),第二轮再开面积优化。这样不会因为面积压太狠而把时序搞崩。

3.5 综合报告怎么看?别只会看“绿勾”

综合结束后,工具会吐出好几份报告。我见过不少新人只扫一眼“Timing Met”就打勾走人,结果到后端突然发现内部hold不收敛。你得看这几项:

  • 时序报告:不仅看setup/hold slack,还要看最差的几百条路径的分布。我曾经吃过一次亏:几十条路径slack都在-0.01ns以内,工具说是“可忽略”,但后端布局一跑直接变成-0.15ns。所以建议把约束收严5%~10%。
  • 面积报告:注意组合逻辑和寄存器分别占多少。如果组合逻辑占比异常高,很可能你RTL写成了“巨无霸状态机”。
  • 功耗报告:动态功耗主要看toggle rate。我建议在RTL仿真时抓取真实的翻转率(VCD),不要用默认0.1,那太假了。

3.6 综合中的常见坑

坑1:综合工具不认你的assign语句?
多半是因为你用了wire型变量做多个驱动,或者组合反馈环路。我曾经花了一整天排查一个“神秘信号”,最后发现是assign x = a & b和assign x = c | d写在了两个地方,综合时直接报multidrive。
坑2:时序始终无法收敛?
先检查一下约束里有没有遗漏的时钟,或者有没有误设set_clock_uncertainty。我年轻时在一个项目里把uncertainty设了0.5ns,结果工具死活不满足,后来改成0.1ns就过了——其实后端根本不需要那么大裕量。
一句话总结: 综合 = 在约束的“框”里,让工具自动做出面积、时序、功耗的平衡。你掌握好SDC写法,理解优化引擎原理,就能让你的RTL顺利变成可实现的网表。