第四章 静态时序分析入门:STA的基本概念、时序路径的分类、建立时间与保持时间、时序约束文件(SDC)解析

静态时序分析,说白了就是检查你的芯片能不能跑在目标频率上。我刚开始做设计时总觉得这步可有可无,直到有一次流片回来芯片死活上不了高频……嗯,从那以后我再也不敢跳过 STA 了。

STA 是后端设计的“守门员”。它不跑仿真,不关心功能对不对,只盯着一个事:所有信号能不能在规定时间内到达该去的地方。你想想看,一个 1GHz 的芯片,时钟周期才 1ns,信号在路上多耽搁一丁点,整个芯片就崩了。

4.1 STA 的基本概念

STA 是一种穷举式的时序验证方法。它把芯片里所有的时序路径都拎出来,一条一条算延迟,然后跟约束条件做对比。跟动态仿真不一样,STA 不需要输入激励向量,所以跑得飞快,而且覆盖率是 100%。

我个人习惯在综合之后、布局之前就跑一遍 STA。这时候虽然物理信息还不全,但能提前发现一些结构性的时序问题,比如扇出太大、逻辑级数太多。等到布局布线完了再跑,那才是真正的“签核”级 STA。

STA 的核心三要素:

  • 时序约束:你告诉工具芯片要跑多快、时钟长什么样、哪些路径是假的。
  • 延迟计算:工具根据工艺库和寄生参数,算出每个单元和连线的延迟。
  • 时序检查:把实际延迟跟约束对比,看有没有违反建立时间或保持时间。

我记得有一次,一个同事死活搞不定一个 2GHz 的设计。他跑 STA 报了一堆 violation,但怎么优化都过不去。我过去一看,发现他用的时序库是 TT(典型工艺角),但实际流片用的是 SS(最慢工艺角)。换了库之后,violation 直接少了一半。所以,选对工艺角比什么都重要。

4.2 时序路径的分类

STA 把芯片里的路径分成四类。每一类的检查方式都不一样,你得心里有数。

路径类型 起点 终点 典型例子
输入到寄存器 输入端口 寄存器的时钟端 片外信号进入芯片后,被第一级寄存器采样
寄存器到寄存器 寄存器的时钟端 寄存器的时钟端 流水线中两级寄存器之间的组合逻辑
寄存器到输出 寄存器的时钟端 输出端口 最后一级寄存器驱动片外信号
输入到输出 输入端口 输出端口 纯组合逻辑路径,没有寄存器

这里面,寄存器到寄存器是最常见的,也是后端优化的主战场。我一般会先看这类路径,因为它们占了芯片里 80% 以上的时序路径。

你可能会问:“为什么输入到输出路径也要检查?” 嗯,有些设计里确实有纯组合逻辑的直通路径,比如一些简单的地址译码器。如果这些路径太长,也会影响芯片的整体性能。

时序路径分类结构图 输入端口 寄存器 A 组合逻辑 寄存器 B 输出端口 输入→寄存器 寄存器→寄存器 寄存器→寄存器 寄存器→输出 输入→输出(直通) 端口 寄存器 组合逻辑 直通路径

4.3 建立时间与保持时间

这两个概念是 STA 的灵魂。你要是搞不清楚建立时间和保持时间,那 STA 基本等于白学。

建立时间(Setup Time):数据必须在时钟沿到来之前稳定下来的最短时间。如果数据来得太晚,寄存器就采不到正确的值。

保持时间(Hold Time):数据必须在时钟沿到来之后保持稳定的最短时间。如果数据变得太快,寄存器也会采错。

我打个比方:你坐公交车,车门关上的瞬间你才跳上去,这叫建立时间 violation。你刚上车还没站稳,司机一脚油门把你甩出去,这叫保持时间 violation。两个都不能出问题。

我的经验:建立时间 violation 通常可以通过降低频率、减少逻辑级数、换用更快的单元来修复。保持时间 violation 则比较麻烦,因为它跟频率无关,只能靠插入延迟缓冲器(buffer)或者调整走线来解决。

我曾经遇到过一个保持时间 violation,查了三天才发现问题出在时钟树上。时钟到达两个寄存器的延迟差太大,导致数据在时钟沿之后变化太快。最后在短路径上插了几个 buffer,才把保持时间修好。

4.4 时序约束文件(SDC)解析

SDC 是 Synopsys Design Constraints 的缩写,它是 STA 的“剧本”。没有 SDC,工具就不知道你要检查什么。我见过不少新手,一上来就写 SDC,结果漏了关键约束,导致流片失败。

下面是一个典型的 SDC 文件片段,我逐行给你解释:

# 创建时钟,周期 10ns,占空比 50%
create_clock -name clk -period 10 [get_ports clk]

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

# 设置输出延迟,相对于时钟 clk
set_output_delay -clock clk -max 3.0 [get_ports data_out]
set_output_delay -clock clk -min 1.0 [get_ports data_out]

# 设置伪路径,不检查
set_false_path -from [get_ports rst_n]

# 设置多周期路径,数据需要两个周期才能稳定
set_multicycle_path -setup 2 -from [get_pins reg_a/Q] -to [get_pins reg_b/D]

这里面有几个关键点:

  • create_clock:定义时钟的周期和波形。这是所有约束的基础。
  • set_input_delay:告诉工具,片外信号到达芯片输入端口需要多长时间。max 对应建立时间检查,min 对应保持时间检查。
  • set_output_delay:告诉工具,片外需要数据在时钟沿之后多长时间到达。同样分 max 和 min。
  • set_false_path:告诉工具,这条路径不用检查。比如异步复位信号,它跟时钟没有固定的时序关系。
  • set_multicycle_path:告诉工具,这条路径需要多个时钟周期才能稳定。比如一些慢速的运算模块。

注意:set_false_path 和 set_multicycle_path 一定要慎用。我曾经见过一个工程师,把一条关键路径误设成了 false path,结果芯片在高温下直接失效。所以,能用约束解决的问题,尽量不要用 false path 绕过去。

写 SDC 的时候,我建议你遵循一个原则:先写时钟,再写 IO 延迟,最后写例外路径。这样结构清晰,不容易漏。另外,每次修改 SDC 之后,一定要重新跑一遍 STA,看看有没有新增的 violation。

好了,这一章的内容就到这里。STA 是后端设计的核心技能,你得多练、多踩坑,才能真正掌握它。