Yosys defparam 警告/报错排查:层次覆盖语义与 #() 传参修复
更新时间:2026-10-30。长尾速查篇,属于 EDA 报错排查系列。遇到 Yosys defparam 警告/报错,先不要把结论写成“Yosys 不支持 defparam”:是否诊断取决于版本、前端和具体层次写法。本文用 Yosys 0.33 跑一个最小设计,再迁移到实例参数列表
#(),相关仿真侧对照见 iverilog 参数绑定失败报错排查。
首屏答案:本次 Yosys 0.33 对 defparam u_override.WIDTH = 8; 没有 warning,也没有 error,而是正常接受;默认实例保持 WIDTH=4,覆盖实例得到 WIDTH=8,依赖参数 OUT_WIDTH=WIDTH+1 也从 5 正确变成 9。尽管如此,新代码仍建议改成 child #(.WIDTH(8)) u_override (...):参数在实例化处绑定,不再单独维护一条相对层次路径。若你的版本真的报“不支持”,应保留该版本的完整输出,不能用本文结果反推旧版本行为。
这张图把“诊断”和“验证”分开:没有红色报错不代表参数一定生效,出现工具能力报错也不能靠猜语义。中间必须留下可检查的参数值与位宽,最后的 #() 迁移才有对照基线。
本文要回答的问题
- Yosys 0.33 对简单 defparam 到底报警、报错,还是静默接受?
u_override.WIDTH的层次路径相对于哪里解析,派生模块在哪里出现?- 为什么可用的新代码仍应优先选择
#()具名传参? - 改写后如何同时证明默认值、覆盖值和派生位宽没有被破坏?
一、先复现原始行为:没有诊断不等于没有覆盖
最小设计有两个 child 实例。u_default 不覆盖参数,u_override 由独立 defparam 改为 8;输出位宽故意交给第二个参数计算,这样能观察 Yosys 是否重新求值依赖参数。
module child #(
parameter integer WIDTH = 4,
parameter integer OUT_WIDTH = WIDTH + 1
) (
output wire [OUT_WIDTH-1:0] y
);
assign y = {OUT_WIDTH{1'b1}};
endmodule
module top (
output wire [4:0] default_y,
output wire [8:0] override_y
);
child u_default (.y(default_y));
child u_override (.y(override_y));
defparam u_override.WIDTH = 8;
endmodule对这份设计执行 read_verilog; hierarchy -check -top top; proc; opt_clean; check -assert; write_json,关键原始输出如下。这里没有 warning/error 行;相反,hierarchy 明确读到了 WIDTH=8,并生成一个独立的参数化模块。
2. Executing HIERARCHY pass (managing design hierarchy).
2.1. Analyzing design hierarchy..
Top module: \top
Used module: \child
Parameter \WIDTH = 8
2.2. Executing AST frontend in derive mode using pre-parsed AST for module `\child'.
Parameter \WIDTH = 8
Generating RTLIL representation for module `$paramod\child\WIDTH=s32'00000000000000000000000000001000'.
2.3. Analyzing design hierarchy..
Top module: \top
Used module: $paramod\child\WIDTH=s32'00000000000000000000000000001000
Used module: \child读这段输出要抓住“一个变体、一个原版”:带 $paramod 的模块服务覆盖实例,原来的 child 仍服务默认实例。后文 JSON 检查进一步得到 u_default WIDTH=4 OUT_WIDTH=5、u_override WIDTH=8 OUT_WIDTH=9,所以本次不是“静默忽略”,而是静默接受并正确展开。这与“工具静默丢弃覆盖”是两种完全不同的问题,修复前必须分开。
二、根因在层次绑定,不在参数算术
defparam u_override.WIDTH = 8; 写在 top 的作用域里,u_override 因而是从当前作用域向下找的实例名,不是 child 模块名,也不是任意全局字符串。Yosys 在层次展开时收集这个覆盖,重新生成参数化模块;OUT_WIDTH=WIDTH+1 属于模块内部依赖计算,所以随新 WIDTH 重新求值。
这也解释了 defparam 的维护成本:实例化在一处,覆盖在另一处,二者靠层次路径连接。移动实例、包裹一层 generate、重命名层级或调整前端处理能力,都会让“覆盖是否仍能解析”变成需要重新验证的问题。这里的“不推荐”不是说本文用法在 0.33 失效,更不是声称标准已经删除 defparam,而是新设计没有必要继续承担这条独立路径的维护风险。
如果当前环境确实出现 defparam not supported 类诊断,先把 yosys -V、完整前端命令和最小源码放在一起。本文没有复现该诊断,不能据此断言旧版本、其他 Verilog 前端或更复杂跨层路径也具有相同行为。
三、三种写法怎么取舍
| 写法 | 参数与实例的位置 | 层次路径风险 | 本文实测 | 建议 |
|---|---|---|---|---|
| defparam | 分离,可位于实例化之后 | 有,目标按作用域解析 | 0.33 静默接受,位宽正确 | 只维护必要的存量代码 |
#() 具名传参 | 同一处,.WIDTH(8) 清晰可见 | 无独立覆盖路径 | 同样得到 8 与 9 | 新代码优先 |
#() 位置传参 | 同一处,但只写 #(8) | 无独立覆盖路径 | 本次未测试 | 多参数模块避免依赖声明顺序 |
具名传参的优势不是“算得更准”,两种写法在本例得到相同结果;优势是把设计意图放回实例化现场,并减少一个故障来源。位置传参则把风险换成参数顺序,模块增加参数时尤其难审阅。
四、按版本、路径、派生结果逐项排查
红色是待解决问题,蓝色两步用于固定事实,橙色是迁移动作,绿色才是行为验证,紫色负责防止回潮。实际排查不要跳过蓝色直接批量替换:如果原设计本来就因路径错误没有生效,只比较“替换后能否编译”会发现不了行为变化。至少要保留一个默认实例、一个覆盖实例,并检查派生端口宽度。
落地时可以按这份清单逐项打勾:
- 版本一致:复现与修复使用同一个 Yosys 构建或同一容器镜像,避免迁移同时夹杂工具升级。
- 路径可解释:在 defparam 所在模块画出到目标实例的相对路径;解释不出每一级,就先缩小设计,不要直接改数值试错。
- 结果可观察:除
check -assert外,用dump或write_json查看派生模块和连接位宽,别只统计 warning 数量。 - 回归有反例:保留默认实例作为反例,防止把模块默认值整体改错后仍得到“覆盖实例看似正确”的假阳性。
五、修复代码:把覆盖值移回实例化处
子模块和顶层端口完全不变,只删除独立 defparam,把 u_override 改成具名参数列表。
- child u_override (
- .y(override_y)
- );
-
- defparam u_override.WIDTH = 8;
+ child #(.WIDTH(8)) u_override (
+ .y(override_y)
+ );默认实例仍写 child u_default (.y(default_y));,这是刻意的对照项。若迁移时误改模块默认参数,两个实例可能一起变宽;若只改顶层连线,子模块派生位宽又可能没跟上。保留默认与覆盖两条断言,能把这两类误改都挡住。
六、复跑 PASS:比较行为,不声称形式等价
修复版走与原设计完全相同的 Yosys pass 和 JSON 检查。终端汇总同时给出两版的参数、位宽、最终退出码:
两版都得到默认 4/5、覆盖 8/9,check -assert 均为 0 problems,末尾 ALL CHECKS PASS、exit=0。这里证明的是本文两个设计在指定 pass 下的参数和连接位宽符合预期;没有运行形式等价检查,也没有覆盖所有 defparam 层次形态,因此不声称两种写法在任意设计里形式等价。
实验环境
- 机器/系统:macOS / Docker 容器 Linux
- 工具版本:Yosys 0.33(git sha1 2584903a060,镜像
geek-eda:0.2);Node.js v26.3.0 - 日期:2026-10-30
- 完整命令:
docker run --rm geek-eda:0.2 yosys -V;bash /Users/derekzhuo/Project/geek-doc.cn/experiments/ts-yosys-defparam-not-supported/run.sh。脚本对defparam.v与fixed.v分别执行docker run --rm -v "$ROOT:/work" -w /work geek-eda:0.2 yosys -p "read_verilog <设计>; hierarchy -check -top top; proc; opt_clean; check -assert; write_json <临时 JSON>",再以node check-json.mjs <临时 JSON> <标签>执行相同断言。
代码获取
完整源码、检查脚本和原始日志位于公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/ts-yosys-defparam-not-supported。安装 Docker 与 Node.js 后,在仓库根目录运行 bash experiments/ts-yosys-defparam-not-supported/run.sh 即可复现两版检查。
这一篇在知识体系里的位置
本篇属于 开源 EDA 报错排查导航 的 Yosys 参数展开分支,关注“工具是否接受”与“参数是否生效”的区别。仿真器对 defparam 错误参数名和错误层级的不同诊断,可对照 iverilog 参数绑定失败报错排查;全系列入口见 EDA 报错排查系列。
总结
Yosys defparam 警告/报错不能脱离版本下结论:本文 Yosys 0.33 对简单 u_override.WIDTH 静默接受,默认实例为 WIDTH=4、OUT_WIDTH=5,覆盖实例为 WIDTH=8、OUT_WIDTH=9。新代码仍应改成 child #(.WIDTH(8)) 具名传参,消除独立层次路径;复跑必须用相同检查同时验证默认与覆盖参数。本文只确认指定设计的行为,不扩展为所有版本支持或任意设计形式等价的结论。