SystemVerilog验证 测试平台编写指南 第六章 随机化 上

6.1 介绍
原因:产生完整的激励集测试设计的功能越来越困难,编写足够的定向测试集变得不可能;功能项的关系是大多错误的来源,该错误很难通过清单检查方法排除。
采用受约束的随机测试法(CRT:constrained random test)自动产生测试集。
CRT环境不仅需要产生激励,还需要通过参考模型、传输函数或其它方法来预测输出结果。
CRT由两部分组成:使用随机的数据流为DUT产生输入的测试代码,以及伪随机数发生器(PRNG)的种子(seed)。
6.2 什么需要随机化
必须对DUT里的所有关键点都采用随机化的技术。随机化使控制路径里的每一个分支都可能被测试
考虑设计输入的各个方面,如:
(1)器件配置;
在RTL级的设计中,需要测试足够多的配置。
(2)环境配置;
通常你设计的器件在一个包含了若干个器件的环境里工作。当验证DUT的时,它连接到一个模拟了这种环境的测试平台。你应该随机化整个环境,包括对象的数量以及它们如何配置。
(3)原始数据输入;
(4)封装后的输入数据;
(5)协议异常、错误(error)和违规(violation);
任何有可能出错的地方最终都会出错。好的验证工程师会测试设计在设计规范边界处的行为,甚至测试在设计规范之外的行为。
测试平台应该能够产生功能正确的激励,然后通过翻转某一个配置位,在随机的时间间隔里产生随机的错误类型。
(6)延时;
测试平台应该在每一个测试里都使用随机的、有效的延时,以便于发现设计中的Bug。
(7)事务状态
6.3 System Verilog中的随机化
当和OOP同时使用时,System Verilog中的随机激励产生是最有效的。首先建立一个具有一组相关的随机变量的类,然后用随机函数为这些变量赋随机值,并用约束来限制随机值的范围。受约束的随机激励是在事务级产生的,通常不会一次只产生一个值。
6.3.1 带有随机变量的简单类
例6.1 简单的随机类

class packet;
	//随机变量
	rand bit [31:0] src, dst, data[8]; 
	randc bit [7:0] kind; 
	//src的约束
	constraint c {src > 10;
					src<15;} 
endclass

Packet p;
	initial begin
	p=new(); // 产生一个包
	assert (p.randomize());//断言,随机化成功,函数返回1
	else $fatal(0,“Packet::randomize failed”); //随机化失败,函数返回0,并显示错误信息
	transmit (p);
end

普通随机数rand 修饰符,每次随机化这个类时,这些变量都会赋一个值。
循环随机数randc修饰符,周期随机性,即所有可能的值赋过值后随机值才可能重复。
约束是一组用来确定变量的值的范围的关系表达式,表达式的值永远为真。约束表达式放在{…}括号中,因为这段代码是声明性质,而不是程序性质。
randomize()函数在遇到约束方面的问题时返回0。上例使用断言来检查randomize函数的结果,并使用$fatal来终止仿真。针对不同的仿真工具,需使用相应的选项来使断言能够终止仿真过程。

6.3.2 检查随机化(randomize)的结果
randomize()函数为类里所有的rand和randc类型的随机变量赋一个随机值,并保证不违背所有有效的约束。当代码里有矛盾的约束时,随机化过程会失败,因此一定要检查随机化的结果。如果不检查,变量可能会赋未知值,从而导致仿真失败。
例6.1使用断言检查randomize()的结果。如果随机化成功,函数返回1。如果失败,函数返回0,断言检查到错误后会显示错误信息。你必须对仿真器做一些设置,使仿真器在错误时能自动结束仿真。

6.3.3 约束求解
约束表达式的求解是由System Verilog 的约束求解器完成的。求解器能够选择满足约束的值,这个值由System Verilog的PRNG从一个初始值(seed)产生。

6.3.4 什么可以被随机化
System Verilog可以随机化整形变量,即由位组构成的变量。尽管只能随机化2值数据类型,但位也可以是2值或4值类型。所以可以使用整数和位矢量,但不能使用随机字符串,或在约束在指向句柄。

