Vivado 命令返回非零:batch 模式退出码报错速查
更新时间:2026-10-22。FAQ 速查页,面向 FPGA 入门 阶段开始用
vivado -mode batch -source build.tcl跑脚本化构建的同学。GUI 里点按钮出错,至少弹窗把错误怼到脸上;batch 模式下 Vivado 往往只在终端末尾留一行退出状态,CI 日志里就一句exited with code 1,真正的原因埋在成百上千行 run log 中。本篇专讲「命令确实跑起来了、结束时退出码非 0」这一族的通用定位法:先认退出码,再找日志首条 ERROR,最后查退出码在 shell 与 Tcl 两层是怎么传播的。如果进程是直接崩溃(段错误弹窗或 core),那是 Vivado 段错误崩溃速查 的专题;进程一直不退出、卡在某个阶段,见 Vivado 运行卡死/超时速查。
本文要回答的问题
vivado -mode batch跑完返回非零,退出码 1、139、137 分别意味着什么?- run log 几千行、满屏 WARNING,第一条真正的 ERROR 怎么捞出来?
- 为什么 Tcl 脚本最后一条命令失败了,Vivado 进程有时却返回 0?
- shell 包了一层脚本后,
set -e、exit $rc会不会把 Vivado 的退出码吞掉或改写? - Tcl 里用
exec调外部命令,子进程非零退出为什么直接打断脚本,catch怎么接?
一、报错原文与一句话根因
CI 或 shell 里最常见的形态,是作业脚本只告诉你进程失败了:
$ vivado -mode batch -source build.tcl
# ... 几千行输出后 ...
ERROR: [Common 17-69] Command failed: synth_design failed
$ echo $?
1第二类是退出码明显大于 128——这不是 Vivado 自己返回的业务错误码,而是进程被信号杀死,shell 按 128 + 信号编号 报告:
$ vivado -mode batch -source build.tcl; echo $?
139 # 128 + 11(SIGSEGV),进程段错误崩溃
$ vivado -mode batch -source build.tcl; echo $?
137 # 128 + 9(SIGKILL),常被 OOM killer 或人工 kill -9 终结第三类出现在 shell 包壳里:明明 Vivado 已经报错,作业脚本却继续往下跑,或者退出码被最后一条命令覆盖:
#!/bin/bash
vivado -mode batch -source build.tcl # 失败了,但脚本没检查 $?
echo "build finished" # 这条返回 0,整个脚本退出码变成 0一句话根因:非零退出码只是「进程没成功结束」的汇总信号,真正原因要靠退出码大小先分诊、再到 Vivado 的 run log 里找时间线上第一条 ERROR。 退出码 1 多为 Tcl 命令/综合实现失败;139 是崩溃;137 是被杀(内存超限最常见);而「明明失败却报成功」几乎都是 shell 或 Tcl 层没有把返回值继续向上传播。
二、报错 → 原因 → 处置速查表
| 退出码/现象 | 最可能原因 | 一条命令确认 | 处置 |
|---|---|---|---|
| 退出码 1 | Tcl 命令失败、综合/实现报错被中止 | 日志里搜首条 ERROR: | 按首条 ERROR 的编号对症处理 |
| 退出码 139 | SIGSEGV 段错误,进程崩溃 | 日志末尾有异常堆栈或直接中断 | 转 段错误崩溃篇,先升级补丁版本 |
| 退出码 137 | SIGKILL,OOM 或外部强杀 | dmesg | grep -i oom(Linux) | 加内存/减并行作业;资源超限见 资源超限篇 |
| 退出码 134 | SIGABRT,多为断言失败 | 日志末尾有 abort 信息 | 同崩溃路径处理,保留日志交官方支持 |
| WARNING 成片找不到 ERROR | 真正的 ERROR 被刷出可视区 | grep -n "ERROR:" vivado.log | head | 只看首条 ERROR,后续连锁报错先忽略 |
| 日志里有 ERROR 但退出码是 0 | Tcl 用了 catch 吞错后正常返回 | 查脚本里 catch 后是否 return -code | catch 到错误后显式 exit 1 |
| shell 脚本退出码恒为 0 | 没检查 $?,被后续命令覆盖 | 脚本结尾 echo $? | set -e 或保存并 exit $rc |
| exec 调外部命令脚本中断 | Tcl 的 exec 子进程非零即抛异常 | 报错含 child process exited abnormally | 用 catch {exec ...} 接返回码 |
| Jenkins/GitLab CI 偶发 137 | 容器内存限制触发 cgroup OOM | 查 CI 节点监控与 OOM 记录 | 调高内存 limit 或降 -jobs 并发 |
| 综合报错但信息指向 IP/约束 | 退出码 1 的具体病因在首条 ERROR | 看 ERROR 方括号编号 [Synth 8-xxxx] | 按编号查对应专题篇,不要只盯退出码 |
三、排查决策图
按「先认退出码、再找首条 ERROR、再查返回值传播」三层收敛,任一岔路命中就按对应专题处理:
读这张图抓住第一层分叉:红色入口之后第一件事是 echo $? 拿数字,而不是直接翻日志——数字先把问题切成「信号杀死」和「命令失败」两类。蓝色两环负责取证:读码、捞首条 ERROR。橙色三条是处置路径,其中「日志有错但退出码为 0」最反直觉,要去查 shell 和 Tcl 两层的传播;所有路径最后都汇到绿色节点,重跑确认退出码 0 且日志无 ERROR,缺一条都不算修好。
四、逐条排查展开
第一步:用退出码完成分诊。 shell 中命令结束后立刻 echo $?(或在 CI 里看步骤的 exit code)。退出码 1 是 Vivado/Tcl 层的常规失败——某条命令没成功,脚本中止;大于 128 则是信号致死:139 对应 SIGSEGV,137 对应 SIGKILL,134 对应 SIGABRT。看到 139 不要在 batch 用法上纠缠,直接按 Vivado 段错误崩溃速查 处理;看到 137 先怀疑内存,综合大设计时 opt_design/place_design 峰值内存不足,Linux OOM killer 会静默发 SIGKILL,用 dmesg | grep -i oom 取证,CI 容器则查 cgroup 内存限制。
第二步:只认时间线上第一条 ERROR。 Vivado 的日志哲学是「第一条错误引发后续连锁报错」,后面几十条 ERROR 往往只是前者的回声。工程目录下 vivado.log 是总日志,各 run 还有 *.runs/synth_1/runme.log:
grep -n "ERROR" vivado.log | head -20
# run 目录的日志信息更全,综合报错优先看它
grep -n "ERROR" D:/proj/demo/demo_top.runs/synth_1/runme.log | headWARNING 默认刷得很凶,排错时反过来用 -n 带行号定位首条 ERROR,再回到该行往上看几十行——真正的诱因(某条命令的异常输出、IP 生成失败、约束找不到)通常就在紧邻上文。把首条 ERROR 的方括号编号(如 [Synth 8-xxxx]、[Common 17-69])拿去对照具体专题,比盯着退出码有用得多。
第三步:查 shell 层的退出码传播。 batch 作业外面几乎总有一层 bash/bat。两个高频写法错误:一是不检查 $?,后续 echo、cd 等命令返回 0 覆盖了真实退出码;二是流水线末尾吞码。稳妥模板是显式保存并退出,或直接 set -e 让脚本遇错即停:
#!/bin/bash
set -euo pipefail
vivado -mode batch -source build.tcl需要在失败时做清理的,保存原始码再原样退出:
vivado -mode batch -source build.tcl
rc=$?
if [ $rc -ne 0 ]; then
echo "vivado failed with $rc" | tee -a build-summary.log
fi
exit $rc注意 set -o pipefail:写成 vivado -mode batch -source build.tcl | tee run.log 时,没有 pipefail 的 shell 默认取管道最后一个命令(tee)的退出码,Vivado 失败也会被记成成功——这是 CI 里「日志明明红了却绿勾」的经典原因。
第四步:查 Tcl 层的 catch 与 exec。 Tcl 脚本自己也会改写退出语义。两种情形:其一,catch {synth_design ...} err 把错误接住后脚本继续执行,最后正常走到结尾,Vivado 进程返回 0——接错可以,但接住后要判断并显式退出:
if {[catch {synth_design -top demo_top} err]} {
puts "ERROR synth failed: $err"
exit 1
}其二,Tcl 的 exec 调外部命令(如第三方脚本、压缩工具)时,子进程只要非零退出,exec 就直接抛 child process exited abnormally,连 stderr 一并变成异常信息;想让失败可控,同样用 catch 包住并取返回码:
set rc [catch {exec ./prepare_data.tcl >@stdout} result options]
if {$rc != 0} {
puts "prepare failed: $result"
exit 1
}另外,脚本末尾用裸 exit(不带参数)等价 exit 0,会把之前的失败状态清零;批处理脚本结尾需要的是「让最后一条 Vivado 命令的成败成为进程成败」,中间不插无意义的 exit 0。
五、预防与纪律
- 作业脚本固定模板:
set -euo pipefail开头,Vivado 命令不裸接管道;必须接tee时保留 pipefail,失败即红。 - 失败先取两样证据:退出码数字 +
grep -n "ERROR" vivado.log | head的首条,提单或问人时一次给全,别只贴最后一行。 - Tcl 接错必收口:每个
catch分支都要有exit 1或等价的失败传播,禁止「catch 完继续跑」;脚本结尾不写裸exit。 - CI 内存留余量:大综合机器内存按工程规模预留峰值,容器显式设置内存 limit 并监控,把偶发 137 当容量问题而不是工具玄学。
- 日志留档再清理:失败构建的
vivado.log与runme.log先归档再reset_run,现场一旦被新 run 覆盖,首条 ERROR 就永远找不回来了。
这一篇在知识体系里的位置
本篇是 FPGA 工具排错速查簇中「批处理运行失败」节点的通用入口 FAQ,讲退出码与日志的定位方法,不展开任何一个具体病因。进程崩溃专题见 Vivado 段错误崩溃速查,卡住不退出见 Vivado 运行卡死/超时速查,137 背后的资源超限见 Vivado 资源超限报错速查,磁盘写满导致的写入失败见 Vivado 磁盘空间不足速查,版本与工程不兼容见 Vivado 版本不兼容报错速查;batch 流程的基础用法见 Vivado 快速上手,更多入门条目在 FPGA 入门目录。
一句话总结
Vivado 命令返回非零先看数字:退出码 1 是 Tcl 命令失败,立刻 grep -n "ERROR" vivado.log | head 只认时间线上首条 ERROR 并按方括号编号对症,139 是段错误崩溃、137 多为 OOM 强杀、134 是 abort,分别转崩溃与资源专题;「失败却返回 0」查两层传播——shell 用 set -euo pipefail 并保存原始码 exit $rc,防管道被 tee 吞码,Tcl 里 catch 与 catch {exec ...} 接住错误后必须显式 exit 1、结尾不写裸 exit;修完重跑要同时满足退出码 0 与日志无 ERROR 才算闭环。