第二讲:SDC约束入门——文件结构、时钟定义与时钟组
大家好,欢迎来到时序约束实战的第二讲。
上一讲我们聊了STA的基本概念。今天,咱们直接上手干活——学SDC。
SDC,全称Synopsys Design Constraints。说白了,就是告诉工具:你的芯片要在什么频率下工作,哪些路径要特别照顾。我刚开始学的时候,觉得这玩意儿不就是写几行命令吗?后来踩了坑才发现,SDC写得好不好,直接决定了芯片能不能跑起来。
一、SDC文件长什么样?
SDC文件本质上是一个文本文件,里面是一行一行的Tcl命令。工具(比如Design Compiler、PrimeTime)会逐行读取并执行。
一个典型的SDC文件结构是这样的:
# ============================================
# 1. 时钟定义
# ============================================
create_clock -name clk_sys -period 10 [get_ports clk]
create_clock -name clk_uart -period 83.33 [get_ports uart_clk]
# ============================================
# 2. 时钟特性(抖动、延迟等)
# ============================================
set_clock_uncertainty -setup 0.2 [get_clocks clk_sys]
set_clock_latency -source 0.5 [get_clocks clk_sys]
# ============================================
# 3. 输入输出延迟
# ============================================
set_input_delay -clock clk_sys -max 2.0 [get_ports data_in]
set_output_delay -clock clk_sys -max 3.0 [get_ports data_out]
# ============================================
# 4. 时序例外
# ============================================
set_false_path -from [get_clocks clk_uart] -to [get_clocks clk_sys]
set_multicycle_path -setup 2 -from [get_pins reg_a/Q] -to [get_pins reg_b/D]
# ============================================
# 5. 设计约束
# ============================================
set_max_fanout 8 [current_design]
set_max_transition 0.5 [current_design]
嗯,结构很清晰对吧?从上到下,先定义时钟,再约束IO,最后处理例外路径。我个人习惯把SDC分成这五大块,每块之间用注释隔开。这样后期查问题,一眼就能定位。
# Rev 1.2 - 2024-03-15 - Added UART clock group。团队协作时,这能省去很多沟通成本。
二、时钟定义——create_clock 是核心
时钟是时序分析的基准。没有时钟,STA就没法做。create_clock命令就是用来告诉工具:哪个端口是时钟,频率是多少。
基本语法:
create_clock -name <时钟名> -period <周期(ns)> [get_ports <端口名>]
举个例子:
create_clock -name clk_core -period 5.0 [get_ports clk]
这条命令的意思是:端口clk是一个时钟,名字叫clk_core,周期5ns(对应200MHz)。
你可能会问:为什么还要给时钟起个名字?直接用端口名不行吗?
行,但不好。我在项目中遇到过这样的情况:同一个端口在不同模式下频率不同。比如芯片测试模式用50MHz,正常工作用200MHz。这时候你就要定义两个时钟:
create_clock -name clk_func -period 5.0 [get_ports clk]
create_clock -name clk_test -period 20.0 [get_ports clk] -add
注意那个 -add 参数。不加的话,第二个create_clock会覆盖第一个。加了,工具就知道这个端口上有两个时钟,分别用于不同场景。
时钟的波形定义
默认情况下,时钟的上升沿在0ns,下降沿在周期的一半。但有些时钟不是50%占空比,比如DDR接口的时钟。这时候要用 -waveform 参数:
# 占空比 60%:高电平持续 3ns,低电平持续 2ns(周期5ns)
create_clock -name clk_ddr -period 5.0 -waveform {0 3.0} [get_ports ddr_clk]
-waveform 后面跟两个值:上升沿时间和下降沿时间。上面这个例子,上升沿在0ns,下降沿在3ns,所以高电平是3ns,低电平是2ns。
三、时钟组——clock_group 解决跨时钟域问题
一个芯片里通常有多个时钟。比如CPU核跑1GHz,UART跑12MHz,USB跑480MHz。这些时钟之间如果没有任何关系,工具默认会分析它们之间的所有路径。
但问题是:跨时钟域的路径,真的需要分析吗?
不一定。如果两个时钟域之间用了同步器(比如两级触发器),那这些路径就是异步路径,不需要做setup/hold检查。如果不告诉工具,工具会傻乎乎地去分析,结果报出一堆假违例。
这时候就要用 set_clock_groups 命令:
set_clock_groups -asynchronous -group [get_clocks clk_core] -group [get_clocks clk_uart]
这条命令告诉工具:clk_core和clk_uart是异步时钟组,它们之间的路径不需要做时序分析。
还有一种情况是互斥时钟。比如芯片有两种工作模式,模式A用clk_a,模式B用clk_b,但两个时钟不会同时存在:
set_clock_groups -exclusive -group [get_clocks clk_a] -group [get_clocks clk_b]
这样工具就知道:分析模式A时只看clk_a的路径,分析模式B时只看clk_b的路径,不会把两个时钟混在一起分析。
四、知识体系结构图
下面这张图总结了SDC约束的核心逻辑:
五、实战建议与避坑指南
讲完理论,分享几个我实际项目中的经验:
- 时钟命名要有规范。 我习惯用
clk_模块名的格式,比如clk_core、clk_ddr、clk_ahb。这样在report_timing里一眼就能看出是哪个时钟。 - 时钟组不要乱设。 我曾经把两个同源时钟设成了异步组,结果工具忽略了所有跨时钟域路径,导致流片后功能异常。记住:同源时钟(比如同一个PLL分出来的)一般不需要设异步组。
- 用report_clock检查。 写完SDC后,先跑一下
report_clock看看时钟定义对不对。我每次都会检查:周期对不对?有没有漏掉时钟?有没有多余的时钟? - 版本管理。 SDC文件一定要纳入版本控制。我见过有人直接改服务器上的SDC,结果改错了回退不了。用Git管理,每次修改都有记录。
好了,这一讲就到这里。下一讲我们会深入 create_generated_clock——生成时钟的定义,这是处理PLL分频、倍频时钟的关键。到时候见。
可可精品资料库