1、受约束的随机测试法(CRT)
随着设计变得越来越大,要产生一个完整的激励集来测试设计的功能变得越来越困难。解决的办法是采用受约束的随机测试法自动产生测试集。CRT环境比定向测试的环境复杂,不仅需要产生激励,还需要通过参考模型、传输函数或其他方法预测输出结果。然而只要准备好了这个环境,就可以运行上百次的仿真而无需手工检查结果,从而提高工作效率,这也是CRT的优势:用CPU时间来换取人工检查的时间。
2、SV中的随机化
rand修饰符:表示每次堆积化这个类时,这些变量都会赋一个值。
randc修饰符:表示周期随机性,即所有可能的值都赋过值后随机化值才可能重复。
约束:一组用来确定变量的值的范围的关系表达式,表达式的值永远为真。约束的表达式放在括号“{ }”中。
randomize()函数在遇到约束方面的问题时返回0.
不能在类的构造函数里随机化对象,类里的所有变量都应该是随机的和公有的,这样测试平台才能最大程度地控制DUT。
3、约束
使用约束来定义各个变量的相互关系以产生有用的激励。
约束块包含若干个表达式,在一个表达式中最多只能使用一个关系操作符(<,<=,==,>=,>)
权重分布:使用dist操作符,如 src dist {0:=40,[1:3]:=60},这表示0的权重是40,1,2,3的权重都是60,总权重为220. dst dist {0:/40,[1:3]:/60},这表示0的权重是40,1,2,3三个值的总权重为60. 值和权重可以是常数或变量,设为变量可以动态改变权重。
集合成员和inside运算符:可以用inside运算符产生一个值的集合,各个值的选取机会相等。c inside {[2:6]} ,这表示2<=c<=6。在inside约束中即使有重复的值,各个值的概率也是均等的。
在集合里使用数组:把集合里的值保存到数组里后就可以使用这些值了。
条件约束:让一个约束表达式只在某些时候才有效。例如,一条总线支持字节、字和长字的读操作,但只支持长字的写操作。“->”操作符可以产生和case操作符效果类似的语句块,它可以用于枚举类型的表达式,“if-else”适合于“真-假”类型的表达式。“A->B”表示满足A的情况下执行B
双向约束:SV的约束是双向的,这表示它会同时计算所有的随机变量的约束。增加或删除任一个变量的约束都会直接或间接影响所有相关变量值的选取。“->”和“if-else”也是双向的,如{(a==1)->(b==0)}约束下有 (a,b)=(0,0)、(a,b)=(0,1)、(a,b)=(1,0)这三种情况,a==1时对b有约束,但b==0时对a没有约束。
4、解的概率
使用solve……before约束引导概率分布:使用该约束不会改变解的个数,只会改变各个值的概率分布。不要过度使用solve……before,会降低速度
5、控制多个约束块
一个类可以包含多个约束块,在运行期间,可以使用内建函数打开或者关闭约束。handle.constrain.constraint_mode()控制一个约束块的开启(1)或关闭(0);handle.constraint_mode()控制对象的所有约束。
6、有效性约束
设置多个约束以保证随机激励的正确性。例如,总线的“读-修改-写”命令只允许操作字数据长度。
rand enum {BYTE,WORD,LWRD,QWRD} length;
rand enum {READ,WRITE,RMW,INTR} opc;
constraint valid_RWM_LWRD{
(opc==RMW)->length==LWRD;
}
7、内嵌约束
随着测试的进行,你面对的约束越来越多。它们会相互作用,最终产生难以预测的结果;用来使能和禁止这些约束的代码也会增加测试的复杂性。很多测试只会在代码的一个地方随机化对象。SV允许使用randomize() with来增加额外的约束,这和在类里增加约束是等效的。一般是在带约束的基类上进一步增加约束。如:
constraint c1 {addr inside {[0:100],[1000:2000]};}
t.randomize() with {addr >=50; addr <= 1500;};
8、pre_randomize和post_randomize函数
可以在随机化之前设置类里的一些非随机变量,或者随机化之后计算随机数据的误差校正位。
9、随机数函数
(1)$random()——平均分布,返回32位有符号随机数
(2)$urandom()——平均分布,返回32位无符号随机数
(3)$urandom_range()——在制定范围内的平均分布,如a=$urandom_range(3,10)
10、约束的技巧和技术
(1)使用变量的约束:如改变变量范围的上下限,改变权重变量的dist分布
(2)使用非随机值:使用rand_mode函数可以把变量设置为非随机变量。如:
rand bit [7:0] length,payload[];
assert (p.randomize());
p.length.rand_mode(); //设置包长为非随机值
p.length=42; //设置包长为常数
assert (p.randomize());
(3)随机化个别变量
在调用randomize()函数时可以只传递变量的一个子集,这样就只会随机化类里的几个变量。只有参数列表里的变量才会被随机化,其他变量会被当做状态变量而不会被随机化。
(4)打开或关闭约束
可以使用条件操作符(->或if-else)来构建由非随机变量控制的约束。
也可以使用constraint_mode()打开或关闭约束。
(5)在测试过程中使用内嵌约束
当新的约束是对缺省约束的补充时,可以使用randomize() with内嵌的约束语句使约束的作用范围局部化。
(6)在测试过程中使用外部约束
函数的函数体可以在函数的外部定义,同样,约束的约束体也可以在类的外部定义。可以在一个文件里定义一个类,这个类只有一个空的约束,然后在不同的测试里定义这个约束的不同版本以产生不同的激励。
program automatioc test;
include "packet.sv" constraint Packet::c_external {length==1;}
11、数组约束
(1)约束动态数组的大小
rand logic [31:0] d[];
constraint d_size {d.size() inside {[1:10]};}
(2)约束数组元素的和
rand bit strob [10];
constraint c_set_four {strob.sum()==4'h4;}
(3)约束数组和队列的每一个元素
下面的例子随机化一个长度[1:8]的数组,数组元素和小于1024:
rand [9:0] len[]; //因为1024为10bit数,因此len的元素至少为10bit,和的位宽与元素的一样
constraint c_len { foreach (len[i])
len[i] inside {[1:255]};
len.sum < 1024; //1024为10bit数
len.size() inside {[1:8]};}
(4)使用randc可以辅助产生唯一的元素值的数组
12、随机控制
在程序性语句里使用randcase,例如:
randcase
1:len=$urandom_range(0,2); //10%:0,1,2
9:len=$urandom_range(3,5): // 90%:3,4,5
endcase
13、代码示例
(1)类定义
package class_define; //简单约束 class Packet; rand bit [31:0] src,dst,data[8]; rand bit a,b; //设置约束 constraint c { src <10; } // -> 类似case语句 constraint c2 { (a==1)->(b==0); //(a,b)=(1,0)或者(0,0)或者(0,1) b>0; //现在只有(0,1)这一种情况 } endclass:Packet //inside 和 指定权重的随机化 class Stim; typedef enum {READ,WRITE,CONTROL} stim_e; randc stim_e kind; rand bit [31:0] len,src,dst; bit c_stim_control; bit [31:0] kind_weight=90; constraint c_stim { if(c_stim_control){ src inside {[0:5]}; } else src inside {[6:10]}; //使用权重随机化 kind dist { READ := 0, WRITE := 100 - kind_weight, CONTROL := kind_weight }; } // 运行过程打开或关闭约束 constraint c1 { len == 1; } constraint c2 { len == 2; } endclass:Stim //让集合中的数只被取一次 class RandcInside; int array[]; //待选取的值 randc bit [15:0] index; //指向数组的指针 function automatic new(input int a[]); //构造,初始化 array = a; endfunction function int pick; //返回刚选取出来的值 return array[index]; endfunction constraint c_size {index<array.size;} endclass:RandcInside //随机化数组,各个元素的值为1-255,和为1024 //注意:SV默认和的位宽与加数位宽一样,为了 //避免溢出,加数位宽为10bit class array_random; rand bit [9:0] len[]; constraint c_len { foreach (len[i]) len[i] inside {[1:255]}; len.sum < 1024; len.size() inside {[4:8]}; } function void display(); $write("sum=%4d,val=",len.sum); foreach(len[i]) $write("%4d",len[i]); $display; endfunction:display endclass:array_random endpackage:class_define
(2)测试program
import class_define::*; program test6; initial begin Packet p; RandcInside ri; Stim st; array_random ar; p = new(); ri = new('{1,3,5,7,9,11,13}); st = new(); ar = new(); // ========== 简单测试 ============= // ========== 条件约束 ============= // ========== 内嵌约束 ============= $display("====== class Packet ========"); assert (p.randomize()) else $fatal(0,"Packet::randomize failed"); $display("p.src=%0d,p.a=%0d,p.b=%0d",p.src,p.a,p.b); assert (p.randomize() with {src>8;}); // 添加一个内嵌测试,8<src<10 $display("p.src=%0d,p.a=%0d,p.b=%0d",p.src,p.a,p.b); // ========== 带条件的约束、带权重的约束、 // ========== 运行过程打开或关闭约束 ============= $display("====== class Stim ======"); st.constraint_mode(0); //关闭所有约束 st.c1.constraint_mode(1); //开启c1约束 st.c_stim.constraint_mode(1); //开启c_stim约束 st.c_stim_control = 1; //改变c_stim的控制模式 st.kind_weight = 90; // 改变kind变量的分配权重 assert(st.randomize()); $display("st.len=%0d,st.src=%0d,st.kind=%s",st.len,st.src,st.kind.name); st.constraint_mode(0); //关闭所有约束 st.c2.constraint_mode(1); //开启c2约束 st.c_stim.constraint_mode(1); //开启c_stim约束 st.c_stim_control = 0; //改变c_stim的控制模式 st.kind_weight = 90; // 改变kind变量的分配权重 assert(st.randomize()); $display("st.len=%0d,st.src=%0d,st.kind=%s",st.len,st.src,st.kind.name); //让集合中的数只被取一次 $display("====== class RandcInside ======"); repeat (ri.array.size) begin assert(ri.randomize()); $display("Picked %2d [%0d]",ri.pick(),ri.index); end //随机化数组 $display("====== class array_random ======"); assert(ar.randomize()); ar.display(); end endprogram:test6
(3)输出
# ====== class Packet ======== # p.src=5,p.a=0,p.b=1 # p.src=9,p.a=0,p.b=1 # ====== class Stim ====== # st.len=1,st.src=5,st.kind=CONTROL # st.len=2,st.src=10,st.kind=WRITE # ====== class RandcInside ====== # Picked 11 [5] # Picked 3 [1] # Picked 5 [2] # Picked 9 [4] # Picked 1 [0] # Picked 7 [3] # Picked 13 [6] # ====== class array_random ====== # sum= 298,val= 40 110 50 93 5