EDA后端从基础到实战 - 第4节
DC启动与脚本结构:从零开始跑综合
说实话,很多新手第一次打开Design Compiler时,面对那个黑乎乎的shell窗口,心里是发怵的。我当年也一样,对着dc_shell-t敲了半天,不知道下一步该干嘛。其实DC的启动没那么玄乎,说白了就两种方式:图形界面和命令行模式。
DC的两种启动方式
图形界面适合刚入门时看看综合结果,真正干活还得靠命令行。我个人习惯用dc_shell-t模式,脚本化运行,方便回归和版本管理。
# 命令行模式启动
dc_shell-t
# 图形界面启动
design_vision
嗯,这里要注意:dc_shell-t是Tcl模式,支持Tcl脚本语法。如果你用dc_shell,那是旧的Synopsys模式,现在基本淘汰了。我建议你直接上dc_shell-t,别走弯路。
脚本结构:我的标准模板
一个完整的DC综合脚本,我通常会分成四个部分:
- 库文件设置 — 告诉DC用哪个工艺库
- 读入设计 — 把RTL代码喂给DC
- 时序约束 — 告诉DC你的频率目标
- 综合与输出 — 跑综合,吐出门级网表
下面是我常用的脚本框架,你可以直接拿去改:
# ============================================
# Part 1: 库文件设置
# ============================================
set target_library "scc40nll_hs_rvt_tt_1p1v_25c.db"
set link_library "* $target_library"
set symbol_library "scc40nll_hs_rvt.sdb"
# ============================================
# Part 2: 读入设计
# ============================================
analyze -format verilog {./rtl/top.v ./rtl/sub_module.v}
elaborate top
current_design top
link
# ============================================
# Part 3: 时序约束
# ============================================
create_clock -name clk -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.2 [get_clocks clk]
set_input_delay -max 2.0 -clock clk [get_ports data_in]
set_output_delay -max 2.5 -clock clk [get_ports data_out]
# ============================================
# Part 4: 综合与输出
# ============================================
compile_ultra
write -format verilog -output ./output/top_synth.v
write_sdc ./output/top_synth.sdc
我在项目中遇到过一个问题:有人把target_library和link_library搞混了。target_library是DC用来映射门电路的库,link_library是解析实例化模块用的。如果你只设了target_library没设link_library,DC会报一堆"找不到模块"的错误。
时序约束设置:别让芯片跑不起来
时序约束是综合中最关键的一步。你想想看,如果约束设错了,综合出来的电路要么太松(浪费面积),要么太紧(根本跑不到)。
时钟约束:核心中的核心
时钟约束决定了你的芯片能跑多快。最基本的设置就两个:时钟周期和时钟不确定性。
# 设置时钟周期为10ns,对应100MHz
create_clock -name clk -period 10 [get_ports clk]
# 时钟不确定性:包含jitter和margin
set_clock_uncertainty -setup 0.2 [get_clocks clk]
set_clock_uncertainty -hold 0.05 [get_clocks clk]
为什么setup和hold的不确定性不一样?setup uncertainty要留大一点,因为工艺偏差、电压波动主要影响setup。hold uncertainty可以小一些,hold问题更多是局部偏差。我曾经因为把hold uncertainty设得太大,导致综合结果面积暴增20%,后来才发现是这里的问题。
输入输出延迟:告诉DC外面的世界
芯片不是孤立存在的,它要和外部电路交互。输入延迟告诉DC数据从外部时钟沿到芯片引脚的延迟,输出延迟告诉DC数据从芯片引脚到外部寄存器的延迟。
# 输入延迟:外部数据在时钟沿后2ns到达
set_input_delay -max 2.0 -clock clk [get_ports data_in]
set_input_delay -min 0.5 -clock clk [get_ports data_in]
# 输出延迟:外部电路需要数据在时钟沿前2.5ns准备好
set_output_delay -max 2.5 -clock clk [get_ports data_out]
set_output_delay -min 0.3 -clock clk [get_ports data_out]
这里有个坑:min delay对应的是hold检查,max delay对应的是setup检查。很多人只设max不设min,结果hold违例一堆。我建议你两个都设,哪怕min设个0.1ns也行。
面积与功耗优化策略:鱼和熊掌怎么兼得
面积、功耗、性能,这三者永远在打架。DC提供了多种优化策略,关键看你怎么取舍。
面积优化:别让芯片太大
DC默认会优先优化时序,面积是次要目标。如果你想压面积,可以试试这些方法:
# 设置面积约束
set_max_area 0
# 启用面积优化
compile_ultra -area_high_effort_script
set_max_area 0的意思是"能压多小压多小"。但要注意,这可能会牺牲时序。我一般先跑一版不加面积约束的,看看时序余量,再决定要不要压面积。
功耗优化:低功耗设计的实战技巧
功耗优化分动态功耗和静态功耗。DC的compile_ultra自带了一些低功耗选项:
# 启用功耗优化
set_power_prediction true
compile_ultra -gate_clock -retime
# 时钟门控:最有效的低功耗手段
set_clock_gating_style -sequential_cell latch -positive_edge_logic {and} -negative_edge_logic {or}
insert_clock_gating
时钟门控是降低动态功耗的利器。我在一个项目中做过对比:不加时钟门控,功耗是120mW;加了之后降到85mW,降幅接近30%。代价是面积增加了不到5%,完全值得。
综合报告解读:别被数字忽悠了
综合跑完后,DC会生成一堆报告。新手往往只看"有没有违例",老手会从报告里读出更多信息。
时序报告:看余量,别只看违例
时序报告里最重要的不是WNS(最差负余量),而是TNS(总负余量)。WNS告诉你最差的那条路径有多差,TNS告诉你所有违例路径的总和。如果WNS是-0.5ns但TNS只有-1ns,说明只有一两条路径有问题,修起来不难。如果TNS是-100ns,那问题就大了。
# 生成时序报告
report_timing -delay_type max -nworst 10 -path_type full_clock
面积报告:看结构,别只看数字
面积报告会列出每个模块用了多少门。我习惯先看组合逻辑和时序逻辑的比例。如果组合逻辑占比超过70%,说明设计可能偏控制逻辑;如果时序逻辑占比高,说明寄存器多,可能是数据通路。
# 生成面积报告
report_area -hierarchy
功耗报告:看动态和静态的占比
功耗报告会告诉你动态功耗和静态功耗各占多少。在先进工艺下(28nm以下),静态功耗占比越来越高。如果你发现静态功耗占比超过40%,可以考虑用多阈值电压库来优化。
# 生成功耗报告
report_power -hierarchy
知识体系总览
下面这张图总结了本章的核心内容,你可以把它当作一个快速索引:
这张图把DC综合的四个核心模块串起来了。启动和脚本是基础,时序约束是灵魂,面积功耗优化是手艺,报告解读是检验标准。四个模块缺一不可。
好了,这一章的内容就到这里。记住一句话:DC综合不是跑一遍就完事的,它是一个迭代优化的过程。多跑几版,多对比报告,慢慢你就能找到感觉了。
可可精品资料库