一、简介
why
- 芯片体积增大,复杂度逐渐提高,定向测试已经无法满足验证的需求,随机化验证的比例逐渐提高;
- 定向测试能够找到你认为可能存在的缺陷,而随机测试可以找到你都没法想到的缺陷;
- 随机测试的环境要求比定向测试复杂,它需要激励、参考模型和在线比较。
- 随机测试相对定向测试可以减少相当多的代码量,而产生的激励较定向测试也更多项;
- CRT(constraint random test)能够提高效率,提供测试激励和测试场景;
what
- 器件配置:通过寄存器和系统信号
- 环境配置:随机化验证环境,例化合理的时钟和外部反馈信号;
- 原始输入数据:例如数据包的长度、宽度,数据的顺序
- 延时:握手信号之间的时序关系,例如valid和ready,req和ack之间的时序关系
- 协议异常:如果反馈信号给出异常,那么设计能否保持数据处理的稳定性
二、随机化种类
class packet;
//定义随机变量
rand bit[31:0] src,dst,data[8];
randc bit[7:0] kind;
//约束变量
constraint c//约束需要有实例名
{
src > 10;
src < 15;
}//注意没有;号
endclass
program test;
packet p;
initial begin
p = new();
assert(p.randomize)
else $fatal(0,"Packet :: randomize failed");
transmit(p);
end
endprogram
- rand是可以重复的随机定义,randc可以类比是在一副牌里抽一张出来,但是抽出来之后就不放回去了,等待所有牌都被抽完才会重新抽,并且在随机约束解析时,会先解析randc的变量,不同的解析顺序可能导致不一样的随机分布结果;
- randc循环性质随机,但是他的资源占用比多大,因为它需要保存前面的随机值;
- randomize把类里面的所有属性进行随机;
- 一般随机化使用方法:
- 定义类,在类中声明关心的属性为rand/randc;
- new对象
- handle调用随机函数randomize;
- 需要注意断言中的使用:
assert(p.randomize) $info(...);else $fatal(...);
注意assert后面如有有info可以加分号,如果没有消息需要打印则不需要加
assert(condition) expression;
else expression;
assert(condition)
else expression;
2.1布尔表达式
一般是在约束里所有表达式进行判断进行逻辑判断.
class order;
rand bit [7:0] lo,med,hi;
constraint bad
{
lo<med<hi;
}
endclass
- 约束bad按照从左自右顺序分割两个表达式:
((lo<med)<hi)
,首先计算lo<med,它们的值是0或1,然后根据约束,hi的值要大于0或1,所以变量lo和med虽然随机化了,但是没有受到约束;
class order;
rand bit [15:0] lo,med,hi;
constraint good
{
lo < med;
med <hi;
}
endclass
2.2 权重分配(weighted distributions)
符号 | 含义 |
---|---|
:= | 表示值范围内的每一个值的权重都是相同的; |
: / | 表示权重要均分到值范围内的每一个值 |
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
}
带变量的权重分配
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;
READ16 := read16_wt;
READ32 := read32_wt;
};
}
endclass
- 权重read8/16/32_wt后续可以更改,这里的8/16/32是总线访问分别对应字节访问、半字访问还有字访问,所以在数据访问是不一样的;
- 字节访问第0位是保留下来,半字访问是第0位是不关心的,字访问是第0位和第一位不关心的;
2.3 范围表达式随机(range expressions)
用inside
运算符产生一个值的集合,除非对变量还有其他约束,否则sv在值的集合中取随机值时,各个值的选取机会是相等的,在集合中也可以使用变量
rand int c;
int lo,hi;
constraint c_range
{
c inside{[lo:hi]};//!(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:$]};
c inside {[$:4],[20:$]};
}
在集合中使用数据
rand int f;
int fib[5] = '{1,2,3,5,8};
constraint c_fibonacci
{
f inside fib;
}
2.4 条件表达式
通常约束块里面的所有约束都是有效的,但是有时我们只想让约束在某些时刻有效,例如在总线支持字节、半字、字的读操作,但是我们想只支持字的操作时。
- 使用
if...else
class stim;
bit flag;
rand bit [31:0] dst;
constraint c_stim
{
if(flag)
{
dst inside {[40:80]};
}
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
2.5 外部约束
外部约束可以在需要的时候才添加约束,也可以不加约束;
class packet;
rand bit [7:0] length;
rand bit [7:0] payload[];
constraint c_valid
{
length > 0;
payload.size() == length;
}
constraint c_external;
endclass
program test;
constraint packet::c_external{length == 1;}
endprogram
显示外部约束的格式:在使用之前如果没有前面的extern
会报错;
extern constraint c_ext
2.6 双向约束
约束块不想自上而下执行程序性代码,它们是声明性的代码,是并行执行的,所有的约束表达式同时有效,古SV会同时计算所有的随机标量约束,取他们的交集
rand logic[15:0] r,s,t;
constraint c_bitir
{
r < t;
s == r;
t <30;
s >25;
}
有一个练习:
parameter MAX_SIZE = 10;
class packet;
rand bit [31:0] src,dst,data[8];
randc bit [2:0] kind;
constraint c
{
src > 10;
src < 15;
}
endclass:packet
//随机化句柄数组
class randarray;
rand packet array[];
constraint c
{
array.size() inside{[1:MAX_SIZE]};
}
function new();
array = new[MAX_SIZE];
foreach(array[i])
array[i] = new();
endfunction
endclass:randarry
module top_tb;
packet p;
initial begin
p = new();
assert (p.randomize)
else $fatal(0,"packet::randomize failied");
foreach(p.array[i])begin
$display("src =%d",p.array[i].src);
$display("dst =%d",p.array[i].dst);
$display("data = ",p.array[i].data);
$display("kind = %d",p.array[i].kind);
end
end
endmodule
三、随机约束分布概率
3.1 没有约束的类
class unconstrained;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3
endclass
module top_tb;
unconstrained u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
u = new();
for(int i = 1;i<20000;i++)begin
assert(u.randomize());
case({u.x,u.y})
3'b000:cnt000 = cnt000+1'b1;
3'b001:cnt001 = cnt001+1'b1;
3'b010:cnt010 = cnt010+1'b1;
3'b011:cnt011 = cnt011+1'b1;
3'b100:cnt100 = cnt100+1'b1;
3'b101:cnt101 = cnt101+1'b1;
3'b110:cnt110 = cnt110+1'b1;
3'b111:cnt111 = cnt111+1'b1;
endcase
end
$display("{u.x,u.y} is 000 time %0d",cnt000);
$display("{u.x,u.y} is 001 time %0d",cnt001);
$display("{u.x,u.y} is 010 time %0d",cnt010);
$display("{u.x,u.y} is 011 time %0d",cnt011);
$display("{u.x,u.y} is 100 time %0d",cnt100);
$display("{u.x,u.y} is 101 time %0d",cnt101);
$display("{u.x,u.y} is 110 time %0d",cnt110);
$display("{u.x,u.y} is 111 time %0d",cnt111);
end
endmodule
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 1/8 |
B | 0 | 1 | 1/8 |
C | 0 | 2 | 1/8 |
D | 0 | 3 | 1/8 |
E | 1 | 0 | 1/8 |
F | 1 | 1 | 1/8 |
G | 1 | 2 | 1/8 |
H | 1 | 3 | 1/8 |
- 没有约束的类是可以根据x和y分别对应不同的解来计算概率的,例如x出现的概率是1/2,y出现0的概率是1/4,所以概率位1/8
3.2 关系操作
class impact1;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3
constraint c_xy
{
(x == 0)->y == 0;
}
endclass
module top_tb;
impact1 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
u = new();
for(int i = 1;i<20000;i++)begin
assert(u.randomize());
case({u.x,u.y})
3'b000:cnt000 = cnt000+1'b1;
3'b001:cnt001 = cnt001+1'b1;
3'b010:cnt010 = cnt010+1'b1;
3'b011:cnt011 = cnt011+1'b1;
3'b100:cnt100 = cnt100+1'b1;
3'b101:cnt101 = cnt101+1'b1;
3'b110:cnt110 = cnt110+1'b1;
3'b111:cnt111 = cnt111+1'b1;
endcase
end
$display("{u.x,u.y} is 000 time %0d",cnt000);
$display("{u.x,u.y} is 001 time %0d",cnt001);
$display("{u.x,u.y} is 010 time %0d",cnt010);
$display("{u.x,u.y} is 011 time %0d",cnt011);
$display("{u.x,u.y} is 100 time %0d",cnt100);
$display("{u.x,u.y} is 101 time %0d",cnt101);
$display("{u.x,u.y} is 110 time %0d",cnt110);
$display("{u.x,u.y} is 111 time %0d",cnt111);
end
endmodule
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 1/5 |
B | 0 | 1 | 0 |
C | 0 | 2 | 0 |
D | 0 | 3 | 0 |
E | 1 | 0 | 1/5 |
F | 1 | 1 | 1/5 |
G | 1 | 2 | 1/5 |
H | 1 | 3 | 1/5 |
- 这里增加了关系约束,x等于0的时候,y等于0;
- y的值依赖于x的值,所以只有五种可能,所以都为1/5;
class impact2;
randc bit x;//0、1
rand bit[1:0]y;//0、1、2、3
constraint c_xy
{
(x == 0)->y == 0;
}
endclass
module top_tb;
impact2 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
u = new();
for(int i = 1;i<20000;i++)begin
assert(u.randomize());
case({u.x,u.y})
3'b000:cnt000 = cnt000+1'b1;
3'b001:cnt001 = cnt001+1'b1;
3'b010:cnt010 = cnt010+1'b1;
3'b011:cnt011 = cnt011+1'b1;
3'b100:cnt100 = cnt100+1'b1;
3'b101:cnt101 = cnt101+1'b1;
3'b110:cnt110 = cnt110+1'b1;
3'b111:cnt111 = cnt111+1'b1;
endcase
end
$display("{u.x,u.y} is 000 time %0d",cnt000);
$display("{u.x,u.y} is 001 time %0d",cnt001);
$display("{u.x,u.y} is 010 time %0d",cnt010);
$display("{u.x,u.y} is 011 time %0d",cnt011);
$display("{u.x,u.y} is 100 time %0d",cnt100);
$display("{u.x,u.y} is 101 time %0d",cnt101);
$display("{u.x,u.y} is 110 time %0d",cnt110);
$display("{u.x,u.y} is 111 time %0d",cnt111);
end
endmodule
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 1/2 |
B | 0 | 1 | 0 |
C | 0 | 2 | 0 |
D | 0 | 3 | 0 |
E | 1 | 0 | 1/8 |
F | 1 | 1 | 1/8 |
G | 1 | 2 | 1/8 |
H | 1 | 3 | 1/8 |
- randc是先解析的,所以先解析x的值(0、1)。如果x等于0,则y肯定是0,针对x值0是1/2的概率;
- 其他情况x等于1时为1/2,然后y出现0、1、2、3的概率为1/4,所以E-H是1/8;
class impact3;
rand bit x;//0、1
randc bit[1:0]y;//0、1、2、3
constraint c_xy
{
(x == 0)->y == 0;
}
endclass
module top_tb;
impact3 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
u = new();
for(int i = 1;i<20000;i++)begin
assert(u.randomize());
case({u.x,u.y})
3'b000:cnt000 = cnt000+1'b1;
3'b001:cnt001 = cnt001+1'b1;
3'b010:cnt010 = cnt010+1'b1;
3'b011:cnt011 = cnt011+1'b1;
3'b100:cnt100 = cnt100+1'b1;
3'b101:cnt101 = cnt101+1'b1;
3'b110:cnt110 = cnt110+1'b1;
3'b111:cnt111 = cnt111+1'b1;
endcase
end
$display("{u.x,u.y} is 000 time %0d",cnt000);
$display("{u.x,u.y} is 001 time %0d",cnt001);
$display("{u.x,u.y} is 010 time %0d",cnt010);
$display("{u.x,u.y} is 011 time %0d",cnt011);
$display("{u.x,u.y} is 100 time %0d",cnt100);
$display("{u.x,u.y} is 101 time %0d",cnt101);
$display("{u.x,u.y} is 110 time %0d",cnt110);
$display("{u.x,u.y} is 111 time %0d",cnt111);
end
endmodule
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 1/8 |
B | 0 | 1 | 0 |
C | 0 | 2 | 0 |
D | 0 | 3 | 0 |
E | 1 | 0 | 1/8 |
F | 1 | 1 | 1/4 |
G | 1 | 2 | 1/4 |
H | 1 | 3 | 1/4 |
- 这里与上面不同之处在于先解析y,然后y(0、1、2、3)分别为1/4,x的取值有两种可能,但是由于约束的原因,在y等于1、2、3时,x不可能为0, 所以y=0,x=0、1分别为1/8;
- 所以x等于1,y等于1、2、3的概率为1/4
class impact1;
randc bit x;//0、1
randc bit[1:0]y;//0、1、2、3
constraint c_xy
{
(x == 0)->y == 0;
}
endclass
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 1/4 |
B | 0 | 1 | 0 |
C | 0 | 2 | 0 |
D | 0 | 3 | 0 |
E | 1 | 0 | 0 |
F | 1 | 1 | 1/4 |
G | 1 | 2 | 1/4 |
H | 1 | 3 | 1/4 |
- 首先randc是轮询的
- x = 0,y只能是0;
- x = 1,y只能在1、2、3里面
- 但是这里两个randc,可能不同仿真工具可能不一样的仿真结果,我们应该避免这种让仿真工具有问题;
class impact5;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3
constraint c_xy
{
y > 0;
(x == 0)->y == 0;
}
endclass
module top_tb;
impact5 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
u = new();
for(int i = 1;i<20000;i++)begin
assert(u.randomize());
case({u.x,u.y})
3'b000:cnt000 = cnt000+1'b1;
3'b001:cnt001 = cnt001+1'b1;
3'b010:cnt010 = cnt010+1'b1;
3'b011:cnt011 = cnt011+1'b1;
3'b100:cnt100 = cnt100+1'b1;
3'b101:cnt101 = cnt101+1'b1;
3'b110:cnt110 = cnt110+1'b1;
3'b111:cnt111 = cnt111+1'b1;
endcase
end
$display("{u.x,u.y} is 000 time %0d",cnt000);
$display("{u.x,u.y} is 001 time %0d",cnt001);
$display("{u.x,u.y} is 010 time %0d",cnt010);
$display("{u.x,u.y} is 011 time %0d",cnt011);
$display("{u.x,u.y} is 100 time %0d",cnt100);
$display("{u.x,u.y} is 101 time %0d",cnt101);
$display("{u.x,u.y} is 110 time %0d",cnt110);
$display("{u.x,u.y} is 111 time %0d",cnt111);
end
endmodule
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 0 |
B | 0 | 1 | 0 |
C | 0 | 2 | 0 |
D | 0 | 3 | 0 |
E | 1 | 0 | 0 |
F | 1 | 1 | 1/3 |
G | 1 | 2 | 1/3 |
H | 1 | 3 | 1/3 |
3.3 solve…before
class slovebefore;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3
constraint c_xy
{
(x == 0)->y == 0;
solve x before y;
}
endclass
module top_tb;
slovebefore u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
u = new();
for(int i = 1;i<20000;i++)begin
assert(u.randomize());
case({u.x,u.y})
3'b000:cnt000 = cnt000+1'b1;
3'b001:cnt001 = cnt001+1'b1;
3'b010:cnt010 = cnt010+1'b1;
3'b011:cnt011 = cnt011+1'b1;
3'b100:cnt100 = cnt100+1'b1;
3'b101:cnt101 = cnt101+1'b1;
3'b110:cnt110 = cnt110+1'b1;
3'b111:cnt111 = cnt111+1'b1;
endcase
end
$display("{u.x,u.y} is 000 time %0d",cnt000);
$display("{u.x,u.y} is 001 time %0d",cnt001);
$display("{u.x,u.y} is 010 time %0d",cnt010);
$display("{u.x,u.y} is 011 time %0d",cnt011);
$display("{u.x,u.y} is 100 time %0d",cnt100);
$display("{u.x,u.y} is 101 time %0d",cnt101);
$display("{u.x,u.y} is 110 time %0d",cnt110);
$display("{u.x,u.y} is 111 time %0d",cnt111);
end
endmodule
解 | x | y | 概率 |
---|---|---|---|
A | 0 | 0 | 1/2 |
B | 0 | 1 | 0 |
C | 0 | 2 | 0 |
D | 0 | 3 | 0 |
E | 1 | 0 | 1/8 |
F | 1 | 1 | 1/8 |
G | 1 | 2 | 1/8 |
H | 1 | 3 | 1/8 |
- 与randc的先解析相似,概率分布也和randc的概率分布一样;
- 但是不要滥用solve…before,会降低计算速度;
四、随机约束控制
4.1 控制多个约束块constraint_mode()
class packet;
rand int length;
constraint c_short {length inside{[1:32]};}
constraint c_long {length inside{[1000:1023]};}
endclass
module top_tb;
packet p;
initial begin
p = new();
p.c_short.constraint_mode(0);//disabling short constraint
assert(p.randomize());
$display("p::length = %d",p.length);
p.constraint_mode(0);
p.c_short.constraint_mode(1);
assert(p.randomize());
$display("p::length = %d",p.length);
end
endmodule
- 可以单独关闭类中的某个约束块,可以单独开启每个约束块,使用0/1即可;
- 可以一口气把类中的所有的约束块都给关了;
4.2 控制随机变量rand_mode()
class packet;
rand bit [7:0] length,payload[];
constraint c_valid {length > 0;payload.size() ==length;}
endclass
module top_tb;
packet p;
initial begin
p = new();
p.length.rand_mode(0);
p.length = 42;
assert(p.randomize());
$display("p::length = %d",p.length);
p.length.rand_mode(1);
assert(p.randomize());
$display("p::length = %d",p.length);
end
endmodule
- 可以通过rand_mode对某个随机变量的随机功能进行开关控制;
4.3 控制随机变量randomize() with{}
class transaction;
rand bit [31:0]addr,data;
constraint c1{addr inside{[0:100],[1000:2000]};}
endclass
module top_tb;
transaction t;
initial begin
t = new();
assert(tr.randomize() with {addr >= 50;addr <= 1500;data < 10;});
$display("t::addr is %d",t.addr);
$display("t:data is %d",t.data);
assert(t.randomize() with{addr == 2000;data >10;});//force addr to a specific value,data >10
$display("t::addr is %d",t.addr);
$display("t:data is %d",t.data);
end
endmodule
- 这里的randomize() with{}可以强制约束甚至可以变成一个特定的值;
4.4 randomize单独控制变量
class rising;
byte low;
rand byte med,hi;
constraint c_up{low<med;med<hi;}
endclass
module top_tb;
rising r;
initial begin
r = new();
r.randomize();
$display("r::low is %d,r:med is %d,r::hi is %d",r.low,r.med,r.hi);
r.randomize(med);
$display("r::low is %d,r:med is %d,r::hi is %d",r.low,r.med,r.hi);
r.randomize(low);
$display("r::low is %d,r:med is %d,r::hi is %d",r.low,r.med,r.hi);
end
endmodule
- 这里可以让不是rand类型的变量也可以进行随机化,实现对单变量的控制;
- 但是这里不推荐一个一个变量进行随机化;
4.5 pre_randomize与post_randomize
它们的特点:
- 都是function,不会消耗事件;
pre_randomize->randomize->post_randomize
- pre_randomize()和post_randmize()函数可以被用户重写,用户使用pre_randomize()函数,在随机前修改代码中非随机变量的值,如上下限、权重等;
- post_randmize()函数可以在随机后修改随机值或做一些其它的计算。
- randomize失败的化post不会执行,
- pre和post可以重写,但是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);
endfunction
function void post_randomize();
$display("call post_randomize!");
if(wr_addr == 1)
broadcast = 1;
else broadcast = 0;
endfunction
endclass
module top_tb;
wr_tran tr;
initial begin
tr = new();
tr.constraint_en = 0;
tr.randomize() with
{
wr_addr == 200;
wr_data == 3;
valid == 0;
};
$display("wr_addr = %d,
wr_data = %d,
valid = %d,
broadcast = %d",
tr.wr_addr,tr.wr_data,tr.valid,tr.broadcast);
end
endmodule
五、随机化常见错误
- 在随机变量中使用了有符号变量导致随机化错误;
- 变量可能会溢出导致高位去除而随机化出错;
避免随机化错误的技巧: - 避免使用乘除和取模的操作,如果要使用就通过左移和右移代替乘除;
- 使用AND mask代替取模;
sig1:12345678
sig 1 &000FF 000
class test;
rand bit[7:0]sig1;
rand int unsigned sig1;
constrain c
{
sig2>1000;
sig1>sig2;//sig1的最大值256,无法达到1000,需要注意位宽、符号
rand bit[7:0]sig1;
rand bit[7:0]sig2;
constraint c1{sig1>5;}
constraint c2{sig2>10;}
constraint c2{sig1+sig2 ==13;}//这里也是不对的,多随机变量的出现形式可能会出现相互矛盾的;
所以我们在使用随机化时需要注意
- 数据类型
- 数据位宽(精确些)
- 多个约束块中的变量之间的关系
- 约束块中表达式的结果
- 约束块中各约束冲突