文章目录
前言
2023.3.10 天气晴
2023.3.11 天气阴
2023.3.15 更新solve…before
2023.8.5 更新产生唯一元素的数组
芯片体积增大,复杂度增加,定向测试 direct test
无法满足要求。随机测试 random test
比定向测试用例代码量更少,产生的激励更多样,构建验证环境得代码量未必减少,但测试用例得代码量会减少
一、随机化变量及随机约束种类
0、随机化变量
rand
:每次随机概率是相同的
randc
:周期化随机。上次随机的结果不会再出现,c表示cycle,会遍历。randc的成员先于rand成员完成随机化。string
和real
类型无法随机化。
可以随机化的变量类型:bit
,logic
,enum
,packed struct
,int
,byte
std::randomize()
:先声明后调用随机函数来随机化变量,调用后返回0表示随机失败,所有的数据都不会随机成功,返回1表示随机化成功。只有通过声明rand变量,并且在后期通过对象调用randomize()函数才能随机化变量
constraint
:约束求解器/constraint solver
可以选择出满足约束的值,这个值由伪随机数发生器PRNG/Pseudo random number generator
从一个seed产生。不同仿真器对于同一个约束类和种子值求解出来的数值可能不相同。SV只能随机化2值数据类型,无法随机出X/Z值。如果真的要随机化成X值,可以先randomize,然后再修改其中的某一位为X。
class date;
rand bit [2:0] month;
rand bit [4:0] day;
rand int year;
constraint c_date{
month inside {[1:12]};
day inside {[1:31]};
year inside {[2010:2030]};
}
endclass
//使用 $ 指定最大值最小值
rand bit[6:0] b; // 0 <= b <= 127
constraint c_range{
b inside {[$:4],[20:$]};// 0<=b<=4 || 20<=b<=127
}
class Stim;
const bit [31:0] ADDR = 42; //静态变量,声明时赋初值,只赋值一次
typedef enum{READ, WRITE, CONTROL} stim_e;
randc stim_e kind; //枚举变量
rand bit [31:0] len, src, dst;
bit test;
constraint c_stim{
len<1000;
len>0; //也可以写成inside
if(test){
dst inside {[ADDR-100:ADDR+100]};
src == ADDR;
}
else
src inside {0, [2:10], [100:107]};
}
endclass
Stim p;
initial begin
p = new();//创建对象
//利用句柄调用randomize()函数
//立即断言assert
//randomize()函数会返回一个值,0代表随机化失败,1代表随机化成功
assert(p.randomize())
else
$fatal(0,"Package::randomize failed");
transmit(p);
end
1、权重约束dist
权重不用百分比表示,也不一定加起来是100
:=
表示值范围内的每一个值的权重都是相同的
:/
表示权重要平均分到值范围内的每一个值
src dist{0:=40,[1:3]:=60}
//这表示 src=0 40/220 ,因为40+60x3=220
// src= 1 60/220 ;src= 2 60/220
src dist{0:/40,[1:3]:/60}
//这表示 src=0 40/100 ,因为40+60=100
// src= 1 20/100 ;src= 2 20/100 ,因为60要平均分配给1,2,3,
2、条件约束
通过 ->
或者if …else
来实现条件约束
constraint c_io{
(io_space_mode) -> //当c_io满足io_space_mode=1时,执行下面表达式
addr[31] ==1'b1;
}
3、内嵌约束
内嵌约束in-line,randomize() with
来增加额外的约束,和在class里面约束是等价的,这个是在class的对象实例化之后再进行的额外的约束。
class Transaction;
rand bit addr;
constraint c_addr{
soft addr inside{[0:100]};
}
endclass
Transaction t;
initial begin
t = new();
assert(t.randomize() with{
addr>=50; addr<=75;}
);
end
4、软约束soft
产生约束冲突时,soft
修饰的优先级会更低
如果没有soft,当外加约束和定义约束出现冲突时,只要有一个不符合,直接所有的变量都不能够随机化,都会报错。加了soft后优先级降低,则可以生成随机化数值。
constraint c_addr{
soft addr inside{[0:100]};
}
二、约束块控制
1、双向约束
约束不是自上至下运行的,而是声明性代码,并行运行
的,要同时满足所有条件。
双向约束
:它会同时计算所有的随机变量的约束。增加或者删除任何一个变量的约束都会直接或间接影响所有相关值的选取。
(两个constraint同时执行,一个约束条件改变时,会影响到所有的取值。)
约束在类中声明时是可以继承的,子类的约束必须满足父类的约束,同时满足。
2、约束块的控制
可以使用if-else
或者分别打开关闭冲突的constraint
来解决两个矛盾的问题
内建函数:constraint_mode(int a)
,a的值可以取0或者1,0表示关闭这个约束或者整个class的约束,1表示打开
class Package;
//声明随机变量
rand int length;
//约束变量值,两个约束相互冲突
constraint c_short {length inside {[1:32]};}
constraint c_long {length inside {[1000:1023]};}
endclass
Package p;//声明句柄
initial begin
p = new();//创建的对象
p.c_short.constraint_mode(0);//关闭c_short
assert(p.randomize());//c_long 生效
transmit(p);
p.constrant_mode(0);//关掉p中所有的约束
p.c_short.constraint_mode(1);//打开c_short
end
//【注意】如果没有禁止其中任一约束,直接调用randomize()后,p.length的值会怎样?
// 此randomize()返回0,但不同的仿真器处理结果不同吗,不一定会编译报错
// 所以用约束时,要考虑返回值是0时,程序该怎么执行
三、数组的约束
1、随机化数组
动态数组的大小和内容,数组应该最先给定大小,防止生成体积过大的数组或空数组。
size
:对数组长度限制sum
:求和product
:求积and
:相与or
:相或xor
:异或
class dyn_size;
rand logic [31:0] d[];
constraint d_size {d.size() inside {[1:10]}; }
endclass
class good_sum5;
rand unit len[];
constraint c_len{
foreach(len[i]) len[i] inside {[1:255]};
len.size() inside {[1:8]};
len.sum() < 1024;
}
endclass
2、产生唯一元素的数组unique
如果想要产生一个随机数组,它的每一个元素的值都是唯一的。如果使用randc
数组,那么数组中的每一个元素只会独立地随机化(意思是一个元素的话是不会重复的,但是两个元素他们都在独立地随机,可能会产生重复)
,并不会按照我们的本意使得数组中的元素值是唯一的。
周期性是指单一变量的周期性
:单一变量会在周期内选取元素,周期结束之后才有可能发生重复
两种方法产生唯一元素数组:
class uniquea;
rand bit [7:0] ua[64];
constraint {
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外面
foreach (ua[i]) begin
assert(rc8.randomize());
ua[i]=rc8.val; //随机化之后存到数组里面
end
endfunction
endclass
3、随机化句柄数组(高级应用)
rand:随机化对象时,随机化句柄指向的对象。句柄在随机化之前要指向对象,是一个非悬空的指针,句柄指向的对象是随机的。
数组里面有最大数量的对象,在随机化句柄时,数组数量可能不是最大值,然后他们可以随机指向不同的对象
4、随机序列
随机序列randsequence
、endsequence
,建立复杂的测试序列
5、随机决策树
随机控制randcase
建立随机决策树,但带来的问题是没有变量可供追踪调试
randsequence
和randcase
是针对轻量级的随机控制的应用。而我们可以通过定义随机类取代上述随机控制的功能,并且由于类的继承性使得在后期维护代码时更加方便。
randsequence的相关功能我们在协调激励组件和测试用例时,可能会用到。randcase则对应着随机约束中的dist权重约束+if-else条件约束
的组合。
四、随机数函数
$random()
:平均分布,返回32位有符号随机数
$urandom()
:平均分布,返回32位无符号随机数
$urandom_range()
:在指定范围内的平均分布; 例如: $urandom_rang(0,100) ,urandom_range(150,0)-50:里面包括上下界,括号内数字大小相反时会自动改
$random_range(0,50)
:里面的数字就是上下界,且下界大于等于0
pre_randomize()
:两个回调函数,调用randomize的时候会调用这两个函数
post_randomize()
:可以将随机化的数据存储在里面,有助于提高覆盖率
五、solve…before
对两个随机变量的约束先后顺序进行限制,会使得随机概率发生改变
(1)这个a和data随机后有5种可能
a=1,data=3
a=0,data=0,1,2,3 每个概率为1/5
(2)加上solve a before data
后
a=1,概率为1/2 data=3 概率为1 总体为1/2
a=0,概率为1/2 data=0,1,2,3 概率为1/4 总体各为1/8
class transaction;
rand bit a;
rand bit[1:0] data;
constraint c1{ a -> data==2'h3;} //条件约束
endclass
module gen_data;
initial begin
transaction tr=new() ;
for(int i=0; i<10; i++ ) begin
tr.randomize() ;
$display("a= %0d, data= %0d",tr.a, tr.data) ;
end
end
endmodule
六、约束的高级用法
1、关闭约束
有时候经过随机化测试后,还会有部分地方没有覆盖到,此时需要产生一些定向的激励,来检测DUT,因此可以先禁止随机化,再将随机变量设置为固定值。
p.length.rand_mode(0);
p.length = 42;
assert(p.randomzie()); //length是固定值,再随机化其他随机变量
2、对数组大小进行约束
- 注意声明数组大小为bit类型,如果是有符号数,随机化可能产生负数
- 注意加法或者其他运算可能存在溢出
class num_5;
rand bit len[];
constraint c_len{
foreach(len[i])
len[i] inside {[1:255]};
len.sum<1024;
len.size() inside {[1:8]};
}
endclass
七、问题
1、约束的种类
权重约束、条件约束、软约束、内嵌约束
2、约束的控制
constraint_mode(0/1)
rand_mode(0/1)