BCD加法器Verilog循环索引报错原因咨询(Quartus Error 10734)
在完成HDLBits的Bcdadd100练习时,我尝试在Verilog的for循环中用a[(4*i)+3:4*i]这种类似C/C++的动态索引方式写BCD加法器,但Quartus报出Error 10734,提示“i is not a constant”。想知道这种索引是否符合Verilog语法,以及错误原因。
我的代码:
module testing( input [399:0] a, b, input cin, output reg cout, output reg [399:0] sum ); // bcd needs 4 bits + 1-bit carry --> 5 bits [4:0] reg [4:0] temp_1; always @(*) begin for (int i = 0; i < 100; i++) begin if (i == 0) begin // taking care of cin so the rest of the loop works smoothly temp_1[4:0] = a[3:0] + b[3:0] + cin; sum[3:0] = temp_1[3:0]; cout = temp_1[4]; end else begin temp_1[4:0] = a[(4*i)+3:4*i] + b[(4*i)+3:4*i] + cout; sum[(4*i)+3:4*i] = temp_1[3:0]; cout = temp_1[4]; end end end endmodule
Quartus错误信息:
Error (10734): Verilog HDL error at testing.v(46): i is not a constant File: ../testing.v Line: 46
Error (10734): Verilog HDL error at testing.v(47): i is not a constant File: ../testing.v Line: 47
错误核心原因
Verilog语法明确要求:向量位选择(比如a[msb:lsb])的上下边界必须是编译期常量——也就是编译时就能确定具体数值的表达式。你代码里的i是always块for循环的运行时变量,编译阶段没法确定它的具体值,用它计算位选择的上下界自然不符合语法,这就是Quartus报10734错误的直接原因。
另外你原代码还有逻辑bug:循环里的cout会被每次循环覆盖,最后只保留最后一次循环的结果,完全不符合BCD加法器逐位传递进位的逻辑——每一位的进位应该作为下一位的输入,而不是直接覆盖。
两种可行修正方案
方案1:用Generate循环(推荐,硬件友好)
Generate循环是编译时展开的循环,循环变量genvar属于编译期常量,满足位选择的要求,同时能正确实现进位传递:
module testing( input [399:0] a, b, input cin, output cout, output [399:0] sum ); wire [99:0] carry; // carry[0]是第一位的输入进位,carry[100]是最终输出cout assign carry[0] = cin; // 编译时展开成100个独立的BCD加法单元 generate for (genvar i = 0; i < 100; i++) begin : bcd_add_unit wire [4:0] temp_sum; assign temp_sum = a[4*i+3 : 4*i] + b[4*i+3 : 4*i] + carry[i]; // BCD加法超过9要加6修正(这里用减10等价),同时产生进位 assign sum[4*i+3 : 4*i] = (temp_sum > 9) ? (temp_sum - 10) : temp_sum[3:0]; assign carry[i+1] = (temp_sum > 9) ? 1'b1 : temp_sum[4]; end endgenerate assign cout = carry[100]; endmodule
方案2:用移位操作替代动态位选择
如果坚持用always块的for循环,可以通过移位操作获取每一位的BCD组——移位操作的操作数允许是运行时变量:
module testing( input [399:0] a, b, input cin, output reg cout, output reg [399:0] sum ); always @(*) begin reg [4:0] temp_sum; reg carry_in; carry_in = cin; sum = 0; for (int i = 0; i < 100; i++) begin // 通过移位提取当前i位的BCD组 temp_sum = (a >> (4*i))[3:0] + (b >> (4*i))[3:0] + carry_in; // BCD溢出修正 if (temp_sum > 9) begin sum[4*i+3 : 4*i] = temp_sum - 10; carry_in = 1'b1; end else begin sum[4*i+3 : 4*i] = temp_sum[3:0]; carry_in = temp_sum[4]; end end cout = carry_in; end endmodule
关键知识点总结
- Verilog的位选择(
vector[msb:lsb])必须用编译期常量,运行时变量不能用来计算边界。 genvar是编译期变量,用于Generate循环生成重复硬件;int是运行时变量,只能在always块里做逻辑运算,不能用于位选择。- BCD加法必须做溢出修正:当两数相加结果大于9时,要减去10并产生进位,你原代码漏掉了这个关键逻辑。
内容的提问来源于stack exchange,提问作者johnny_1010

