建立时间检查原理:从公式推导到 OpenSTA 报告逐列解读
更新时间:2026-10-28。本文是「集成电路大类 · EDA 工具」入门层(beginner)的一篇原理做实文。时序分析入门:建立时间与保持时间 把建立时间(setup time)的概念讲清楚了:触发器不是瞬间采样的理想开关,时钟沿到来之前,数据必须先稳定一小段时间。概念懂了,下一个问题来了:这个「必须先稳定」在工具里到底怎么算?一条路径上几十级门、每级延迟不一样,工具凭什么一句话告诉你「过了」还是「违例」?本篇先用两条公式把建立时间检查原理推到底,再用开源静态时序分析工具 OpenSTA 对一段真实综合网表跑出 setup 报告,把报告里 data arrival、data required、slack 三行数字逐列拆开对回公式。静态时序分析在签核中的位置 一篇讲的是 STA 在流程里管什么,本篇管的是它内部最核心那一次比较。
本文要回答的问题
- 建立时间检查的数学本质是什么?data arrival 与 data required 两条公式各自累加了哪些项?
- 发射沿与捕获沿为什么是「相邻两个沿」,而不是同一个沿?
- OpenSTA 的
report_checks -path_delay max报告里,Delay 列与 Time 列各是什么含义? - slack 为正为什么算过,library setup time 前面为什么是负号?
先把全篇的角色分区和公式结论摆出来,后面每一节都是对它的展开:
这张图抓住一个对称结构:左边一列从发射沿出发往前「加」,右边一列从捕获沿出发往回「减」,最后在下方做唯一一次比较。OpenSTA 报告的排版就是这个结构的竖排版本——上半段是 arrival 账、下半段是 required 账、结尾三行是判决。
一、公式推导:数据什么时候到,什么时候必须到
拿最典型的寄存器到寄存器路径来说。源寄存器 FF1 的时钟脚在 t=0 收到发射沿,数据从 FF1 的 Q 端出发,穿过若干级组合逻辑,到达目标寄存器 FF2 的 D 端。FF2 在 t=T(时钟周期)收到捕获沿。建立时间检查要回答的就是:数据到达 FF2.D 的时刻,是否早于捕获沿减去 setup 时间。
把两段旅程各自写成加法链,就是 STA 教科书里的两条核心公式:
data arrival time = launch edge + Tclk1(源时钟网络延迟) + Tco(寄存器输出延迟)
+ Tcomb(组合逻辑延迟)
data required time = capture edge + Tclk2(目标时钟网络延迟) - Tsetup(库建立时间)
slack = data required time - data arrival time每一项都有明确的物理对应:发射沿时刻由 create_clock 的周期与波形定义;时钟网络延迟是时钟树从时钟源到寄存器 CK 脚的传播延迟,理想时钟(ideal clock)下为 0,真实设计里由时钟树综合决定;Tco 与每级组合门延迟来自标准单元库(liberty 文件)按输入转换时间和输出负载查表加插值算出来的数;Tsetup 是库给每个触发器单元标定的属性——数据必须比时钟沿早稳定这么久,否则触发器可能采到过渡态。
「为什么用相邻两个沿」值得单独说一句。数据由第 n 拍的发射沿打出,它要服务的是第 n+1 拍的捕获——因为同一个沿既要打旧数据又要采新数据,采到的必然是旧数据离开之前的值,考察它没有意义。所以 setup 检查天然跨越一个时钟周期:周期 T 就是这条路径被允许花掉的全部预算。这也是为什么主频一提、周期一缩,最先吃不消的就是 setup 余量。
还有两个公式里没写出来、但工程师必须知道的修正项。其一是时钟偏斜(clock skew):发射寄存器与捕获寄存器的时钟网络延迟通常不相等,Tclk1 与 Tclk2 的差值直接增减有效预算——捕获侧时钟到得晚(正偏斜)对 setup 是利好,到得早(负偏斜)则是利空。本实验用理想时钟,两项都是 0,真实设计里这两个数由时钟树综合后的寄生参数反标决定,经常是 setup 违例的隐形推手。其二是时钟不确定性(clock uncertainty):抖动与裕量通常用 set_clock_uncertainty 从 required 一侧再扣一笔,相当于主动把周期打折扣用。看懂这两个修正项之后,setup 公式在所有 STA 工具里都是同一副骨架,区别只在每一项的取值来源。
发射沿与捕获沿的相位关系,用波形看得最直观:
读这张图盯住两个沿:clk 第一个上升沿(t=0)是发射沿,FF1.Q 在此之后变化,新数据沿组合逻辑逐级传播,到 FF2.D 稳定下来共花 2.82 ns——这就是后文 OpenSTA 报告里 data arrival 一行的来源;clk 第二个上升沿(t=10 ns)是捕获沿,FF2.D 上的数据必须不晚于 10 - 0.08 = 9.92 ns 就已经稳定,这中间空出来的 7 ns 多就是这条路径的「余粮」。波形里的每一个数字都来自下面真实报告,不是示意。
二、实验设计:一段最小但完整的门级路径
要把公式做实,需要一条既有输入延迟、又有多级组合逻辑、终点是真实触发器的路径。本篇实验用 Yosys 把一小段 RTL 综合到 SkyWater 130nm 开源工艺的标准单元(sky130_fd_sc_hd),得到门级网表后交给 OpenSTA 做静态时序分析。约束只有三条:一个 10 ns 周期的时钟 clk,输入端口加 2.0 ns 外部延迟,输出端口加 1.5 ns 外部延迟。
工具报告找到的最差 setup 路径是:输入端口 en → and4(四输入与门)→ nand4(四输入与非门)→ or3(三输入或门)→ a21boi(复合门)→ 触发器 44 的 D 端。这条路径不大,但公式里每一项都能在报告里找到对应行,正适合教学对读。综合用 Yosys 0.9,时序分析用 OpenSTA 2.4.0,均在 eda-golden 镜像的 OpenLane 环境内完成;OpenSTA 没有独立的 sta 发行包,实际调用的是镜像内 /build/bin/sta。
一个容易忽略的细节:STA 报的永远是最差路径。设计里从 en 出发其实有多条路径通往不同触发器,report_checks 默认按 slack 从小到大排序、只打印最紧的那条——因为只要最差的那条过了,其余路径必然全过,这就是「最差路径分析」省掉穷举的数学依据。想看次差的几条,加 -path_count N;想看每个终点各报一条最差路径,加 -endpoint_count。此外 Path Type: max 这一行标记的是延迟角的取法:setup 检查用每级门的最大延迟(最慢情形),与 hold 检查用最小延迟正好相反,同一份网表同一条路径,两种取法算出的 arrival 是不同的数。
在放完整报告之前,先把它与公式的对应关系列成一张对照表,读报告时可以随时回查:
| 公式项 | 报告中的行 | 本实验数值 |
|---|---|---|
| 发射沿时刻 | clock clk (rise edge) | 0.00 ns |
| 源时钟网络延迟 | clock network delay (ideal) | 0.00 ns(理想时钟) |
| 输入外部延迟 | input external delay | 2.00 ns(set_input_delay 2.0) |
| 组合逻辑延迟 | _21_/X 到 _36_/Y 四级门 | 0.21+0.15+0.32+0.14 = 0.82 ns |
| data arrival | data arrival time | 2.82 ns |
| 捕获沿时刻 | clock clk (rise edge) | 10.00 ns |
| 目标时钟网络延迟 | clock network delay (ideal) | 0.00 ns |
| 库建立时间 | library setup time | -0.08 ns |
| data required | data required time | 9.92 ns |
| slack | slack (MET) | +7.11 ns |
三、OpenSTA 报告逐列解读
下面是 report_checks -path_delay max -format full 对这条最差路径的完整输出实录:
这份报告从上往下分四段读。开头三行 Startpoint / Endpoint / Path Group / Path Type 是路径身份证:起点是输入端口 en(被 clk 时钟约束),终点是上升沿触发器 44,路径组 clk,检查类型 max——max 就是 setup 检查(数据「最晚」什么时候到),对应的 min 是 hold 检查。
上半段是 data arrival 的累加账,两列数字各管一件事:Delay 列是这一级自己贡献的增量,Time 列是从发射沿算到这里的累计时刻。clock clk (rise edge) 与 clock network delay (ideal) 两行 0.00 对应公式里的发射沿与理想时钟网络;input external delay 2.00 是 set_input_delay 2.0 这条约束的原样体现——外部世界的数据最迟 2 ns 才到 en 端口。接下来四级门每级一行:and4 贡献 0.21 ns 累计到 2.21,nand4 贡献 0.15 累计到 2.36,or3 贡献 0.32 累计到 2.67,a21boi 贡献 0.14 累计到 2.82。每行行尾的上箭头或下箭头(^ / v)标注这一级输出是上升还是下降翻转——与非门、反相输出复合门会翻转极性,工具按翻转方向查不同的延迟表,这就是为什么同一条路径的上升/下降 arrival 可能不同。最后一行 data arrival time 2.82 就是公式第一条的数值答案:数据在 2.82 ns 时刻到达触发器 D 端。
下半段是 data required 的账,从捕获沿出发:10.00 ns 的第二个时钟沿、理想时钟网络 0 延迟、clock reconvergence pessimism 0.00(这一项是同一时钟既当发射又当捕获时的悲观度回收,理想时钟下为零,有公共时钟树路径时才非零),到达触发器时钟脚。然后是全报告最容易看错的一行:library setup time -0.08——负号不是「建立时间是负的」,而是表示从捕获沿时刻里扣掉 0.08 ns:数据必须在 10.00 - 0.08 = 9.92 ns 之前就稳定。这 0.08 ns 是 sky130 库给 dfxtp 触发器标定的 setup 属性,公式里 - Tsetup 那一项在报告里就长这样。
结尾三行是判决,原样摘录:
9.92 data required time
-2.82 data arrival time
---------------------------------------------------------
7.11 slack (MET)9.92 减 2.82 得 7.11,slack 为正,标注 MET——这条路径离违例还有 7.11 ns 余量。换句话说,即使组合逻辑再慢 7 ns,数据依然赶得上捕获沿。如果哪一天时钟周期从 10 ns 提到 5 ns,required 变成 4.92、slack 缩到 2.11;提到 3 ns 以下,slack 就会翻负变成 VIOLATED,那就是 OpenSTA 报负 slack 怎么修 一篇要处理的局面了。
把读法再压成一句口诀:上半段只加不减、下半段从周期往回扣、结尾一减定生死。Delay 列管「这一级花了多少」,Time 列管「现在是几点」,两半段的分界线永远是 data arrival time 与下一个 clock clk (rise edge)。
顺着这条口诀,拿到任何一份 setup 报告都可以按同一个顺序问诊:先看 Endpoint 与 Path Type 确认这是谁的、哪种检查;再看 arrival 段的每一级 Delay,找出贡献最大的几级门——它们就是优化候选(换驱动更强的单元、减少逻辑级数、插流水线);然后看 required 段的构成,确认时钟周期与 uncertainty 是否符合预期;最后才看 slack 下结论。很多新人读报告的习惯是直奔最后一行 slack,看到 MET 就翻篇——这会错过真正有信息量的部分:slack 只是体检结论,中间两列数字才是病历。
四、从这条报告能带走的三个工程判断
第一,slack 大不代表路径不重要,只代表当前约束下不急。本例 slack 7.11 ns 看起来很富余,但它吃的是「外部输入延迟只有 2 ns」这个假设——如果上游芯片的数据实际 8 ns 才到,arrival 变成 8.82,slack 只剩 1.10。约束写松了,报告再漂亮也是假的安全。
第二,报告里每一级延迟都来自库查表,不是仿真。OpenSTA 不需要跑任何激励向量,它按拓扑把每条从起点到终点的路径都走一遍,延迟从 liberty 库按 slew 与负载插值。这决定了 STA 的两个天性:全覆盖(不遗漏任何路径,不像仿真依赖向量)与只看结构不看功能(假路径也会被报出来,需要 set_false_path 人工排除,虚假路径不管的后果 一篇有专门讨论)。
第三,setup 与 hold 是同一对沿上的两次相反检查。setup 用 max 延迟、检查「数据别太晚」;hold 用 min 延迟、检查「数据别太早走」。两者对工艺角的要求正好拧着:高温低压慢角难过 setup,低温高压快角难过 hold。本篇只展开 setup 一侧,hold 的公式结构与之一模一样,只是把「下一个沿」换成「同一个沿」、把减 setup 换成加 hold。两类违例的成因对照见 建立时间违例与保持时间违例的根因差异。
给读者留一个动手的延伸:把 run.tcl 里的时钟周期从 10.0 改成 3.0 重跑,同一条路径的 required 会缩到 2.92、slack 只剩 0.10,报告其余各行一字不动——这正好验证「周期只影响捕获侧」这一半的公式结构;再改成 2.5,slack 翻负、末尾标注从 MET 变成 VIOLATED,就能亲眼看到一次违例报告长什么样。改约束重跑是熟悉 STA 最便宜的方式,一次实验几秒钟。
这一篇在知识体系里的位置
- 概念前导:时序分析入门:建立时间与保持时间(setup/hold 是什么、为什么存在);
- 流程位置:静态时序分析在签核中的位置(STA 在数字后端流程里管哪一段);
- 上游制造:逻辑综合在流程中的位置(本实验的门级网表从哪来);
- 下游排错:OpenSTA 报负 slack 怎么修(slack 翻负后的处理清单)与 建立时间违例与保持时间违例的根因差异;
- 同族做实:OpenSTA 的
create_clock报告逐行读法(约束侧的对读姊妹篇)。
一句话总结
建立时间检查原理就是两笔账一次减法:data arrival 从发射沿起把时钟网络延迟、外部输入延迟、组合逻辑逐级延迟一路加到触发器 D 端,data required 从下一个捕获沿起扣掉库 setup 时间,slack = required - arrival 为正即通过。本篇用 OpenSTA 2.4.0 对 sky130 真实网表跑 report_checks -path_delay max,实测 arrival 2.82 ns、required 9.92 ns、slack 7.11 MET,报告每一列都与公式逐项对上——会读这三行,setup 报告就没有秘密。
实验环境
- 机器/系统:eda-golden-v1 镜像 / Ubuntu 22.04(OpenLane 容器环境)
- 工具版本:OpenSTA 2.4.0(镜像内
/build/bin/sta,版本行照抄日志OpenSTA 2.4.0 41a51eaf4c);Yosys 0.9(git sha1 1979e0b,负责 RTL 综合到 sky130_fd_sc_hd 门级网表) - 日期:2026-10-28
- 完整命令:
# 1. Yosys 综合(RTL → sky130 门级网表 sta_demo_net.v)
yosys -p 'read_verilog sta_demo.v; synth -top sta_demo; \
dfflibmap -liberty $SKY130_LIB; abc -liberty $SKY130_LIB; clean; \
write_verilog sta_demo_net.v'
# 2. OpenSTA 静态时序分析(约束 + 报告)
docker run --rm -v $PWD:/work -w /work openlane/eda-golden \
/build/bin/sta -exit src/run.tcl
# run.tcl 内容:
# read_liberty $env(STA_LIB)
# read_verilog sta_demo_net.v
# link_design sta_demo
# create_clock -name clk -period 10.0 [get_ports clk]
# set_input_delay -clock clk 2.0 [get_ports {rst_n en}]
# set_output_delay -clock clk 1.5 [get_ports q]
# report_checks -path_delay max -format full代码获取
本篇的 RTL、综合脚本、OpenSTA 约束脚本与完整报告日志已同步到公开仓库:geek-doc.cn/experiments/sta-setup-check-principle-opensta,克隆后进入该目录,按 README 先跑综合再执行 sta -exit run.tcl 即可复现本文报告。