Search code examples
verilogmodelsim

Verilog's display function is giving an incorrect output?


I spent quite a long time debugging some Verilog code only to realize that the design was correct the entire time and for whatever reason Verilog's display function was having unexpected behavior.

I have the following testbench file:

`timescale 1ns / 1ps
module tb ();

    logic signed [7:0]  Y;
    logic signed [15:0] X;
    logic signed [23:0] Z;
    logic      clk; 
  
    reg [8:0] counter;
   
    // instantiate device under test
    CSAM dut (Z,X,Y);

    // 2 ns clock
    initial 
    begin   
        clk = 1'b1;
        assign counter = 8'b0;
        forever #10 clk = ~clk;
    end

    initial
    begin
        //while (1)
        //begin
            #10     X = 16'b0000000001111011;   
            #0      Y = 8'b11110011;
            
            #20     X = 16'b0000000001111011;   
            #0      Y = 8'b11110011;
            
            $display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);
            
            #20     X = 16'b0000000000001111;   
            #0      Y = 8'b00000000;
            
            $display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);
            
            #20     X = 16'b0000000000001111;
            #0      Y = 8'b11111111;
            
            $display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);
            
            #20     X = 16'b11111111111111; 
            #0      Y = 8'b11111111;
            
            $display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);
            
            #20     X = 16'b1010101010101010;   
            #0      Y = 8'b01010101;
            
            $display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);
            
            #20     X = 16'b0101010101010101;   
            #0      Y = 8'b00101010;
            
            $display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);
            
    end
   
endmodule

You can see that I am running some tests of inputs X,Y and Z is the output.

My display function prints X,Y,Z in decimal, hex, and binary for readability.

$display ("Test data:\ninput A is 0b'%16b or 0h'%4h or %d\ninput B is 0b'%8b or 0h'%2h or %d\noutput is 0b'%24b or 0h'%6h or %d\n", X,X,X,Y,Y,Y,Z,Z,Z);

The output looks like this in Modelsim's terminal:

# Test data:
# input A is 0b'0000000001111011 or 0h'007b or    123
# input B is 0b'11110011 or 0h'f3 or  -13
# output is 0b'111111111111100111000001 or 0h'fff9c1 or     -1599
# 
# Test data:
# input A is 0b'0000000000001111 or 0h'000f or     15
# input B is 0b'00000000 or 0h'00 or    0
# output is 0b'111111111111111100111101 or 0h'ffff3d or      -195
# 
# Test data:
# input A is 0b'0000000000001111 or 0h'000f or     15
# input B is 0b'11111111 or 0h'ff or   -1
# output is 0b'000000000000000000000000 or 0h'000000 or         0

You can see that for 15x0, and 15x-1 the results are actually incorrect. However, if I inspect the Z waveform manually in Modelsim's waveform viewer I can see the answer is actually correct. So somehow, the display function is not behaving as intended and I would like to know what I can do to fix that. It is particularly weird to me that the inputs X,Y display properly for all tests but Z does not.


Solution

  • Since you only have combinatorial logic in your CSAM module, your issue can still be reproduced by changing that line to assign Z = X * Y;, which is equivalent.

    TL;DR: Because the execution of expression evaluation and net update events may be intermingled, race conditions are possible. You can avoid races by changing your code like this:

    #20;
    X = 16'b0000000001111011;
    Y = 8'b11110011;
    #0;
    $display("time: %t, X: %d, Y: %d, X*Y=Z: %d", $time, X, Y, Z);
    

    For a detailed description of this see SystemVerilog IEEE Std 1800-2017 4.8 Race conditions, for example:

    assign p = q;
    initial begin
      q = 1;
      #1 q = 0;
      $display(p);
    end
    

    The simulator is correct in displaying either a 1 or a 0. The assignment of 0 to q enables an update event for p. The simulator may either continue and execute the $display task or execute the update for p, followed by the $display task.


    The long version involving Scheduling semantics

    The SystemVerilog language is defined in terms of a discrete event execution model, that is an event-driven simulation model based on Stratified event scheduler. For details to see SystemVerilog IEEE Std 1800-2017 chapter 4 Scheduling semantics and this paper: SystemVerilog Event Regions, Race Avoidance & Guidelines.

    To simplify things, let's look at event regions in Verilog as follows (Image sourced from the above paper):

    Verilog-2001 event regions

    We can see that both blocking assignments and continuous assignments and $display task are in Active region, so races may occur. We can specify #0 to holds the events to be evaluated after all the Active events are processed to avoid races, see SystemVerilog IEEE Std 1800-2017 4.4.2.3 Inactive events region.

    If events are being executed in the active region set, an explicit #0 delay control requires the process to be suspended and an event to be scheduled into the Inactive region of the current time slot so that the process can be resumed in the next Inactive to Active iteration.


    How to debug?

    Your other confusion is about the waveform:

    However, if I inspect the Z waveform manually in Modelsim's waveform viewer I can see the answer is actually correct.

    You can use Expanded Time Deltas Mode or Expanded Time Events Mode to see waveform details in Modelsim from view waveform tab. For Example, In your actual case, let's look at 50ns:

    • Expanded Time Deltas Mode is displayed as follows:

    delta time

    • Expanded Time Events Mode is displayed as follows:

    event time

    In Expanded Time Deltas Mode, you can roughly see that those signals may have race; In Expanded Time Events Mode, you can see how the simulator makes decisions and schedules at each step.

    Finally, recommend it again, if you want to know more, please see the paper: SystemVerilog Event Regions, Race Avoidance & Guidelines.