Reputation: 65
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.
Upvotes: 3
Views: 4328
Reputation: 1992
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.
Upvotes: 0
Reputation: 42698
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.
Upvotes: 2
Reputation: 1635
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).
Upvotes: 4