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 个模拟器上看到它。
我在 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 个模拟器上看到它。