逻辑综合进阶:DC综合策略、时序驱动综合、面积与功耗的权衡艺术
逻辑综合,说白了就是把 RTL 代码翻译成门级网表。很多人觉得这步就是跑个脚本完事,其实不然。我做了这么多年后端,见过太多综合没做好、后面物理设计怎么修都修不回来的案例。今天咱们就聊聊 DC 综合里那些真正值钱的经验。
核心观点:综合不是一键生成的魔术,而是时序、面积、功耗三者的动态博弈。你给工具的约束越精准,工具回报你的结果就越可靠。
一、DC 综合策略:别让工具替你决策
DC 综合的第一步,是搞清楚你的设计目标。是做高性能计算,还是低功耗 IoT?目标不同,策略天差地别。
我个人习惯把综合策略分成三类:
| 策略类型 | 适用场景 | 核心约束 | 典型代价 |
|---|---|---|---|
| 时序优先 | CPU、GPU、高速接口 | 时钟周期紧、setup slack 为正 | 面积增大 10%~30%,功耗上升 |
| 面积优先 | MCU、低成本芯片 | 门数最小化、cell 密度控制 | 时序余量小,后期 ECO 风险高 |
| 功耗优先 | 可穿戴、电池供电设备 | 动态功耗 + 静态功耗双约束 | 性能折损,可能需要多阈值库 |
你想想看,如果拿时序优先的策略去做 IoT 芯片,功耗可能直接翻倍。反过来,用面积优先的策略做 CPU,流片回来大概率跑不到目标频率。所以,策略选择必须跟产品定义对齐。
我的经验:在项目启动阶段,我会先跑一版「宽松综合」——不加太紧的约束,看看工具默认能推到什么程度。这版结果就是你的 baseline,后面所有优化都拿它做对比。
二、时序驱动综合:约束才是灵魂
时序驱动综合,说白了就是让工具知道你的时序目标在哪。很多新手写约束就写个时钟周期,其他全默认。嗯,这里要注意——工具默认的 input/output delay 往往是 0,这跟实际电路差太远了。
我建议至少包含以下约束:
- 时钟定义:周期、波形、抖动、不确定性
- 输入延迟:外部器件到芯片引脚的路径延迟
- 输出延迟:芯片引脚到外部器件的路径延迟
- 伪路径:跨时钟域、异步复位等不需要 STA 检查的路径
- 多周期路径:明确允许超过一个时钟周期的路径
举个例子,一个典型的时钟约束脚本长这样:
create_clock -name clk -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.3 [get_clocks clk]
set_clock_uncertainty -hold 0.1 [get_clocks clk]
set_input_delay -max 2.0 -clock clk [get_ports data_in]
set_output_delay -max 1.5 -clock clk [get_ports data_out]
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
set_multicycle_path -setup 2 -from [get_pins reg_a/Q] -to [get_pins reg_b/D]
我曾经遇到过一个项目,综合后时序报告全绿,结果后端布局布线后 setup 违例一大堆。查了半天,发现是 clock uncertainty 设得太小,没给后端留余量。从那以后,我习惯在综合阶段就把 uncertainty 设到周期的 5%~10%,给后端留足空间。
避坑指南:我曾经在综合阶段把 input/output delay 设得太紧,结果工具为了满足约束疯狂插入 buffer,面积暴涨 40%。后来发现,那些路径在后端其实可以走更短的线。所以,综合阶段的约束要「合理保守」,别过度约束。
三、面积与功耗的权衡:没有免费的午餐
面积和功耗,本质上是一对矛盾。你想想看,要降低动态功耗,就得用低阈值电压的 cell,但这些 cell 漏电大,静态功耗反而高。反过来,用高阈值电压的 cell 漏电小,但速度慢,为了满足时序就得插更多 buffer,面积又上去了。
DC 提供了几种常用的优化手段:
- 多阈值库混合使用:关键路径用低阈值(快但漏电大),非关键路径用高阈值(慢但漏电小)
- 门级功耗优化:DC 的 compile_ultra 支持 -gate_clock 和 -power 选项
- 时钟门控:自动插入 clock gating,减少无效翻转
- 操作数隔离:对不使用的运算模块做输入隔离,降低动态功耗
我个人的做法是:先跑一版不加功耗优化的综合,拿到面积和时序的 baseline。然后逐步打开功耗优化选项,观察面积和时序的变化。如果面积增加超过 15%,我就得重新评估——功耗省了 10%,面积涨了 20%,这买卖不一定划算。
权衡公式(经验值):每降低 1mW 动态功耗,大约需要付出 5%~10% 的面积代价。如果面积代价超过 15%,建议换工艺或调整架构,别硬靠综合工具扛。
四、综合流程中的关键检查点
综合不是跑完就完事了。我习惯在每个检查点停下来看看结果,别等最后才发现问题。
- 约束检查:跑 report_timing 和 report_constraint,确认所有路径都有约束
- 时序检查:看 setup 和 hold 的 worst negative slack(WNS),别只看 total negative slack(TNS)
- 面积检查:对比综合前后的门数变化,确认没有异常膨胀
- 功耗检查:跑 report_power,看动态功耗和静态功耗的比例
- 网表质量检查:确认没有悬空引脚、驱动强度不足、扇出过大等问题
我记得有一次,综合后面积比预期大了 30%,查了半天发现是某个模块的时钟门控没打开,工具为了满足时序插了大量 buffer。打开 clock gating 后,面积直接降了 25%。所以,检查点不是走形式,是真的能救命。
五、知识体系总览
下面这张图是我自己总结的逻辑综合知识体系,你可以把它当作综合阶段的「导航地图」:
六、综合后的检查清单
综合跑完,别急着往后端交网表。我习惯再过一遍这个清单:
- 时序收敛了吗?WNS 至少为正,最好有 5% 以上的余量
- 面积合理吗?跟预期偏差不超过 10%
- 功耗达标吗?动态功耗和静态功耗分别跟 spec 对比
- 网表有 warning 吗?重点关注 unconstrained paths、multiple driven nets
- DFT 插入了吗?扫描链、BIST 等测试结构是否完整
我的习惯:综合结束后,我会把网表和约束一起交给后端团队。同时附上一份综合报告,标注出哪些路径是「硬骨头」——就是 slack 很小、后端需要特别关注的路径。这样前后端配合起来效率高很多。
逻辑综合这门手艺,说白了就是跟工具打交道。你给工具好的约束,工具还你好的网表。你给工具模糊的目标,工具就给你一个「差不多能用」的结果——但「差不多」在芯片里往往意味着「差很多」。多花点时间在综合阶段,后面物理设计能省一半的力气。