随机约束
- 随机变量与方法
rand,randc:对于randc来说,变量中有三个元素,那么经过三次随机化后变量会被遍历
随机方法(参见随机函数):virtual function int randomize();randomize()方法是一个虚函数(注意由于class不再具有像module的端口,因此声明接口的指针是当作变量声明,在接口指针声明时必须加virtual否则会报错;注意只有定义了rand或者randc的变量才会随机化,randomize()随机化只会随机化()中的变量,如果()中有变量,但是该变量不是rand或者randc,那么实际上该变量是不参与随机化的,也就是保持默认值。并且需要注意的是,无论是否真正的参与随机化过程,均要满足约束条件)
- 约束
dist操作(权重分布)::=每一个值的权重是相同的和:/权重会平均分配到取值范围内的每一个值 eg:constraint a {A dist {[0:1]:=10 [2:3]:/10}} 其中a是自己定义的名称,A是变量名,前者是每个都有十份的可能性,后者是十份的可能性被2和3分(可以在使用dist操作之前,先定义一个枚举类型,再用定义的枚举类型中的变量定义权重分布中的参数,在此后可以动态修改枚举类型中变量的值实现对权重的动态修改,例6.8)
inside运算符:A inside {[a:b]},其中A是变量,a和b是数值范围,另外可以用$来表示取值范围中的最大值与最小值p141,在整个A inside {[a:b]}加上()和!表示对范围的取反
条件约束:(1)->,constrain a {(x)->A},其中条件语句x可以不用加();如果aa和bb均是bit类型,那么约束{(aa==1)->(bb==0);},那么aa和bb会出现三种情况:(1)1,0(2)0,0(3)0,1三种,如果再加上一个(bb==1)的限定,那么就仅为第三种情况。注意->的作用是同步的,另外需要注意的是由于约束仅仅指出二值逻辑所以===和!==是非法操作(2)if-else:constrain a {if(x) A;else B},这里的条件语句最好加上()
foreach和solve...before:(1)foreach:constrain c2 {foreach (a[i]) (i<a.size-1) -> a[i+1]>a[i];}c2数组的每一个值都大于前一个的值,降序排列;对于存在这种排序的时候一定要注意限定i的取值,采用i<a.size-1或者i<=a.size-2。注意;符号在{}内,外部不需要加;(2)solve...before:constrain c {s -> d==0}在这个限定中s和d是同时确定的,假如s和d均是bit,s是默认位宽,d是32位位宽,那么一共有2的33次幂种可能,仅仅在{1,0}这种情况下才成立。如果在这个基础上增加constraint order {solve s before d};那么这种限定的顺序就会变为s和d先后限定,也就是先限定s再限定d。那么s有50%可能位真,接下来d根据s的值来选择,d为0和不为0的概率各占50%
双向约束(所有的表达式操作符都被双向对待):(1)->,限定是同时确定的(2) inside:inside约束变量范围再[10:20],再用另一个表达式子约束变量大于15,那么实际的范围是15-20
约束中的其他问题:一个表达式子中最多只能使用一个关系操作符,不能连续使用。例6.4
- 继承中的约束
子类的约束与父类的约束有同名,那么子类的约束会改写父类的约束
- 约束块的控制
打开和关闭(随机使能控制):
(1) constraint_mode():p.constrain_mode(0);p.a.constraint_mode(1);第一句话关闭p中所有的约束,第二句话打开p中约束a
(2) rand_mode():rand_mode()是针对变量进行的,p.rand_mode(0);p.a.rand_mode(1);b=p.a.rand_mode()将b变量设置为a的激活状态
(3)内嵌约束t.randomize() with{};在使用内嵌约束时,约束体中的变量名的查找顺序默认时从被随机化的对象开始查找的,但是如果调用randomzie()的函数句柄域中也存在相同的变量名,那么就需要使用local::来显示的声明该变量源自于外部函数,而非随机化的变量。
(4)外部约束:在一个文件(该文件名是packet.sv)中定义一个class Packet,这个class中包含constraint c_external;但是在该类中并没有定义约束c_external中的具体约束,可以在另一个文件(与packet.sv不是同一个文件)中定义约束的具体内容,采用语句include"packet.sv"constraint Packet::c_external{具体约束;}
(5)软约束和硬约束:对约束前限定soft使得其变为软约束,软约束的优先级较低
- 随机函数
randomize,pre_randomize,post_randomize(随机方法):randomize只能随机化二值逻辑,一般建议将变量定义为bit。pre_randomize()和post_randomize()是randomize的回调函数,在类中定义这两类函数后会分别在randomzie()前后调用这两类函数;randomize()失败后约束是不可行的,变量保持默认值并且post_randomize不会执行;可以在任何类中重写pre_randomzie()和post_randomize()以实现在randomzie前后的操作,如果这些方法被重写,那么他们必须调用他们的父类方法,否则randomzie前后的处理步骤会被忽略
系统随机函数:(1)$random(平均分布,返回32位有符号的数字),$urandom(与前者类似,但是无符号),$urandom_range( , )在指定范围内平均分布,只有一个值的化就是从0到该值。(2)关于dist中的一些系统函数p153
- 数组约束
基本的点:(1)数组属性的约束:constraint d_size{d.size() inside {[1:10]};};constraint d_size{d.sum<1024;} d中所有元素的和小于1024;除此以外还有product(),and(),or(),xor()(2)数组中的元素:采用foreach对数组(特别是动态数组)中的每一个元素进行约束,此外还可以通过foreach产生元素单调变化的数组:foreach(d[i]) if (i>0) d[i]>d[i-1] 注意这个操作没有限定i值与尺寸之间的关系,是因为只出现了i和i-1
产生唯一元素的数组:(1)采用条件约束和两个foreach语句(foreach ua[i]和foreach ua[j])。条件约束的内容是如果i!=j,那么ua[i]!=ua[j](2)借助pre_randomize()函数,也就是先定义一个类A,其中的变量类型是randc,在定义一个类B(B中定义一个动态数组),在类B中用类A例化一个新的对象a,对a进行随机化,由于A中变量类型是randc,所有其随机化的值是不会重复的,然后采用foreach对B中的数组进行约束,使得动态数组的每一个值都等于例化a中的那个randc变量值。最终实现产生唯一元素的数组。(3)采用UniqueArry类,直接定义UniqueArry ua(随后定义数组大小,ua=new(50)),那么此时该数组中的每个元素都是唯一的
module only_element1;//较为复杂的一种方式,第一次随机随机数组尺寸,通过post随机具体数值
class RandcRange;
randc bit [15:0] value;
int max_value;
function new(int max_value = 10);
this.max_value = max_value;
endfunction
constraint c_max_value {value < max_value;}
endclass
class Uniquearry;
int max_arry_size,max_value;
rand bit [7:0] a[];
constraint c_size {a.size()inside {[1:max_arry_size]};}
function new(int max_arry_size = 2,max_value);
this.max_arry_size = max_arry_size;
if (max_value < max_arry_size)
this.max_value = max_arry_size;
else
this.max_value = max_value;
endfunction
function void post_randomzie();
RandcRange rr;
rr = new(a.size());
foreach (a[i]) begin
assert(rr.randomize());
a[i] = rr.value;
end
endfunction
function void display();
$write("size is %3d",a.size());
foreach(a[i]) $write("%4d",a[i]);
$display;
endfunction
endclass
program automatic test;
Uniquearry ua;
initial begin
ua = new(50,2);
repeat(10) begin
assert(ua.randomize());
ua.display();
end
end
endprogram
endmodule
module only_element2;//在随机化前进行预随机实现唯一性
class randc8;
randc bit [7:0] val;
endclass
class LittleUniquearry;
bit [7:0] ua [64];
function void pre_randomzie();
randc8 rc8;
rc8 = new();
foreach (ua[i]) begin
assert(rc8.randomize());
ua[i] = rc8.val;
end
endfunction
endclass
endmodule
/* module only_element2;
class xu;
randc bit [7:0] val;
endclass
class LittleUniquearry;
rand bit [7:0] ua [8];
xu hanquan;
function void pre_randomzie();
hanquan = new();
foreach (ua[i]) begin
assert(hanquan.randomize());
ua[i] = hanquan.val;
end
endfunction
function void display();
//$write("size is %3d",ua.size());
foreach(ua[i]) $write("%4d",ua[i]);
$display;
endfunction
endclass
program automatic test;
LittleUniquearry aa;
initial begin
aa = new();
repeat(10) begin
assert(aa.randomize());
aa.display();
end
end
endprogram
endmodule */
module only_element3;//通过约束进行预随机
class uniqueslow;
rand bit [7:0] ua [64];
constraint c{
foreach (ua[i])
foreach (ua[j])
if(i != j)
ua[i] != ua[j]
}
endclass
endmodule
句柄数组的随机化:(1)通过动态句柄数组产生多个随机对象,在进行句柄数组的随机化过程中,随机函数会随机化数组中每一个句柄所指的对象(这一点很重要,有两个类,在B类中定义一个存放A类的句柄的动态数组,也就是该动态数组中存放的都是A的句柄,并加上rand修饰,那么在随机化过程中实际上是不仅要随机化该句柄数组(比如句柄数组的个数),也要随机化句柄所指代的对象,也就是A类中的成员,这时,如果A类中成员用rand进行限定了,那么可以随机化成功,如果没有用rand修饰,那么对对象的随机化会失败,其值会保持默认值)(2)在随机化前要进行function new()操作及后续操作,以保证句柄数组中的每一个句柄都是非悬空的。并且最好function new()使得动态数组中分配最大数量的元素(这一点也很重要,比如在上面的那个例子中,如果没有function new()的化,那么在随机化时句柄所指代的对象实际是空的,又因为加上了rand修饰,那么程序又会主动的去寻找对象,此时找不到,程序报错),在随后随机化的过程中元素数量可能保持不变或者减少,但不会增加;(3)总的来说随机化句柄需要注意:指向的对象是否是rand属性,句柄数组不能悬空(function new()函数保证不悬空)
在集合中使用数组:把集合里的值保存到数组里后就可以使用这些值,可以采用约束操作完成保存:constraint c={f inside fib;}其中f是集合,fib是数组,如果先前中定义了数组的值,那么就相当于集合里面有数组的这些值。另外需要注意的是,如果数组中有重复的值,采用inside以后集合中各个值的概率是相同的,而不会因为数组中有某个值重复就使该值的概率增加(例6.14)
从数组中取出随机值的类及从数组中取出随机值:取出整个类可以采用约束中的inside语句,从数组中取出随机值可以i采用::符号逐一取值(例6.16,6.17)
- 随机控制