6.4 约束
需要使用包含一个或多个约束表达式的约束块定义激励向量间的关系,System Verilog会选择满足所有表达式的随机值。
每个表达式至少有一个变量必须是rand或randc类型的随机变量。

6.4.1 什么是约束
举例6.3 受约束的随机类

class Stim;
	const bit [31:0] CONGEST_ADDR=42;
	typedef enum {READ, WRITE, CONTROL} Stim_e;
	randc stim_e kind; //枚举变量
	rand bit [31:0] len, src, dst;
	bit congestion_test;

	constraint c_stim { //约束块
		len<1000; //控制变量len的范围
		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

6.4.2 简单表达式
例6.3中的类有一个约束块,块里包含若干个表达式。前两个表达式控制变量len的范围。变量可以在多个表达式里使用。
在一个表达式中最多只能使用一个关系操作符(<、<=、==、>=、>)。

6.4.3 等效表达式
约束块里只能包含表达式,所以在约束块里不能进行赋值。相反,应该用关系运算符为随机变量赋一个固定的值,例如len = = 42。也可以在多个随机变量之间使用更复杂的关系表达式,例如len==header.addr_mode*4+payload.size()。

6.4.4 权重分布
dist操作符允许产生权重分布,这样某些值的选取机会要比其他值更大一些。dist操作符带有一个值的列表以及相应的权重,中间用:= 或:/分开。值或权重可以是常数或变量。值可以是一个值或值的范围,例如[lo:hi]。权重更不用百分比表示,权重的和也不必是100。:=操作符表示范围内的每一个值的权重是相同的,:/操作符表示权重要均分到值范围内的每一个值。
权重分布不能针对randc变量
例6.7 使用dist的权重随机分布

rand int src,dst;
constraint c_dist {
	src dist {0:=40,[1:3]:=60};
	dst dist {0:/40, [1:3]:/60};
}

src取0的概率为40/220,取1、2、3的概率都为60/220;
dst取0的概率为40/100,取1、2、3的概率都为20/100;
通过改变权重来随时改变值的概率分布,甚至可以把权重设为0,来删除一个值。

6.4.5 集合(set)成员和inside运算符
采用inside {} 运算符产生一个值的集合。除非对变量还存在其它约束,否则System Verilog在值的集合里取随机值时,各个值的选取机会是相等的。在集合里也可以使用变量。
例6.9 随机值的集合

rand int c; //随机变量c
int lo, hi; //作为上限和下限的非随机变量
constraint c_range {
	c inside {[lo:hi]}; // lo < = c 并且 c < = hi
}

例6.9中c的选值范围由lo和hi决定,采用这种方法,可以使约束参数化,这样就可以不用修改约束,测试平台就可以改变激励发生器的行为。若lo>hi,会导致约束错误。
可以使用$来代表取值范围里的最大值和最小值。

例6.10 使用“$”指定最大值和最小值

rand bit [6:0] b; //0 < = b < = 127
rand bit [5:0] e; // 0 < = e < = 63
constraint c_range {
	b inside {[$ : 4],[20 : $]}; //0 < = b < = 4 || 20 < = b < = 127
	e inside {[$ : 4],[20 : $]}; //0 < = e < = 4 || 20 < = e < = 63
}

若想选择一个集合之外的值,只需要用取反操作符!对约束取反
例6.11 随机集合约束的取反

constraint c_range {
 !(c inside {[lo:hi]}) ;//c < lo 或 c > hi
}

6.4.6 在集合里使用数组
把集合里的值保存到数组里后就可以使用这些值。
例6.12 使用数组的随机集合约束

rand int f;
int fib[5]='{1、2、3、5、8};
constraint c_fibonacci{
	f inside fib;
}

例6.13 等价的约束

constraint c_fibonacci{
	(f = = fib[0]) || f = =1
	(f = = fib[1]) || f= =2
	(f = = fib[2]) || f= =3
	(f = = fib[3]) || f= =5
	(f = = fib[4]) || f= =8
}

集合里每一个值取出的概率都是相同的,即使值在数值中出现多次。

6.4.7 条件约束(让一个约束表达式只在某些时候有效)
通常约束块里所有的约束表达式都是有效的,通过两种关系操作:- >和if-else使表达式在某些时候才有效。
操作符- >可以产生和case操作符类似的语句块,用于枚举类型的表达式。
例6.19 带有- >操作符的约束块

class BusOp;
	...
	constraint c_io {
		(io_space_mode) - > 
		addr [31]= = 1'b1; 
}

例:

mode==small -> len <10; //mode为small时 len约束为小于10;
mode==large -> len >100; //mode为large时 len约束为大于100;

if-else操作符更适合“真-假”类型的表达式
例6.20 带有if-else操作符的约束块

 class BusOp;
    	...
    	constraint c_len-rw {
    		if (op== READ)
  	  		len inside {[BYTE:LWRD]};
    		else
    		len==LWRD;
   }

6.4.8 双向约束
约束块是并行的声明性代码,所有的约束条件同时有效。
6.4.9 使用合适的数学运算来提高效率
System Verilog中任何没有显示声明位宽的常数都是作为32位数值对待的,例如:42。

6.5 解的概率
System Verilog并不保证随机约束求解器能给出准确的解,但你可以干预解的概率分布。

6.5.1 没有约束的类
6.5.2 关系操作
约束块中的关系操作决定了取值。
6.5.3 关系操作和双向约束
6.5.4 使用 solve…before约束引导概率分布
solve…before约束不会改变解的个数,只会改变各个值的概率。
同时solve x before y与solve y before x不同,差别较大。
除非对某些值的概率不满意,否则不要使用solve…before。 过度使用solve…before会降低计算的速度,并且使约束难以理解。
仅仅能使用rand,不能使用randc随机变量;
变量必须是整数类型;

6.6 控制多个约束块
一个类可以包含多个约束块。
运行期间,使用内建的constraint_mode()函数打开或关闭约束。 constraint_mode(1)为打开约束,constraint_mode(0)为关闭约束。
用handle.constraint.constraint_mode()控制一个约束块,handle.constraint_mode()控制对象的所有约束块。
例6.28 使用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();
	//通过禁止c_short约束产生长包
	p.c_short.constraint_mode(0);
	assert (p.randomize());
	transmit (p);
	//通过禁止所有的约束,然后使能短包约束来产生短包
	//then enabling only the short constraint
	p.constraint_mode(0);
	p.c_short.constraint_mode(1);
	assert (p.randomize());
	transmit (p);
end

6.7 有效性约束
设置多个约束以保证随机激励的正确性是一种很好的随机化技术,也称为“有效性约束”。
6.8 内嵌约束
许多代码只会在一个地方随机化对象。SystemVerilog允许使用randomize()with {}来增加额外的约束,等同于在类里增加约束。
约束块应该使用{},内嵌约束也应该使用{},{}用于声明性质的代码。
6.9 pre_randomize和post_randomize函数
SystemVerilog可使用两个特殊的void类型的pre_randomize和post_randomize函数来完成调用randomize()前后的一些操作。void类型函数没有返回值,由于函数不是任务,所以不消耗时间。
6.10 随机化函数
(1)$ random()——平均分布,返回32位有符号随机数;
(2)$ urandom()——平均分布,返回32位无符号随机数;
(3)$ urandom_range()——在指定范围内的平均分布;
(4)$ dist_exponential()——指数衰落;
(5)$ dist_normal ()——钟型分布;
(6)$ dist_poisson()——钟型分布;
(7)$ dist_uniform()——平均分布;
$ urandom_range()函数有两个参数,一个上限参数和一个可选的下限参数。
例 设置随机种子
class Packet;
rand bit[15:0] header;

function new (int seed);
this .srandom(seed);
endfunction

endclass
//创建一个类,初始化种子
Packet p=new(200);//Creat p with seed 200
p.srandom(300);//Re-seed p with seed 300

发布了38 篇原创文章 · 获赞 29 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_45270982/article/details/95778042