4、DC综合实战:时序约束与综合策略
好,咱们今天来聊聊DC综合里最核心的部分——约束与策略。说实话,很多工程师觉得DC就是个跑脚本的工具,但真正决定芯片能不能用的,恰恰是这些约束写得好不好。我见过太多项目,因为一条set_input_delay写错,导致后端修时序修到崩溃。
4.1 时序约束:芯片的“交通规则”
时序约束说白了,就是告诉工具你的芯片要跑多快,信号什么时候来,什么时候走。没有约束,DC就像没地图的司机,乱开一气。
4.1.1 create_clock:定义你的时钟
这是最基础的约束。我习惯先问自己几个问题:时钟频率多少?占空比多少?有没有分频或倍频?
# 创建一个50MHz的主时钟,占空比50%
create_clock -name clk -period 20 [get_ports clk]
# 创建一个分频时钟,基于主时钟
create_generated_clock -name clk_div2 -source [get_ports clk] \
-divide_by 2 [get_pins U_FF/Q]
4.1.2 set_input_delay / set_output_delay:定义边界
这个坑我踩过。有一次项目,我忘了设input_delay,结果DC把输入路径优化得特别激进,后端一跑,发现根本收不住。为什么呢?因为工具默认输入延迟为0,它以为信号一开机就到了。
# 假设外部器件输出延迟为2ns,时钟周期10ns
set_input_delay -clock clk -max 2 [get_ports data_in]
set_input_delay -clock clk -min 0.5 [get_ports data_in]
# 输出延迟:外部器件需要3ns的建立时间
set_output_delay -clock clk -max 3 [get_ports data_out]
4.2 面积与功耗约束:平衡的艺术
你想想看,时序、面积、功耗,这三者就像跷跷板。压了时序,面积就涨;省了功耗,时序可能崩。我一般会先满足时序,再优化面积和功耗。
4.2.1 面积约束
DC默认会尽量优化面积,但你可以给它一个目标。不过说实话,面积约束更多是“别超过这个数”,而不是“必须达到这个数”。
# 设置面积目标为10000 um²
set_max_area 10000
嗯,这里要注意:如果你设得太紧,DC可能会牺牲时序来换面积。我建议先跑一版不加面积约束的,看看自然面积是多少,再往下压10%-20%。
4.2.2 功耗约束
功耗现在越来越重要了。尤其是移动芯片,功耗高了直接废掉。DC支持多种功耗优化方式,最常用的是动态功耗和漏电功耗的权衡。
# 设置动态功耗目标
set_max_dynamic_power 0.5
# 设置漏电功耗目标
set_max_leakage_power 0.01
# 或者用更高级的:多阈值电压库选择
set_operating_conditions -library slow -analysis_type on_chip_variation
4.3 综合策略:Top-Down vs Bottom-Up
这是个老生常谈的问题。我两种都用过,各有优劣。
4.3.1 Top-Down(自顶向下)
把整个芯片当成一个模块来综合。优点是简单,工具能看到全局,优化效果好。缺点是——如果芯片太大,DC会跑不动,或者跑几天几夜。
# Top-Down综合,直接读顶层
read_verilog top.v
current_design top
link
uniquify
compile_ultra
4.3.2 Bottom-Up(自底向上)
先综合每个子模块,再拼起来。优点是快,可以并行跑,每个模块独立优化。缺点是——模块间的接口时序容易出问题,需要额外花时间做“接口约束”。
# 先综合子模块A
read_verilog sub_a.v
current_design sub_a
create_clock -name clk -period 10 [get_ports clk]
set_input_delay -clock clk 2 [get_ports data_in_a]
compile_ultra
write -f ddc -hierarchy -output sub_a.ddc
# 再综合子模块B,类似操作
# 最后在顶层把DDC文件读进来,只做连接和顶层逻辑
read_ddc sub_a.ddc
read_ddc sub_b.ddc
current_design top
link
compile_ultra -top
我个人习惯:对于新项目,先用Bottom-Up跑一版,看看每个模块的时序余量。如果某个模块特别紧张,再单独优化它。等所有模块都收敛了,再用Top-Down做一次全局优化——但只跑增量综合,不从头跑。
4.4 分析综合报告:读懂DC的“体检报告”
综合跑完,你得会看报告。不然跑得再快也没用。
4.4.1 面积报告
面积报告告诉你用了多少门,多少内存,多少物理单元。
report_area -hierarchy -physical
我一般关注三个数:组合逻辑面积、时序逻辑面积、总物理面积。如果组合逻辑占比太高,说明你的设计可能太“组合”了,可以考虑加流水线。
4.4.2 时序报告
这是最重要的报告。DC会列出所有违反setup和hold的路径。
report_timing -delay_type max -nworst 10 -significant_digits 3
看时序报告时,我习惯先看WNS(最差负余量)和TNS(总负余量)。如果WNS是负的,说明有路径没满足setup。如果TNS很大,说明很多路径都有问题。
| 指标 | 含义 | 我的经验值 |
|---|---|---|
| WNS | 最差路径的余量 | 应 > 0,最好 > 0.1ns |
| TNS | 所有违例路径的余量总和 | 应 < 1ns,否则问题较多 |
| FEP | 违例路径数量 | 应 < 100,否则需重新约束 |
4.4.3 功耗报告
功耗报告会告诉你动态功耗和静态功耗各占多少。
report_power -hierarchy -analysis_effort high
我一般先看动态功耗占比。如果超过70%,说明你的设计翻转率太高,可以考虑加门控时钟或降低电压。如果静态功耗占比高,说明用了太多低阈值单元,可以换成高阈值的。
4.5 实战避坑指南
最后,分享几个我踩过的坑:
- 时钟没定义好:有一次我忘了定义生成时钟,结果DC把所有路径都按同一个时钟分析,hold时序全崩了。后来花了三天才查出来。
- 输入延迟设反了:max和min搞反,导致setup和hold检查错位。嗯,这个错误我犯过两次,后来我写了个脚本自动检查。
- 面积约束设太紧:DC为了省面积,把关键路径上的buffer都优化掉了,结果时序变差。我后来学乖了,先不加面积约束跑一版,看看自然面积。
- Bottom-Up接口没对齐:两个模块的接口时序约束不一致,导致顶层综合时出现大量违例。解决办法是统一写一个接口约束文件,所有模块共用。
好了,这一章的内容就到这里。下一章我们会讲形式验证和逻辑等价性检查,那是确保你综合前后功能一致的关键一步。记得把今天的约束脚本练熟,这是基本功。