Yosys 参数类型不匹配:字符串 8 静默变成 56 位宽
更新时间:2026-10-30。本文属于 Yosys 参数类型不匹配排查 的一个长尾案例。把
.WIDTH("8")误写成字符串,看起来和.WIDTH(8)只差一对引号,Yosys 0.33 却既不报错也不按十进制文本解析:它把字符8的 ASCII 位型当成参数值,最终得到 56 位总线。相关但不同的连线宽度告警可看 Yosys width mismatch 排查。
首屏答案:这是静默位型转换,不是「字符串转整数失败」。实测坏例退出码为 0,参数显示为 8'00111000,即十六进制 0x38、十进制 56;修复是写显式数值 .WIDTH(8),派生位宽继续由 INDEX_WIDTH = $clog2(WIDTH) 计算,再用 stat 或 dump 检查实际位宽,不能只看综合是否成功。
这张对照卡要抓住「文本形态」和「常量位型」的区别。引号不是可忽略的排版符号,它改变了 Verilog 常量的类型与位宽;所以即使形参声明带 integer,也不能把传入的字符串当作需要解析的十进制文本。
本文要回答的问题
- Yosys 对
.WIDTH("8")会报错、忽略,还是静默转换? - 为什么字符
8最终控制出 56 位,而不是数值 8? - 综合退出码为 0 时,怎样确认参数值和派生位宽?
- 如何修改显式参数,并用可重复的数值检查证明已经修复?
一、完整现象:没有报错,WIDTH 却变成 56
最小坏例让 WIDTH 直接决定 mask 的端口宽度,并把实例输出接到观测端口。这样无需搭建仿真激励,参数一旦算错,线网宽度就会留下证据。
module sized_bus #(
parameter integer WIDTH = 8
) (
output wire [WIDTH-1:0] mask
);
assign mask = {WIDTH{1'b1}};
endmodule
module param_string_bad (
output wire [55:0] observed
);
sized_bus #(.WIDTH("8")) u_dut (
.mask(observed)
);
endmodule运行 read_verilog、hierarchy -top param_string_bad、proc、stat 与 dump 后,关键结果集中在下面这段记录。它同时给出两次退出码、参数位型和修复后的线宽,避免把「进程成功」误读成「参数正确」。
BAD_EXIT=0 是最关键的反直觉点:Yosys 没有拒绝这次覆盖。Parameter \WIDTH = 8'00111000 表示参数拿到的是 8 位二进制常量,而 wire width 56 证明这个常量已经参与范围计算。末尾的 FIX_EXIT=0 与 8 位、3 位两条记录则是修复组;同一脚本两组的退出码都为 0,真正区分对错的是数值证据。
二、工具版本与根因:字符 8 的位型是 0x38
本结论来自 Yosys 0.33 (git sha1 2584903a060),不把它外推成所有前端、所有版本的统一行为。排查类似问题时必须记录版本,因为参数覆盖发生在 Verilog 前端与层次展开阶段,不同实现对非常量或类型边界的诊断可能不同。
| 写法 | 常量含义 | 本版本实测结果 |
|---|---|---|
.WIDTH("8") | 一个字符的字符串位型,ASCII 0x38 | 8'00111000,十进制 56 |
.WIDTH(8) | 无基数十进制整数 | 十进制 8,内部显示有符号 32 位常量 |
.WIDTH(8'd8) | 显式 8 位数值常量 | 数值 8,位宽明确 |
根因不在 integer 关键字失效,而在覆盖值先以字符串常量的位型进入参数系统。Verilog 字符串可以打包成位向量;这里的单字符 8 恰好占 8 位,二进制为 00111000。随后 [WIDTH-1:0] 使用这个数值求范围,自然成为 [55:0]。integer 没有触发「读取字符文本再调用 atoi」这类语言运行时转换,也不会自动把 ASCII 码减成数字。
这也解释了为什么错误容易漏过:语法合法、参数存在、位宽为正、输出连接也能成立。对综合器来说,56 只是一个不寻常但完全可计算的参数;对设计者来说,它却可能让存储深度、地址范围和资源估算一起偏离预期。
这类引号通常不是手写 RTL 时偶然多敲的,而是从生成层带进来的。例如脚本先保存 width = "8",再直接拼出 .WIDTH(${width}),生成的实例就会带字符串引号;Tcl 把值作为字符串保存并不奇怪,但拼进 Verilog 源码前要区分数值字面量与真正的字符串常量。代码评审时可重点看三处:参数生成模板是否额外补了引号,配置转换是否把数字统一序列化成字符串,以及宏展开后是否形成 "8"。修复应落在生成源头,不要只手工改某一次生成文件,否则下次再生成就回退。
还要区分本例与「把字符串当文本使用」的合法场景。文件路径、显示信息等参数确实需要字符串,不能为了规避本例而全局删除引号;只有深度、宽度、使能值等数值形参必须传数值字面量。最稳的约定是在模块接口注释中写清参数含义与合法范围,并在生成模板里对数值字段做格式校验。
三、排查清单:退出码之后继续核对参数和位宽
看到位宽异常时,不要只围着端口连接找问题。按下面顺序,先锁定实例参数的实际值,再看派生范围,最后才修复并复跑。红色表示命中的根因,蓝色是检查动作,橙色是修改,绿色和紫色分别对应通过与防复发。
实际项目可压缩成四项检查:看版本,保存 yosys -V;看覆盖值,在 hierarchy 输出中搜索 Parameter;看展开结果,用 stat 的 Number of wire bits 和 dump 的 wire width 交叉确认;看参数来源,若值由 Tcl、Python 或宏生成,确保拼接的是 8 而不是带引号的 "8"。如果参数值正确而位宽仍错,再转去查端口声明和连接关系,不要混淆两条故障链。
四、修复代码:显式数值参数加派生位宽
修复只需要去掉字符串引号,同时把依赖 WIDTH 的位宽集中写成参数关系。这里增加 INDEX_WIDTH,展示数值修复后派生逻辑也应一起验证。
module sized_bus #(
parameter integer WIDTH = 8,
parameter integer INDEX_WIDTH = (WIDTH <= 1) ? 1 : $clog2(WIDTH)
) (
output wire [WIDTH-1:0] mask,
output wire [INDEX_WIDTH-1:0] index
);
assign mask = {WIDTH{1'b1}};
assign index = {INDEX_WIDTH{1'b0}};
endmodule
module param_fixed (
output wire [7:0] observed_mask,
output wire [2:0] observed_index
);
sized_bus #(.WIDTH(8)) u_dut (
.mask(observed_mask),
.index(observed_index)
);
endmodule.WIDTH(8) 是本次故障的直接修复;INDEX_WIDTH 不是额外补丁,而是把位宽关系写成可检查的不变量。三元表达式为 WIDTH=1 保留 1 位索引,避免 $clog2(1) 得到 0 后形成零宽声明。实例侧只覆盖真正需要修改的 WIDTH,其余派生值自动跟随,减少多处手填造成的不一致。
五、复跑 PASS:用数值自检而不是综合成功收尾
复跑脚本除了保留完整 Yosys 输出,还用 grep -F 对参数值和三条线宽做精确匹配。任何一项缺失,set -e 都会让脚本失败;只有坏例被识别为 56、修复例被确认是 8 与 3,才打印最后的 PASS。
=== observed evidence ===
BAD_EXIT=0
FIX_EXIT=0
Parameter \WIDTH = 8'00111000
Parameter \WIDTH = 8'00111000
wire width 56 output 1 \observed
Parameter \WIDTH = 8
Parameter \WIDTH = 8
wire width 8 output 1 \observed_mask
wire width 3 output 2 \observed_index
PASS: string "8" silently became 56; numeric 8 produced widths 8 and 3这里的 PASS 指「实验观察与预期完全一致」,不是替坏例背书。修复证据是 observed_mask 为 8 位、observed_index 为 3 位,并且 Parameter \WIDTH = 8。若只重新运行综合、看到退出码 0 就结束,恰恰会重复最初漏掉问题的原因。
实验环境
- 机器/系统:macOS(arm64)/ Docker
geek-eda:0.2 - 工具版本:
Yosys 0.33 (git sha1 2584903a060)(yosys -V原始输出) - 日期:2026-10-30
- 完整命令:
./run.sh。脚本分别执行yosys -p 'read_verilog param_string_bad.v; hierarchy -top param_string_bad; proc; opt_clean; stat; dump param_string_bad'与yosys -p 'read_verilog param_fixed.v; hierarchy -top param_fixed; proc; opt_clean; stat; dump param_fixed',合并标准输出和标准错误后检查关键数值。
代码获取
坏例、修复例、自动检查脚本和完整原始记录位于公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/ts-yosys-parameter-type-mismatch。准备 Docker 镜像后,在该目录运行 ./run.sh 即可复现;脚本结尾应给出两组退出码与一行 PASS。
这一篇在知识体系里的位置
本篇是 Yosys 参数类型不匹配 中「参数值合法但语义错误」的案例:端口连接已经产生明确宽度告警时看 Yosys width mismatch 排查;参数名本身不存在、需要区分工具诊断时看 iverilog 参数绑定失败排查。参数覆盖、层次展开和线网位宽应放在同一条证据链里检查。
总结
Yosys 参数类型不匹配不一定产生报错:在 Yosys 0.33 中,.WIDTH("8") 会静默采用字符 8 的 ASCII 位型 0x38,让 WIDTH 成为 56,且退出码仍为 0。修复是改成显式数值 .WIDTH(8),保留 $clog2(WIDTH) 等派生关系,并用 hierarchy 参数输出、stat 和 dump 线宽做数值自检;确认 8 位数据与 3 位索引后再收尾,不能把综合成功当作参数正确。