Search code examples
system-veriloghdl

SystemVerilog: $urandom_range gives values outside of range


I am getting an odd issue in ModelSim where I set an input variable to a random value in a range, but for some reason, I get a value outside of the range. All my code is included below but the essential line is:

write_addrs[i] = $urandom_range(1,NUM_ARCH_REGS);

In ModelSim, it is being assigned to 0 when it shouldn't (as shown in the waveform; the highlighted signal)...

enter image description here

The thing that is confounding me is that I don't ever set the write_adresses to zero except for when I set the initial signals in the INITIAL_VECTOR_VALUES block. I only modify the variable by using the $urandom_range function with the range explicitly excluding the number zero.

The main blocks of code in which I write to this variable are here:

    initial begin : INITIAL_VECTOR_VALUES
        advance = 0;
        checkpoint = 0;
        recover = 0;
        write_before_checkpoint = 0;
        for (int i = 0; i < NUM_READ_PORTS; i++)
            read_addrs[i]  = 0;
        for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
            write_addrs[i] = 0;         //<--------------------- HERE!!!
            wr_en[i] = 0;
            write_data[i] = 0;
            commit_en[i] = 0;
            commit_data[i] = 0;
            commit_addrs[i]= 0;
        end
    end

... and here:

    task random_operations(int repeat_num);
        //local vars
        int operation_select, num_write, num_commit;
        int current_checkpoints;
        int loop_idx;
        ##5;

        current_checkpoints = 0; //initialize
        $display("Begin Random Operations @ %0t", $time());
        while (loop_idx < repeat_num) begin
            ... other stuff ...
            //operand select (sets the stimulus inputs)

            for (int k = 0; k < num_write; k++) begin
                write_addrs[k] = $urandom_range(1,NUM_ARCH_REGS); //<--------------------- HERE!!!
                $display("%0t: num_write = %0d",$time(), num_write);
                $display("%0t: write_addrs[%0d] = %0d", $time(), k, write_addrs[k]);
                write_data[k] = $urandom_range(1,128);
                wr_en[k] = 1;
            end
            ...
            loop_idx++;
            ##1;

            //reset signals (reset stimulus inputs)
            ...
        end : end_while
    endtask : random_operations

Does anyone know why this would be happening?

REFERENCE CODE

