nextpnr 回归结果丢失速查:CI 跑完 .bit/.asc 没生成或时序报告缺失怎么办
更新时间:2026-10-23。FAQ 速查页,属于 FPGA 工具报错排错系列。CI 或 shell 脚本驱动的回归里,
nextpnr-ice40(或 ecp5/gowin 变体)一条一条刷过去,日志看着「跑完了」,可到归档那一步:cp: cannot stat 'build/seed_3/top.asc': No such file or directory,或者事后做 md5 比对发现某几条产物是昨天的旧文件。比特流没生成、时序报告缺页、产物悄悄是旧的——都叫「回归结果丢失」。本篇把六类真实病因排成速查表。如果产物在但两次回归对不上,那是 nextpnr 结果随机不一致速查 的地盘;如果是连输入文件都找不到,先看 nextpnr 找不到文件路径速查。
本文要回答的问题
- CI 日志里 nextpnr 明明打印到结束,归档步骤却报
No such file or directory,产物去哪了? - 脚本没报错、退出码也是 0,为什么拿到的比特流是旧的?
--randomize-seed多种子回归里「上次有这次没有」是结果丢失还是结果漂移?- 并行回归(
xargs -P/make -j)里个别条目的产物为什么内容对不上号? - 日志被重定向后,怎么把丢失的关键 error 行找回来?
一、现象与报错原文
结果丢失的现场通常出现在归档/汇总步骤,而不是 nextpnr 本身:
1. 归档步报文件不存在。
+ cp build/seed_3/top.asc artifacts/
cp: cannot stat 'build/seed_3/top.asc': No such file or directory2. md5 比对失败:产物存在但内容还是上一轮的。
check md5: build/seed_3/top.asc
expect != actual (file mtime: Oct 22 02:14, regression started Oct 23 01:00)时间戳比回归启动时间还早,说明这次根本没写新文件,脚本把旧产物当新结果收走了。
3. nextpnr 日志尾部其实早就失败了,只是没人看。
Info: Placed 1123 cells of 1450
ERROR: Failed to find a route for net 'clk_i$abc$...'
0 warnings, 1 error或者 placer 阶段失败后只打了 warning 继续走,router 阶段直接不写输出文件:
Warning: placement failed for seed 3, continuing anyway...
Info: routing skipped: no valid placement4. 时序报告缺页:.json 输出在,但 nextpnr --report timing.json 生成的报告里没有 Max frequency 段——因为 Fmax 统计在 route 成功之后才写,route 没跑完报告自然残缺。
二、现象 → 区分点 → 处置速查表
| 现象 | 区分点 / 最常见原因 | 处置 |
|---|---|---|
| 归档步 no such file,日志尾部有 ERROR | nextpnr 中途失败,脚本没 set -e 也没查退出码,继续走 | 脚本加 set -euo pipefail 或显式查 $?;失败条目标记 rerun |
| 产物存在但 mtime 是旧的 | 本次运行没写输出,旧产物被静默复用 | 回归前清空输出目录;归档前校验 mtime 必须晚于启动时间 |
| 单机手动跑有产物,CI 里没有 | 输出用相对路径,CI 工作目录与本地不同,产物写到别处 | 输出一律绝对路径;CI 里 find . -name "*.asc" 定位真实落点 |
| 多种子回归「上次有这次没有」 | --randomize-seed 下某些种子本来就可能 place/route 失败 | 固定 --seed N 回归;失败种子单独记录重跑,见 结果随机不一致速查 |
| 并行回归个别产物内容对不上号 | 多任务共享同一输出目录/同名文件互相覆盖 | 每任务独立目录 build/<name>_<seed>/;禁止共享输出文件 |
| placer 失败只 warning,产物整个缺失 | 脚本 ` | |
| 日志里没有 error 行但产物缺失 | 日志被 > /dev/null 或 tee 截断,关键行丢了 | 全量日志落盘;`grep -nE "ERROR |
三、排查决策树
分诊只问三个问题:这次运行退出码是多少?产物实际写到了哪个路径?文件时间戳是不是本次回归的?三个答案把现场切进四条通道:
读这张图抓住一条主线:先用退出码把「nextpnr 自己失败」与「nextpnr 成功但产物没到手」分开——前者(红色)是编译问题被脚本吞掉,后者(绿色)是路径、覆盖、种子三类工程纪律问题;蓝色是排查动作,橙色是两条修复主线,紫色是收口防复发。最忌讳的是发现产物缺失就直接整批重跑:脚本不查退出码,重跑一百次缺的可能还是同一条。
四、逐条排查展开
脚本没查退出码,是最常见也最便宜确认的一类。 典型写法是循环里裸跑 nextpnr-ice40 ... > run.log 2>&1 然后直接下一条,nextpnr 以退出码 1 结束时脚本毫无知觉,归档步才发现文件没有。确认只要一行:
nextpnr-ice40 --hx8k --package ct256 --json top.json --asc out.asc --seed 3 > run.log 2>&1
echo "exit=$?"exit 非零就实锤。修法是脚本顶部加 set -euo pipefail,或每条之后显式判断退出码、失败即标记 rerun。退出码非零本身的分诊(模块找不到、约束冲突、资源超限等)与 yosys 命令行退出码非零速查 的思路完全同构:先抓日志第一处 ERROR,别从尾部倒着看。
相对路径在 CI 里写到了别处。 本地开发在仓库根跑,--asc build/out.asc 落在 ./build/;CI runner 的工作目录可能是 /builds/group/proj/job_12345,产物写去了那边的 build/,归档步却在预期路径找。确认方法是在 CI 里加一句现场勘查:
pwd && find . -name "*.asc" -newer regression.start根治:输出路径一律用绝对路径(CI 里基于预定义环境变量拼接),不依赖隐式工作目录。
--randomize-seed 造成「上次有这次没有」的假象。 多种子回归里,某些种子 place 不收敛或 route 失败是正常概率事件——上次十个种子全过、这次挂了俩,不是产物丢了,是这两个种子本来就失败。把种子从随机改为固定清单,每个种子独立记录成败:
for s in 1 2 3 4 5 6 7 8 9 10; do
nextpnr-ice40 ... --seed "$s" --asc "build/s${s}/top.asc" || echo "SEED $s FAILED" >> failed.txt
done结果稳不稳、如何选种子,展开见 nextpnr 结果随机不一致速查。
并行回归共享输出目录互相覆盖。 xargs -P 8 起八个任务都写 build/top.asc,后写完的把先写完的盖掉,最后目录里只剩最后一条的产物——其余七条「丢失」。特征是产物数量对、但内容全属于同一个配置。根治一句话:每任务独立目录 build/<design>_<seed>/,输出文件名带任务标识,杜绝任何共享写路径。
日志被重定向吃掉关键行。 > /dev/null 2>&1 或 | tail -20 这类写法让 ERROR 行根本没落盘。纪律是日志全量落盘、屏幕只显示摘要,排错时永远有完整现场可 grep:
grep -nE "ERROR|Warning|failed|skipped" build/s3/run.log五、预防与纪律
- 脚本开头
set -euo pipefail,或逐条显式查退出码;失败条目标rerun,绝不静默继续。 - 输出一律绝对路径 + 每任务独立目录:
build/<design>_<seed>/,日志同目录落盘。 - 回归前清空输出目录:旧产物不残留,「md5 对不上」与「用了旧文件」两个问题一起消失。
- 固定种子清单:回归用
--seed N逐种子跑并各自记录成败,--randomize-seed只用于探索阶段。 - 归档前三重校验:预期条数 vs 实际产物数、产物 mtime 晚于回归启动时间、md5 与当轮构建一致,任一不过就报警。
- placer warning 当失败看:出现
placement failed/routing skipped即标记该条失败,不带病进入归档。
这一篇在知识体系里的位置
本篇是 FPGA 排错速查簇中「nextpnr 回归结果丢失」节点的 FAQ 页:若失败条目展开后是具体的报错(缺文件、解析失败),按报错门类走对应速查页,如 nextpnr 找不到文件路径速查;若产物都在但两次回归对不上,属于结果漂移问题,见 nextpnr 结果随机不一致速查;退出码非零的通用分诊思路还可参考 yosys 命令行退出码非零速查。三篇合起来覆盖回归健康的三个维度:跑没跑成、产物在不在、结果稳不稳。
一句话总结
nextpnr 回归结果丢失的分诊起点是把「归档步找不到文件」还原成该条任务的真实现场:退出码非零就是 nextpnr 中途失败被脚本吞掉,加 set -e 与退出码检查;退出码为零但产物是旧的就是路径写错、并行覆盖或种子随机三类纪律问题,改用绝对路径、每任务独立目录、固定种子清单。根治靠回归前清空输出、日志全量落盘、归档前条数加 mtime 加 md5 三重校验,让每一次回归缺产物都自带可回溯的现场。