第1章:SDC约束详解——从生成时钟到时序例外
各位同学,今天我们来聊聊SDC约束。说实话,我刚入行那会儿,觉得SDC就是一堆让人头疼的命令。后来做了几个项目才明白——SDC写得好不好,直接决定了你的芯片能不能按时收敛。嗯,咱们今天就把这块硬骨头啃下来。
1.1 生成时钟(Generated Clock)
生成时钟,说白了就是由主时钟分频或倍频出来的时钟。比如PLL输出的时钟,或者通过寄存器分频得到的时钟。我见过不少新手直接拿分频后的信号当主时钟用,结果时序分析一塌糊涂。
正确的做法是用create_generated_clock命令。举个例子:
create_clock -name clk_main -period 10 [get_ports clk_in]
create_generated_clock -name clk_div2 -source [get_ports clk_in] \
-divide_by 2 [get_pins u_divider/clk_out]
这里要注意几个关键点:
- -source指定的是主时钟的源端,不是生成时钟的源端
- -divide_by和-multiply_by用于分频和倍频
- 生成时钟的波形必须与主时钟对齐
report_clock检查。
1.2 虚拟时钟(Virtual Clock)
虚拟时钟是个很有意思的概念。它不连在任何物理端口或引脚上,纯粹是为了约束输入输出路径。你想想看,芯片外面的信号是跟着外部时钟走的,但那个时钟不在我们芯片内部,怎么办?虚拟时钟就是干这个的。
create_clock -name vclk -period 10
set_input_delay -clock vclk -max 5 [get_ports data_in]
set_output_delay -clock vclk -max 6 [get_ports data_out]
我个人习惯把虚拟时钟的周期设成和系统时钟一样,这样分析起来比较直观。当然,如果你的芯片要接不同频率的外部器件,那就得单独设了。
1.3 时钟组(Clock Groups)
时钟组是用来告诉STA工具:这些时钟之间不需要做时序分析。为什么需要这个?因为异步时钟域之间没有固定的相位关系,硬要分析只会产生一堆假路径。
set_clock_groups -asynchronous -group [get_clocks clk_a] \
-group [get_clocks clk_b]
这里有个容易犯的错误:有人会把同源的时钟也设成异步组。比如PLL输出的两个时钟,虽然频率不同,但相位是确定的,应该做时序分析。我见过一个项目,就是因为这个误设,漏掉了一条关键路径,最后不得不降频。
1.4 时钟延迟(Clock Latency)
时钟延迟包括source latency和network latency。source latency是时钟从源头到芯片边界的延迟,network latency是从芯片边界到寄存器的延迟。
set_clock_latency -source 2 [get_clocks clk_main]
set_clock_latency 1.5 [get_clocks clk_main]
实际项目中,source latency通常由封装和PCB决定,network latency则由时钟树综合(CTS)决定。我建议在综合阶段先设一个估计值,等CTS做完后再更新成实际值。
1.5 时钟不确定性(Clock Uncertainty)
时钟不确定性是用来模拟时钟抖动(jitter)和时钟偏差(skew)的。说白了,就是给时序分析留点余量。
set_clock_uncertainty -setup 0.2 [get_clocks clk_main]
set_clock_uncertainty -hold 0.05 [get_clocks clk_main]
setup的uncertainty通常比hold大,因为setup受时钟抖动影响更大。我记得有个项目,为了省功耗把uncertainty设得很小,结果流片回来setup violation一大堆。嗯,从那以后我再也不敢在这上面省了。
1.6 时序例外(False Path & Multicycle Path)
时序例外是SDC里最灵活也最容易出错的部分。咱们一个一个说。
1.6.1 False Path(假路径)
假路径就是那些不需要做时序分析的路径。比如复位信号、测试模式下的路径、异步时钟域之间的路径。
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
set_false_path -from [get_ports rst_n]
这里要特别小心:不要滥用false_path。我见过有人嫌麻烦,把整个模块的路径都设成false_path,结果漏掉了真正的时序问题。正确的做法是只对确实不需要分析的路径设false_path。
1.6.2 Multicycle Path(多周期路径)
多周期路径是指数据需要多个时钟周期才能稳定的路径。比如一个乘法器,可能需要两个周期才能算出结果。
set_multicycle_path -setup 2 -from [get_pins u_mult/a] -to [get_pins u_mult/b]
set_multicycle_path -hold 1 -from [get_pins u_mult/a] -to [get_pins u_mult/b]
注意:设了setup的multicycle后,hold的multicycle也要相应调整。默认情况下,hold的multicycle是setup减1。这个规则我刚开始也记不住,后来干脆写了个脚本自动生成。
- 生成时钟要指定正确的source和分频关系
- 虚拟时钟用于约束IO路径
- 时钟组要区分同源和异步
- 时钟延迟和不确定性要留合理余量
- 时序例外要精准,不要滥用
好了,SDC约束的核心内容就这些。说实话,这些东西看起来简单,但真正用好需要大量实践。我建议你每写完一条约束,都跑一下report_timing看看效果。慢慢你就会找到感觉了。
这张图把SDC约束的三大块串起来了。你仔细看看,其实每个分支之间都有联系。比如生成时钟和时钟组经常一起用,虚拟时钟和时钟延迟也密不可分。理解了这个关系,写SDC的时候就不会顾此失彼了。