3
votes

As I was working on a SystemVerilog based FPGA design, I came across a situation where I had to compute the sum of an array of 4 elements on a clock edge. I was able to do that using a for loop with non blocking assign statements.

The design synthesized successfully on Quartus 15.0 but when I tried to run the simulations using the same RTL on Modelsim Altera the results were unexpected. I have written a sample code to illustrate the same.

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

This image shows the simulation results.

In this sample code, sum is computed using non-blocking assignments. It was intended to have a value of (1+3+5+6)=15 on the first posedge of clk; which I have observed in original hardware. But in simulation the result was 6 at the posedge of clk(which is abc[3]). Since the systemverilog simulators schedule the assignments for non-blocking statements, I believe 4 instances of sum are created viz.

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

As all the scheduled assignments happen simultaneously, may be the last instance updated sum and we have a value of sum <= 0 + 6. Please correct me if I'm wrong.

Now my question is how do I make the simulator schedule these assignments sequentially so that I get a value of 15 even in simulation? Since blocking assignments are not an option in synthesis, I cant find any way to keep the RTL consistent. Thanks.

3
@skrrgwasme you shouldn't use blocking assignments inside clocked blocks.wilcroft
@wilcroft exactly...electro_sm11

3 Answers

3
votes

Your analysis appears correct. Have you tried the following instead?

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;

This way, the sum is only ever updated on a clock edge, but we can still unroll the for loop in a combinational block (so the add should still work as intended).

2
votes

SystemVerilog has a builtin sum array reduction operator that eliminates the need for a for-loop

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

The cast to 8 bits is needed because, by default, the type of the result is the same as the array element type.

0
votes

I guess, you can use blocking assignment in another always block (Which is not clock edge sensitive, Combinational Block) and it's output you can provide to sum on the posedge clk

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;

I guess, this might work.