第四章 静态时序分析入门: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% 以上的时序路径。
你可能会问:“为什么输入到输出路径也要检查?” 嗯,有些设计里确实有纯组合逻辑的直通路径,比如一些简单的地址译码器。如果这些路径太长,也会影响芯片的整体性能。
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 是后端设计的核心技能,你得多练、多踩坑,才能真正掌握它。