OpenLANE config 键名无效:拼错的 VERILOG_FILES 为何到 Step 0 才报错
更新时间:2026-11-05。报错排查篇,属于 EDA 报错排查系列。config.json 明明写了源文件列表,flow 却在第一个步骤就报
no such variable,这类 OpenLANE config 键名无效问题最迷惑人的地方在于:配置文件被正常读取、banner 之后的 INFO 一行不差,错键本身不会在启动期触发任何警告。本篇用一组对照实验讲清它的静默透传机制,以及怎么在三分钟内定位到那个拼错的键。配置文件整体缺失的报错是另一条路径,见姊妹篇 No design configuration found 速查。
首屏答案:OpenLane 1.x(2023.07.19)加载 config.json 时只做 JSON 语法解析,不校验键名是否在合法变量表内——每个键被无条件 set 成同名环境变量。把 VERILOG_FILES 拼成 VERILOG_FIELS,拼错的键照样进了环境,而步骤真正要读的 ::env(VERILOG_FILES) 从未被定义,于是 Step 0(verilator_lint_check)抛出 no such variable。修复只有一个动作:键名改回 VERILOG_FILES,复跑即可进入 STEP 1 综合。
本文要回答的问题
- config.json 能被正常读取(log 打印了
Using configuration in),为什么还会报变量不存在? - OpenLane 1.x 对未知键到底校不校验?拼错的键在运行期去了哪里?
no such variable (read trace on "::env(VERILOG_FILES)")这行 traceback 怎么读?- 不靠肉眼逐字比对,有什么办法确认一个键只是被透传、从未被流程读取?
- 修复后复跑,log 里出现什么才算真正过关?
一、报错原文与现象
复现设计是一个 12 位时钟使能计数器(small_counter),config.json 里故意把 VERILOG_FILES 拼成 VERILOG_FIELS(少一个 L、顺序颠倒)。注意 JSON 语法完全合法,配置文件也确实被加载,flow 在 Step 0 lint 阶段中止:
读这张终端卡抓住三条线索。第一,第 10 行 Using configuration in '../designs/lab/config.json' 证明配置关已经通过——文件找到了、JSON 也解析成功,所以这不是 No design configuration found 那类文件层问题,错在文件内容。第二,真正的失败点是 Step 0 (verilator_lint_check),traceback 的核心两行是 no such variable 和 (read trace on "::env(VERILOG_FILES)"):Tcl 在读取一个从未被定义的环境变量,触发读取的代码是 lappend arg_list {*}$::env(VERILOG_FILES)——lint 步骤要把 Verilog 源文件列表拼进命令行,却扑了个空。第三,报错位置在 STEP 1 之前的 Step 0,说明问题与综合器、PDK、布线器都无关,纯是配置变量缺失;同属启动期门禁的 PDK 类报错见 PDK sky130A not found 速查。
二、工具与版本
本次复现全部在 OpenLane 官方 Docker 镜像内完成,版本与实验 log 头部记录一致:
| 项 | 实测值 |
|---|---|
| OpenLane 版本 | 2023.07.19(镜像内 commit d054702b) |
| Docker 镜像 | efabless/openlane:d054702 |
| PDK / 标准单元库 | sky130A / sky130_fd_sc_hd |
| 失败步骤 | Step 0:verilator_lint_check(Verilator lint) |
| 触发方式 | flow.tcl -design 非交互模式;修复后用 -interactive 的 run_synthesis 验证 |
版本信息之所以重要,是因为「未知键不校验」是 OpenLane 1.x 配置加载器的行为:它把 config.json 当作环境变量注入表处理,而不是带 schema 的强类型配置。换到 OpenLane 2.x(配置体系改为 Pydantic 模型)后,未知键会在加载期直接报 validation error,症状和排查路径都不同,不要跨版本照搬结论。
三、根本原因:未知键没有白名单,被静默透传成环境变量
OpenLane 1.x 的配置加载逻辑可以拆成五环,拼错的键在第二环被「吞掉」,债务一直记到第四环才爆:
颜色按角色分:红是根因与报错两端,蓝是 OpenLane 的正常处理环节,橙是整条链的关键——静默透传。读法一句话:加载器的职责只是「把 JSON 的每个键值对翻译成 ::env(键)=值」,它手里没有合法键名表,所以 VERILOG_FIELS 与 VERILOG_FILES 在它眼里没有任何区别,都是一个待注入的变量名。真正消费这个变量的是后续步骤脚本,而 Tcl 读取不存在的 ::env(...) 元素会直接抛 no such variable,错误因此从「加载期」延后到了「首次读取期」。这也解释了报错步骤为什么随拼错的键而变:拼错 VERILOG_FILES 在 Step 0 爆,拼错 floorplan 类键可能要到 STEP 3 才爆——键越晚被读取,潜伏期越长。
速查卡四色对应排查时的四个动作面:红区认触发、蓝区理解机制、橙区识别爆点、绿区核对修复。其中绿区第三条是最实用的自检手段——每次跑完,runs/<tag>/config.tcl 会记录本次实际注入的全部变量,打开它搜你拿不准的键名:搜得到但没有任何步骤引用它,基本就是拼错后被透传的「僵尸键」。
四、排查清单
遇到 no such variable 加 read trace on ::env(...) 形态的报错,按下面顺序收敛,不必从头通读配置:
| 步骤 | 动作 | 判别 |
|---|---|---|
| 1 | 从 traceback 的 read trace on "::env(XXX)" 抄出缺失变量名 | 变量名是白给的第一线索,本例即 VERILOG_FILES |
| 2 | 在 config.json 里搜这个变量名 | 搜不到:漏写;搜得到但拼写不同:拼错(本例搜到的是 VERILOG_FIELS) |
| 3 | 对照 OpenLane configuration/ 目录登记的标准变量名 | 以代码里的登记为准,不凭记忆拼写 |
| 4 | 打开 runs/<tag>/config.tcl 反查 | 拼错键作为原样透传变量出现在这里,标准键反而缺席 |
| 5 | 改键名后整文件复跑 | 不要只重跑单步,环境变量在启动期一次性注入 |
两个容易走的弯路。其一,不要试图用「再加一个正确键、错键留着不管」糊过去——错键会继续作为僵尸环境变量存在,虽不报错但会污染 run 目录的配置快照,日后审计时无法判断哪份配置生效过。其二,dir::src/small_counter.v 这种 dir:: 前缀是 OpenLane 的相对路径语法,不是变量名的一部分,排查时别把它当成拼写错误;容器挂载与路径翻译的问题表现为文件找不到而非变量不存在,那条路径的排查见 docker tag 不匹配速查。
五、修复代码与复跑确认
错误配置与正确配置只差一个键名,其余九个键完全不动:
{
"DESIGN_NAME": "small_counter",
"VERILOG_FIELS": "dir::src/small_counter.v",
"CLOCK_PORT": "clk",
"CLOCK_PERIOD": 20
}把第 3 行的键名改回标准拼写,值保持原样:
{
"DESIGN_NAME": "small_counter",
"VERILOG_FILES": "dir::src/small_counter.v",
"CLOCK_PORT": "clk",
"CLOCK_PERIOD": 20
}改完用交互模式复跑同一份设计,让流程至少穿过综合阶段确认变量被正常消费:
printf "run_synthesis\nexit\n" | ./flow.tcl -design /designs/lab -tag fixed_key -interactive复跑 log 换成了一副面孔——Step 0 的 lint 报错消失,流程直接进入编号步骤:
[INFO]: Using configuration in '../designs/lab/config.json'...
[INFO]: PDK Root: ~/.volare
[INFO]: Process Design Kit: sky130A
[STEP 1]
[INFO]: Running Synthesis (log: ../designs/lab/runs/fixed_key/logs/synthesis/1-synthesis.log)...
[STEP 2]
[INFO]: Running Single-Corner Static Timing Analysis (log: ../designs/lab/runs/fixed_key/logs/synthesis/2-sta.log)...过关信号有两个:一是不再出现 Step 0 (verilator_lint_check) failed,二是 log 出现 [STEP 1] Running Synthesis 并继续向 STEP 2 推进——说明 ::env(VERILOG_FILES) 已被正确注入并消费。CASE A 与 CASE B 两次运行的唯一差异,就是 config.json 里那一个键的拼写。
实验环境
- 机器/系统:云主机 / Ubuntu 22.04(OpenLane 官方 Docker 镜像内运行)
- 工具版本:OpenLane 2023.07.19 / sky130A(docker efabless/openlane:d054702),标准单元库 sky130_fd_sc_hd
- 日期:2026-11-04
- 完整命令:
./flow.tcl -design /designs/lab -tag bad_key -overwrite(CASE A,错误配置复现 Step 0 失败);printf "run_synthesis\nexit\n" | ./flow.tcl -design /designs/lab -tag fixed_key -interactive(CASE B,改回VERILOG_FILES后复跑,穿过 STEP 1 综合与 STEP 2 STA)
代码获取
复现用 RTL(12 位时钟使能计数器 small_counter)、错误与正确两份配置(configs/config.bad.json / config.good.json)及对照运行脚本在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/openlane-config-key-unknown。按目录内 README 把设计目录挂进 OpenLane 容器,执行 ./run.sh 即可重现「Step 0 no such variable 失败 → 键名修正后 run_synthesis 通过」的完整对照。
这一篇在知识体系里的位置
本篇是 EDA 报错排查系列 中 OpenLane 配置错误簇的一员,管的是「文件在、语法对、键名错」这一层:上游的文件层缺失见 No design configuration found 速查,环境层的 PDK 与镜像问题见 PDK sky130A not found 速查 与 docker tag 不匹配速查。四者共享同一条排查主线:先看 log 把问题定位到「文件 / 内容 / 环境」哪一层,再动手改配置,而不是凭感觉重跑。
总结
OpenLANE config 键名无效的本质是 OpenLane 1.x 对 config.json 未知键不做启动期校验,拼错的键被静默透传成同名环境变量、真正的标准键反而从未定义,债务在步骤首次读取该变量时以 no such variable (read trace on "::env(...)") 爆出来;排查直接按 traceback 给出的变量名回 config.json 比对拼写,并用 runs/<tag>/config.tcl 反查僵尸键,改回标准键名后复跑到出现 [STEP 1] Running Synthesis 即告修复。