SystemVerilog的for循环中使用非阻塞赋值出现问题?

3

当我在基于SystemVerilog的FPGA设计上工作时,遇到了一个情况,需要在时钟边缘计算4个元素数组的总和。我能够使用for循环和非阻塞赋值语句来完成这个任务。

该设计在Quartus 15.0上成功综合,但是当我尝试在Modelsim Altera上使用相同的RTL运行仿真时,结果出乎意料。我编写了一个示例代码来说明这一点。

module schedule;

logic [7:0] abc [0:3];
logic [7:0] sum=0;
logic clk=0;

always begin 
   #5.0ns clk <= ~clk; 
end

initial begin
    abc = '{1,3,5,6};
end

initial @(posedge clk) begin
    for(int i = 0; i <= 3;i++ ) begin
        sum <= sum + abc[i];
    end
end

initial 
$monitor("Sum is %d",sum);

endmodule

这张图片展示了模拟结果。

在这个样例代码中,使用了非阻塞赋值来计算 sum。原本它应该在 clk 的第一个 posedge 上取到 (1+3+5+6)=15 的值;而我已经在原始硬件上观察到了这个情况。但是在模拟中,结果在 clk 的 posedge 处为 6(即 abc[3])。由于 SystemVerilog 模拟器会为非阻塞语句安排分配,我相信会创建出 4 个 sum 实例。

sum <= sum + abc[0];
sum <= sum + abc[1];
sum <= sum + abc[2];
sum <= sum + abc[3];

由于所有预定的任务同时发生,可能最后一个实例更新的总和为sum,而我们得到的值是sum <= 0 + 6。如果我错了,请纠正我。

现在我的问题是,如何让模拟器按顺序安排这些任务,以便在模拟中获得值15?由于在综合中不能使用阻塞赋值,我找不到任何保持RTL一致的方法。谢谢。


@skrrgwasme,你不应该在时钟块内使用阻塞赋值。 - wilcroft
@wilcroft 确切的说... - electro_sm11
3个回答

3
你的分析看起来是正确的。你试过以下方法吗?
logic [7:0] temp;
always@(*) begin
    temp = 0;
    for (int i = 0; i < 4; i = i + 1) 
        temp = temp + abc[i];
end

always @(posedge clk)
    sum <= temp;

这样,sum 只会在时钟上升沿进行更新,但我们仍然可以在组合逻辑块中展开 for 循环(因此加法应该仍然按预期工作)。


这个可以正常工作,但是当我添加一个基于单个时钟脉冲的使能信号用于温度计算时,总和只会在下一个时钟边沿更新,对吧? - electro_sm11

2

SystemVerilog内置求和数组缩减运算符,消除了for循环的需求。

sum <= abc.sum() with (8'(item));

需要将结果转换为8位,因为默认情况下,结果的类型与数组元素类型相同。


你认为这个约简运算符会在Altera Quartus中合成吗?我会尝试一下。谢谢。 - electro_sm11

0

我猜,你可以在另一个always块(它不是时钟边沿敏感的组合块)中使用阻塞赋值,并将其输出提供给posedge clk上的sum

reg [7:0] temp_sum;

always @ (*)
begin
  temp_sum = abc[0];
  temp_sum = temp_sum + abc[1];
  temp_sum = temp_sum + abc[2];
  temp_sum = temp_sum + abc[3];
end

always @ (posedge clk)
  sum <= temp_sum;

我猜,这可能会起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接