第二章:逻辑综合——Design Compiler 基本流程
逻辑综合,说白了就是把 RTL 代码翻译成门级网表。这一步做不好,后面时序收敛基本没戏。我刚开始做后端时,总觉得综合是前端的事,后来被坑过一次才明白——综合质量直接决定了芯片能不能跑起来。
2.1 Design Compiler 的基本流程
DC 的流程其实不复杂,核心就三步:读入、约束、输出。但每一步都有讲究。
第一步:准备数据
你需要准备好 RTL 代码、库文件(.db)、以及约束文件。我个人习惯把库文件路径写在 .synopsys_dc.setup 里,省得每次都要敲。
set search_path {. /home/libs/synopsys}
set target_library {tcbn28hpcplus.db}
set link_library {* tcbn28hpcplus.db dw_foundation.sldb}
第二步:读入设计
用 analyze 和 elaborate 命令。注意,analyze 会检查语法,elaborate 会展开层次结构。我遇到过有人直接用 read_verilog 跳过 analyze,结果综合出来的网表少了一堆逻辑——嗯,千万别省这一步。
analyze -format verilog {top.v sub_module.v}
elaborate top
第三步:施加约束
这是最核心的一步。没有约束,DC 就不知道往哪个方向优化。约束包括时钟定义、输入输出延迟、以及一些特殊路径。
第四步:编译与输出
运行 compile_ultra,然后写出网表和 SDC。
compile_ultra -no_autoungroup
write -format verilog -hierarchy -output top_synth.v
write_sdc top_synth.sdc
2.2 时序约束(SDC)编写
SDC 是综合的灵魂。你想想看,如果约束写错了,综合出来的网表时序全是假的,后面 STA 再怎么修也白搭。
时钟定义
最基本的,定义时钟周期和占空比。注意,create_clock 要指定时钟源端口,别漏了。
create_clock -name clk -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.2 [get_clocks clk]
set_clock_transition -rise 0.1 [get_clocks clk]
输入输出延迟
这个我踩过坑。有一次我把输入延迟设得太紧,结果 DC 拼命优化组合逻辑,面积大了 30%。后来才明白,输入延迟要参考前级芯片的 Tco 和 PCB 走线延迟。
set_input_delay -max 2.5 -clock clk [get_ports data_in]
set_output_delay -max 3.0 -clock clk [get_ports data_out]
个人经验: 对于跨时钟域路径,记得用 set_false_path 或 set_clock_groups 告诉 DC 不要优化。我曾经忘了设 false path,结果 DC 花了两天时间优化一个根本不需要关心的路径——白白浪费了项目时间。
2.3 综合策略与优化技巧
DC 的优化策略有很多,但常用的就几种。我一般会根据设计规模来选择。
| 策略 | 适用场景 | 注意事项 |
|---|---|---|
| compile_ultra | 高性能设计 | 面积会变大,慎用 |
| compile -map_effort high | 中等规模 | 速度与面积的平衡 |
| compile -incremental | 小改动 | 只做增量优化,快 |
优化技巧一:分组约束
对于复杂设计,别把所有路径混在一起。用 group_path 把关键路径和非关键路径分开,DC 会优先优化关键路径。
group_path -name critical -from [all_inputs] -to [all_outputs] -weight 5
优化技巧二:避免过度优化
DC 有时候会为了满足时序而疯狂插入 buffer,导致面积爆炸。我建议设一个 set_max_area 约束,让 DC 在面积和时序之间找平衡。
set_max_area 0
注意,设成 0 表示面积越小越好,但别指望 DC 能自动收敛到最优——它只是尽量做。
避坑指南: 我曾经在一个项目中用了 compile_ultra -retime,结果 DC 把寄存器位置全改了,导致后端布局布线时发现时序完全对不上。后来我学乖了——除非你非常清楚 retime 的影响,否则别轻易用。
2.4 综合后网表质量检查
综合完别急着往后端送。先做几个检查,能省掉后面一堆麻烦。
检查一:网表完整性
用 check_design 看看有没有悬空端口、未连接的线。我遇到过有人综合出来的网表里有个模块的时钟没连上——结果仿真能过,但流片回来芯片不工作。
check_design -summary
检查二:时序报告
看 report_timing 的 slack。如果 slack 是负的,说明时序没满足。但别只看最差路径,也要看看分布情况。如果大部分路径 slack 都很好,只有一两条很差,那可能是约束写错了。
report_timing -max_paths 10 -nworst 5
检查三:面积和功耗
用 report_area 和 report_power 看看综合结果是否合理。如果面积比预期大了 50%,那肯定有问题——要么是约束太紧,要么是代码风格不好。
report_area -hierarchy
report_power -analysis_effort low
核心要点: 综合后的网表质量检查,说白了就是确认三件事——功能对不对、时序能不能跑、面积功耗是否可控。这三件事都过了,才能放心往后端送。
嗯,逻辑综合这部分内容其实挺多的,但核心就是这些。我个人觉得,综合做得好不好,关键看你对约束的理解深不深。别急着跑流程,先把 SDC 写对,后面会省很多事。