`timescale 1ns/1ns

module testbench;

localparam int NUM_CHECKPOINTS = 8;
localparam int NUM_ARCH_REGS = 32; 
localparam int NUM_READ_PORTS = 4; 
localparam int NUM_WRITE_PORTS = 2;

logic clk;
logic rst;
initial begin : CLOCK_INIT
    clk = 1'b0;
    forever #5 clk = ~clk;
end
default clocking tb_clk @(posedge clk); endclocking


logic [$clog2(32)-1:0] read_addrs [4];
logic [$clog2(32)-1:0] write_addrs [2];
logic wr_en [2];
logic advance;      //moves tail pointer forward
logic checkpoint;       //moves head pointer forward
logic recover;      //moves head pointer backward (to tail)
logic write_before_checkpoint;
logic [$clog2(32)-1:0] commit_addrs [2];
logic [$clog2(128)-1:0] commit_data [2];
logic commit_en [2];
logic [$clog2(128)-1:0] write_data [2];
logic [$clog2(128)-1:0] read_data [4]; 
logic [$clog2(128)-1:0] write_evict_data [2];
logic enable_assertions;

cfc_rat dut(.*);
shadow_rat rat_monitor(.*);

initial begin : INITIAL_VECTOR_VALUES
    advance = 0;
    checkpoint = 0;
    recover = 0;
    write_before_checkpoint = 0;
    for (int i = 0; i < NUM_READ_PORTS; i++)
        read_addrs[i]  = 0;
    for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
        write_addrs[i] = 0;
        wr_en[i] = 0;
        write_data[i] = 0;
        commit_en[i] = 0;
        commit_data[i] = 0;
        commit_addrs[i]= 0;
    end
end

task reset();
    rst = 1;
    ##2;
    rst = 0;
    ##1;
endtask : reset


task random_operations(int repeat_num);
    //local vars
    int operation_select, num_write, num_commit;
    int current_checkpoints;
    int loop_idx;

    ##5;

    current_checkpoints = 0; //initialize

    $display("Begin Random Operations @ %0t", $time());
    while (loop_idx < repeat_num) begin

        operation_select = (loop_idx < 5) ? 1 : ((dut.dfa.chkpt_empty) ? $urandom_range(0,1) : ((dut.dfa.chkpt_full) ? 1 : $urandom_range(0,2)));
        num_write = $urandom_range(0,NUM_WRITE_PORTS);
        num_commit = $urandom_range(0,NUM_WRITE_PORTS);

        case (operation_select)
            0: begin //checkpoint
                if (current_checkpoints+1 < NUM_CHECKPOINTS) begin
                    $display("Checkpoint @ %0t", $time());
                    write_before_checkpoint = $urandom_range(0,1);
                    checkpoint = 1;
                    current_checkpoints++;
                end
                else begin
                    loop_idx--;
                    continue;
                end
            end
            1: $display("Normal RW @ %0t",$time()); //no operation, only read and write
            2: begin //advance
                if (current_checkpoints > 0) begin 
                    advance = 1;
                    $display("Advance @ %0t", $time());
                    current_checkpoints--;
                end
                else begin
                    loop_idx--;
                    continue;
                end
            end
            3: begin //recover
                $display("Recover @ %0t", $time());
                recover = 1;
                current_checkpoints = 0;
            end
            default:;
        endcase // operation_select

        //operand select (sets the stimulus inputs)
        for (int k = 0; k < NUM_READ_PORTS; k++)
            read_addrs[k]  = $urandom_range(0,NUM_ARCH_REGS);

        for (int k = 0; k < num_write; k++) begin
            write_addrs[k] = $urandom_range(1,NUM_ARCH_REGS);
            $display("%0t: num_write = %0d",$time(), num_write);
            $display("%0t: write_addrs[%0d] = %0d", $time(), k, write_addrs[k]);
            write_data[k] = $urandom_range(1,128);
            wr_en[k] = 1;
        end
        for (int k = 0; k < num_commit; k++) begin
            commit_addrs[k] = $urandom_range(1,NUM_ARCH_REGS);
            commit_data[k] = $urandom_range(1,128); 
            commit_en[k] = 1;
        end
        loop_idx++;
        ##1;

        //reset signals (reset stimulus inputs)
        checkpoint = 0;
        recover = 0;
        advance = 0;
        write_before_checkpoint = 0;
        for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
            write_data[i] = 0;
            wr_en[i] = 0;
        end
        for (int i = 0; i < NUM_READ_PORTS; i++)
            read_addrs[i] = 0;
        for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
            commit_en[i] = 0;
            commit_data[i] = 0; 
        end
    end : end_repeat
endtask : random_operations




initial begin : TEST_VECTORS
    enable_assertions = 1; //for testing the monitor

    reset();
    random_operations(5000);

    ##10;
    $display("Finished Successfuly! @ %0t",$time());
    $finish;
end

endmodule

Solution

  • The problem is that $urandom_range(1, NUM_ARCH_REGS) returns values from 1 to 32. But, write_addrs is declared as logic [4:0], which means it can only take on values from 0 to 31. When $urandom_range returns 32 (which is the same as 6'b10_000), the assignment in your code truncates it to 5 bits, dropping the MSB, and 5'b0_0000 is stored in write_addrs.

    To fix this, only allow random values up to 31.

    Change:

            write_addrs[k] = $urandom_range(1, NUM_ARCH_REGS);
    

    to:

            write_addrs[k] = $urandom_range(1, NUM_ARCH_REGS-1);
    

    Here is a complete example to demonstrate the problem:

    module tb;
    
    localparam int NUM_ARCH_REGS = 32; 
    logic [$clog2(32)-1:0] write_addrs [2];
    
    initial begin
        repeat (100) begin
            for (int k=0; k<2; k++) begin
                write_addrs[k] = $urandom_range(1, NUM_ARCH_REGS);
                $write("write_addrs[%0d]=%02d ", k, write_addrs[k]);
            end
            $display;
        end
    end
    endmodule
    

    The problem you see is not specific to ModelSim; I am able to see it on 2 other simulators.