3. 时序约束入门

大家好,我是你们的芯片后端工程师老K。今天咱们聊聊时序约束里最基础、也最绕不开的五个命令。说实话,我刚入行那会儿,看到SDC文件里密密麻麻的约束,头都大了。后来慢慢摸出门道,发现其实核心就这几个命令——create_clock、set_input_delay、set_output_delay、set_max_delay、set_min_delay。掌握了它们,时序约束就算入门了。

核心观点:时序约束的本质,就是告诉工具「芯片应该跑多快」以及「芯片外面是什么样子」。你给的信息越准确,工具帮你优化的结果就越靠谱。

时序约束五大命令 create_clock 定义时钟周期与波形 set_input_delay 外部到芯片的延迟 set_output_delay 芯片到外部的延迟 set_max_delay 路径最大延迟约束 set_min_delay 路径最小延迟约束 时序约束知识体系

1. create_clock:一切时序的起点

没有时钟,时序分析就无从谈起。create_clock 就是告诉工具:「嘿,这个端口上有个时钟,周期是这么长,波形长这样。」

我个人习惯,写时钟约束时一定会把 name 参数带上。为什么?因为后续所有跟这个时钟相关的约束,都要靠这个名字来引用。你想想看,如果芯片里有十几个时钟,全靠端口名去关联,那SDC文件读起来得多痛苦。

# 最基本的时钟定义
create_clock -name clk_sys -period 10.0 [get_ports clk]

# 带占空比的写法(上升沿在0ns,下降沿在5ns)
create_clock -name clk_sys -period 10.0 -waveform {0 5} [get_ports clk]

# 虚拟时钟(没有物理端口,用于IO约束)
create_clock -name vclk -period 10.0

小技巧:虚拟时钟是个好东西。我在做多时钟域设计时,经常用虚拟时钟来解耦IO约束和内部时钟。这样即使内部时钟改了,IO约束也不用大动。

2. set_input_delay:告诉工具「外面有多慢」

芯片不是孤岛。数据从上一级芯片飞过来,经过PCB走线,才到达你的输入引脚。这段路上的延迟,就是 set_input_delay 要描述的东西。

说白了,这个命令是在说:「数据在时钟沿之后多久才稳定到达我的引脚?」或者「数据在时钟沿之前多久就已经准备好了?」

# 最大输入延迟(setup检查用)
set_input_delay -clock vclk -max 5.0 [get_ports data_in]

# 最小输入延迟(hold检查用)
set_input_delay -clock vclk -min 1.0 [get_ports data_in]

# 带参考时钟沿的写法
set_input_delay -clock vclk -max 5.0 -clock_fall [get_ports data_in]

注意:我曾经在一个项目中,因为忽略了 -min 选项,导致hold时序分析结果偏乐观。流片回来,芯片在低温下频繁出错。从那以后,我每次写 set_input_delay 都会把 -max 和 -min 都写上,一个不落。

3. set_output_delay:反过来看「外面有多慢」

跟输入延迟相反,set_output_delay 描述的是数据从芯片输出引脚到下一级芯片的延迟。工具会根据这个约束,倒推出芯片内部输出路径需要多快。

嗯,这里要注意一个容易搞混的点:set_output_delay 的值越大,留给芯片内部的时间就越少。你想想看,如果外部延迟占了8ns,时钟周期是10ns,那内部路径就只有2ns可以用了。

# 基本输出延迟
set_output_delay -clock vclk -max 6.0 [get_ports data_out]

# 带参考时钟沿
set_output_delay -clock vclk -max 6.0 -clock_fall [get_ports data_out]

# 同时约束上升沿和下降沿
set_output_delay -clock vclk -max 6.0 -rise [get_ports data_out]
set_output_delay -clock vclk -max 6.0 -fall [get_ports data_out]

经验之谈:IO延迟的数值怎么定?我一般会跟封装工程师和PCB工程师一起开会,把PCB走线长度、负载电容、驱动能力这些参数摆出来,算一个合理的范围。千万别自己拍脑袋写个数字,那是在给自己挖坑。

4. set_max_delay:给路径设个「天花板」

这个命令比较灵活。它不局限于IO路径,可以约束任意两条时序节点之间的最大延迟。我常用它来处理跨时钟域路径、异步复位释放路径,或者一些不需要严格时序但也不能太慢的组合逻辑。

# 约束跨时钟域路径
set_max_delay 8.0 -from [get_clocks clk_a] -to [get_clocks clk_b]

# 约束特定路径
set_max_delay 5.0 -from [get_pins inst_a/reg_a/CK] -to [get_pins inst_b/reg_b/D]

# 约束组合逻辑
set_max_delay 3.0 -from [get_ports data_in] -to [get_ports data_out]

避坑指南:我曾经在一个项目中,用 set_max_delay 约束了一条异步路径,结果忘了同时加 set_false_path。工具为了满足这个约束,疯狂插buffer,把面积和功耗都搞上去了。所以,用 set_max_delay 之前,先想清楚这条路径到底要不要做时序收敛。

5. set_min_delay:防止「太快」出问题

hold violation 的本质就是数据跑得太快,把上一拍的数据冲掉了。set_min_delay 就是给路径设个「地板」,告诉工具:「这条路径不能太快,至少要有这么长的延迟。」

说实话,这个命令我用得不多。大部分hold问题靠 set_input_delay -min 和 set_output_delay -min 就能解决。但有些特殊场景,比如异步复位信号,就需要它来兜底。

# 约束异步复位路径的最小延迟
set_min_delay 2.0 -from [get_ports rst_n] -to [get_pins *reg*/RN]

# 约束跨时钟域路径的最小延迟
set_min_delay 1.5 -from [get_clocks clk_a] -to [get_clocks clk_b]

重要提醒:set_min_delay 和 set_max_delay 同时使用时,要确保 min 小于 max。这听起来像废话,但我真见过有人把 min 设得比 max 还大,结果工具直接报错,整个时序分析都跑不下去。

小结一下

这五个命令,说白了就是两件事:定义时钟 和 描述芯片边界。create_clock 是基础,set_input_delay 和 set_output_delay 管的是IO边界,set_max_delay 和 set_min_delay 则是更灵活的路径约束。

我个人觉得,学时序约束最好的方法就是动手写。找个简单的设计,从 create_clock 开始,一步步把IO约束加上,然后跑一遍STA看看结果。哪里报violation了,回头检查约束是不是写错了。这样折腾几轮,你自然就懂了。

好了,这一章就聊到这儿。记住,约束写得越准,芯片流片回来的成功率就越高。别偷懒,每个数字都要有依据。


可可精品资料库