芯片体积增大,复杂度越来越高,定向测试已无法满足验证的需求,而随机测试的比例逐渐提高;
如果没有约束,产生有效激励的同时也产生很多无效和非法的激励;
器件配置:通过寄存器和系统信号;
在随机测试中,可以随机化下列哪些要素? 答案:AC
A、设计的功能配置;B、设计的结构;C、输入数据;D、输出数据
设计的结构是静态的,在编译时确定;验证的结构是动态的,发生在仿真阶段。
随机化是为了产生更多可能的驱动,将相关数据有机整理在一个类中,使用rand
关键词表明变量的随机属性。
randc
表示周期随机性,即所有可能的值都赋过值后随机值才可以重复;std::randomize()
使用;constraint
也同随机变量一起在类中声明。class Packet;
//The random variables
rand bit [31:0] src, dst, data[8];
randc bit [7:0] kind;
// Limit the value for src
constraint c {
src > 10;
src < 15;
}
endclass
Packet P;
initial begin
P = new(); //Create a packet
assert (Packet.randomize())
else $fatal(0,"Packet::randomize failed");
transmit(P);
end //需要随机的变量有4个,只要其中一个约束有问题,那么所有变量都没有进行随机
约束表达式的求解由SV的约束求解器完成。
class data;
rand bit [2:0] month;
rand bit [4:0] day;
rand int year;
constraint c_data {
month inside {[1:12]};
day inside {[1:31]};
year inside {[2010:2030]};
}
endclass //注意位数的限制,比如month只有三位只能随机0-7
class Stim;
const bit [31:0] CONGEST_ADDR = 42; //定义常量
typedef enum {READ,WRITE,CONTROL} stim_e; //自定义枚举类型,stim_e类型名
randc stim_e kind; //Enumerated var
rand bit [31:0] len,src,dst; //随机变量
bit congestion_test; //非随机变量
constraint c_stim{
len < 1000;
len > 0;
if (congestion_test){
dst inside {[CONGEST_ADDR + 100 : CONGEST_ADDR - 100]};
src == CONGEST_ADDR;
}
else
src inside {0,[2:10],[100:107]};
}
endclass
关键词dist
可以在约束中用来产生随机数值的权重分布;
dist
操作符带有一个值的列表以及相应的权重,中间用:=
或:/
分开。值或权重可以是常数或变量;rand int scr,dst;
constraint c_dist{
src dist {0:=40,[1:3]:=60};// :=的意思是产生40个0,1-3每个数产生60个,共产生220个数
//src = 0,weight(权重) = 40/220
//src = 1,weight(权重) = 60/220
//src = 2,weight(权重) = 60/220
//src = 3,weight(权重) = 60/220
dst dist (0:/40,[1:3]:/60);// :/的意思是0的概率是40%,1-3的概率一共是60%
//dst = 0,weight(权重) = 40/100
//dst = 1,weight(权重) = 20/100
//dst = 2,weight(权重) = 20/100
//dst = 3,weight(权重) = 20/100
}
inside
是常见的约束运算符,表示变量应属于某些值的集合,除非还存在其他约束,否则随机变量在集合里面取值的概率是相等的,集合里面也可使用变量。例如c inside {[low:high]}
; //low<=c && c<=high$
指定最大值和最小值,例如c inside {[$:4],[20:$]}
; //0<=c<=4 || 20<=c<=63(因为rand bit [5:0] c ; // 0<=c<=63)条件约束:可以通过 “->”或if-else让一个约束表达式在特定时刻有效。
一个类可包含多个约束块,可以把不同约束块用于不同测试;
constraint_mode()
函数可以根据不同需要打开或关闭约束;class Packet;
rand int length;
constraint c_short {length inside {[1:32]};}
constraint c_long {length inside {[1000:1023]};}
endclass
Packet p;
initial begin
p = new(); //创建一个long packet通过禁止短约束块
p.c_short.constraint_mode(0);//关闭short packet,只有long_packet约束有效
assert (p.randomize());
transmit(p);
//创建一个short packet通过禁止所有约束然后只使能短约束块
p.constraint_mode(0);//long和short的约束都失效
p.c_short.constraint_mode(1);//打开short的约束
transmit(p);
end
//如果随机化该对象时不禁止其中任何一个约束块,那么调用随机函数randomize()后,
//p.length的值为0也会报错,因为约束冲突导致randomize失败有warning返回默认值0。
约束块之间会相互作用,最终产生难以预测的结果,使能和禁止这些约束的代码也会增加测试的复杂性;
randomize() with
增加额外的约束,和在类里增加约束是等效的,但同时要注意内部约束和外部约束之间应协调,如果出现互相违背的情况,那么随机数值求解会失败;soft
是修饰软约束,当与其它外部约束冲突时软约束优先级会更低。class Transaction;
rand bit [31:0] addr,data;
constraint c1 {soft addr inside {[0:100],[1000:2000]};}
endclass
Transaction t;
initial begin
t= new();
assert(t.randomize() with {addr >= 50; addr <= 1500; data < 10;});
driveBus(t);
//force addr to a specific value,data>10
assert(t.randomize() with {addr == 2000; data > 10;});
driverBus(t);
end
如果加上t.randomize() with {addr inside [200:300]; data inside [10:20]; },若没有关键词soft,addr的约束和内嵌矛盾,addr约束不满足,addr和data都会报错。但因为有soft,减低了约束的优先级,使得内嵌约束起作用。
pre_randomize()和post_randomize()函数
随机数函数:SV提供一些常用的系统随机函数。这些随机函数可直接调用来返回随机数值(不通过class,直接调用)。
随机化个别变量
class Rising;
byte low, //非随机变量,初始值为0
rand byte med,hi; //随机变量,初始值为0
constraint up {low < med; med < hi;}
endclass
initial begin
Rising r;
r = new();
r.randomize();//随机化med,hi,但不改变low,此时low=0,假设med=5,hi=9
//先整体的randomize产生合理的low,med和hi值
r.randomize(med); //只随机化med;此时low=0,hi=9,med可以是1-8,假设med=6
r.randomize(low);//只随机化low;此时med=6,hi=9,low可以是0-5,假设low=3
end
//在例化r之后如果先调用r.randomize(low),那么可能产生low=报错,med=0,hi=0;
//因为med和hi不参与randomize,保持为0
在约束随机标量的同时,也可以对随机化数组进行约束。
class dyn_size;
rand logic[31:0] d[];
constraint d_size {d.size() inside {[1:10]}; }
endclass
数组的大小应该给定范围,防止生成过大体积的数组或空数组;
sum()
,product()
,and()
,or()
,xor()
;foreach
对数组的每个元素进行约束,和直接写对固定大小数组的每个元素的约束相比更简洁;foreach
更适合于对非固定大小数组中每个元素的约束。如果要产生一个随机数组,其每个元素的值唯一,可使用双循环嵌套;
如果直接使用randc数组,那么数组中的每个元素只会独立地随机化,并不会按照本意使得数组中的元素值唯一。
class UniqueSlow;
rand bit[7:0] ua[64];
constraint c{
foreach(ua[i]) //对数组中的每个元素操作
foreach(ua[j])
if(i != j) //除了元素自己
ua[i] != ua[j]; //和其他元素比较
}
endclass
可使用randc变量辅助生成唯一元素的数组。
class randc8;
randc bit[7:0] val;
endclass
class LittleUniqueArray;
bit[7:0] ua[64];
function void pre_randomize();
randc8 rc8;
rc8 = new();
//此句若放在foreach里,则不断创建新对象,跟用rand声明val无区别
foreach(ua[i]) begin
assert(rc8.randomize());
//若调用LittleUniqueArray.randomize(),
//则只执行该类的pre_randomize(),故ua数组得不到rc8随机化的数值
ua[i] = rc8.val;
end
endfunction
endclass
class packet;
rand bit [3:0] da[];
//该动态数组不需要例化,因为添加了rand
//后面randomize后会帮助创建、例化、填充这些元素
constraint da {
da.size() inside {[3:5]};
foreach(da[i])
da[i] <= da[i + 1];
}
endclass
packet p;
initial begin
p = new();
p.randomize() with {da.size() inside {3,5};};
end
//一开始约束size为3-5,后来进行了独立约束size为3或5。
//但foreach(da[i]) da[i] <= da[i + 1];时没有限制i的取值,i=5时,i的范围时[0:4]。
//da[4]<=da[5](对应第六个元素)超出了范围,所以约束有冲突,随机化失败会报错
//解决办法:在foreach(da[i])后加上if(i<=da.size()-2)
parameter MAX_SIZE = 10;
class RandStuff;
bit [1:0] value = 1;
endclass
class RandArray;
rand RandStuff array[];
constraint c{
array.size() inside {[1:MAX_SIZE]};
}
function new();
//分配最大容量
array = new [MAX_SIZE];
foreach (array[i])
array[i] = new(); //每次创建均为RandStuff创建后的值,即为value=1
endfunction
endclass
RandArray ra;
initial begin
//构造数组和所有对象
ra = new();
//约束数组的数量为两个
assert(ra.randomize()with {array.size == 2});
foreach (ra.array[i])
$display(ra.array[i].value);
end
随机序列:产生事务序列的另一个方法是使用SV的randsequence结构,这对于随机安排组织原子测试序列很有帮助。
initial begin
for(int i = 0; i < 15; i++) begin
randsequence(stream)
stream: cfg_read := 1 | //权重分布
io_read := 2 |
mem_read := 5;
cfg_read : {cfg_read_task;} |
{cfg_read_task;} cfg_read; //1/8
io_read : {io_read_task;} |
{io_read_task;} io_read; //2/8
mem_read : {mem_read_task;} |
{mem_read_task;} mem_read; //5/8
endsequence
end
end
可以使用randcase建立随机决策树,但没有变量可供追踪调试。
initial begin
int len;
randcase
1: len = $urandom_range(0,2); // 10% : 0,1,2
8: len = $urandom_range(3,5); // 80% : 3,4,OR 5
1: len = $urandom_range(6,7); // 10% : 6 OR 7
endcase
$display("len=%0d", len);
end
randsequence和randcase是针对轻量级的随机控制的应用,可通过定义随机类取代上述随机控制的功能,且由于类的继承性使得在后期维护代码更方便。randsequence的相关功能在协调激励组件和测试用例时可能会用到,而randcase对应随机约束中的dist权重约束和if-else条件约束的组合。