3. 时序约束入门:创建时钟、生成时钟、输入延迟与输出延迟约束
时序约束,说白了就是给芯片设计定规矩。没有规矩,STA工具就不知道该怎么分析你的电路。我刚开始做后端时,觉得约束是前端的事,后来发现——后端工程师不懂约束,就像开车不看仪表盘,迟早要翻车。
这一章,我们聊聊最基础的四个约束:创建时钟、生成时钟、输入延迟、输出延迟。掌握了这些,你就能让STA工具听懂你的设计意图。
3.1 创建时钟(create_clock)
时钟是时序分析的基准。没有时钟,STA就没法算路径延迟。创建时钟的语法很简单:
create_clock -name clk -period 10 [get_ports clk_in]
这条命令的意思是:在端口 clk_in 上创建一个名为 clk 的时钟,周期是10ns(也就是100MHz)。
嗯,这里有几个坑要注意:
- 时钟名要唯一。我见过有人把两个时钟都叫"clk",结果STA报错说时钟重复定义。
- 周期单位是ns。默认是ns,但有些工具可以设成ps。建议统一用ns,省得换算出错。
- 占空比默认50%。如果你需要非50%的占空比,可以加 -waveform 参数。
实际案例:我在一个项目中,前端给的时钟周期是10ns,但实际PLL输出有抖动。我加了 -jitter 0.2 来模拟时钟抖动,结果发现一条关键路径的slack从正变负。后来调整了时钟不确定性,才让时序收敛。
3.2 生成时钟(generate_clock)
生成时钟,是从已有时钟分频或倍频出来的时钟。比如PLL输出、分频器输出,都属于生成时钟。
create_generated_clock -name clk_div2 -source [get_clocks clk] -divide_by 2 [get_pins u_div/clk_out]
这条命令的意思是:从 clk 时钟分频2倍,得到 clk_div2 时钟,输出在 u_div/clk_out 这个pin上。
我个人习惯,生成时钟一定要指定 -master_clock 或 -source。否则工具可能找不到源时钟,导致分析出错。
避坑指南:我曾经遇到一个bug——生成时钟的源时钟是虚拟时钟(virtual clock),结果STA报了一堆unconstrained path。后来发现,生成时钟的源时钟必须是真实存在的时钟,不能是虚拟的。
生成时钟的常用参数:
| 参数 | 说明 | 示例 |
|---|---|---|
| -divide_by | 分频系数 | -divide_by 2 |
| -multiply_by | 倍频系数 | -multiply_by 4 |
| -invert | 反相输出 | -invert |
| -edges | 自定义边沿 | -edges {1 3 5} |
3.3 输入延迟(set_input_delay)
输入延迟,描述的是外部信号到达芯片输入端口的时间。说白了,就是数据从外部芯片出来,经过PCB走线,再到我们芯片输入端口,花了多长时间。
set_input_delay -clock clk -max 2.5 [get_ports data_in]
这条命令的意思是:data_in 这个端口,相对于 clk 时钟,最大输入延迟是2.5ns。
你想想看,为什么要有 -max 和 -min?因为芯片工作在不同PVT条件下,延迟会变化。 -max 对应最差情况(慢工艺、高温、低压), -min 对应最好情况(快工艺、低温、高压)。
注意:输入延迟的值,通常由系统级仿真或PCB设计给出。不要自己瞎猜。我曾经见过有人随便填了个1ns,结果流片回来芯片根本没法跟外部器件通信。
输入延迟的常见写法:
- 单沿采样:只用一个时钟沿采样数据
- 双沿采样:DDR接口,上下沿都采样
- 虚拟时钟:如果外部时钟和内部时钟不同源,需要先创建虚拟时钟
3.4 输出延迟(set_output_delay)
输出延迟,描述的是数据从芯片输出端口,到外部器件接收,需要多长时间。跟输入延迟是对称的。
set_output_delay -clock clk -max 3.0 [get_ports data_out]
这条命令的意思是:data_out 这个端口,相对于 clk 时钟,最大输出延迟是3.0ns。
输出延迟的约束逻辑,其实是在告诉STA工具:芯片内部的数据,必须在时钟沿之前提前准备好,留出足够的时间给外部走线和器件。
经验之谈:我建议输出延迟的 -max 值,至少留0.5ns的余量。因为PCB走线长度、连接器阻抗,都会引入额外的延迟。留点余量,总比流片回来发现IO时序违例要好。
3.5 知识体系总览
下面这张图,是我画的一个时序约束知识框架。你可以看到,四个约束分别对应芯片的四个边界:
这张图想表达的是:创建时钟和生成时钟,定义了芯片内部的时序基准;输入延迟和输出延迟,定义了芯片与外部世界的接口时序。四个约束缺一不可。
3.6 实战小贴士
最后,分享几个我在项目中积累的经验:
- 先写时钟,再写IO延迟。顺序很重要,因为IO延迟依赖于时钟定义。
- 用report_timing检查。写完约束后,跑一下report_timing,看看有没有unconstrained path。
- 虚拟时钟别乱用。虚拟时钟只用于IO延迟,不要用在内部路径上。
- 多跟前端沟通。约束值不是后端自己定的,需要前端提供系统级时序信息。
我的习惯:每次写完约束,我都会在SDC文件开头加一段注释,写明时钟来源、频率、IO延迟的依据。这样三个月后自己回来看,也能快速想起来当初为什么这么设。
好了,时序约束入门就聊到这里。记住一句话:约束写得好,时序收敛早。下一章我们聊聊更进阶的约束——多周期路径和伪路径,那才是真正考验后端工程师功底的地方。