OpenSTA 负裕量修复流程:定位最差路径、改 RTL 或改约束、复跑收敛闭环
更新时间:2026-11-03。OpenSTA report_checks 报告解读 解决的是「报告怎么读」,这一篇解决紧接着的问题:读到负 slack 之后干什么。修复不是一拍脑袋改时钟,而是「定位最差路径 → 判断违例性质 → 选修复手段 → 重新综合或改约束 → 复跑 setup 与 hold」的闭环。全部数字来自 Yosys 0.33 与 OpenSTA 2.0.17 对一份最小教学设计的真实运行。
本文要回答的问题
- 拿到一份尾部写着 VIOLATED 的报告,按什么顺序定位是哪条路径、哪几级逻辑拖垮了时序?
- 修复负 slack 有哪两条路线,什么情况下改 RTL、什么情况下才能动 SDC?
- 插了流水寄存器以后为什么必须重新综合,STA 命令序列怎么接回闭环?
- 放松周期会不会只是把问题盖住?修 setup 为什么要同时复跑 hold?
这张卡是本篇的主线:定位与定性决定后面三拍,任何修复动作之后都必须回到第 5 拍验证,而不是看到红字消失在屏幕外就算修好。
一、这一步在流程里的位置
STA 不是一次性命令,而是综合与约束之间的反馈回路。上游交给分析环节的是三样东西:时序库(liberty)、综合网表(Verilog)、时钟约束(SDC 风格的 Tcl)。报告出现负 slack 后,修复动作分两条路回到上游:动结构就回 RTL 重新综合,动预算就改约束文件,二者最终都要再过一遍 STA。
读图抓住两个回路的区别:改 RTL 的回路要经过 Yosys 重新生成网表,门级结构真的变了;改 SDC 的回路只动时钟预算,网表一个字节都不变,所以它只能用于「约束本身定得不合理」的场景。STA 的第一拍怎么建工程见 OpenSTA 第一次静态时序分析,SDC 怎么写见 OpenSTA SDC 约束文件写法。
二、命令逐段解读:定位、定性、复跑
定位最差路径只用一条命令,关键是旗标要给全:
report_checks -path_delay max -fields {slew capacitance}-path_delay max 做 setup 检查(捕获沿取最晚的下一个周期);-fields {slew capacitance} 让报告多出 Slew、Cap 两列,定位时可以顺手看到是不是某一级负载异常偏大。2.0.17 里字段名是全称 capacitance、slew、input_pin、net,写简写会被忽略。报告默认只给每个端点最差的一条路径,想看同一端点的上升/下降两条就加 -endpoint_count 2。定性阶段再补两条:report_checks -unconstrained 把漏约束的端点也列出来(全部端点都已受约束时它与默认报告相同);report_checks -path_delay min 出 hold 报告,修复后必跑。
读报告时只取三样东西:Endpoint(谁在收数据)、data path 里累计延时最大的那几级(修哪)、slack 数字(差多少)。字段的逐行读法在 OpenSTA report_checks 报告解读 里讲过,这里不再展开。
决定改 RTL 后,重新综合的命令序列如下,dfflibmap 负责把行为级触发器映射到库单元,abc -liberty 负责组合逻辑的工艺映射:
yosys -p 'read_liberty -lib teaching.lib
read_verilog rtl/top_fix.v
synth -flatten -top top
dfflibmap -liberty teaching.lib
abc -liberty teaching.lib
write_verilog -noattr -noexpr build/netlist_top_fix.v'本次实验的组合门在 RTL 里已直接例化为库单元,Yosys 日志如实打印 mapped 2 $_DFF_P_ cells to \DFF_P cells 之后 Don't call ABC as there is nothing to map;产线上行为级 RTL 的逻辑会真正进 ABC 映射。改 SDC 则没有综合这一拍,把 create_clock -period 的数值改对,直接重跑 STA。
三、输入输出文件
修复闭环涉及的文件分两类。一直不变的是 teaching.lib(教学用最小库:DFF_P 的 CLK→Q 为 0.20ns、setup 0.10ns,BUF_X1 每级 0.30ns,不对应任何真实工艺,产线替换为 PDK 签核库)。每轮迭代变化的是网表与约束:
| 文件 | 角色 | 这一轮改了什么 |
|---|---|---|
rtl/top_bad.v | 违例版 RTL:两级寄存器间串四级 BUF | 初始版本 |
rtl/top_fix.v | 修复版 RTL:链中间插一级寄存器 | 新增一个 always 赋值 |
build/netlist_*.v | Yosys 写出的门级网表,STA 真正吃的文件 | 重新综合后变化 |
bad.tcl / relaxed.tcl / fix.tcl | STA 脚本(库+网表+时钟+报告命令) | 分别对应三拍实验 |
Yosys 写出的网表是标准结构写法,触发器已经是库里的 DFF_P,OpenSTA 直接 read_verilog 就能链接:
DFF_P _1_ (.C(clk), .D(d_in), .Q(c0));
BUF_X1 u0 (.A(c0), .Y(c1));
BUF_X1 u1 (.A(c1), .Y(c2));四、常见错与检查点
| 现象 | 先查什么 | 纠正 |
|---|---|---|
| 对着 setup 报告修了半天,流片后 hold 翻车 | 是否只复跑了 max | 每次修复后 max 与 min 都要重跑 |
| 违例消失但 FPGA 上提不了频 | 是否靠无限放松周期消字 | 先确认目标规格周期,放松只能在规格允许范围内做 |
| 改了 RTL,STA 数字纹丝不动 | STA 读的是不是旧网表 | 改 RTL 必须重新综合,并确认 read_verilog 指向新文件 |
| 报告里的违例路径「不认识」 | 报告头端点是否没被时钟覆盖 | 先跑 report_checks -unconstrained 排除漏约束,再谈优化 |
第四条要多说一句:漏约束的端点不参与正常检查,在大设计里常伪装成诡异报告。负 slack 本身的物理含义与分级见 OpenSTA slack (VIOLATED) 负裕量解读。另外记住 路径 VIOLATED 时 sta 进程退出码仍是 0,脚本里不能用退出码判时序。
五、真实跑一遍:违例、放松、插流水级
违例版 RTL 在两级寄存器之间串了四级 BUF,数据路径到达时间 0.20+4×0.30=1.40ns。周期先定 1.2ns,要求时间 1.20−0.10=1.10ns:
读这张报告:Endpoint 是收端触发器 _0_,data path 上 u0–u3 四级 BUF 每级各 0.30ns,累计 1.40ns;底部 1.10−1.40 得出 −0.30 slack (VIOLATED)。差 0.30ns,恰好是一级 BUF 的延时,这直接暗示修复方向:把链切断。
第一条修复路线只改约束:同一张网表,周期从 1.2ns 放松到 2.0ns,数据路径数字一个不变,要求时间抬到 1.90ns:
slack 变成 +0.50 MET,但这是以降到 500MHz 为代价的。若 1.2ns 周期是硬规格,这条路只是消字,没有修结构,必须走第二条路线。
第二条路线改 RTL:在四级 BUF 中间插入一级流水寄存器 sm,一条 1.40ns 的长路径切成两段各 0.80ns(0.20+2×0.30)。用 Yosys 重新综合出新网表,周期维持 1.2ns 复跑:
两段路径都只剩两级 BUF,到达 0.80ns、要求 1.10ns,+0.30 slack (MET),频率没有让步。代价是数据从输入到输出多一个周期延迟、多一个触发器——这正是流水线修复的固有成本。最后同场景的 report_checks -path_delay min 给出 hold slack +0.80 MET,确认切短路径没有引入 hold 违例,闭环到此才收敛。
实验环境
- 机器/系统:本地 Docker 容器,Ubuntu 24.04;宿主为 macOS(Apple Silicon)。
- 工具版本:
sta -version输出2.0.17,yosys -V输出Yosys 0.33 (git sha1 2584903a060)(镜像 geek-eda:0.3-sta)。 - 日期:2026-11-03。
- 完整命令:
docker run --rm -v "$PWD":/work -w /work geek-eda:0.3-sta bash run.sh(等价于make run),先综合 top_bad 与 top_fix 两个版本,再依次跑 bad、relaxed、fix 三个 STA 场景。 - 实验边界:教学 liberty 只有三个单元、标量延时,数值用于演示修复闭环,不能外推为任何工艺的签核结论。
代码获取
完整教学库、两版 RTL、综合模板与三个场景 Tcl 位于 geek-doc.cn/experiments/flow-opensta-fix-negative-slack,克隆后进入该目录执行 make run 即可在 geek-eda:0.3-sta 容器中复现 −0.30 VIOLATED、放松周期 +0.50、插流水级 +0.30 三份对照报告,本机装有 Yosys 与 OpenSTA 时也可用 STA_BIN=sta YOSYS_BIN=yosys bash run.sh 直接运行。
这一篇在知识体系里的位置
本篇讲「负 slack 之后的修复闭环」:还没跑过第一份报告,先读 OpenSTA 第一次静态时序分析;报告逐字段读不熟,配 OpenSTA report_checks 报告解读 一起看;想建立 setup/hold 的物理概念,读 静态时序分析是什么 与 STA setup 检查原理(OpenSTA 实操);修复中发现约束写了不生效,读 OpenSTA clock uncertainty 未生效排查。
总结
OpenSTA 负裕量修复流程的核心是五拍闭环:用 report_checks -path_delay max -fields {slew capacitance} 定位最差路径的 endpoint 与拖时长的几级逻辑,先用 -unconstrained 排除漏约束,再在「改 RTL」与「改 SDC」之间选手段——放松周期不改网表、只在规格允许时使用,插流水寄存器切短组合路径才是保频率的结构修复,但必须经 Yosys dfflibmap 加 abc -liberty 重新综合出新网表;最后复跑 setup 与 hold 两份报告,本次实验中 1.40ns 长路径在 1.2ns 周期下为 −0.30 VIOLATED,放松到 2.0ns 转正 +0.50 但降频,插一级流水级后两段各 0.80ns、同周期转正 +0.30 且 hold 仍为 +0.80 MET。全程只认 slack 数字判收敛,因为违例时 sta 退出码依然是 0。