4、逻辑综合入门:DC(Design Compiler)基本流程、读取RTL与约束、综合策略(Top-down vs Bottom-up)
好,咱们进入第四讲。逻辑综合,说白了就是把RTL代码翻译成门级网表。这一步是衔接前端设计和后端物理实现的桥梁。我个人觉得,这是整个流程里最需要「感觉」的一环——你不仅要懂代码,还得懂工艺库,懂时序。
今天咱们就聊聊DC(Design Compiler)的基本玩法。我会把流程拆开,一步步讲清楚。
4.1 DC基本流程:三步走
DC的流程,我习惯分成三步:
- 读入设计——把RTL代码和库文件加载进来
- 施加约束——告诉工具你的芯片要跑多快、功耗多少
- 综合优化——工具根据约束把RTL映射到门级网表
嗯,听起来简单。但每一步都有坑。我曾经在一个项目里,因为库文件路径配错了,综合出来的网表面积大了30%。排查了两天才找到原因。所以,每一步都要仔细。
4.2 读取RTL与约束
4.2.1 读取RTL
DC支持多种读入方式。我个人最常用的是read_verilog和read_vhdl。举个例子:
# 设置库路径
set target_library "typical.db"
set link_library "* typical.db"
set symbol_library "generic.sdb"
# 读入RTL
read_verilog {top.v sub_module1.v sub_module2.v}
# 或者用 analyze & elaborate
analyze -format verilog {top.v sub_module1.v sub_module2.v}
elaborate top
这里有个细节:read_verilog是直接读,而analyze + elaborate会做语法检查和模块解析。我建议新手用后者,因为报错信息更友好。你想想看,如果代码里有个端口没对上,read_verilog可能直接崩掉,而elaborate会告诉你具体是哪个模块、哪个信号出了问题。
link 命令检查一下模块是否全部解析成功。如果出现 "Unresolved reference" 的警告,说明有模块没找到,赶紧回头检查。
4.2.2 施加约束
约束是综合的灵魂。没有约束,工具就不知道往哪个方向优化。我见过不少新手,上来就写 create_clock -period 10 [get_ports clk],然后就不管了。这其实远远不够。
一个完整的约束文件,至少包含以下几类:
| 约束类型 | 常用命令 | 说明 |
|---|---|---|
| 时钟约束 | create_clock, set_clock_uncertainty |
定义时钟周期、占空比、抖动 |
| 输入延迟 | set_input_delay |
外部信号到达芯片引脚的时间 |
| 输出延迟 | set_output_delay |
芯片输出到外部寄存器的建立时间要求 |
| 伪路径 | set_false_path |
告诉工具某些路径不需要时序检查 |
| 多周期路径 | set_multicycle_path |
某些路径需要多个时钟周期才能稳定 |
举个例子,一个典型的约束文件长这样:
# 时钟约束
create_clock -name clk -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.2 [get_clocks clk]
set_clock_uncertainty -hold 0.1 [get_clocks clk]
# 输入延迟
set_input_delay -clock clk -max 3 [get_ports data_in]
set_input_delay -clock clk -min 1 [get_ports data_in]
# 输出延迟
set_output_delay -clock clk -max 4 [get_ports data_out]
set_output_delay -clock clk -min 1 [get_ports data_out]
# 伪路径:跨时钟域
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
4.3 综合策略:Top-down vs Bottom-up
综合策略的选择,直接影响项目进度和结果质量。我经历过两种策略的优劣,这里分享一下。
4.3.1 Top-down 综合
Top-down,就是把整个芯片当作一个整体,一次性综合。DC会从顶层开始,自动处理所有子模块。
优点:
- 全局优化效果好——工具能看到所有模块之间的交互
- 脚本简单——只需要一个约束文件,一个脚本
- 适合小规模设计(百万门以内)
缺点:
- 运行时间长——设计越大,综合越慢
- 内存消耗大——DC需要把整个设计加载到内存
- 调试困难——如果某个子模块出问题,很难定位
我记得有一次,一个同事用Top-down综合一个500万门的设计,跑了整整三天。结果因为一个约束写错了,全部重来。嗯,那滋味不好受。
4.3.2 Bottom-up 综合
Bottom-up,就是先综合每个子模块,生成各自的网表,然后再把网表拼起来做顶层综合。
优点:
- 并行度高——多个模块可以同时综合,节省时间
- 内存占用小——每次只处理一个模块
- 调试方便——哪个模块出问题,单独修就行
缺点:
- 全局优化受限——工具看不到跨模块的路径
- 脚本复杂——需要为每个模块写单独的约束和脚本
- 接口约束要精确——子模块的输入输出延迟必须算准
我个人的习惯是:小设计用Top-down,大设计用Bottom-up。具体来说,如果设计规模超过200万门,或者有多个时钟域、多个电压域,我会优先考虑Bottom-up。
4.4 综合后的检查
综合完成后,别急着往后端送。先做几个检查:
- 时序报告——看有没有违例路径,特别是建立时间和保持时间
- 面积报告——看总面积是否在预算内
- 功耗报告——看动态功耗和静态功耗是否达标
- 网表检查——用
check_design命令检查网表是否有悬空、短路等问题
我每次都会跑一遍 report_qor,这个命令会汇总所有关键指标。如果看到某个指标异常,比如面积突然比预期大了20%,我会立刻回头查约束和代码。
4.5 小结
逻辑综合,说白了就是「翻译+优化」。翻译不难,难的是优化。而优化的关键,在于约束是否合理、策略是否得当。
我建议你从一个小模块开始练手,比如一个简单的SPI接口。先跑通Top-down流程,再试试Bottom-up。等把DC的脚本写熟了,你会发现,综合其实没那么神秘。
下一讲,咱们聊聊综合后的网表如何做形式验证。嗯,那又是一个容易踩坑的地方。