SystemVerilog:$urandom_range 给出超出范围的值

SystemVerilog: $urandom_range gives values outside of range

我在 ModelSim 中遇到一个奇怪的问题,我将一个输入变量设置为一个范围内的随机值,但由于某种原因,我得到了一个超出范围的值。我的所有代码都包含在下面,但基本行是:

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

在 ModelSim 中,它在不应该被分配到 0 时被分配给 0(如波形所示;突出显示的信号)...

让我感到困惑的是,我从来没有将 write_adresses 设置为零 除了 当我在 [=40] 中设置初始信号时=] 块。我只通过使用范围明确排除数字零的 $urandom_range 函数来修改变量。

我写入这个变量的主要代码块在这里:

    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

...这里:

    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

有谁知道为什么会这样?

参考代码

`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

问题是 $urandom_range(1, NUM_ARCH_REGS) returns 值从 1 到 32。但是,write_addrs 被声明为 logic [4:0],这意味着它只能取值0 到 31。当 $urandom_range returns 32(与 6'b10_000 相同)时,代码中的赋值将其截断为 5 位,丢弃 MSB,并且 5'b0_0000存储在 write_addrs.

要解决此问题,只允许最大为 31 的随机值。

变化:

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

至:

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

下面是演示问题的完整示例:

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

您看到的问题不是 ModelSim 特有的;我可以在其他 2 个模拟器上看到它。