第四章 SDC约束编写实战:时钟定义,生成时钟,输入输出延迟,伪路径与多周期路径

各位同学,欢迎来到SDC约束实战这一章。

说实话,SDC约束是时序收敛里最容易被低估的一环。我见过太多项目,RTL仿真跑得飞起,一到后端实现就崩了。查到最后,往往是约束写错了。你想想看,工具再聪明,你给它的约束是错的,它怎么可能给你好结果?

4.1 时钟定义——时序分析的基石

时钟定义是SDC里最基础、也最关键的部分。没有时钟,STA根本跑不起来。

create_clock 是最基本的命令。我个人习惯这样写:

create_clock -name clk_sys -period 10.0 [get_ports clk_in]

这里有几个要点:

  • 时钟名:最好和设计里的信号名区分开。我习惯加个后缀,比如 clk_sys 而不是直接用 clk。
  • 周期:单位是纳秒。10.0 对应 100MHz。
  • 波形:默认是50%占空比,上升沿在0时刻。如果你需要非50%占空比,用 -waveform 指定。

重要提醒:时钟定义一定要精确到端口或pin。别偷懒用 [all_clocks] 去定义,那会出大问题。

我在项目中遇到过一件事:有个同事把时钟定义在了某个buffer的输出上,结果综合工具自动优化掉了那个buffer,时钟就丢了。嗯,从那以后我坚持只把时钟定义在顶层端口或PLL输出上。

4.2 生成时钟——分频与倍频的处理

实际设计中,我们很少直接用外部时钟。PLL分频、倍频,或者门控时钟,都需要用 create_generated_clock 来处理。

看个例子:

create_generated_clock -name clk_div2 \
  -source [get_ports clk_in] \
  -divide_by 2 \
  [get_pins u_pll/clk_out]

这里要注意 -source 参数。它指定的是生成时钟的源时钟,不是生成时钟本身。很多人搞混这个,结果时序分析完全对不上。

小技巧:对于同源时钟,尽量用 -master_clock 明确指定主时钟。这样工具不会猜错。

还有一种情况是门控时钟。比如:

create_generated_clock -name clk_gated \
  -source [get_pins u_clk_gate/clk_in] \
  -combinational \
  [get_pins u_clk_gate/clk_out]

-combinational 表示这个生成时钟和源时钟是组合关系,没有分频或倍频。说白了,就是纯门控。

4.3 输入输出延迟——芯片与外部交互的桥梁

芯片不是孤岛。它要和外部器件通信,所以我们需要告诉工具:数据从外部进来时,相对于时钟是什么关系?

输入延迟:

set_input_delay -clock clk_sys -max 2.5 [get_ports data_in]

这个2.5ns表示:外部数据在时钟沿之后2.5ns才稳定到达芯片引脚。工具会据此检查内部寄存器能不能正确采到数据。

输出延迟:

set_output_delay -clock clk_sys -max 3.0 [get_ports data_out]

这个3.0ns表示:外部器件需要数据在时钟沿之前3.0ns就准备好。工具会确保内部路径满足这个要求。

避坑指南:我曾经因为输入输出延迟设得太紧,导致工具疯狂插buffer,面积暴涨。后来发现是外部器件的datasheet看错了。所以,一定要和系统工程师对齐外部时序参数,别自己瞎猜。

4.4 伪路径——告诉工具哪些路径不用管

不是所有路径都需要时序检查。比如跨时钟域的同步器,或者测试模式下的路径。

伪路径用 set_false_path:

set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

这表示:从clk_a到clk_b的所有路径,都不做时序检查。

还有一种常见用法:

set_false_path -through [get_pins u_mux/sel]

这表示:经过这个选择器sel引脚的路径,全部忽略。常用于测试模式下的路径。

核心原则:伪路径要慎用。用多了会漏掉真正的时序问题。我一般只在以下情况使用:

  • 跨时钟域的同步器路径
  • 测试模式下的专用路径
  • 复位信号路径(通常用异步复位)

4.5 多周期路径——给慢速路径多一点时间

有些路径不需要在一个时钟周期内完成。比如从慢速模块到快速模块的数据传输。

多周期路径用 set_multicycle_path:

set_multicycle_path -setup 2 -from [get_pins u_slow/q] -to [get_pins u_fast/d]

这表示:数据需要2个时钟周期才能到达目的地。工具会放宽建立时间检查。

但要注意,多周期路径默认只改建立时间,保持时间还是按1个周期算。所以通常要同时设保持时间:

set_multicycle_path -hold 1 -from [get_pins u_slow/q] -to [get_pins u_fast/d]

为什么?因为保持时间检查的是数据变化太快,和周期数无关。你想想看,数据虽然可以慢点到,但不能变得太快,否则会采到错误值。

个人经验:多周期路径最容易出错的地方是忘记设保持时间。我曾经有个设计,功能仿真全对,流片回来就是随机出错。查了两个月,最后发现是多周期路径的保持时间没设对。从那以后,我每次设多周期路径都会检查保持时间。

4.6 知识体系总览

下面这张图总结了本章的核心内容。你可以把它当作SDC约束的检查清单:

SDC约束编写知识体系 SDC约束 时钟定义 create_clock 生成时钟 create_generated_clock 输入输出延迟 set_input/output_delay 伪路径 set_false_path 多周期路径 set_multicycle_path 核心原则 精确约束 + 合理放宽 = 最优时序结果 少用伪路径,慎设多周期,对齐外部参数

4.7 实战建议

最后,我给大家几个实战中的建议:

  1. 先写时钟,再写其他约束。时钟是基础,时钟错了后面全白搭。
  2. 用report_clock检查。写完时钟定义后,跑一下 report_clock,看看有没有悬空的时钟。
  3. 输入输出延迟要留余量。别卡着datasheet的极限值设,留10%-20%的余量。
  4. 伪路径要加注释。为什么设伪路径?哪个模块负责的?写清楚,方便后续维护。
  5. 多周期路径要成对出现。设了setup,别忘了hold。

最后提醒:SDC约束不是写一次就完事的。随着设计迭代,约束也要跟着更新。我习惯在每个版本里都跑一遍 check_timing,看看有没有遗漏的路径。

好了,这一章的内容就到这里。记住,SDC约束是时序收敛的第一步,也是最重要的一步。写好了,后面事半功倍;写不好,后面全是坑。

可可精品资料库