随机约束
- 随着设计变得越来越大,要产生一个完成的激励来测试设计的功能也变得越来越困难
- 定向激励的测试方法早已无法满足检查功能完整性的要求
- SOC的集成度提高带来的模块之间交互的复杂度也是指数攀升,这就是的verifier无法预测接下来用户使用过程中会发生什么样的状况
- 随机+约束,这两个词组合在一起就构成了目前仿真验证的主流方法
- 随机约束测试(CRT Constrained-Random Test)即能够产生你感兴趣的、你想不到的测试向量,通过回归测试、替换随机种子的方式来提高单位测试用例的覆盖率收集效率
- 随机测试带来的额外负担是环境的复杂度会提高,因为从环境组件上考虑不再只需要发送激励的组件,而且还包括监测器,比较器等
- 随即组件带来的环境复杂度还包括由于环境复用和测试复用带来的组件封装要求,会使得代码量加大
- 这种额外的代码量,环境集成要求也随之带来了更加灵活、更易集成复用的验证环境
- 接下来的实验中,将会给予MCDT的SV验证试验以及UVM验证试验,主要目的在于建立验证环境结构的概念以及理解测试激励的生成和发送过程
随机数的产生
- 使用系统函数
randomize()
产生随机数,随机成功返回1,失败返回0 $urandom()
,可以生成一个32位的无符号随机数$urandom_rage(maxval,minval)
,可以生成maxval和minval之间的数
随机变量
- 任何类中的整型(bit/byte/int)变量都可以声明为rand/randc
- 定长数组、动态数组、关联数组和队列都一个声明为rand/randc,可以对动态数组和队列的长度加以约束
- 指向对象的句柄成员也可以声明为rand(不能被声明为randc),随机时该句柄指向对象中的随机变量也会一并被随机
- module里面不能用rand
带有随机变量的类
class Packet;
rand bit[31:0] src, dst, data[8];
randc bit[7:0] kind;
constraint cstr {
src > 10;
src < 15;
} // 限定src的取值范围
function print();
$display("src is %0d \n dst is %0d \n kind is %0d \n data is %p", src, dst, kind, data);
endfunction
endclass
module tb;
Packet p; // 定义句柄
initial begin
p = new(); // 例化对象
p.print(); // 执行方法,打印属性值,此时全为0
p.randomize(); // 调用随机函数
p.print(); // 打印随机后的属性值
end
endmodule
带随机的结构体
typedef struct {
rand bit[31:0] src; // 定义为随机
rand bit[31:0] dst;
rand bit[31:0] data[4];
} my_struct;
module tb;
my_struct my_s; // 通过结构体类型创建一个结构体数据
initial begin
$display("src is %0d \ndst is %0d \ndata is %p", my_s.src, my_s.dst, my_s.data); // 随机之前,所有值打印都为0
std::randomize(my_s); // 执行随机
$display("src is %0d \ndst is %0d \ndata is %p", my_s.src, my_s.dst, my_s.data); // 打印出随机数
std::randomize(my_s) with {my_s.src > 10;my_s.src < 15;}; // 执行随机并添加临时约束
$display("src is %0d \ndst is %0d \ndata is %p", my_s.src, my_s.dst, my_s.data); // 打印出随机数
end
endmodule
结构体如果在类中时,结构体也需要带rand
随机变量
module tb;
bit[31:0] src; // 定义变量
bit[31:0] dst;
bit[31:0] data[4];
initial begin
$display("src is %0d \ndst is %0d \ndata is %p", src, dst, data); // 随机之前,所有值打印都为0
std::randomize(src, dst, data); // 执行随机
$display("src is %0d \ndst is %0d \ndata is %p", src, dst, data); // 打印出随机数
end
endmodule
seed值
往仿真器加载设计时,vsim -voptargs=+acc ....
命令中添加-sv_seed 111
设置seed值,可以确保每次生成的随机数一致,参考monkey测试中的seed值理解
rand和randc
- rand:表示在可生成的范围内,随机取值,每次每个值的可能性都是相同的
- randc:表示在可生成的范围内随机遍历取值,直到所有可能的值取完了,再重新遍历
随机约束
-
有用的激励不仅仅时随机值,变量之间也有着相互关系
-
没有约束的随机变量会包含许多无效和非法的值,这会使得有效激励的产生变得低效
-
需要用包含一个或者多个约束表达式的约束块定义这些相互关系
-
约束块支持整形通过set操作符来设置它们的可取值范围
rand integer x,y,z; constraint c1 {x inside {3,5,[9:15],[24:32],[y:2*y],z};} // x 的值可以=3,=5,在9~15之间,在24~32之间,在y~2y之间,=z rand integer a,b,c; constraint c2 {a inside {b,c};} // a的值=b或者=c integer fives[4] = '{5, 10, 15, 20}; rand integer v; constraint c3 {v inside {fives};} // v的值在列表fives中随机取值
-
约束块(权重分布)
-
:=
操作符,它们表示每个值的权重时相同的// x再100,101,102,200和300的权重是1-1-1-2-5 x dist {[100:102]:=1, 200:=2,300:=5}
-
:/
操作符,它们表示权重会平均分配到每个值// x在100,101,102,200和300的权重是1/3-1/3-1/3-2-5 x dist {[100:102]:/1, 200:=2,300:=5}
-
-
约束块(唯一标识)
-
unique可以用来约束一组变量,使得其在随机后变量之间不会有相同的数值
rand byte a[5]; rand byte b; rand byte excluded; // a[2],a[3],b,excluded在随机后将包含不相同的数值 constraint u {unique {b, a[2:3],excluded};}
-
-
约束块(条件约束)
-
可以使用
if-else
或者->
操作符来表示条件约束mode == little -> len < 10; // 如果mode==little条件满足,len < 10 mode == big -> len > 100; // 如果mode==big条件满足,len > 100 // 等价于下面的if if (mode == little) len < 10; else if (mode == big) len > 100; bit[3:0] a, b; constraint c {(a == 0) -> (b == 1);} // 随机约束:如果a==0,b==1
-
-
约束块(迭代约束)
-
foreach
可以用来迭代约束数组中的元素,这些数组可以是定长数组、动态数组、关联数组或者队列class C; rand byte A[]; constraint C1 { foreach (A[i]) A[i] inside {2,4,8,26}; } // 约束每个元素的值在(2,4,8,26)中取值 constraint C2 { foreach (A[j]) A[j] > 2 * j; } // 约束每个元素的值大于索引的2倍 endclass
-
也可以使用缩减方法做迭代约束
class C; rand bit[7:0] A[]; constraint c1 {A.size() == 5;} // 约束动态数组长度=5 constraint c2 {A.sum() < 1000;} // 约束动态数组中所有元素之和<1000 endclass
-
-
约束块(函数调用)
-
有时候在表达式中无法简单的表述约束,例如要计算合并数组中的’1‘,可以在约束块中调用函数来描述约束
function int count_ones (bit[9:0] w); for (count_ones=0;w!=0;w=w>>1) count_ones += w & 1'b1; endfunction // 调用函数描述约束 constraint c1 {length == count_ones(v);}
-
-
约束块(软约束)
-
在没有
soft
描述时的约束,我们称之为硬约束,而带有soft
描述的是软约束 -
软约束用来指定变量的默认值和权重
-
如果用户在使用时,指定了外部约束对同一个变量做二次约束,或者用户定义了子类中对同一个变量做了二次约束,那么硬约束可以覆盖软约束,并且不会导致随机数产生的失败
class Packet; rand int length; constraint deflt {soft length inside {32, 1024};} // 软约束,约束length取值为32/1024 endclass Packet p = new(); p,randomize() with {p.length == 1512;} // 外部约束覆盖软约束
-
软约束相互冲突时,根据就近原则,哪里调用随机,哪里的软约束有效
-
-
内嵌约束(指向模糊)
class C1; rand integer x; endclass class C2; integer x; integer y; task doit(C1 f, integer x, integer z); int result; result = f.randomize() whit {x < y + z;}; // 这里的x根据就近原则,指的是C1里的x,而非tash里面的x或C2里的x,加this.x也是指的C1里的x,因为这个约束是施加给C1的 endclass
-
local域指向
-
使用内嵌约束时,对于同名的变量处在不同域中会出现指向模糊的情况
-
可以使用
local::
的域索引方式来明确随机变量的指向class C1; rand integer x; endclass class C2; bit[7:0] x; function my_func(C1 c, integer x); c.randomize() with {x = x}; // 这里的两个x都指的是C1中的x c.randomize() with {x = local::x}; // local::x指的是函数my_func中的参数x c.randomize() with {x = local::this.x}; // local::this.x指的是C2中的变量x endfunction endclass
-
-
随机控制
- rand_mode可以用来使能或者禁止随机变量
- 当随机数被禁止时,它会同为声明为随机变量的普通变量一样,不会参与到随机化过程中
-
约束控制
- constraint_mode,类似于随机控制,一些约束块或者某个类的约束块集合都可以单个控制或者集体控制
- 可以通过约束控制来使能或者关闭某些约束块
-
内嵌变量控制
-
在使用类的随机化函数
randomize()
时,如果有参数,那么只会随机化参数变量,而其余变量无论之前是否被声明为rand/randc,都不会参与到随机化中class CA; rand byte x, y; byte v, w; constraint c1 {x < v && y > w;} endclass CA a = new(); a.randomize(); // 随机x,y a.randomize(x); // 只随机x a.randomize(v,w); // 随机v和w a.randomize(w,x); // 随机w,x
-