一、随机化与约束
1、为什么引入随机化?
- 芯片体积增大,复杂度日渐提高,定向测试已经无法满足验证的需求,而随机测试的比例逐渐提高。
- 定向测试能找到你认为可能存在的缺陷,而随机测试可以找到连你都没有想到的缺陷。
- 随机测试的环境要求比定向测试复杂,它需要激励、参考模型和在线比较。上百次的仿真不再需要人为参与,以此来提高验证效率。
- 随机测试相对于定向测试可以减少相当多的代码量,而产生的激励较定向测试也更多样
2、什么需要随机化测试?
- 器件配置:通过寄存器和系统信号
- 环境配置:随机化验证环境,例如合理的时钟和外部反馈信号
- 原始输入数据:例如数据包的长度、带宽,数据间的顺序
- 延时:握手信号之间的时序关系,例如valid和ready,req和ack之间的时序关系
- 协议异常:如果反馈信号给出异常,那么设计是否可以保持后续数据处理的稳定性
3、什么是约束?
SV的约束求解器(CRT)可以对约束表达式进行求解,求解器能够通过SV的PRNG(伪随机数发生器)由一个初始值(seed)产生满足约束的值。
- 只要改变seed的值就可以改变CRT的行为。
- 不同仿真器对于同一个约束类和种子值求解出的数值可能是不同的。
- SV只能随机化出2值数据类型,也就是说无法随机化出X值和z值,也无法随机化字符串。
二、随机化方法
class Packet;
// The random variables
rand bit [31:0] src, dst, date[8];
randc bit [7:0] kind1;
function void pre_randomize();
$display ( "before pre_randomize src = %0d, dst = %0d" , src,dst) ;
$display ( "after pre_randomize src = %0d , dst = %0d" , src, dst);
endfunction
// Limit the values for src
constraint c
{
src > 10;
src < 15;
}
function void post_randomize() ;
$display ( "before post_randomize src = %0d,dst = %0d " , src,dst ) ;
$display ( "after post_randomize src = %0d,dst = %0d" ,src,dst) ;
endfunction
endclass
program test18;
Packet p;
initial begin
p = new();
assert(p.randomize)
else $fatal(0, "Packet::randomize failed");
$display("src= %0d",p.src);
$display("dst= %0d ",p.dst);
//$display("data=%0P",p.date);
$display(p.date);
$display("kind=%0d",p.kind1);
end
endprogram
打印结果:
before pre_randomize src = 0, dst = 0
after pre_randomize src = 0 , dst = 0
before post_randomize src = 14,dst = 3053944240
after post_randomize src = 14,dst = 3053944240
src= 14
dst= 3053944240
'{'h4279b9b, 'h90134d03, 'hd093f049, 'h2589af1f, 'h5057fb67, 'h1bf88803, 'hbbf86411, 'h663d54f4}
kind=24
说明:
rand:在对应范围内随机数据,在随机周期内出现的值可能重复
randc:在对应范围内随机数据,在随机周期内出现的值不会重复
randomize:该函数为类里面所有的rand和randc类型的随机变量赋一个随机值,并保证不违背所有有效约束;
在调用randomize()时可以传递随机变量的子集,这样只会随机化类里的某几个变量,而其他随机变量保持默认,不会被随机,如randomize(src,dst)随机,data[8],kind不会被随机
;pre/post_randmize()回调函数:通过pre/post_randmize()函数在调用randomize()之前或者之后执行一些操作,这两个函数randomize()函数是自动去调用,如果没有定义这两个函数,那么就认为这两个函数是什么也不做的。pre/post_randmize()其实并没有做任何随机的操作,只是在随机的前后做一些function的操作,对随机的结果进行调整,改变随机的值等
随机化函数:
- $random() 平均分布,返回32位有符号随机数。
- $urandom()平均分布,返回32位无符号随机数。
- $urandom_range() 在指定范围内平均分布。
三、约束种类
1、布尔表达式 Boolean expressions
class order;
rand bit [7:0] lo, med, hi;
constraint right
{
lo < med; // right
med < hi; // right
//lo < med < hi;//error不能这样表达
}
endclass
2、 权重分配dist
关键词dist用来在约束中产生随机数值的权重分布。
dist操作符::= 或 :/,值或权重可以是常数或者变量。
:= :每一个值的权重是相同的,全部相加总和的占比;
:/ :权重 要平均分到值范围内的每一个值,按百分比来。
rand int src, dst;
constraint c_dist
{
src dist {0:=40, [1:3]:=60};
// 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};
// dst = 0, weight = 40/100
// dst = 1, weight = 20/100
// dst = 2, weight = 20/100
// dst = 3, weight = 20/100
}
3、带变量的权重分配dist
typedef enum {READ8,READ16,READ32} read_e;
class ReadCommands;
rand read_e read_cmd;
int read8_wt=1,read16_wt=1,read32_wt=1;//权重值
constraint c_read
{
read_cmd dist
{
READ8:=read8_wt, //READ8概率1/3
READ16:=read16_wt,//READ16概率1/3
READ32:=read32_wt //READ32概率1/3
};
}
//通过这种变量赋值权重值的方式,可从类的外部通过变量改变权重值
endclass
4、范围表达式inside
//规定随机值的上下限
rand int c; // Random variable
int lo, hi; // Non-random variables
constraint c_range
{
c inside {[lo:hi]}; // lo <= c && c <= hi
}
//随机值的上下限取反
rand int c; // Random variable
int lo, hi; // Non-random variables
constraint c_range
{
!(c inside {[lo:hi]}); // c < lo or c > hi
}
//$ 指定上下限
rand bit [6:0] b; // 0 <= b <= 127
rand bit [5:0] e;
constraint c_range
{
b inside {[$:4], [20:$]}; // 0 <= b <= 4 || 20 <= b <= 127
e inside {[$:4], [20:$]}; // 0 <= e <= 4 || 20 <= e <= ?
}
//使用数组
rand bit [6:0] b; // 0 <= b <= 127
int fib [5] = ’{1, 5, 3, 9, 2};
constraint c_range
{
b inside fib;
}
说明:
- inside是常见的约束运算符,表示变量应该属于某一个值的集合。
5、条件表达式
//方式一
class Stim;
bit flag;
rand bit [31:0] dst;
constraint c_stim
{
if (flag)
{// not begin
dst inside {[40:80]};
}// not end
else
dst inside {[2:10], [50:67]};
}
endclass
//方式二
class Stim;
bit flag;
rand bit [31:0] dst;
constraint c_stim
{
flag -> dst inside {[40:80]};
!flag -> dst inside {[2:10], [50:67]};
}
endclass
6、外部约束
class Packet;
rand bit [7:0] length;
rand bit [7:0] payload[];
constraint c_valid {length > 0;payload.size() == length;}
constraint c_external;
endclass
constraint Packet::c_external {length == 1;} //在类外定义
7 、 双 向约束
约束块是声明性的代码,是并行执行的。所有的约束表达式同时有效。SV会同时计算所有的随机变量约束,取它们的交集
rand logic [15:0] r,s,t;
constraint c_bitir {
r < t;
s ==r;
t <30;
s >25;
}
四、约束的控制
1、控制约束开关函数constraint_mode()
0:表示关闭对应约束块,1:表示使能对应约束块
class Packet;
rand int length;
constraint c_short {length inside {[1:32]};}
constraint c_long {length inside {[1000:1023]};}
endclass
program test;
Packet p;
initial begin
p = new();
p.c_short.constraint_mode(0); //disabling short constraint
assert(p.randomize());
p.constraint_mode(0); //disabling all constraints
p.c_short.constraint_mode(1); //enabling short constraints
assert(p.randomize());
transmit(p);
end
endprogram
2、控制随机变量开关函数rand_mode()
0:表示不随机对应约束里的某个变量,1:表示使能对应约束里的随机变量去随机
class Packet;
rand bit [7:0] length, payload[];
constraint c_valid {length > 0;payload.size() == length;}
endclass
program test;
Packet p;
initial begin
p = new();
p.length.rand_mode(0); // Make length nonrandom
p.length = 42; // set it to a constant value
assert (p.randomize()); // then randomize the payload
p.length.rand_mode(1); // Make length random
end
endprogram
3、控制随机变量 randomize() with {}
表示在调用时想在原来约束的基础上进一步加约束
class Transaction;
rand bit [31:0] addr, data;
constraint c1 {addr inside{[0:100],[1000:2000]};}
endclass
program test;
Transaction t;
initial begin
t = new();
// addr is 50-100, 1000-1500, data < 10
assert(t.randomize() with {addr >= 50; addr <= 1500; data < 10;});
$display("t::addr=%d,data=%d",t.addr,t.data);
// force addr to a specific value, data > 10
assert(t.randomize() with {addr == 2000; data > 10;});
$display("t::addr=%d,data=%d",t.addr,t.data);
end
endprogram
4、randomize() 控制随机变量
只会随机化类里的某几个随机变量,而其他随机变量保持默认,不会被随机
class Rising;
byte low;
rand byte med, hi;
constraint c_up
{
low < med;
med < hi;
}
endclass
program test;
Rising r;
initial begin
r = new();
r.randomize(); //对所有随机变量在约束上进行随机
r.randomize(med); //只随机med,hi保持上一个值
r.randomize(low); //只随机low,但low不是随机变量,med和hi保持上一个值
end
endprogram
5、pre_randomize 与post_randomize 函数
他们都是function, 不消耗仿真时间;他们通常用来设置一些非随机的变量或者计算随机数据中的错误如randomize调用失败,那么post_randomize将不会被执行,但是pre_randomize不randomize是否成功都会执行,它们是由randomize()函数自动去调用的。
- pre_randomize: 在执行randomize() 之前调用,主要用来设定与randomize相关的各个变量的值;
- post_randomize: 在randomize()之后调用.主要用来调用相关的函数对randomize出来的变量进行后续处理。
class wr_tran;
int constraint_en;
int broadcast;
rand int wr_addr;
rand int wr_data;
rand bit valid;
constraint generic_c { valid ==1; wr_addr<100;}
function void pre_randomize();
$display("call the pre_randomize !");
if(!constraint_en) generic_c.constraint_mode(0);
$display("disabled constraint generic_c !");
endfunction
function void post_randomize();
$display("call post_randomize !");
if(wr_addr ==1) broadcast =1;
else broadcast =0;
endfunction
endclass
module test18;
wr_tran m_tr;
initial begin
m_tr =new();
$display("wr_addr=%0d,wr_data=%0d,valid=%0d,broadcast=%0d,constraint_en=%0d",m_tr.wr_addr, m_tr.wr_addr,m_tr.valid,m_tr.broadcast,m_tr.constraint_en);
m_tr.constraint_en=0;
m_tr.randomize() with {wr_addr==200;wr_data==3;valid==0;};
$display("wr_addr=%0d,wr_data=%0d,valid=%0d,broadcast=%0d,constraint_en=%0d",m_tr.wr_addr, m_tr.wr_addr,m_tr.valid,m_tr.broadcast,m_tr.constraint_en);
end
endmodule
打印结果:
wr_addr=0,wr_data=0,valid=0,broadcast=0,constraint_en=0
call the pre_randomize !
disabled constraint generic_c !
call post_randomize !
wr_addr=200,wr_data=200,valid=0,broadcast=0,constraint_en=0
6、约束的重载
1、采用constraint_mode(0)关闭约束,然后用randomize() with {} 重新约束;
class constrainend;
rand bit x;
rand bit [1:0] y;
constraint c_xy {(x==0)-> y==0};
endclass
program test;
constrainend p;
initial begin
p=new();
p.c_xy.constraint_mode(0); //disable c_xy constraint
assert(p.randomize() with {x==0; y==0;});
$display("x = %d",p.x);
$display("y = %d",p.y);
end
endprogram
2、子类extends父类,然后定义同名约束覆盖父类约束
class constrainend;
rand bit x;
rand bit [1:0] y;
constraint c_xy {(x==0)-> y==0;}
endclass
class overwrite extends constrainend;
constraint c_xy {x==0; y==0;}
endclass
program test;
overwrite p;
initial begin
p=new();
assert(p.randomize());
$display("x = %d",p.x);
$display("y = %d",p.y);
end
endprogram