Syncing combinational assign statement with sequential blocks

I have a simple example to show as a use case. Practically, we have a 1-D vector of operands which we want to sum inside a sequential block. The code I have written is the following:



    reg signed [31:0] operands_s [4] = {32'd 30, 32'd 40, -32'd 50, -32'd 20};
    reg signed [31:0] operand_s_a;
    reg signed [31:0] operand_s_b;
    int i = 0;

    logic signed [31:0] sum;
    assign sum = $signed(operand_s_a) + $signed(operand_s_b); 

    always_ff @(posedge clock) begin 
        if (reset == 0) begin 
            operand_s_a = operands_s[i];
            operand_s_b = operands_s[i+1];
            i=i+2;

            $display("A = %0d, B = %0d, S = %0d", operand_s_a, operand_s_b, sum);
        end 
    end 

Now the output after the execution as I would like it to be is:

A = 30, B = 40, S = 70
A = -50, B = -20, S = -70

Instead, the assign statement is off-by-1 and the result is this

A = 30, B = 40, S = x
A = -50, B = -20, S = 70
A = x, B = x, S = -70

In my understanding (which seems to be wrong), on the positive edge of the clock cycle the sequential block (in this case, the operands A and B) should be available to any other block downstream (in this case the adder). Since the assign is of combinational nature, on the same clock cycle that A and B are loaded I expected that the sum will be also the correct one and not the previous value (X) which is due to the reset being asserted.

Inside the IEEE Standard for SystemVerilog in the 10.3.2 section the following is stated:

Assignments on nets or variables shall be continuous and automatic. In other words, whenever an operand in
the right-hand expression changes value, the whole right-hand side shall be evaluated. If the new value is
different from the previous value, then the new value shall be assigned to the left-hand side.

In this case, the RHS as the output shows changes values, but the LHS does not change. What am I missing here? Is it maybe updated, but my $display statement is in the wrong position to reflect that change?

Thank you in advance!

In reply to Broxigar:

This is a simulation race condition. SystemVerilog guarantees executing the statements within a begin/end block in source order. But there are no guarentees of ordering between processes execting in the same event region. The continuous assign is a separate process. There is no ordering guarantees when the right hand side gets a evaluated with respect to the other process. It could happen immediately when there is a change, or happen after the begin/end block finishes.

You should be using nonblocking assignments to any variable written in a sequential block that is read outside the block. Then $display is guaranteed to print the old value of sum. In any case, you could use $strobe instead which prints the new value of sum.

BTW, in the code shown, there is no need for $signed as the operands are already signed. Also use 32’sd for signed decimal literals.

In reply to dave_59:

After a little bit of thought, it even makes sense. At least to me, let me know if I am wrong here.

I have “illustrated” the scenario above

pattern loader
/-------------\ D
| seq element | < - - - - .
\-------------/           |
   | Q                    |
   |         adder        |
   |     /-------------\  |
   *- -> | comb element| -*         
      I  \-------------/ O

In the very first clock cycle, the sequential element feeds the logic with the inputs but the response will be registered in the next rising edge of the clock and stored in the sequential element. Correct? Hence the behavior is justified.

The way I have resolved it is by implementing a reset == 1 case and slightly modifying the reset == 0 block as follows:


    always_ff @(posedge clock) begin 
        if (reset == 1) begin 
            operand_s_a = operands_s[i];
            operand_s_b = operands_s[i+1];
        end else begin
            i=i+2;
            operand_s_a = operands_s[i];
            operand_s_b = operands_s[i+1];
        end 
    end 

I have tried also using non-blocking assignments and $display indeed again printed the old value (X). I haven’t used strobe, but thank you for making me aware of it.

Indeed, $signed can be omitted from the assign statement.

However after using a GUI version of a simulator I saw in the Waves that the signals are actually in sync, this is why I asked about my usage of the $display statement.