Verilog/SystemVerilog中能否在循环内修改变量位选位置?
报错根本原因
Verilog语法规定,使用[msb:lsb]格式的固定范围位选时,msb和lsb必须是编译阶段可确定的常量表达式。代码中用integer类型循环变量i计算位选边界,i不属于编译期常量,因此触发illegal operand for constant expression报错。
这个问题不属于思路性错误,只是使用了不符合语法规则的位选写法,有成熟的语法方案可以实现动态位选需求。
推荐实现方案:Verilog-2001 索引部分位选
Verilog-2001标准新增了可变偏移的部分位选语法,支持以变量作为位选起始位置,仅要求选取的位宽为常量,完全匹配每次固定选取8位的场景,语法规则如下:
[起始位 +: 位宽]:从起始位开始,向高位方向选取指定位宽,等价于[起始位+位宽-1 : 起始位][起始位 -: 位宽]:从起始位开始,向低位方向选取指定位宽,等价于[起始位 : 起始位-位宽+1]
针对逐字节选取的需求,每次固定取8位,可直接用-:操作符实现,修正后的代码如下:
integer i; localparam TOTAL_BYTES = 16; // 按实际需求设置总字节数 reg [7:0] x; reg [8*TOTAL_BYTES-1 : 0] data_bus; // 待切片的宽位宽总线 always @(*) begin // 原代码循环逻辑存在死循环问题:i递增同时设置i>=1的终止条件永远成立,此处修正为正确的递减逻辑 for (i = TOTAL_BYTES-1; i >= 0; i = i-1) begin // 等价于原需求的 data_bus[(i*8)-1 : i*8 - 8] x = data_bus[(i*8)-1 -: 8]; // 若需求为给宽总线的第i个字节赋值,反向写法同样合法: // data_bus[(i*8)-1 -: 8] = x; end end
该写法在所有主流仿真器、综合器中均支持,可正常综合为实际硬件电路。
备选方案:generate 生成循环
如果循环的作用是生成固定的硬件连接结构、而非运行时的逻辑赋值,可以使用generate块搭配genvar类型循环变量实现。genvar是生成阶段的常量,使用固定范围位选不会触发报错,示例如下:
genvar i; localparam TOTAL_BYTES = 16; reg [8*TOTAL_BYTES-1 : 0] data_bus; reg [7:0] x_slice [0:TOTAL_BYTES-1]; // 每个字节对应一个独立的8位变量 generate for(i=0; i<TOTAL_BYTES; i=i+1) begin : gen_byte_connect // i为genvar类型,属于生成阶段常量,固定位选写法合法 assign x_slice[i] = data_bus[i*8 +7 : i*8]; end endgenerate
注意事项
- 原示例代码的for循环存在逻辑错误:循环初始值设为大数、终止条件为
i>=1、循环更新为i=i+1会导致i无限递增,触发仿真死循环或综合失败,循环变量更新方向需要和终止条件匹配。 - 索引部分位选的位宽参数必须是编译期常量,不可使用变量赋值,固定位宽选取场景下该限制不会产生影响。
- 若需兼容不支持Verilog-2001的极老旧工具,只能通过移位、掩码拼接的方式实现位选,但目前工业界所有主流EDA工具均已支持Verilog-2001标准,无特殊需求无需做兼容处理。
内容的提问来源于stack exchange,提问作者Nisharg
相关产品推荐
相关产品推荐

