PrimeTime 命令返回非零:pt_shell 退出码非零速查
更新时间:2026-10-22。FAQ 速查页,属于 EDA 工具排错系列。PrimeTime(简称 PT,Synopsys 的静态时序分析与签收工具,交互壳是
pt_shell)签收作业挂掉时,调度系统或 CI 只告诉你一件事:退出码非零。这个数字本身不分内科外科——脚本里第一条 ERROR、Tclcatch吞掉的异常、LSF 到点杀作业、tee管道吃掉了真实返回值,都会表现成同一个「失败」。综合侧同款问题见 Design Compiler 命令返回非零,分层分诊的思路相通;本篇只讲 PrimeTime 签收场景特有的坑:Error-ID 连锁、无错误处理脚本跑出残缺时序报告、以及最容易误判的一点——report_constraint有违例(WNL)根本不叫失败。签收方法论背景见 静态时序分析与 PrimeTime 签收。
本文要回答的问题
pt_shell -f signoff.tcl返回非零,日志里 Error、Warning、时序违例混在一起,先看哪一条?- 作业在 LSF 里显示 Exited with code 127 / 134,这些数字是谁返回的?
- 脚本里写了
catch,为什么失败被吞掉、pt_shell 还退出 0? - CI 日志里明明看到 ERROR,为什么流水线判定通过(或反过来,时序违例为什么让作业红了)?
... | tee pt.log之后拿到的退出码为什么总是 0?
一、报错原文与现象
最常见的形态:pt_shell 日志末尾是一条 Error,shell 拿到 1。
$ pt_shell -f signoff.tcl
...
Error: Cannot open file 'inputs/top.netlist.spef.gz' for reading. (UID-32)
Error: Timing analysis was not performed. (UID-114)
$ echo $?
1注意上面两条 Error 的关系:第二条(UID-114)只是第一条的连锁反应——SPEF 没读进来,时序分析自然没法做。首条 Error 才是根因。
第二种形态更迷惑:日志最后一行是正常的结束信息,非零却来自外层。
$ ./run_pt.sh; echo "exit=$?"
...
pt_shell> quit
exit=127pt_shell 自己像是跑完了,127 却是作业脚本层抛出来的(命令找不到),与 PT 无关。第三种形态是「该红的不红」:
pt_shell -f signoff.tcl | tee logs/pt.log
echo $? # 永远是 0:拿到的是 tee 的退出码二、可能原因速查表
| 现象 / 退出码 | 最可能原因 | 处置 |
|---|---|---|
| pt_shell 返回 1,日志有多条 Error | 首条 Error 触发连锁报错 | grep 定位行号最小的 Error-ID |
| pt_shell 返回 0 但报告残缺 | Tcl catch 吞掉了 error | 检查 catch 分支是否记录并上抛 |
| 首个失败后脚本继续跑 | 无错误处理、未 fail-fast | 加统一错误处理钩子(见第三节) |
| LSF:Exited with code 127 | 作业脚本里某命令不存在 / 环境未加载 | 作业首行 source 环境,见 PATH 篇 |
| LSF:code 134 / 139 | 进程被信号杀死(134=SIGABRT,139=段错误) | 按工具 crash 流程收集信息另报 |
| 作业显示 TER_RUNLIMIT / 到点退出 | 超过 bsub -W 或队列 RUNLIMIT | 提限时或拆阶段,别当脚本 bug 修 |
管道后 $? 为 0 | tee 吃掉了 pt_shell 返回值 | set -o pipefail 或用 PIPESTATUS |
| CI/Makefile 中途停止、日志戛然而止 | set -e 顶掉中途失败命令 | 可疑命令显式 ` |
| 日志被截断、看不到首条 Error | 输出重定向覆盖或轮转 | 全程 tee -a 落盘,先存后筛 |
| WNL 违例但退出码 0 | 时序不收敛 ≠ 工具执行失败 | 签收判定要 grep WNL/TNS,不靠退出码 |
三、逐条排查展开
先分清「谁返回的非零」。 直接绕开包装跑一次,立刻接住 $?——shell 里它只记住上一条命令,中间插任何语句都会被覆盖:
pt_shell -f signoff.tcl
echo "PT_EXIT=$?"数字对表:1 是 pt_shell 进程层的普通失败(脚本 Error、Tcl 异常);127 是 shell 层「命令不存在」,问题在作业脚本而不在 PT;大于等于 128 表示被信号杀死,128+6=134 是 abort、128+11=139 是段错误,这类不是脚本问题,按工具 crash 收集 core 与最小复现另报。
只追第一条 Error,按 Error-ID 回溯。 PrimeTime 的报错带稳定标识(形如 (UID-32)),后续消息常是「前置动作没做成,我也没法继续」的连锁反应。不要从日志末尾往前看:
grep -nE "^(Error|.*Error-[A-Z]+)" logs/pt.log | head -10
grep -n "UID-" logs/pt.log | head -10行号最小那条指向的输入文件、约束、单元库缺失问题,九成就是根因;修完它,后面一串 Error 通常一起消失。
用 set echo 把失败行逼出来。 日志只有 Error 没有上下文时,在 Tcl 脚本开头打开命令回显,每条命令执行前先打印自身,日志里最后一条回显就是失败行:
set echo on
set verbose_log_path logs/pt.verbose.log外层 shell 包装则用 bash -x run_pt.sh 或脚本首行加 set -x。两条线对应两层:Tcl 层失败看 echo,shell 层失败看 -x。
检查 catch 是在处理错误还是在埋葬错误。 这是「退出码 0 但报告残缺」的头号来源:
# 危险写法:catch 的返回值被丢弃,异常静默消失
catch {read_parasitics inputs/top.spef.gz}
# 稳妥写法:接住返回码,记录后决定是否继续
if {[catch {read_parasitics inputs/top.spef.gz} msg]} {
puts "ERROR: read_parasitics failed: $msg"
exit 1
}签收脚本尤其忌讳对 read_verilog、read_sdc、read_parasitics 这类前置命令无条件 catch——它们失败后继续跑 report_constraint,产出的是基于残缺数据的时序报告,比直接挂掉更危险。
让脚本 fail-fast,并统一错误出口。 PrimeTime 支持把未捕获错误导向统一处理;最省心的纪律是脚本开头声明失败即停,所有阶段串成一条主干道:
proc abort_on_error {msg} {
puts stderr "FATAL: $msg"
exit 1
}
# 每个关键阶段后自检
if {[catch {read_sdc inputs/top.sdc} msg]} { abort_on_error $msg }无错误处理的脚本会在首个失败后一路冲到底,留下看似完整、实则缺项的报告目录——回归系统只看「作业退出 0」就会放过它。
LSF 的退出码语义要单独看。 bsub 提交成功本身返回 0,与作业成败无关;作业真正的退出码用 bjobs -l <id> 或 bhist -l <id> 查 Exited with code。127 是作业 shell 找不到命令(多半又是环境脚本没 source,见 PrimeTime 命令不在 PATH);134/139 是信号杀死;作业状态是 TER_RUNLIMIT 或 Exited/Exit code 141 一类,则是撞了 bsub -W 限时或队列 RUNLIMIT——先 bhist 确认 elapsed 时间再决定提限时还是拆阶段,不要在脚本里瞎改。
CI / Makefile 里守住 set -e 与 pipefail 两个坑。 set -e 下任何一条命令非零都会让脚本中途消失,症状是日志戛然而止、末尾停在某个无害命令;可容忍的失败要显式标注:
set -euo pipefail
pt_shell -f signoff.tcl 2>&1 | tee logs/pt.log
PT_EXIT=${PIPESTATUS[0]}
echo "pt_shell exit=$PT_EXIT"
exit $PT_EXIT不加 pipefail 时管道退出码取最后一个命令(tee),pt_shell 挂了流水线仍然绿;Makefile 同理,规则里用管道就要在 shell 行显式开 set -o pipefail。日志落盘建议全程 tee -a 追加,避免重跑覆盖后只剩半截、首条 Error 永远找不回来。
读图抓住颜色语义:红色是入口现象,蓝色两步是分诊(先拿真实退出码,再按数字分层),橙色三条才是真正的修复支线,紫色收尾是签收判定。三条支线互斥——退出码 1 进日志查首条 Error,退出码 0 但产物不对查 catch,127 或 128 以上才往作业环境和信号方向走,别一上来就混着查。
最后澄清最常见的误判:时序违例不是工具失败。 report_constraint 打出 WNT/WNS 违例、slack 为负,pt_shell 的退出码仍然是 0——这表示工具忠实地算完了时序,只是设计没收敛。回归判定要把两件事分开测:
PT_EXIT=${PIPESTATUS[0]}
WNL=$(grep -c "VIOLATED" logs/pt_constraint.rpt || true)
[ $PT_EXIT -ne 0 ] && { echo "TOOL FAILED"; exit 1; }
[ $WNL -gt 0 ] && { echo "TIMING NOT CLOSED: $WNL endpoints"; exit 2; }退出码管「工具是否执行成功」,违例计数管「设计是否签收通过」,用不同失败码区分,CI 上才看得出红的是哪一类。约束层面的时序问题见 PrimeTime 时钟/约束缺失,网表层读入事故见 PrimeTime 未解析引用。
四、预防与纪律
- 每个关键阶段后显式检查 catch 返回值,
read_verilog/read_sdc/read_parasitics失败一律exit 1,禁止无条件 catch 后继续产出报告。 - 签收脚本固化 fail-fast 模板:统一错误出口、全程
tee -a落日志、退出码显式接住并exit $PT_EXIT,不靠set -e的默认行为传递。 - CI 与 Makefile 规则一律
set -o pipefail,管道后用PIPESTATUS[0]取 pt_shell 的真实退出码。 - 判定口径双条件:退出码非零 = 工具执行失败;报告 VIOLATED 计数大于零 = 时序未签收,两者用不同退出码上报。
- LSF 作业先
bhist -l看状态与 elapsed,127 归环境、134/139 归 crash、RUNLIMIT 归限时,三类各走各的流程。
这一篇在知识体系里的位置
本篇是 EDA 排错速查簇中「PrimeTime 批量流程/退出码」节点的 FAQ 页,与 PrimeTime 命令不在 PATH(127 的主要来源)、PrimeTime 时钟/约束缺失(违例与失败的分界)、PrimeTime 未解析引用(首条 Error 的常见内容)互补;综合侧对应篇是 Design Compiler 命令返回非零,全簇索引见 开源/EDA 工具报错排查总览。
一句话总结
PrimeTime 命令返回非零先分诊:绕开包装直接接住 $?——1 查日志首条 Error-ID 与其连锁反应,0 但报告残缺查 Tcl catch 吞错并补 fail-fast,127 归作业环境、134/139 归信号 crash、RUNLIMIT 归 LSF 限时;CI 里开 pipefail、用 PIPESTATUS[0] 取真实退出码,最后记住 report_constraint 的时序违例是设计未收敛而非工具失败,退出码与违例计数必须双条件分别判定。