第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用于分频和倍频
  • 生成时钟的波形必须与主时钟对齐
⚠️ 我曾经踩过的坑:生成时钟的-source如果指向了错误的位置,STA工具会报warning,但不会报error。结果就是时序分析完全不对,流片回来才发现问题。所以写完SDC一定要跑一下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约束 时钟定义 时钟约束 时序例外 主时钟 生成时钟 虚拟时钟 时钟组 时钟延迟 时钟不确定性 False Path Multicycle Path 每个分支都包含多个子知识点,需要结合实际项目理解 时钟定义 时钟约束 时序例外

这张图把SDC约束的三大块串起来了。你仔细看看,其实每个分支之间都有联系。比如生成时钟和时钟组经常一起用,虚拟时钟和时钟延迟也密不可分。理解了这个关系,写SDC的时候就不会顾此失彼了。

可可精品资料库