一、产生原子激励和场景
1、和历史相关的原子发生器
产生相关的事务流的最简单的办法是采用基于以前事务的随机值的原子发生器。这个类可以约束总线事务在80%的时间里重复过去的命令。例如写操作,并且在过去的目的地址上增加一个增量。你可以使用post_randomize函数复制产生的事务,用于下一次randomize()调用。
2、随机序列
产生事务序列的另一个方法是使用SV的randsequence结构。
initial begin
for(int i = 0; i < 15; i++) begin
randsequence(stream) //生产了stream序列,它可以是cfg_read、io_read或mem_read,随机序列的引擎会随机地从三种操作中选取一种
stream:cfg_read:=1 | //cfg_read的权重是1
io_read:=2 | //权重是2
mem_read:=5; //权重是5
cfg_read:{cfg_read_task;} | //cfg_read可以是对cfg_read_task任务的一次调用,也可以是在该任务调用后尾随一个cfg_read,所以cfg_read任务至少会被调用一次或多次
{cfg_read_task;} cfg_read;
mem_read:{mem_read_task;} |
{mem_read_task;} mem_read;
io_read: {io_read_task;} |
{io_read_task;} io_read;
endsequence
end
end
task cfg_read_task;
...
endtask
3、随机对象数组
还有一种产生随机序列的形式是随机化整个对象数组,可以建立指向数组的前一个和后一个对象的约束,SV求解器会同时求解所有的约束。由于整个序列同时产生,你可以在发送第一个事务前就知道所有数据的校验和、事务的总数等信息。
4、组合序列
可以把多个序列组合在一起,形成一个更实际的事务流。
二、随机控制
使用randcase和$urandom_range的随机控制
initial begin;
int len;
randcase
1: len = $urandom_range(0, 2); //10% : 0, 1, 2
8: len = $urandom_range(3, 5); //80% : 3, 4, 5
1: len = $urandom_range(6, 7); //10% : 6, 7
endcase
$display("len=%0d", len);
end
等效的约束类
class LenDist;
rand int len;
constraint c{
len dist {[0:2]:=1, [3:5]:=8, [6:7]:=1};}
endclass
LenDist lenD;
initial begin
lenD = new();
assert(lenD.randomize());
$display("Chose len=%0d", lenD.len);
end
1、用randcase建立决策树
initial begin
//level 1
randcase
one_write_wt: do_one_write();
one_read_wt: do_one_read();
seq_write_wt: do_seq_write();
seq_read_wt: do_seq_read();
endcase
end
//level 2
task do_one_write;
randcase
mem_write_wt: do_mem_write();
io_write_wt: do_io_write();
cfg_write_wt: do_cfg_write();
randcase
endtask
task do_one_read;
randcase
mem_read_wt: do_mem_read();
io_read_wt: do_io_read();
cfg_read_wt: do_cfg_read();
endcase
endtask
三、随机数发生器
测试平台需要不相关的随机值来产生不同于定向测试的随机激励,即使设计或测试平台只做了微小的修改,或在调试特殊的测试时,又都需要不断重复某个测试模式。
1、伪随机数发生器
简单的伪随机数发生器
/*
内部有一个32位的内部状态,要计算下一个随机值,先计算出状态的64位平方值,取中间的32位数值,然后加上原来的32位数值
*/
reg[31:0] state = 32'h12345678;
function logic[31:0] my_random;
logic[63:0] s64;
s64 = state * state;
state = (s64 >> 16) + state;
my_random = state;
endfunction
/*
这段代码可以产生随机的数据流,并且可以通过设置相同的种子来重复码流。
SV把它称为PRNG,通过randomize()和randcase来调用它产生随机值。
*/
2、随机稳定性——多个随机发生器
Verilog在整个仿真过程中使用一个PRNG,而测试平台通常会有多个激励发生器同时运行,为被测设计产生数据。如果两个码流共享一个PRNG,它们获得的都是随机数的一个子集。
当共享一个随机发生器时,其中一个类改变时就可能发生问题,影响到另一个类。
SV中每个对象和线程都有一个独立的PRNG,改变一个对象不会影响其他对象获得随机数。
3、随机稳定性和层次化种子
SV的每个对象都有自己的PRNG和独立的种子,当启动一个新的对象或线程时,子PRNG的种子由父PRNG产生。所以在仿真开始时的一个种子可以产生多个随机激励流,它们之间又是相互独立的。
代码的变化也会使得测试平台产生不同的随机值,这使得当调试DUT的故障时,很难重现相同的激励。把新增加的对象和线程放在现有对象和线程之后,可以减少修改代码带来的影响。
4、随机器件配置
测试DUT的一个重要工作是测试DUT内部设置和环绕DUT的系统的配置,测试应该随机化环境。
以太网交换机配置类
/*
该类定义了4端口以太网交换机的配置
它在环境类里例化,在测试里使用
在测试里修改了一个配置值,打开了全部四个端口
*/
class eth_cfg;
rand bit[3:0] in_use; //测试中使用的端口
rand bit[47:0] mac_addr[4]; //MAC地址
rand bit[3:0] is_100; //100MB模式
rand unit run_for_n_frames; //测试中的帧数
//在unicast模式时设置某些地址位
constraint local_unicast{
foreach(mac_addr[i])
mac_addr[i][41:40] == 2'b00;
}
//限制测试长度
constraint reasonable{
run_for_n_frames inside {[1:100]};
}
endclass : eth_cfg
使用随机配置建立环境
/*
在环境类的不同阶段使用了配置类,配置在环境类的构造函数里创建,但直到gen_cfg阶段才随机化
这就可以在调用randomize()之前打开或关闭约束
可以在build阶段前修改产生的配置,创建DUT周围的虚拟元件
*/
class Environment;
eth_cfg cfg;
eth_src gen[4];
eth_mii drv[4];
function new();
cfg = new();
endfunction
function void gen_cfg;
assert(cfg.randomize());
endfunction
//使用随机配置建立环境
function void build();
foreach(gen[i])
if(cfg.in_use[i])
begin
gen[i] = new();
drv[i] = new();
if(cfg.is_100[i])
drv[i].set_speed(100);
end
endfunction
task run();
foreach(gen[i])
if(cfg.in_use[i])
begin
//启动测试平台的事务处理器
gen[i].run();
...
end
endtask
task wrap_up();
...
endtask
endclass : Environment
使用随机配置的简单测试
/*
现在已经有了建立测试所需的所有元件,该测试例化了环境类,然后依次运行
*/
program test
Environment env;
initial begin
env = new(); //创建环境
env.gen_cfg; //建立随机配置
env.build(); //建立测试平台的环境
env.run(); //运行测试
env.wrap_up(); //整理&产生报告
end
endprogram
修改随机配置的简单测试
/*
可以修改随机配置,来覆盖某个边界条件,该测试先随机化了配置类,然后打开了所有端口
*/
program test;
Environment env;
initial begin
env = new();
env.gen_cfg;
//修改随机值-打开四个端口
env.cfg.in_use = 4'b1111;
env.build();
env.run();
env.wrap_up();
end
endprogram
四、结论
CRT是产生验证复杂设计所需激励的唯一可行的方法。SV提供了很多种产生随机激励的方法。测试必须是灵活的,允许使用产生的缺省值,也可以约束或修改缺省值以实现最终目标。在建立测试平台前务必事先规划,留出足够的“钩子”,这样才能在不修改现有代码的情况下控制测试平台。