(十)随机化约束的种类及其控制

一、随机化与约束

1、为什么引入随机化?

  • 芯片体积增大,复杂度日渐提高,定向测试已经无法满足验证的需求,而随机测试的比例逐渐提高。
  • 定向测试能找到你认为可能存在的缺陷,而随机测试可以找到连你都没有想到的缺陷。
  • 随机测试的环境要求比定向测试复杂,它需要激励、参考模型和在线比较。上百次的仿真不再需要人为参与,以此来提高验证效率。
  • 随机测试相对于定向测试可以减少相当多的代码量,而产生的激励较定向测试也更多样

2、什么需要随机化测试?

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

3、什么是约束?

SV的约束求解器(CRT)可以对约束表达式进行求解,求解器能够通过SV的PRNG(伪随机数发生器)由一个初始值(seed)产生满足约束的值。

  • 只要改变seed的值就可以改变CRT的行为。
  • 不同仿真器对于同一个约束类和种子值求解出的数值可能是不同的。
  • SV只能随机化出2值数据类型,也就是说无法随机化出X值和z值,也无法随机化字符串。

二、随机化方法

class Packet;
// The random variables
rand bit [31:0] src, dst, date[8];
randc bit [7:0] kind1;

function void pre_randomize();
    $display ( "before pre_randomize src = %0d, dst = %0d" , src,dst) ;
    $display ( "after pre_randomize src = %0d , dst = %0d" , src, dst);
endfunction

// Limit the values for src
constraint c
{
    src > 10;
    src < 15;
}

function void post_randomize() ;
    $display ( "before post_randomize src = %0d,dst = %0d " , src,dst ) ;
    $display ( "after post_randomize src = %0d,dst = %0d" ,src,dst) ;
endfunction

endclass

program test18;
    Packet p;
initial begin
    p = new();    
    assert(p.randomize)
    else $fatal(0, "Packet::randomize failed");
    $display("src= %0d",p.src);
    $display("dst= %0d ",p.dst);
    //$display("data=%0P",p.date);
	$display(p.date);
    $display("kind=%0d",p.kind1);
end
endprogram

打印结果:
before pre_randomize src = 0, dst = 0
after pre_randomize src = 0 , dst = 0
before post_randomize src = 14,dst = 3053944240 
after post_randomize src = 14,dst = 3053944240
src= 14
dst= 3053944240 
'{'h4279b9b, 'h90134d03, 'hd093f049, 'h2589af1f, 'h5057fb67, 'h1bf88803, 'hbbf86411, 'h663d54f4} 
kind=24

说明:

  • rand:在对应范围内随机数据,在随机周期内出现的值可能重复
  • randc:在对应范围内随机数据,在随机周期内出现的值不会重复
  • randomize:该函数为类里面所有的rand和randc类型的随机变量赋一个随机值,并保证不违背所有有效约束;在调用randomize()时可以传递随机变量的子集,这样只会随机化类里的某几个变量,而其他随机变量保持默认,不会被随机,如randomize(src,dst)随机,data[8],kind不会被随机;
  • pre/post_randmize()回调函数:通过pre/post_randmize()函数在调用randomize()之前或者之后执行一些操作,这两个函数randomize()函数是自动去调用,如果没有定义这两个函数,那么就认为这两个函数是什么也不做的。pre/post_randmize()其实并没有做任何随机的操作,只是在随机的前后做一些function的操作,对随机的结果进行调整,改变随机的值等

随机化函数:

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

三、约束种类

1、布尔表达式 Boolean expressions

class order;
rand bit [7:0] lo, med, hi;
constraint right
{
    lo < med; // right
    med < hi; // right
    //lo < med < hi;//error不能这样表达
}
endclass

2、 权重分配dist

关键词dist用来在约束中产生随机数值的权重分布。

dist操作符::= :/,值或权重可以是常数或者变量。

:= :每一个值的权重是相同的,全部相加总和的占比

:/ :权重 要平均分到值范围内的每一个值,按百分比来

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
}

3、带变量的权重分配dist

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,  //READ8概率1/3
      READ16:=read16_wt,//READ16概率1/3
      READ32:=read32_wt //READ32概率1/3
    };
  }
//通过这种变量赋值权重值的方式,可从类的外部通过变量改变权重值
endclass

4、范围表达式inside

//规定随机值的上下限
rand int c; // Random variable
int lo, hi; // Non-random variables
constraint c_range
{
    c inside {[lo:hi]}; // lo <= c && c <= hi
}

//随机值的上下限取反
rand int c; // Random variable
int lo, hi; // Non-random variables
constraint c_range
{
    !(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:$]}; // 0 <= b <= 4 || 20 <= b <= 127
    e inside {[$:4], [20:$]}; // 0 <= e <= 4 || 20 <= e <= ?
}

//使用数组
rand bit [6:0] b; // 0 <= b <= 127
int fib [5] = ’{1, 5, 3, 9, 2};
constraint c_range
{
    b inside fib;
}

说明:

  • inside是常见的约束运算符,表示变量应该属于某一个值的集合。

5、条件表达式

