2. 时序约束入门
各位同学,咱们今天聊聊时序约束的四个基本功。说实话,我刚入行那会儿,觉得约束就是随便写几条命令,让工具别报错就行。后来被现实狠狠教育了一顿——有一次芯片回来,功能全对,就是频率上不去,查了三天,发现是生成时钟的相位设错了。从那以后,我再也不敢小看这几条命令了。
时序约束,说白了就是告诉工具:你的设计要在什么样的节奏下工作。时钟是心跳,输入输出延迟是手脚的响应时间。搞不清楚这些,后面修时序违例就是瞎忙活。
2.1 创建时钟(create_clock)
这是最基础的命令。你想想看,芯片里那么多寄存器,它们靠什么同步?靠时钟。所以第一步,就是告诉工具时钟长什么样。
create_clock -name clk -period 10 [get_ports clk_in]
这条命令的意思是:在端口 clk_in 上创建一个名为 clk 的时钟,周期是 10ns,也就是 100MHz。
我个人习惯,创建时钟时一定会加上 -waveform 参数,明确指定占空比。虽然默认是 50%,但万一你用的是非对称时钟呢?
create_clock -name clk -period 10 -waveform {0 5} [get_ports clk_in]
这里 {0 5} 表示上升沿在 0ns,下降沿在 5ns。嗯,这里要注意:波形列表的个数必须是偶数,前一个数是上升沿,后一个是下降沿。
2.2 生成时钟(generate_clock)
生成时钟,也叫衍生时钟。它是在芯片内部由主时钟通过分频、倍频或门控产生的。比如你用了一个 PLL,或者用计数器做了个分频时钟。
create_generated_clock -name clk_div2 -source [get_ports clk_in] -divide_by 2 [get_pins u_div/clk_out]
这条命令告诉工具:在 u_div/clk_out 这个引脚上,有一个时钟叫 clk_div2,它是从 clk_in 分频 2 倍得到的。
我曾经遇到过一个坑:生成时钟的源路径没指定对。工具默认会找最近的时钟源,但有时候它会找到别的地方去。结果时序分析全乱套了。所以我建议,-source 参数一定要写清楚,别偷懒。
还有一种情况是门控时钟。比如你用 AND 门把时钟和使能信号合在一起。这时候也要用生成时钟来描述:
create_generated_clock -name gclk -source [get_pins u_and/A] -combinational [get_pins u_and/Y]
这里的 -combinational 表示这个生成时钟是组合逻辑产生的,没有分频或倍频。
2.3 输入延迟(set_input_delay)
输入延迟描述的是:数据从芯片外部到达内部寄存器之前,已经花了多少时间。说白了,就是外部器件把数据送出来,经过 PCB 走线,再到芯片引脚,这中间的时间。
set_input_delay -clock clk -max 2.5 [get_ports data_in]
这条命令的意思是:在 clk 时钟域下,数据 data_in 到达芯片引脚时,相对于时钟沿已经过去了 2.5ns。这个 2.5ns 是最大延迟。
你可能会问:为什么要分最大和最小?因为芯片内部建立时间检查要用最大延迟,保持时间检查要用最小延迟。这是 STA 的基本逻辑。
我一般会这样写:
set_input_delay -clock clk -max 3.0 [get_ports {data_in[*]}]
set_input_delay -clock clk -min 1.0 [get_ports {data_in[*]}]
注意这里用了 [*] 表示总线。如果数据位宽很大,这样写省事多了。
2.4 输出延迟(set_output_delay)
输出延迟和输入延迟是对称的。它描述的是:芯片内部的数据从寄存器输出,经过芯片引脚,再到外部器件被采到,这中间需要多少时间。
set_output_delay -clock clk -max 4.0 [get_ports data_out]
这条命令的意思是:数据 data_out 从芯片引脚输出后,外部器件会在 4.0ns 之后才采样它。所以芯片内部必须在时钟沿之后的 4.0ns 内把数据送到引脚。
嗯,这里有个容易混淆的地方:输出延迟是相对于外部器件的建立时间要求的。说白了,外部器件要求数据在时钟沿之前多久稳定,这个时间就是输出延迟的一部分。
举个例子,假设外部器件的建立时间是 2ns,保持时间是 1ns,PCB 走线延迟是 0.5ns,那么:
set_output_delay -clock clk -max 2.5 [get_ports data_out]
set_output_delay -clock clk -min -0.5 [get_ports data_out]
注意这里的 -min 是负值。为什么?因为保持时间要求数据在时钟沿之后还要稳定一段时间。如果外部器件的保持时间是 1ns,PCB 走线是 0.5ns,那么芯片内部的数据必须在时钟沿之后至少 0.5ns 内保持不变。这个 0.5ns 就是输出延迟的最小值,但它是相对于时钟沿之后的,所以用负值表示。
2.5 知识体系总览
说了这么多,咱们用一张图来总结一下这四个约束的关系。我画了个简单的流程图,帮你理清思路:
这张图很直观:先有主时钟,再有生成时钟。然后输入延迟和输出延迟分别描述数据进出芯片的时序关系。这四个命令搞定了,你的时序约束框架就搭起来了。
最后说一句:约束不是写完了就完事的。我每次写完约束,都会跑一遍 report_timing 看看有没有 unexpected 的路径。如果发现有些路径没被约束到,或者约束得太松,赶紧回头改。别等到后端跑完了才发现问题,那时候改起来成本就高了。
可可精品资料库