数字芯片后端基础操作入门教程 - 第3节

一、逻辑综合到底是什么?

逻辑综合,说白了就是把 RTL 代码翻译成门级网表的过程。你写的是 always @(posedge clk) q <= d;,综合工具会把它变成一堆标准单元——触发器、与非门、或非门,然后连起来。

我刚开始接触这行时,觉得综合就是个“翻译器”。后来踩了坑才明白,它更像一个“优化器”。同样的 RTL,不同的约束能综合出完全不同的电路。面积、速度、功耗,全看你怎么“调教”它。

核心概念:逻辑综合 = RTL → 门级网表 + 时序/面积/功耗优化

综合的三个阶段

DC 做综合,其实分三步走:

  1. 转换(Translation):把 RTL 读进来,转成 GTECH 格式的通用网表。这一步不优化,就是直译。
  2. 逻辑优化(Logic Optimization):开始动刀了。冗余逻辑去掉、共用逻辑合并、路径重定时……说白了就是让电路更“聪明”。
  3. 映射(Mapping):把优化后的电路映射到目标工艺库里的标准单元。这一步决定了最终用哪些门、多大驱动。

我见过不少新手,跑完综合发现面积爆炸,回头一看——映射阶段没给面积约束,工具默认往快了推。嗯,这里要注意。

逻辑综合三阶段流程 阶段1:转换 RTL → GTECH 通用网表 阶段2:逻辑优化 冗余消除 · 结构重构 阶段3:映射 工艺库单元绑定 输入:RTL + 约束 + 库 输出:门级网表 三个阶段顺序执行,但优化和映射之间可以迭代——DC 会反复调整直到满足约束

二、DC 的基本使用流程

DC 怎么跑?我习惯用 Tcl 脚本驱动。图形界面当然也有,但脚本可重复、可版本管理,项目里没人点 GUI 的。

启动 DC

dc_shell -f run.tcl | tee dc.log

加 tee 是为了把屏幕输出同时写进日志。我吃过亏——跑了一晚上综合,第二天发现 log 没存,报错信息全丢了。从那以后,tee 成了肌肉记忆。

读入设计与链接库

这是综合的第一步,也是最容易出问题的一步。你需要告诉 DC 三件事:

  • 你的设计文件在哪(RTL、网表、IP)
  • 目标工艺库是什么(比如台积电 28nm 的 tcbn28hpcplusbwp12t30p140ss_ccs.db)
  • 链接库有哪些(综合库、IO 库、Memory Compiler 生成的库)

典型脚本长这样:

# 设置库路径
set target_library "tcbn28hpcplusbwp12t30p140ss_ccs.db"
set link_library   "* $target_library dw_foundation.sldb"

# 设置搜索路径
set search_path    ". /home/techlib/tsmc28nm /home/design/rtl"

# 读入设计
analyze -format verilog {top.v sub1.v sub2.v}
elaborate top
current_design top
link
注意:link_library 里的 * 不能省。它表示“先搜已经读入的设计”,没有它,DC 会找不到顶层模块。

我见过一个同事,link_library 漏了 *,结果 DC 报了一堆 unresolved reference。他查了两小时,最后发现是少了个星号。这种坑,踩一次就记住了。

综合策略:两种路线

DC 提供两种综合策略,我根据项目阶段选:

策略 命令 适用场景 特点
Top-down compile_ultra 小模块、单次综合 全局优化好,但内存消耗大
Bottom-up 分模块 compile 再整合 大规模芯片、多时钟域 可控性强,适合团队并行

我个人习惯:芯片规模小于 50 万门时用 top-down,简单粗暴。超过 100 万门,老老实实 bottom-up,不然 DC 能把服务器内存吃光。

三、约束怎么写?

约束是综合的灵魂。没有约束,DC 不知道你要快还是要省。我刚开始做综合时,随便写了几个时钟周期就跑了,结果后仿时序一塌糊涂。

时钟约束

时钟是时序的基准。最基本的写法:

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]

-period 10 表示周期 10ns,也就是 100MHz。uncertainty 是时钟抖动 + 裕量,我一般设周期的 2%~5%。

经验值:28nm 工艺下,uncertainty 设 0.2~0.3ns 比较合理。设太小,后仿容易崩;设太大,面积白白浪费。

输入输出延迟

芯片不是孤岛。输入信号从片外进来,输出信号要送到片外,你得告诉 DC 外部延迟:

set_input_delay  -max 2.5 [get_ports data_in] -clock clk
set_output_delay -max 3.0 [get_ports data_out] -clock clk

这些值怎么定?看芯片规格书里的接口时序图。我曾经偷懒随便填了个 1ns,结果流片回来接口对不上——嗯,那次加班加得挺惨。

面积与功耗约束

时序优先,面积和功耗次之。但如果你不设,DC 会往死里优化时序:

set_max_area 0
set_max_dynamic_power 0.5
set_max_leakage_power 0.1

set_max_area 0 的意思是“面积越小越好”。注意,这只是一个指导方向,DC 不会真的追求零面积。

四、综合脚本的完整骨架

把上面这些串起来,一个完整的综合脚本大概长这样:

# ============================================
# DC 综合脚本 - top.syn.tcl
# ============================================

# 1. 库设置
set target_library  "tcbn28hpcplusbwp12t30p140ss_ccs.db"
set link_library    "* $target_library dw_foundation.sldb"
set symbol_library  "tcbn28hpcplusbwp12t30p140ss_ccs.sdb"
set search_path     ". /home/techlib/tsmc28nm /home/design/rtl"

# 2. 读入设计
analyze -format verilog {top.v sub1.v sub2.v}
elaborate top
current_design top
link

# 3. 时钟约束
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]

# 4. IO 约束
set_input_delay  -max 2.5 [all_inputs] -clock clk
set_output_delay -max 3.0 [all_outputs] -clock clk

# 5. 面积/功耗约束
set_max_area 0
set_max_dynamic_power 0.5

# 6. 综合
compile_ultra

# 7. 保存结果
write -format verilog -hierarchy -output ./output/top_syn.v
write_sdc -output ./output/top_syn.sdc
write_sdf -version 2.1 -output ./output/top_syn.sdf

# 8. 生成报告
report_timing > ./output/timing.rpt
report_area   > ./output/area.rpt
report_power  > ./output/power.rpt
关键输出文件:
  • top_syn.v — 综合后的门级网表
  • top_syn.sdc — 时序约束文件(给后端用)
  • top_syn.sdf — 标准延迟格式文件(给后仿用)
  • timing.rpt / area.rpt / power.rpt — 报告

五、常见问题与避坑

问题1:综合后时序不满足

先看 critical path 在哪。是组合逻辑太长?还是扇出太大?我一般先调 compile_ultra -retime,让 DC 自动重定时。还不行,就回去改 RTL。

问题2:面积爆炸

检查是不是 set_max_area 没设。或者约束太紧,DC 用了大量大驱动单元。试试 compile_ultra -no_autoungroup,关掉自动合并层次。

问题3:DC 跑不动

内存不够?试试 bottom-up 策略。或者加 set_host_options -max_cores 8 开多核。我遇到过 200 万门的设计,单核跑了 12 小时,开 8 核后 3 小时搞定。

避坑:综合前一定要跑 lint 检查。我曾经拿了一个有语法错误的 RTL 直接跑综合,DC 报了一堆看不懂的错,浪费半天。后来养成习惯——先 lint,再综合。

逻辑综合是后端流程的起点。网表质量好不好,直接决定后面布局布线顺不顺。我个人觉得,花时间把约束写清楚、把策略选对,比后面反复迭代省时得多。嗯,今天就先聊到这。