%Warning-SELRANGE: Selection index out of range 排查:Verilator 位选越界修复
更新时间:2026-10-29。FAQ 速查页,属于 EDA 报错排查系列。
%Warning-SELRANGE: Selection index out of range是 Verilator 对「位选/部分选择的下标超出向量声明范围」的告警——比如 8 位向量data[7:0]却写了data[8]。下标问题还有一类近亲:可变下标的位宽超出寻址需要时报的是 Verilator WIDTH 族告警,本篇会把两者的分野讲清楚。
首屏答案:SELRANGE 的意思是取位表达式的下标区间不在信号的声明区间内。wire [7:0] data 的合法下标是 0 到 7,写 data[8] 或 data[9:6] 都是越界,Verilator 直接报 SELRANGE 并给出越界区间(8:8 outside 7:0)。而 data[idx] 这种可变下标,当下标信号的位宽比寻址所需更宽时(8 项只要 3 位、idx 却有 4 位),Verilator 5.020 报的是 WIDTHTRUNC 而不是 SELRANGE——前者是编译期就能确定的越界,后者是「运行时才可能越界」的位宽不匹配。修法永远是让下标回到合法区间:常量改回声明范围内,可变下标先判界再取位,越界时给确定的默认值。
本文要回答的问题
%Warning-SELRANGE报错串里8:8 outside 7:0这组数字怎么读?- 常量位选、部分选择、可变下标三种越界各报什么告警?
- 可变下标越界为什么仿真器之间行为不一致?
- 修完怎么确认告警闭环?
一、报错原文与现象
最小复现把三种越界写法放在同一个模块里:8 位输入 data,4 位索引 idx(范围 0~15,而 data 只有 8 位):
module selrange_bad (
input wire [7:0] data,
input wire [3:0] idx,
output wire bit_const,
output wire bit_var,
output wire [3:0] nibble
);
assign bit_const = data[8]; // 常量位选越界:合法下标是 0~7
assign bit_var = data[idx]; // 可变下标超宽:idx 最大 15
assign nibble = data[9:6]; // 部分选择越界:最高到 9
endmodule对这个文件跑 verilator --lint-only -Wall,真实输出如下:
读这段输出抓三个要点。第一,两条 SELRANGE 各自给出越界区间与合法区间的对照:8:8 outside 7:0 表示你取的是 8:8(即 data[8]),合法范围是 7:0;9:6 outside 7:0 同理。第二,可变下标 data[idx] 报的不是 SELRANGE,而是 %Warning-WIDTHTRUNC: Bit extraction of var[7:0] requires 3 bit index, not 4 bits——Verilator 在编译期无法确定 idx 的值,只能指出「这个下标可能越界」的位宽证据,这是两类告警的分水岭。第三,顺带还有一条 UNUSEDSIGNAL 提醒 idx[3] 没被用到,它其实是同一个问题的另一种表述:最高位只在「越界」时才非零。末尾 %Error: Exiting due to 4 warning(s) 表示 lint 模式下告警被提升为退出错误,CI 会卡在这里。
二、越界的三种形态与根因
三种写法共享一条根因——下标区间与声明区间不一致——但编译期的可确定性不同,告警族和修法也不同:
这张漏斗卡的读法是按「编译期能不能确定越界」分两层:常量下标的值写在源码里,Verilator 直接算出 8 超出 7:0,报 SELRANGE;可变下标要等运行时才有值,工具只能用位宽推断「可能越界」,归入 WIDTH 族。为什么可变下标越界值得认真对待?因为不同仿真器对越界读的默认行为不一致:有的返回 x,有的返回 0,有的回绕到声明区间内——同一份 RTL 在 Icarus 和 Verilator 里可能跑出不同结果,这类「仿真器各自为政」的坑与 iverilog 隐式网线定义 里讲的默认行为分歧是同源的。
三、修复与验证
针对三种写法逐一修:常量下标改回合法范围;可变下标先判界、越界时给确定的默认值 0;部分选择回到声明区间内:
module selrange_fixed (
input wire [7:0] data,
input wire [3:0] idx,
output wire bit_const,
output wire bit_var,
output wire [3:0] nibble
);
assign bit_const = data[7]; // 常量下标改回 [7:0]
assign bit_var = (idx < 8) ? data[idx[2:0]] : 1'b0; // 判界 + 截到 3 位
assign nibble = data[7:4]; // 部分选择改回 [7:4]
endmodule修复有两个细节值得说。第一,idx[2:0] 的截取解决了 WIDTHTRUNC 的位宽不匹配,外层的 idx < 8 判断解决了「运行时才越界」的语义问题——两者缺一不可:只截位宽不判界,idx=8 会回绕成 data[0],静默读错数据;只判界不截位宽,告警还在。第二,判界写法顺带让 idx[3] 被真正用到,UNUSEDSIGNAL 一并消失。对修复后的文件重跑同一条 lint 命令:
# command: docker run --rm -v .:/work -w /work geek-eda:0.2 verilator --lint-only -Wall selrange_fixed.v
# date: 2026-10-29
# host-toolchain: Verilator 5.020 (geek-eda:0.2 docker)
---
$ verilator --lint-only -Wall selrange_fixed.v
# exit_code: 0输出干净、退出码 0,告警闭环。强调一句:不要用 /* verilator lint_off SELRANGE */ 圈掉告警当修复。SELRANGE 指向的是编译期铁定越界的常量下标,硬件上根本不存在那根线,综合器实现出来的行为与你写的表达式对不上——这类告警的正确消法永远是改下标,不是让工具闭嘴。
四、预防清单
- 声明改窄先全文搜下标:把
[15:0]改成[7:0]时,搜一遍所有data[引用,常量下标跟着改。 - 索引位宽按需取:寻址 8 项就用 3 位索引,不要随手
[3:0]取整——位宽刚刚好,WIDTHTRUNC 自然不报。 - 可变下标必判界:
data[idx]出现在源码里时,顺手写上越界分支的默认值,语义立刻明确。 - 跨工具复验:关键设计在 iverilog 和 Verilator 各跑一遍,越界读的行为分歧会在对照中暴露。
- CI 固化
-Wall:让 SELRANGE/WIDTHTRUNC 在提交阶段就拦下,不流进仿真回归。
实验环境
- 机器/系统:docker 容器 geek-eda:0.2(Debian)
- 工具版本:Verilator 5.020 2024-01-01 rev (Debian 5.020-1)
- 日期:2026-10-29
- 完整命令:
verilator --lint-only -Wall selrange_bad.v(报错复现,容器内-v挂载实验目录);verilator --lint-only -Wall selrange_fixed.v(修复后复验)
代码获取
复现用源码(selrange_bad.v 报错版、selrange_fixed.v 修复版)在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/verilator-warn-selrange。装好 Verilator 后跑 verilator --lint-only -Wall selrange_bad.v 复现告警,再对 selrange_fixed.v 跑同一命令验证修复。
这一篇在知识体系里的位置
本篇是 EDA 报错排查系列中 Verilator lint 告警簇的一员:下标越界看本篇;位宽不匹配的全族告警看 Verilator WIDTH 告警;信号有驱动没人读看 %Warning-UNUSEDSIGNAL 排查;分支不全推出锁存器看 %Warning-LATCH 相关排查。开源工具链报错的总入口见 开源 EDA 报错排查导航,整个系列的目录在 EDA 报错排查系列。
总结
%Warning-SELRANGE: Selection index out of range 就是位选/部分选择的下标超出声明区间,告警里的 8:8 outside 7:0 直接给出越界区间与合法区间的对照;常量下标越界报 SELRANGE,可变下标超宽在 Verilator 5.020 里报 WIDTHTRUNC,两者根因相同、修法都是让下标回到合法区间——常量改回声明范围,可变下标「判界 + 截位宽 + 给默认值」三件套缺一不可;越界读在不同仿真器里行为不一致,不靠 lint 拦下就会埋成隐蔽 bug;修复后重跑 -Wall 零告警才算闭环,永远不要用 lint_off 消音代替修复。