OpenSTA read_spef 寄生文件读取失败:SPEF 名映射与网表层级对不上的排查
更新时间:2026-11-12。长尾速查篇,属于 EDA 报错排查系列,汇总入口见 开源 EDA 报错排查导航。
静态时序分析里,SPEF(Standard Parasitic Exchange Format,标准寄生参数交换格式)负责把布线后的电阻电容交给分析工具。OpenSTA 的 read_spef 在名映射对不上时并不中止流程,只打印 instance ... not found 警告,随后 report_checks 照常给出一条 MET 路径。本篇用一份两级缓冲器的最小工程,把 OpenSTA read_spef 这类「警告不报错、结果已失真」的问题跑透;延迟反标阶段的同类现象另见 OpenSTA read_sdf 标注失败:实例路径与设计名不匹配排查,本篇只讲 SPEF。
首屏答案:SPEF 的 *CONN 段按「实例名:引脚名」全名去链接后的网表里查找对象,坏例把负载引脚写成网表中不存在的 u_bufx:A,read_spef 连报 instance u_bufx:A not found 与 net u_bufx not found 两条警告后跳过该网络的寄生标注,连线电容按零处理,时序路径只保留单元弧延迟,报告照样输出且系统性偏乐观。修复方式是把 SPEF 改回真实层级 u_buf2:A,复跑零告警,再用 report_net -connections 核对驱动与负载落点。
这张漏斗对应本文的排查顺序:先承认警告就是故障本身,再回到 SPEF 文件按行号定位连接段,接着核对分隔符约定,最后用网表侧的 report_net 反查真实引脚。跳过警告核对、只看末尾 slack 是否 MET,是这类问题最常见的弯路。
本文要回答的问题
- OpenSTA read_spef 报
instance ... not found、net ... not found的完整原文是什么,警告之后流程为什么还能继续? - SPEF 的
*NAME_MAP、*CONN、*CAP段各自按什么名字去网表里找对象,层级分隔符和引脚分隔符分别是什么? - 寄生标注被跳过后,
report_checks的数字为什么偏乐观,零连线电容在报告里留下什么痕迹? - 修复后应该看到哪些输出才算闭环,为什么本例好坏两次运行的 slack 数值相同?
一、完整警告原文
最小复现工程顶层 top 只有两级缓冲器:u_buf1 驱动内网 n1,u_buf2 做负载。坏 SPEF 文件 top-bad.spef 在 *CONN 段把负载引脚写成了网表里根本不存在的 u_bufx:A,*CAP 段跟着引用同一个名字。在容器内执行 sta -exit -no_init -no_splash e135-bad.tcl,完整输出原样摘录如下:
===== [1] 复现原始报错:e135-bad.tcl =====
Warning: top-bad.spef, line 22 instance u_bufx:A not found.
Warning: top-bad.spef, line 26 net u_bufx not found.
Startpoint: d (input port clocked by clk)
Endpoint: q (output port clocked by clk)
Path Group: clk
Path Type: max
Delay Time Description
---------------------------------------------------------
0.00 0.00 clock clk (rise edge)
0.00 0.00 clock network delay (ideal)
0.50 0.50 ^ input external delay
0.00 0.50 ^ d (in)
0.05 0.55 ^ u_buf1/Y (BUF_X1)
0.05 0.60 ^ u_buf2/Y (BUF_X1)
0.00 0.60 ^ q (out)
0.60 data arrival time
10.00 10.00 clock clk (rise edge)
0.00 10.00 clock network delay (ideal)
0.00 10.00 clock reconvergence pessimism
-0.30 9.70 output external delay
9.70 data required time
---------------------------------------------------------
9.70 data required time
-0.60 data arrival time
---------------------------------------------------------
9.10 slack (MET)警告要分两条读。第一条指向 top-bad.spef 第 22 行的连接引脚 u_bufx:A:OpenSTA 在链接后的网表里找不到这个实例引脚对象。第二条指向第 26 行,*CAP 段引用的网络名 u_bufx 同样查无对应网络,于是整条 D_NET 的寄生标注被跳过。关键在于级别是 Warning 而不是 Error——工具没有中止,随后照样打印了从 d 到 q 的完整路径,slack 9.10 且 MET。如果脚本只按退出码或 slack 正负判断成败,这个故障会被直接放行。
二、工具与版本
本次实验在 macOS 上用 Docker 镜像 geek-eda:0.3-sta 运行,容器内 OpenSTA 版本为 2.0.17,命令统一带 -exit -no_init -no_splash 走批处理模式,不进交互界面。版本先固定,因为 read_spef 的诊断措辞随版本演进,老版本可能不给行号,本文的警告原文只对 2.0.17 这个构建负责。
问题发生在寄生标注阶段,时序引擎本身、Liberty 库和 SDC 约束都是好的:read_liberty、read_verilog、link_design、read_sdc 四步全部静默通过,只有 read_spef 出警告。多工作角下如何组织 SPEF 与约束的加载顺序,可参考 OpenSTA 多角多模时序分析流程。
三、根本原因:SPEF 全名在网表里查无对象
SPEF 不是按信号名随便对应,它有一套严格的名映射机制。文件头先用 *DIVIDER / 声明层级分隔符、*DELIMITER : 声明实例与引脚之间的分隔符;*NAME_MAP 段把内部短名(如 *1)映射到真实网名(如 n1);每个 *D_NET 段的 *CONN 列出该网络的全部连接引脚,写法是「实例路径:引脚名」全名,*CAP 段的接地电容节点同样写连接引脚全名而不是网名 ID。OpenSTA 读入时拿着这些全名去 link_design 之后的网表实例表里逐个查找,查不到就发警告并丢弃该段标注。
图中灰色是分隔符约定,蓝色是 SPEF 解析与查找动作,紫色是网表侧的对象供给,绿色是名映射命中的正常支路,红色是本例实际走到的故障支路。坏例的网表只有 u_buf1、u_buf2 两个实例,u_bufx 从未在 Verilog 中出现过,全名 u_bufx:A 必然失配;失配后该网络没有任何电容电阻入账,时序引擎按零连线电容处理。下图画出这条静默回退路径,解释警告为什么能与 MET 报告共存:
蓝色是入口动作,红色是故障节点,橙色是工具的容错处理,灰色是建模层面的隐式回退。末端红色提醒:报告能出不代表数据可信,缺失的连线电容只会让延迟偏小、slack 偏大,在紧路径上足以掩盖真实违例。整片 SPEF 的合并与层级展开流程可参考 全芯片 STA 的 SPEF 合并与标注流程。
四、排查清单:先对分隔符,再对实例全名
落地排查按下面六步走,前四步解决「对不上」,后两步防止合法 SPEF 栽在语法细节上。
- 先把警告当故障:
read_spef任何not found都意味着有网络没拿到寄生参数,批处理脚本应抓取 stderr 中的Warning:并判失败,不能只看退出码。 - 按行号读
*CONN段:警告给出的行号直接落在连接段或电容段,先确认*I 实例:引脚 方向三元组里的实例名与 Verilog 实例是否逐字一致,本例差在u_bufx与u_buf2一个字符。 - 核对两个分隔符:层级路径用
*DIVIDER声明的/拼接,实例与引脚之间用*DELIMITER声明的:拼接;层次设计里子模块引脚要写全路径,如u_core/u_buf2:A,不能只写 RTL 信号名。 - 用
report_net -connections反查网表:对失配网络执行该命令,直接读出真实的 Driver pin 与 Load pin 全名,再回填 SPEF。本例n1的驱动是u_buf1/Y、负载是u_buf2/A,与坏文件里的u_bufx:A一对即见分晓。 - 守住 SPEF 语法细节:OpenSTA 解析器要求
*DESIGN_FLOW的参数带引号;段名是*NAME_MAP而不是*NAMES;内部网络*CAP的接地电容节点写连接引脚全名(如u_buf1:Y),不能写*NAME_MAP里的短名 ID;文件只有D_NET级的*END,不存在文件级*END,多写反而解析失败。 - 改完先看零告警再看时序:复跑时
read_spef与report_checks之间不得出现任何警告,这是标注生效的前置证据;之后再评估时序数字本身。
失败路径的终端记录如下,注意两条警告之后路径报告完整输出,进程并未中断:
五、修复代码与复跑确认
修复只动 SPEF 的连接段与电容段,把不存在的 u_bufx 全部改回网表真实实例 u_buf2,两份文件的差异如下:
*D_NET *1 0.02
*CONN
*I u_buf1:Y O
-*I u_bufx:A I
+*I u_buf2:A I
*CAP
1 u_buf1:Y 0.01
-2 u_bufx:A 0.01
+2 u_buf2:A 0.01
*END驱动侧的 Tcl 流程不需要任何改动,仍然是「读库、读网表、链接、读约束、读 SPEF、出报告」六步:
read_liberty tiny.lib
read_verilog top.v
link_design top
read_sdc base.sdc
read_spef top.spef
report_checks同一条容器命令跑修复后的 good.tcl,read_spef 阶段零警告,report_checks 同样给出 slack 9.10 MET;紧接着用 net.tcl 跑 report_net -connections n1,明确列出 u_buf1/Y 为驱动、u_buf2/A 为负载,证明 SPEF 名映射与网表层级已经对上:
===== [2] 修复后复跑:e135-good.tcl =====
Startpoint: d (input port clocked by clk)
Endpoint: q (output port clocked by clk)
Path Group: clk
Path Type: max
Delay Time Description
---------------------------------------------------------
0.00 0.00 clock clk (rise edge)
0.00 0.00 clock network delay (ideal)
0.50 0.50 ^ input external delay
0.00 0.50 ^ d (in)
0.05 0.55 ^ u_buf1/Y (BUF_X1)
0.05 0.60 ^ u_buf2/Y (BUF_X1)
0.00 0.60 ^ q (out)
0.60 data arrival time
10.00 10.00 clock clk (rise edge)
0.00 10.00 clock network delay (ideal)
0.00 10.00 clock reconvergence pessimism
-0.30 9.70 output external delay
9.70 data required time
---------------------------------------------------------
9.70 data required time
-0.60 data arrival time
---------------------------------------------------------
9.10 slack (MET)
===== [3] 修复后核对:report_net -connections n1 =====
Net n1
Driver pins
u_buf1/Y output (BUF_X1)
Load pins
u_buf2/A input (BUF_X1)这里要如实说明一个反直觉点:好坏两次运行的 slack 都是 9.10,数值没有变化。原因是本例 Liberty 延迟表为单点取值,路径延迟只由单元弧决定,SPEF 电容在这个最小工程里不改变弧延迟数值;两次运行的真正差异在标注状态——坏例的网络寄生被整体跳过、按零电容建模,好例的 0.02pF 电容真正入账。因此判定修复成立看两条证据:read_spef 零警告,以及 report_net 的连接关系与 SPEF 全名一致;绝不能用「slack 没变」反推「原来没坏」。修复段与连接核对的终端记录如下:
实验环境
- 机器/系统:macOS 本机,Docker 镜像
geek-eda:0.3-sta(容器内 Linux 环境) - 工具版本:OpenSTA 2.0.17,批处理命令
sta -exit -no_init -no_splash <tcl> - 日期:2026-11-12
- 完整命令:在实验目录执行
bash run.sh,脚本依次跑sta bad.tcl复现u_bufx名映射失配的两条警告、sta good.tcl验证改回u_buf2:A后零告警复跑、sta net.tcl用report_net -connections n1核对驱动与负载引脚。
代码获取
完整源码与复现脚本位于公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/ts-opensta-spef-read-fail,含 tiny.lib、top.v、base.sdc、好坏两份 SPEF(top.spef 与 top-bad.spef)、三个 Tcl 脚本与 run.sh。本地装有对应 Docker 镜像后,在该目录执行 bash run.sh 即可原样复现「警告复现、零告警修复、连接核对」三步运行。
这一篇在知识体系里的位置
本篇属于 开源 EDA 报错排查导航 的静态时序分析分支,回答「SPEF 名字写在文件里为什么标注不到网表上」。它与 OpenSTA read_sdf 标注失败:实例路径与设计名不匹配排查 是同一家族的两侧:SDF 反标单元弧延迟,SPEF 标注连线寄生参数,两者都靠实例全名与链接后的网表对账,且失败时都只警告不中断;工程上两份文件通常来自同一套层级名,一处失配往往另一处也值得复查。多工作角加载顺序见 OpenSTA 多角多模时序分析流程,大规模设计的 SPEF 合并见 全芯片 STA 的 SPEF 合并与标注流程;全系列入口见 EDA 报错排查系列。
总结
OpenSTA read_spef 的 instance ... not found、net ... not found 本质是 SPEF 名映射在链接后的网表里查无对象,而不是文件读不进来:*CONN 与 *CAP 段按「实例路径:引脚名」全名对账,分隔符由文件头 *DIVIDER /、*DELIMITER : 约定。本例坏 SPEF 把负载写成不存在的 u_bufx:A,OpenSTA 2.0.17 打印两条 Warning 后跳过该网络标注,连线电容按零处理,report_checks 照常输出 slack 9.10 MET,结果系统性偏乐观。排查按「警告即故障、按行号读连接段、核对分隔符、report_net 反查真实引脚、守住 NAME_MAP 与 DESIGN_FLOW 语法细节」五步走;修复把名字改回真实层级 u_buf2:A,复跑零警告且 report_net 显示驱动 u_buf1/Y、负载 u_buf2/A。两次 slack 数值相同是库表单点所致,不代表故障不存在——让脚本拦截 Warning、以零告警加连接核对作为验收口径,才能避免乐观时序流入签核。