4. 时序约束入门:让芯片知道该跑多快

说实话,我刚入行那会儿,觉得时序约束就是个形式。随便写几个时钟,工具能跑就行。直到有一次,我负责的一个模块在流片回来后,死活跑不到目标频率。老板盯着我,我盯着波形……嗯,从那以后,我再也不敢小看 SDC 文件了。

时序约束,说白了就是告诉工具:你的芯片要在什么频率下工作。没有约束,工具就不知道路径该优化到多快。就像你让一个人跑步,却不告诉他终点在哪——他要么跑过头,要么根本不动。

4.1 什么是时序约束?

时序约束(Timing Constraint)是数字芯片后端设计的“交通规则”。它定义了:

  • 时钟的频率、占空比、相位
  • 数据信号相对于时钟的到达时间
  • 信号从输入到输出需要多快
  • 哪些路径是假的(false path),不需要检查

这些规则都写在一个叫 SDC(Synopsys Design Constraints)的文件里。SDC 是行业标准,几乎所有 EDA 工具都认它。

核心观点:没有时序约束的芯片设计,就像没有导航的远航——你可能出发了,但大概率到不了目的地。

4.2 SDC 文件的基本语法

SDC 文件其实很简单。它由一条条命令组成,每条命令以 # 开头的是注释。我习惯把 SDC 分成几个区块:时钟定义、输入延迟、输出延迟、例外路径。

来看一个最基本的 SDC 文件骨架:

# ============================================
# 时钟定义
# ============================================
create_clock -name clk -period 10 [get_ports clk]

# ============================================
# 输入延迟约束
# ============================================
set_input_delay -clock clk -max 5 [get_ports data_in]
set_input_delay -clock clk -min 2 [get_ports data_in]

# ============================================
# 输出延迟约束
# ============================================
set_output_delay -clock clk -max 6 [get_ports data_out]
set_output_delay -clock clk -min 3 [get_ports data_out]

# ============================================
# 例外路径
# ============================================
set_false_path -from [get_clocks rst_clk] -to [get_clocks clk]

每条命令的格式都差不多:命令 + 参数 + 对象。对象用 get_ports、get_pins、get_clocks 来指定。

小技巧:我习惯在 SDC 文件开头加一个版本注释,写上日期和修改人。团队协作时,这能省去很多“谁改了什么”的扯皮时间。

4.3 时钟定义:create_clock

时钟是时序分析的“心跳”。没有时钟,工具就不知道什么时候该采样数据。

create_clock 是最常用的命令。它的基本语法是:

create_clock -name <时钟名> -period <周期(ns)> [get_ports <端口名>]

举个例子:

# 定义一个 100MHz 的时钟,占空比 50%
create_clock -name sys_clk -period 10 [get_ports clk_in]

如果你想定义占空比不是 50% 的时钟,可以用 -waveform 参数:

# 定义占空比 60% 的时钟(高电平 6ns,低电平 4ns)
create_clock -name pwm_clk -period 10 -waveform {0 6} [get_ports clk_pwm]

-waveform 后面跟两个时间:上升沿时刻和下降沿时刻。默认是 {0 5},也就是 50% 占空比。

注意:我曾经犯过一个低级错误——把时钟周期写成了 10ns,但实际晶振输出是 20ns。结果芯片流片回来,所有时序都差了 2 倍。嗯,从那以后我每次都要对着原理图确认时钟频率。

4.4 输入延迟约束:set_input_delay

芯片的输入信号来自外部器件。外部器件什么时候把数据送到芯片引脚上?这个时间就是 输入延迟。

set_input_delay 告诉工具:数据相对于时钟边沿,最早和最晚什么时候到达。

# 数据在时钟上升沿之后 2ns 到 5ns 之间到达
set_input_delay -clock clk -min 2 [get_ports addr_in]
set_input_delay -clock clk -max 5 [get_ports addr_in]

这里有两个关键参数:

  • -min:数据最早到达时间(用于 hold 检查)
  • -max:数据最晚到达时间(用于 setup 检查)

我一般这样理解:-max 决定芯片内部能不能在下一个时钟沿之前把数据采进来;-min 决定数据会不会被上一个时钟沿“抢走”。

参数 含义 影响
-max 数据最晚到达时间 影响 setup 时序
-min 数据最早到达时间 影响 hold 时序

经验之谈:输入延迟的值怎么定?看外部器件的 datasheet。它通常会给出输出延迟(tco),再加上 PCB 走线延迟,就是你的输入延迟。别自己瞎猜,我见过有人随便写个 1ns,结果后端优化出来的电路根本不能用。

4.5 输出延迟约束:set_output_delay

输出延迟和输入延迟是对称的。它描述的是:芯片输出数据后,外部器件需要多长时间才能稳定采样。

# 数据在时钟上升沿之前 3ns 到 6ns 必须稳定
set_output_delay -clock clk -min 3 [get_ports data_out]
set_output_delay -clock clk -max 6 [get_ports data_out]

注意这里的“方向”:-max 表示外部器件要求数据在时钟沿之前多久到达;-min 表示时钟沿之后多久数据还能保持有效。

我习惯画一个时序图来理解:

输出延迟时序示意图 时钟 T0 T1 T2 数据有效 tco 采样 tsu thd 输出延迟 = 外部器件的建立时间 + PCB 走线延迟 set_output_delay -max 对应 tsu,-min 对应 thd

这张图里,芯片在 T0 时刻输出数据,经过 tco(时钟到输出延迟)后数据才稳定。外部器件在 T1 时刻采样,要求数据在采样前 tsu 时间内稳定,采样后 thd 时间内保持。

set_output_delay -max 的值就是 tsu + PCB 延迟,-min 的值是 thd - PCB 延迟(通常为负值或 0)。

避坑指南:我曾经把输出延迟的 -max 和 -min 写反了。结果工具报了很多 hold 违例,我查了三天才发现是约束写反了。记住:-max 对应 setup,-min 对应 hold。这个顺序别搞混。

4.6 本章小结

时序约束是后端设计的“宪法”。没有它,工具就不知道往哪个方向优化。这一章我们讲了:

  • SDC 文件:用命令描述时序要求,每条命令都有固定格式
  • create_clock:定义时钟的频率、占空比、相位
  • set_input_delay:告诉工具数据什么时候从外部进来
  • set_output_delay:告诉工具数据什么时候要送到外部

这些是时序约束的“三件套”。后面几章我们会深入讲例外路径、多周期路径、时钟分组这些更高级的内容。但基础打牢了,后面就顺了。

嗯,记住一句话:约束写得好,后端下班早。