逻辑综合入门:DC工具、综合策略与时序约束基础

逻辑综合,说白了就是把 RTL 代码翻译成门级网表。你写的是 always @(posedge clk),DC 会把它变成一堆与门、或门、触发器的连线。这个过程听起来简单,但里面门道不少。我刚开始做后端时,总觉得综合是前端的事,后来发现——综合做不好,后端跑死也修不了时序。

DC 工具到底在干什么?

Design Compiler 是 Synopsys 家的旗舰综合工具。它的核心任务就三件事:

  • 翻译:把 Verilog/VHDL 转成 GTECH 网表(通用工艺无关的网表)
  • 映射:把 GTECH 网表映射到目标工艺库的单元上
  • 优化:在面积、时序、功耗之间找平衡

我见过不少新人,上来就丢一个 compile 命令,然后等着出结果。嗯,这样也能跑,但出来的网表质量嘛……你想想看,DC 有几十个优化选项,你不告诉它重点优化什么,它只能按默认策略来。默认策略往往不是你要的。

DC 综合的输入输出

  • 输入:RTL 代码 + 工艺库 + 时序约束
  • 输出:门级网表 + 时序报告 + 面积报告
  • 中间产物:GTECH 网表、DDC 文件(可增量综合)

三种综合策略,你选哪种?

DC 提供三种综合策略,我按实际项目中的使用频率排个序:

策略 适用场景 我个人的看法
Top-down 小模块、单芯片 简单直接,但大芯片跑不动
Bottom-up 大芯片、多模块 我最常用的方式,灵活可控
Incremental ECO、微调 修时序时救命用的

Top-down 综合:把整个芯片当黑盒,一次跑完。好处是全局优化好,坏处是——我遇到过 500 万门的模块,Top-down 跑了两天没出结果。后来拆成 Bottom-up,半天搞定。

Bottom-up 综合:先综合每个子模块,设好边界约束,再拼起来。这样做的好处是:

  • 每个模块可以单独调优
  • 并行跑,节省时间
  • 某个模块改了,只重跑那个模块就行

Incremental 综合:在已有网表上做增量优化。我曾经有个项目,时序差了 20ps,重新跑一遍综合要 6 小时。用 Incremental 模式,只花了 40 分钟就修好了。

我的建议:新项目先用 Top-down 跑一版,看看整体时序和面积。然后切到 Bottom-up 做精细调优。最后用 Incremental 收尾。

时序约束,综合的灵魂

没有约束的综合,就像没有地图的导航。DC 不知道你要跑多快,它只会往面积最小的方向优化。结果就是——网表面积很小,但时序一塌糊涂。

最基本的时序约束包括:

# 时钟定义
create_clock -name clk -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.2 [get_clocks clk]
set_clock_latency -source 0.5 [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/CK] -to [get_pins reg_b/D]

这里我重点说几个容易踩坑的地方:

时钟不确定性:很多人只设 setup 的 uncertainty,忘了 hold。我曾经吃过这个亏——综合时 hold 没设 uncertainty,结果后端修 hold 修到崩溃。

输入输出延迟:这个值怎么设?我的经验是:看芯片的接口协议。比如 DDR 接口,你要算清楚 PCB 走线延迟、片外负载、时钟偏斜。别随便填个 2ns 就完事。

伪路径:跨时钟域的路径,一定要设 false_path。不设的话,DC 会花大量时间去优化这些根本不需要关心的路径,浪费资源。

注意:多周期路径的约束,setup 和 hold 要分开设。很多人只设了 setup 的 multicycle,hold 没设,结果 hold 检查还是按单周期来算,导致误报。

综合流程,一步都不能少

我总结了一套综合流程,项目里用了好几年:

  1. 读入设计:read_verilog 或 read_vhdl,记得加上 -sv 选项读 SystemVerilog
  2. 链接库:link 命令,检查所有实例化是否都能找到对应的库单元
  3. 加约束:时钟、IO 延迟、伪路径、多周期路径
  4. 选择策略:Top-down / Bottom-up / Incremental
  5. 编译:compile_ultra 或 compile
  6. 分析结果:看时序报告、面积报告、功耗报告
  7. 输出网表:write -f verilog 写出网表,write_sdc 写出约束

这里面最容易出问题的是第 2 步。我遇到过好几次,link 时报错说某个模块找不到。查了半天,原来是库路径没设对。所以我现在习惯在脚本开头就设好 search_path 和 target_library。

综合后的检查清单

综合跑完,别急着往后端交。先过一遍这个清单:

  • 时序报告里有没有 setup 违例?hold 违例?
  • 面积是不是在预期范围内?
  • 有没有未约束的路径?report_analysis_coverage 看一下
  • 有没有悬空端口?report_bus 检查一下
  • 时钟门控有没有正确插入?

我一般会跑一个 check_design,看看有没有设计规则违例。这个命令会检查最大扇出、最大电容、最大转换时间等。如果这些不过,后端会很难受。

小技巧:综合完成后,用 write -f ddc 保存一份 DDC 文件。这样下次做增量综合时,可以直接读 DDC,不用重新编译,能省不少时间。

知识结构图:逻辑综合的核心脉络

逻辑综合核心知识结构 逻辑综合 (DC) DC 工具介绍 综合策略 时序约束基础 RTL → GTECH → 门级网表 输入:RTL + 库 + 约束 输出:网表 + 报告 Top-down:全局优化 Bottom-up:模块化并行 Incremental:增量微调 时钟定义 + 不确定性 输入/输出延迟 伪路径 + 多周期路径 核心流程:读入设计 → 链接库 → 加约束 → 选择策略 → 编译 → 分析 → 输出 综合后检查:时序违例、面积、未约束路径、悬空端口、设计规则

这张图把逻辑综合的核心脉络串起来了。你从 DC 工具出发,往左走是工具本身的能力,往中间走是策略选择,往右走是约束设置。三者缺一不可。

最后说一句:综合不是一次就能搞定的。我一般会迭代 3-5 轮,每轮看报告、调约束、改代码。别指望一次跑出完美结果,那不太现实。