第三章:时序约束入门

时序约束,说白了就是告诉工具你的芯片要跑多快、信号什么时候来、什么时候走。我刚开始做数字IC时,总觉得这玩意儿是后端的事,前端只管写代码就行。结果有一次综合出来的网表时序一团糟,折腾了两周才发现是约束没写对。嗯,从那以后我再也不敢轻视时序约束了。

核心要点:时序约束是连接RTL设计与物理实现的桥梁。没有正确的约束,工具就像蒙着眼睛走路——不知道往哪个方向优化。

3.1 创建时钟——一切时序的起点

时钟是数字电路的“心跳”。创建时钟,就是告诉工具这个心跳的频率和相位。

我个人习惯用 create_clock 命令来定义主时钟。语法其实很简单:

# 创建一个50MHz的时钟,占空比50%
create_clock -name clk -period 20 [get_ports clk]

# 创建一个带相移的时钟
create_clock -name clk_90 -period 20 -waveform {5 15} [get_ports clk]

这里要注意几个坑:

  • 时钟源必须是端口或引脚——不能直接定义在内部节点上
  • 周期单位是纳秒——50MHz对应20ns,别算错了
  • 波形用大括号括起来——第一个值是上升沿,第二个是下降沿

我曾经踩过的坑:有一次我把时钟定义在了PLL的输出引脚上,结果综合工具死活不认。后来才发现,主时钟必须定义在芯片的输入端口上。PLL输出应该用 create_generated_clock 来定义。

生成时钟(Generated Clock)也很常用。比如PLL分频出来的时钟:

# 二分频时钟
create_generated_clock -name clk_div2 -source [get_ports clk] \
  -divide_by 2 [get_pins pll/clk_out]

为什么要有生成时钟?你想想看,如果直接把分频后的信号当普通数据路径处理,工具会认为它和主时钟是异步关系,时序分析就全乱套了。

3.2 设置输入延迟——告诉工具数据从哪来

输入延迟描述的是:外部器件发送数据到芯片输入端口,这中间花了多少时间。

公式其实很简单:

输入延迟 = 外部器件的时钟到输出延迟 + PCB走线延迟

对应的命令是 set_input_delay:

# 设置输入延迟,相对于时钟clk的上升沿
set_input_delay -clock clk -max 5.0 [get_ports data_in]
set_input_delay -clock clk -min 2.0 [get_ports data_in]

这里 -max 和 -min 分别对应最坏情况和最好情况。我建议你两个都设,别偷懒只设一个。为什么?因为建立时间检查用max,保持时间检查用min,缺一个工具就没法完整分析。

小技巧:如果你不确定外部器件的参数,可以先用数据手册里的典型值。等后期时序收敛了,再根据实际情况调整。别一开始就追求完美,先让工具跑起来再说。

还有个容易忽略的点:输入延迟是相对于哪个时钟沿?默认是上升沿。如果你的设计用下降沿采样,记得加 -clock_fall:

set_input_delay -clock clk -clock_fall -max 4.0 [get_ports data_in]

3.3 设置输出延迟——告诉工具数据要去哪

输出延迟和输入延迟是对称的。它描述的是:芯片输出数据后,外部器件需要多少时间才能正确采样。

命令是 set_output_delay:

# 设置输出延迟,相对于时钟clk的上升沿
set_output_delay -clock clk -max 6.0 [get_ports data_out]
set_output_delay -clock clk -min 1.5 [get_ports data_out]

这里有个容易混淆的地方:输出延迟是外部器件的建立时间要求。也就是说,你的芯片必须在时钟沿到来之前,提前把数据送到输出端口,留出足够的时间给外部器件建立。

我记得有一次,一个同事把输出延迟设反了——他以为输出延迟是芯片内部路径的延迟。结果综合出来的电路时序裕量全是负的,查了两天才发现是约束写反了。嗯,这种错误其实挺常见的。

参数 输入延迟 输出延迟
含义 外部数据到达芯片的时间 芯片数据到达外部器件的时间
max值 影响建立时间 影响建立时间
min值 影响保持时间 影响保持时间
参考时钟 芯片内部时钟 芯片内部时钟

3.4 时序例外——打破常规的路径

不是所有路径都需要严格的时序检查。有些路径天生就是“例外”。

常见的时序例外有四种:

  • 假路径(False Path)——这条路径永远不会被激活,比如跨时钟域的同步器路径
  • 多周期路径(Multi-Cycle Path)——数据不需要在一个时钟周期内到达,比如慢速控制信号
  • 最大延迟(Max Delay)——自定义路径延迟上限
  • 最小延迟(Min Delay)——自定义路径延迟下限

命令示例:

# 假路径:跨时钟域
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

# 多周期路径:数据需要2个周期
set_multicycle_path -setup 2 -from [get_pins reg_a/Q] -to [get_pins reg_b/D]

# 最大延迟约束
set_max_delay 10.0 -from [get_ports rst_n] -to [get_pins reg_c/D]

我曾经犯过的错:有一次我设了一个多周期路径,但只设了setup的周期数,忘了设hold的周期数。结果工具默认hold也是2个周期,导致保持时间检查过于宽松。正确的做法是:set_multicycle_path -setup 2 -hold 1,hold的周期数要比setup少1。

使用时序例外要谨慎。我见过有人为了快速收敛时序,到处设假路径。结果流片回来芯片功能不正常——原来有些路径根本不是假的,只是时序难收敛而已。说白了,假路径是最后的手段,不是偷懒的工具。

3.5 本章知识体系

下面这张图总结了时序约束的核心逻辑:

时序约束知识体系 时序约束 创建时钟 设置输入延迟 设置输出延迟 时序例外 主时钟 生成时钟 max/min 时钟沿 外部建立时间 外部保持时间 假路径 多周期路径 最大/最小延迟 核心目标:让工具知道你的设计意图,优化出正确的电路

我的建议:刚开始学时序约束时,别想着一步到位。先写一个简单的时钟约束,跑一遍综合看看结果。然后逐步加上输入输出延迟,再处理例外路径。每加一条约束,都问问自己:这条约束合理吗?有没有更好的写法?

时序约束入门,说白了就是四个步骤:告诉工具时钟长什么样、数据从哪来、数据去哪、哪些路径可以放宽。掌握了这些,你就已经迈过了时序收敛的第一道门槛。

可可精品资料库