SystemVerilog学习笔记——随机约束和分布

文章目录

  • 1、随机约束和分布
    • 1.1 为何需要随机?
    • 1.2 为何需要约束?
    • 1.3需要随机什么?
    • 1.4声明随机变量的类
  • 2、什么是约束?
    • 2.1权重分布
    • 2.2约束块控制
  • 3.随机函数
    • 3.1pre_randomize()和post_randomize()函数
    • 3.2随机化个别变量


1、随机约束和分布

1.1 为何需要随机?

  • 芯片体积增大,复杂度越来越高,定向测试已无法满足验证的需求,而随机测试的比例逐渐提高;
  • 定向测试能找到认为可能存在的缺陷,随机测试可以找到意想不到的缺陷;
  • 随机测试的环境要求比定向测试复杂,需要激励、参考模型、在线比较;
  • 随机测试相对定向测试可以减少很多代码量,产生的激励较定向测试也更多样。(减少测试用例的代码量,更多的复用,但验证环境的结果增多了)

1.2 为何需要约束?

  • 如果没有约束,产生有效激励的同时也产生很多无效和非法的激励;
  • 随机自由是一种合法的随机,需要限定激励的合法范围;
  • 随机的对象不只是一个数据,而是有联系的变量集。通常这些变量被封装在一个数据类中,同时需要在类中声明数据之间的约束关系。因此约束之后要产生随机数据需一个“求解器”,即在满足数据本身和数据之间约束关系时的随机数值解;
  • 约束不但可以指定数据的取值范围,还可以指定各个数值的随机权重分布。

1.3需要随机什么?

器件配置:通过寄存器和系统信号;

  • 环境配置:随机化验证环境,例如合理的时钟和外部反馈信号;
  • 原始输入数据:例如MCDF数据包的长度、带宽,数据间的顺序;
  • 延时:握手信号之间的时序关系,如valid和ready,req和ack之间的时序关系;
  • 协议异常:如果反馈信号给出异常,设计是否可以保持后续数据处理的稳定性。

在随机测试中,可以随机化下列哪些要素? 答案:AC
A、设计的功能配置;B、设计的结构;C、输入数据;D、输出数据

设计的结构是静态的,在编译时确定;验证的结构是动态的,发生在仿真阶段。

1.4声明随机变量的类

随机化是为了产生更多可能的驱动,将相关数据有机整理在一个类中,使用rand关键词表明变量的随机属性。

  • randc表示周期随机性,即所有可能的值都赋过值后随机值才可以重复;
  • 随机属性需要配合SV预定义的类随机函数std::randomize()使用;
  • 约束constraint也同随机变量一起在类中声明。
 class Packet;
	//The random variables
	rand bit [31:0] src, dst, data[8];
	randc bit [7:0] kind;
	// Limit the value for src
	constraint c {
                   src > 10;
                   src < 15;
                }
endclass
Packet P;
initial begin
	P = new(); //Create a packet
	assert (Packet.randomize())
		else $fatal(0,"Packet::randomize failed");
	transmit(P);
end  //需要随机的变量有4个,只要其中一个约束有问题,那么所有变量都没有进行随机

2、什么是约束?

约束表达式的求解由SV的约束求解器完成。

  • 求解器能够选择满足约束的值,这个值是由SV的PRNG(伪随机数发生器)从一个初始值seed产生。只要改变种子的值,就可以改变CRT的行为。
  • SV标准定义了表达式的含义及产生的合法值,但没有规定求解器计算约束的准确顺序,即不同仿真器对同一个约束类和种子值求解的数值可能不相同。
  • SV只能随机化二值数据类型,无法随机化出X值和Z值,也无法随机化字符串。
class data;
	rand bit [2:0] month;
	rand bit [4:0] day;
	rand int year;
	constraint c_data {
		month inside {[1:12]};
		day inside {[1:31]};
		year inside {[2010:2030]};
		}