//方式一
class Stim;
bit flag;
rand bit [31:0] dst;
constraint c_stim
{
    if (flag)
    {// not begin
        dst inside {[40:80]};
    }// not end
    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

6、外部约束

class Packet;
    rand bit [7:0] length;
    rand bit [7:0] payload[];
    constraint c_valid {length > 0;payload.size() == length;}
    constraint c_external;
endclass

constraint Packet::c_external {length == 1;} //在类外定义

7 、 双 向约束

约束块是声明性的代码,是并行执行的。所有的约束表达式同时有效。SV会同时计算所有的随机变量约束,取它们的交集

rand logic [15:0] r,s,t;
constraint c_bitir {
    r < t;
    s ==r;
    t <30;
    s >25;
}

四、约束的控制

1、控制约束开关函数constraint_mode()

0:表示关闭对应约束块,1:表示使能对应约束块

class Packet;
	rand int length;
	constraint c_short {length inside {[1:32]};}
	constraint c_long {length inside {[1000:1023]};}
endclass

program test;
Packet p;
initial begin
	p = new();
	p.c_short.constraint_mode(0); //disabling short constraint
	assert(p.randomize());
	p.constraint_mode(0); 		  //disabling all constraints
	p.c_short.constraint_mode(1); //enabling short constraints
	assert(p.randomize());
	transmit(p);
end
endprogram

2、控制随机变量开关函数rand_mode()

0:表示不随机对应约束里的某个变量,1:表示使能对应约束里的随机变量去随机

class Packet;
	rand bit [7:0] length, payload[];
	constraint c_valid {length > 0;payload.size() == length;}
endclass

program test;
Packet p;
initial begin
	p = new();
	p.length.rand_mode(0);  // Make length nonrandom
	p.length = 42; 			// set it to a constant value
	assert (p.randomize()); // then randomize the payload
	p.length.rand_mode(1);  // Make length random
end
endprogram

3、控制随机变量 randomize() with {}

表示在调用时想在原来约束的基础上进一步加约束

class Transaction;
	rand bit [31:0] addr, data;
	constraint c1 {addr inside{[0:100],[1000:2000]};}
endclass

program test;
Transaction t;
initial begin
	t = new();
	// addr is 50-100, 1000-1500, data < 10
	assert(t.randomize() with {addr >= 50; addr <= 1500; data < 10;});
	$display("t::addr=%d,data=%d",t.addr,t.data);
	// force addr to a specific value, data > 10
	assert(t.randomize() with {addr == 2000; data > 10;});
	$display("t::addr=%d,data=%d",t.addr,t.data);
end
endprogram

4、randomize() 控制随机变量

只会随机化类里的某几个随机变量,而其他随机变量保持默认,不会被随机

class Rising;
  byte low;
  rand byte med, hi;
  constraint c_up 
  {
    low < med; 
    med < hi;
  }
endclass
 
program test;
  Rising r;
  initial begin
    r = new();
    r.randomize();    //对所有随机变量在约束上进行随机
    r.randomize(med); //只随机med,hi保持上一个值
    r.randomize(low); //只随机low,但low不是随机变量,med和hi保持上一个值
  end
endprogram

5、pre_randomize 与post_randomize 函数

他们都是function, 不消耗仿真时间;他们通常用来设置一些非随机的变量或者计算随机数据中的错误如randomize调用失败,那么post_randomize将不会被执行,但是pre_randomize不randomize是否成功都会执行,它们是由randomize()函数自动去调用的。

  • pre_randomize: 在执行randomize() 之前调用,主要用来设定与randomize相关的各个变量的值;
  • post_randomize: 在randomize()之后调用.主要用来调用相关的函数对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);
	$display("disabled constraint generic_c !");
endfunction

function void post_randomize();
	$display("call post_randomize !");
	if(wr_addr ==1) broadcast =1;
	else broadcast =0;
endfunction

endclass

module test18;
	wr_tran m_tr;
initial begin
	m_tr =new();
	$display("wr_addr=%0d,wr_data=%0d,valid=%0d,broadcast=%0d,constraint_en=%0d",m_tr.wr_addr, m_tr.wr_addr,m_tr.valid,m_tr.broadcast,m_tr.constraint_en);
	m_tr.constraint_en=0;
	m_tr.randomize() with {wr_addr==200;wr_data==3;valid==0;};
	$display("wr_addr=%0d,wr_data=%0d,valid=%0d,broadcast=%0d,constraint_en=%0d",m_tr.wr_addr, m_tr.wr_addr,m_tr.valid,m_tr.broadcast,m_tr.constraint_en);
end
endmodule

打印结果:

wr_addr=0,wr_data=0,valid=0,broadcast=0,constraint_en=0
call the pre_randomize !
disabled constraint generic_c !
call post_randomize !
wr_addr=200,wr_data=200,valid=0,broadcast=0,constraint_en=0

6、约束的重载

1、采用constraint_mode(0)关闭约束,然后用randomize() with {} 重新约束;

class constrainend;
    rand bit x;
    rand bit [1:0] y;
    constraint c_xy {(x==0)-> y==0};
endclass

program test;
    constrainend p;
initial begin
    p=new();
    p.c_xy.constraint_mode(0); //disable c_xy constraint
    assert(p.randomize() with {x==0; y==0;});
    $display("x = %d",p.x);
    $display("y = %d",p.y);
end
endprogram

2、子类extends父类,然后定义同名约束覆盖父类约束

class constrainend;
    rand bit x;
    rand bit [1:0] y;
    constraint c_xy {(x==0)-> y==0;}
endclass

class overwrite extends constrainend;
    constraint c_xy {x==0; y==0;}
endclass

program test;
    overwrite p;
initial begin
    p=new();
    assert(p.randomize());
    $display("x = %d",p.x);
    $display("y = %d",p.y);
end
endprogram

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值