endclass  //注意位数的限制,比如month只有三位只能随机0-7
class Stim;
	const bit [31:0] CONGEST_ADDR = 42; //定义常量
	typedef enum {READ,WRITE,CONTROL} stim_e; //自定义枚举类型,stim_e类型名
	randc stim_e kind;    //Enumerated var
	rand bit [31:0] len,src,dst;  //随机变量
	bit congestion_test; //非随机变量
	constraint c_stim{
		len < 1000;
		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

2.1权重分布

  • 关键词dist可以在约束中用来产生随机数值的权重分布;
  • dist操作符带有一个值的列表以及相应的权重,中间

用:=或:/分开。值或权重可以是常数或变量;
权重不用百分比表示,权重的和也不必是100;

  • := 操作符表示值范围内的每一个值得权重是相同的,:/操作符表示权重平均分到值范围内的每一个值。
rand int scr,dst;
constraint c_dist{
	src dist {0:=40,[1:3]:=60};// :=的意思是产生40个0,1-3每个数产生60个,共产生220个数
        //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);// :/的意思是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
}
  • inside是常见的约束运算符,表示变量应属于某些值的集合,除非还存在其他约束,否则随机变量在集合里面取值的概率是相等的,集合里面也可使用变量。例如c
    inside {[low:high]}; //low<=c && c<=high 使用KaTeX parse error: Expected '}', got 'EOF' at end of input: …值,例如c inside {[:4],[20:$]}; //0<=c<=4 || 20<=c<=63(因为rand bit [5:0] c ; // 0<=c<=63)
  • 条件约束:可以通过 “->”或if-else让一个约束表达式在特定时刻有效。
    SystemVerilog学习笔记——随机约束和分布_第1张图片

2.2约束块控制

打开或关闭约束
一个类可包含多个约束块,可以把不同约束块用于不同测试;

  • 各个约束块之间的约束内容是互相协调不违背的;
  • 使用内建的constraint_mode()函数可以根据不同需要打开或关闭约束;
  • p.constraint_mode(0)是关闭约束,p.constraint_mode(1)是打开约束。
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(); //创建一个long packet通过禁止短约束块
	p.c_short.constraint_mode(0);//关闭short packet,只有long_packet约束有效
	assert (p.randomize());
	transmit(p);
	//创建一个short packet通过禁止所有约束然后只使能短约束块
	p.constraint_mode(0);//long和short的约束都失效
	p.c_short.constraint_mode(1);//打开short的约束
	transmit(p);
end
//如果随机化该对象时不禁止其中任何一个约束块,那么调用随机函数randomize()后,
//p.length的值为0也会报错,因为约束冲突导致randomize失败有warning返回默认值0。

内嵌约束
约束块之间会相互作用,最终产生难以预测的结果,使能和禁止这些约束的代码也会增加测试的复杂性;

  • 约束需要考虑类的开放封闭原则;
  • SV允许使用randomize()with增加额外的约束,和在类里增加约束是等效的,但同时要注意内部约束和外部约束之间应协调,如果出现互相违背的情况,那么随机数值求解会失败;
  • 关键字soft是修饰软约束,当与其它外部约束冲突时软约束优先级会更低。
class Transaction;
	rand bit [31:0] addr,data;
	constraint c1 {soft addr inside {[0:100],[1000:2000]};}
endclass
Transaction t;
initial begin
	t= new();
	assert(t.randomize() with {addr >= 50; addr <= 1500; data < 10;});
	driveBus(t);
  //force addr to a specific value,data>10
    assert(t.randomize() with {addr == 2000; data > 10;});
    driverBus(t);
end

如果加上t.randomize() with {addr inside [200:300]; data inside [10:20]; },若没有关键词soft,addr的约束和内嵌矛盾,addr约束不满足,addr和data都会报错。但因为有soft,减低了约束的优先级,使得内嵌约束起作用。

3.随机函数

3.1pre_randomize()和post_randomize()函数

两个预定义的void类型函数pre_randomize()和post_randomize()函数,可看作randomize()函数的回调函数。用户可在类中定义这两个函数,分别在其中定义随机化前的行为和随机化后的行为。
随机数函数:SV提供一些常用的系统随机函数。这些随机函数可直接调用来返回随机数值(不通过class,直接调用)。

  • $random()平均分布,返回32位有符号随机数;
  • $urandom()平均分布,返回32位无符号随机数;
  • $urandom_range(A,B)在指定范围内的平均分布。

3.2随机化个别变量

  • 在调用randomize()时可以在括号里定义个别变量;
  • 只有在括号里的变量才会被随机化,其他变量被当做状态变量而不会被随机化; 所有约束仍然保持有效;
    -== 类里所有被指定或没有指定rand的变量都可作为randomize()的参数而被随机化==;
  • randomize的参数若不是之前定义的rand变量,则类的rand变量不会被随机化。
class Rising;
	byte low, //非随机变量,初始值为0
	rand byte med,hi; //随机变量,初始值为0
	constraint up {low < med; med < hi;}
endclass
initial begin
	Rising r;
	r = new();
	r.randomize();//随机化med,hi,但不改变low,此时low=0,假设med=5,hi=9	 
    //先整体的randomize产生合理的low,med和hi值
	r.randomize(med); //只随机化med;此时low=0,hi=9,med可以是1-8,假设med=6
	r.randomize(low);//只随机化low;此时med=6,hi=9,low可以是0-5,假设low=3
end

按照上面的代码仿真结果log如下图所示:SystemVerilog学习笔记——随机约束和分布_第2张图片
在例化r之后如果先调用r.randomize(low),那么可能产生low=报错,med=0,hi=0;实际例子如下图;
因为med和hi不参与randomize,保持为0
SystemVerilog学习笔记——随机约束和分布_第3张图片

你可能感兴趣的:(11,硬件工程)