随机约束、随机分布、随机数组等-systemverilog

一、简介

why

  • 芯片体积增大,复杂度逐渐提高,定向测试已经无法满足验证的需求,随机化验证的比例逐渐提高;
  • 定向测试能够找到你认为可能存在的缺陷,而随机测试可以找到你都没法想到的缺陷;
  • 随机测试的环境要求比定向测试复杂,它需要激励、参考模型和在线比较。
  • 随机测试相对定向测试可以减少相当多的代码量,而产生的激励较定向测试也更多项;
  • CRT(constraint random test)能够提高效率,提供测试激励和测试场景;

what

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

二、随机化种类

class packet;
//定义随机变量
rand bit[31:0] src,dst,data[8];
randc bit[7:0] kind;
//约束变量
constraint c//约束需要有实例名
{
  src > 10;
  src < 15;
}//注意没有;号
endclass

program test;
packet p;
initial begin
  p = new();
  assert(p.randomize)
  else $fatal(0,"Packet :: randomize failed");
  transmit(p);
end
endprogram
  • rand是可以重复的随机定义,randc可以类比是在一副牌里抽一张出来,但是抽出来之后就不放回去了,等待所有牌都被抽完才会重新抽,并且在随机约束解析时,会先解析randc的变量,不同的解析顺序可能导致不一样的随机分布结果;
  • randc循环性质随机,但是他的资源占用比多大,因为它需要保存前面的随机值;
  • randomize把类里面的所有属性进行随机;
  • 一般随机化使用方法:
  • 定义类,在类中声明关心的属性为rand/randc;
  • new对象
  • handle调用随机函数randomize;
  • 需要注意断言中的使用:assert(p.randomize) $info(...);else $fatal(...);注意assert后面如有有info可以加分号,如果没有消息需要打印则不需要加
assert(condition) expression;
else              expression;
assert(condition) 
else              expression;

2.1布尔表达式

一般是在约束里所有表达式进行判断进行逻辑判断.

class order;
rand bit [7:0] lo,med,hi;
constraint bad
{
  lo<med<hi;
  }
endclass
  • 约束bad按照从左自右顺序分割两个表达式:((lo<med)<hi),首先计算lo<med,它们的值是0或1,然后根据约束,hi的值要大于0或1,所以变量lo和med虽然随机化了,但是没有受到约束;
class order;
rand bit [15:0] lo,med,hi;
constraint good
{
  lo < med;
  med <hi;
}
endclass

2.2 权重分配(weighted distributions)

符号 含义
:= 表示值范围内的每一个值的权重都是相同的;
: / 表示权重要均分到值范围内的每一个值
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
}

带变量的权重分配

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;
    READ16 := read16_wt;
    READ32 := read32_wt;
  };
}
endclass
  • 权重read8/16/32_wt后续可以更改,这里的8/16/32是总线访问分别对应字节访问、半字访问还有字访问,所以在数据访问是不一样的;
  • 字节访问第0位是保留下来,半字访问是第0位是不关心的,字访问是第0位和第一位不关心的;

2.3 范围表达式随机(range expressions)

inside运算符产生一个值的集合,除非对变量还有其他约束,否则sv在值的集合中取随机值时,各个值的选取机会是相等的,在集合中也可以使用变量

rand int c;
int lo,hi;
constraint c_range
{
  c inside{[lo:hi]};//!(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:$]};
  c inside {[$:4],[20:$]};
}

在集合中使用数据

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

2.4 条件表达式

通常约束块里面的所有约束都是有效的,但是有时我们只想让约束在某些时刻有效,例如在总线支持字节、半字、字的读操作,但是我们想只支持字的操作时。

  • 使用if...else
class stim;
bit flag;
rand bit [31:0] dst;
constraint c_stim
{
  if(flag)
  {
    dst inside {[40:80]};
  }
  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

2.5 外部约束

外部约束可以在需要的时候才添加约束,也可以不加约束;

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

program test;
  constraint packet::c_external{length ==  1;}
endprogram

显示外部约束的格式:在使用之前如果没有前面的extern会报错;

extern constraint c_ext

2.6 双向约束

约束块不想自上而下执行程序性代码,它们是声明性的代码,是并行执行的,所有的约束表达式同时有效,古SV会同时计算所有的随机标量约束,取他们的交集

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

有一个练习:

parameter MAX_SIZE = 10;
class packet;
rand bit [31:0] src,dst,data[8];
randc bit [2:0] kind;
constraint c
{
  src > 10;
  src < 15;
}
endclass:packet
//随机化句柄数组
class randarray;
rand packet array[];
constraint c
{
  array.size() inside{[1:MAX_SIZE]};
}
function new();
  array = new[MAX_SIZE];
  foreach(array[i])
    array[i] = new();
endfunction
endclass:randarry

module top_tb;
packet p;
initial begin
  p = new();
  assert (p.randomize)
  else $fatal(0,"packet::randomize failied");
  foreach(p.array[i])begin
    $display("src =%d",p.array[i].src);
    $display("dst =%d",p.array[i].dst);
    $display("data = ",p.array[i].data);
    $display("kind = %d",p.array[i].kind);
  end
end
endmodule

三、随机约束分布概率

3.1 没有约束的类

class unconstrained;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3
endclass
module top_tb;
unconstrained u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
  u = new();
  for(int i = 1;i<20000;i++)begin
  assert(u.randomize());
  case({u.x,u.y})
  3'b000:cnt000 = cnt000+1'b1;
  3'b001:cnt001 = cnt001+1'b1;
  3'b010:cnt010 = cnt010+1'b1;
  3'b011:cnt011 = cnt011+1'b1;
  3'b100:cnt100 = cnt100+1'b1;
  3'b101:cnt101 = cnt101+1'b1;
  3'b110:cnt110 = cnt110+1'b1;
  3'b111:cnt111 = cnt111+1'b1;
  endcase
  end
  $display("{u.x,u.y} is 000 time %0d",cnt000);
  $display("{u.x,u.y} is 001 time %0d",cnt001);
  $display("{u.x,u.y} is 010 time %0d",cnt010);
  $display("{u.x,u.y} is 011 time %0d",cnt011);
  $display("{u.x,u.y} is 100 time %0d",cnt100);
  $display("{u.x,u.y} is 101 time %0d",cnt101);
  $display("{u.x,u.y} is 110 time %0d",cnt110);
  $display("{u.x,u.y} is 111 time %0d",cnt111);
  end
endmodule
x y 概率
A 0 0 1/8
B 0 1 1/8
C 0 2 1/8
D 0 3 1/8
E 1 0 1/8
F 1 1 1/8
G 1 2 1/8
H 1 3 1/8

随机约束、随机分布、随机数组等-systemverilog

  • 没有约束的类是可以根据x和y分别对应不同的解来计算概率的,例如x出现的概率是1/2,y出现0的概率是1/4,所以概率位1/8

3.2 关系操作

class impact1;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3

constraint c_xy
{
  (x == 0)->y == 0;
}
endclass
module top_tb;
impact1 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
  u = new();
  for(int i = 1;i<20000;i++)begin
  assert(u.randomize());
  case({u.x,u.y})
  3'b000:cnt000 = cnt000+1'b1;
  3'b001:cnt001 = cnt001+1'b1;
  3'b010:cnt010 = cnt010+1'b1;
  3'b011:cnt011 = cnt011+1'b1;
  3'b100:cnt100 = cnt100+1'b1;
  3'b101:cnt101 = cnt101+1'b1;
  3'b110:cnt110 = cnt110+1'b1;
  3'b111:cnt111 = cnt111+1'b1;
  endcase
  end
  $display("{u.x,u.y} is 000 time %0d",cnt000);
  $display("{u.x,u.y} is 001 time %0d",cnt001);
  $display("{u.x,u.y} is 010 time %0d",cnt010);
  $display("{u.x,u.y} is 011 time %0d",cnt011);
  $display("{u.x,u.y} is 100 time %0d",cnt100);
  $display("{u.x,u.y} is 101 time %0d",cnt101);
  $display("{u.x,u.y} is 110 time %0d",cnt110);
  $display("{u.x,u.y} is 111 time %0d",cnt111);
  end
endmodule
x y 概率
A 0 0 1/5
B 0 1 0
C 0 2 0
D 0 3 0
E 1 0 1/5
F 1 1 1/5
G 1 2 1/5
H 1 3 1/5

随机约束、随机分布、随机数组等-systemverilog

  • 这里增加了关系约束,x等于0的时候,y等于0;
  • y的值依赖于x的值,所以只有五种可能,所以都为1/5;
class impact2;
randc bit x;//0、1
rand bit[1:0]y;//0、1、2、3

constraint c_xy
{
  (x == 0)->y == 0;
}
endclass
module top_tb;
impact2 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
  u = new();
  for(int i = 1;i<20000;i++)begin
  assert(u.randomize());
  case({u.x,u.y})
  3'b000:cnt000 = cnt000+1'b1;
  3'b001:cnt001 = cnt001+1'b1;
  3'b010:cnt010 = cnt010+1'b1;
  3'b011:cnt011 = cnt011+1'b1;
  3'b100:cnt100 = cnt100+1'b1;
  3'b101:cnt101 = cnt101+1'b1;
  3'b110:cnt110 = cnt110+1'b1;
  3'b111:cnt111 = cnt111+1'b1;
  endcase
  end
  $display("{u.x,u.y} is 000 time %0d",cnt000);
  $display("{u.x,u.y} is 001 time %0d",cnt001);
  $display("{u.x,u.y} is 010 time %0d",cnt010);
  $display("{u.x,u.y} is 011 time %0d",cnt011);
  $display("{u.x,u.y} is 100 time %0d",cnt100);
  $display("{u.x,u.y} is 101 time %0d",cnt101);
  $display("{u.x,u.y} is 110 time %0d",cnt110);
  $display("{u.x,u.y} is 111 time %0d",cnt111);
  end
endmodule
x y 概率
A 0 0 1/2
B 0 1 0
C 0 2 0
D 0 3 0
E 1 0 1/8
F 1 1 1/8
G 1 2 1/8
H 1 3 1/8

随机约束、随机分布、随机数组等-systemverilog

  • randc是先解析的,所以先解析x的值(0、1)。如果x等于0,则y肯定是0,针对x值0是1/2的概率;
  • 其他情况x等于1时为1/2,然后y出现0、1、2、3的概率为1/4,所以E-H是1/8;
class impact3;
rand bit x;//0、1
randc bit[1:0]y;//0、1、2、3

constraint c_xy
{
  (x == 0)->y == 0;
}
endclass
module top_tb;
impact3 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
  u = new();
  for(int i = 1;i<20000;i++)begin
  assert(u.randomize());
  case({u.x,u.y})
  3'b000:cnt000 = cnt000+1'b1;
  3'b001:cnt001 = cnt001+1'b1;
  3'b010:cnt010 = cnt010+1'b1;
  3'b011:cnt011 = cnt011+1'b1;
  3'b100:cnt100 = cnt100+1'b1;
  3'b101:cnt101 = cnt101+1'b1;
  3'b110:cnt110 = cnt110+1'b1;
  3'b111:cnt111 = cnt111+1'b1;
  endcase
  end
  $display("{u.x,u.y} is 000 time %0d",cnt000);
  $display("{u.x,u.y} is 001 time %0d",cnt001);
  $display("{u.x,u.y} is 010 time %0d",cnt010);
  $display("{u.x,u.y} is 011 time %0d",cnt011);
  $display("{u.x,u.y} is 100 time %0d",cnt100);
  $display("{u.x,u.y} is 101 time %0d",cnt101);
  $display("{u.x,u.y} is 110 time %0d",cnt110);
  $display("{u.x,u.y} is 111 time %0d",cnt111);
  end
endmodule
x y 概率
A 0 0 1/8
B 0 1 0
C 0 2 0
D 0 3 0
E 1 0 1/8
F 1 1 1/4
G 1 2 1/4
H 1 3 1/4

随机约束、随机分布、随机数组等-systemverilog

  • 这里与上面不同之处在于先解析y,然后y(0、1、2、3)分别为1/4,x的取值有两种可能,但是由于约束的原因,在y等于1、2、3时,x不可能为0, 所以y=0,x=0、1分别为1/8;
  • 所以x等于1,y等于1、2、3的概率为1/4
class impact1;
randc bit x;//0、1
randc bit[1:0]y;//0、1、2、3

constraint c_xy
{
  (x == 0)->y == 0;
}
endclass
x y 概率
A 0 0 1/4
B 0 1 0
C 0 2 0
D 0 3 0
E 1 0 0
F 1 1 1/4
G 1 2 1/4
H 1 3 1/4
  • 首先randc是轮询的
  • x = 0,y只能是0;
  • x = 1,y只能在1、2、3里面
  • 但是这里两个randc,可能不同仿真工具可能不一样的仿真结果,我们应该避免这种让仿真工具有问题;
class impact5;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3

constraint c_xy
{
  y > 0;
  (x == 0)->y == 0;
}
endclass
module top_tb;
impact5 u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
  u = new();
  for(int i = 1;i<20000;i++)begin
  assert(u.randomize());
  case({u.x,u.y})
  3'b000:cnt000 = cnt000+1'b1;
  3'b001:cnt001 = cnt001+1'b1;
  3'b010:cnt010 = cnt010+1'b1;
  3'b011:cnt011 = cnt011+1'b1;
  3'b100:cnt100 = cnt100+1'b1;
  3'b101:cnt101 = cnt101+1'b1;
  3'b110:cnt110 = cnt110+1'b1;
  3'b111:cnt111 = cnt111+1'b1;
  endcase
  end
  $display("{u.x,u.y} is 000 time %0d",cnt000);
  $display("{u.x,u.y} is 001 time %0d",cnt001);
  $display("{u.x,u.y} is 010 time %0d",cnt010);
  $display("{u.x,u.y} is 011 time %0d",cnt011);
  $display("{u.x,u.y} is 100 time %0d",cnt100);
  $display("{u.x,u.y} is 101 time %0d",cnt101);
  $display("{u.x,u.y} is 110 time %0d",cnt110);
  $display("{u.x,u.y} is 111 time %0d",cnt111);
  end
endmodule
x y 概率
A 0 0 0
B 0 1 0
C 0 2 0
D 0 3 0
E 1 0 0
F 1 1 1/3
G 1 2 1/3
H 1 3 1/3

3.3 solve…before

class slovebefore;
rand bit x;//0、1
rand bit[1:0]y;//0、1、2、3

constraint c_xy
{
  (x == 0)->y == 0;
  solve x before y;
}
endclass
module top_tb;
slovebefore u;
int cnt000,cnt001,cnt010,cnt011,cnt100,cnt101,cnt110,cnt111;
initial begin
  u = new();
  for(int i = 1;i<20000;i++)begin
  assert(u.randomize());
  case({u.x,u.y})
  3'b000:cnt000 = cnt000+1'b1;
  3'b001:cnt001 = cnt001+1'b1;
  3'b010:cnt010 = cnt010+1'b1;
  3'b011:cnt011 = cnt011+1'b1;
  3'b100:cnt100 = cnt100+1'b1;
  3'b101:cnt101 = cnt101+1'b1;
  3'b110:cnt110 = cnt110+1'b1;
  3'b111:cnt111 = cnt111+1'b1;
  endcase
  end
  $display("{u.x,u.y} is 000 time %0d",cnt000);
  $display("{u.x,u.y} is 001 time %0d",cnt001);
  $display("{u.x,u.y} is 010 time %0d",cnt010);
  $display("{u.x,u.y} is 011 time %0d",cnt011);
  $display("{u.x,u.y} is 100 time %0d",cnt100);
  $display("{u.x,u.y} is 101 time %0d",cnt101);
  $display("{u.x,u.y} is 110 time %0d",cnt110);
  $display("{u.x,u.y} is 111 time %0d",cnt111);
  end
endmodule
x y 概率
A 0 0 1/2
B 0 1 0
C 0 2 0
D 0 3 0
E 1 0 1/8
F 1 1 1/8
G 1 2 1/8
H 1 3 1/8

随机约束、随机分布、随机数组等-systemverilog

  • 与randc的先解析相似,概率分布也和randc的概率分布一样;
  • 但是不要滥用solve…before,会降低计算速度;

四、随机约束控制

4.1 控制多个约束块constraint_mode()

class packet;
rand int length;
constraint c_short {length inside{[1:32]};}
constraint c_long {length inside{[1000:1023]};}
endclass
module top_tb;
packet p;
initial begin
p = new();
p.c_short.constraint_mode(0);//disabling short constraint
assert(p.randomize());
$display("p::length = %d",p.length);
p.constraint_mode(0);
p.c_short.constraint_mode(1);
assert(p.randomize());
$display("p::length = %d",p.length);
end
endmodule
  • 可以单独关闭类中的某个约束块,可以单独开启每个约束块,使用0/1即可;
  • 可以一口气把类中的所有的约束块都给关了;

4.2 控制随机变量rand_mode()

class packet;
rand bit [7:0] length,payload[];
constraint c_valid {length > 0;payload.size() ==length;}
endclass
module top_tb;
packet p;
initial begin
p = new();
p.length.rand_mode(0);
p.length = 42;
assert(p.randomize());
$display("p::length = %d",p.length);
p.length.rand_mode(1);
assert(p.randomize());
$display("p::length = %d",p.length);
end
endmodule
  • 可以通过rand_mode对某个随机变量的随机功能进行开关控制;

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

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

module top_tb;
transaction t;
initial begin
  t = new();
  assert(tr.randomize() with {addr >= 50;addr <= 1500;data < 10;});
  $display("t::addr is %d",t.addr);
  $display("t:data is %d",t.data);
  assert(t.randomize() with{addr == 2000;data >10;});//force addr to a specific value,data >10
  $display("t::addr is %d",t.addr);
  $display("t:data is %d",t.data);
end
endmodule
  • 这里的randomize() with{}可以强制约束甚至可以变成一个特定的值;

4.4 randomize单独控制变量

class rising;
byte low;
rand byte med,hi;
constraint c_up{low<med;med<hi;}
endclass
module top_tb;
rising r;
initial begin
  r = new();
  r.randomize();
  $display("r::low is %d,r:med is %d,r::hi is %d",r.low,r.med,r.hi);
  r.randomize(med);
  $display("r::low is %d,r:med is %d,r::hi is %d",r.low,r.med,r.hi);
  r.randomize(low);
  $display("r::low is %d,r:med is %d,r::hi is %d",r.low,r.med,r.hi);
end
endmodule

随机约束、随机分布、随机数组等-systemverilog

  • 这里可以让不是rand类型的变量也可以进行随机化,实现对单变量的控制;
  • 但是这里不推荐一个一个变量进行随机化;

4.5 pre_randomize与post_randomize

它们的特点:

  • 都是function,不会消耗事件;
  • pre_randomize->randomize->post_randomize
  • pre_randomize()和post_randmize()函数可以被用户重写,用户使用pre_randomize()函数,在随机前修改代码中非随机变量的值,如上下限、权重等;
  • post_randmize()函数可以在随机后修改随机值或做一些其它的计算。
  • randomize失败的化post不会执行,
  • pre和post可以重写,但是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);
   endfunction
  function void post_randomize();
     $display("call post_randomize!");
     if(wr_addr == 1)
       broadcast = 1;
     else broadcast = 0;
   endfunction
 endclass
module top_tb;
  wr_tran tr;
  initial begin
    tr = new();
    tr.constraint_en = 0;
    tr.randomize() with
    {
      wr_addr == 200;
      wr_data == 3;
      valid   == 0;
    };
    $display("wr_addr = %d,
              wr_data = %d,
              valid   = %d,
              broadcast = %d",
              tr.wr_addr,tr.wr_data,tr.valid,tr.broadcast);
              end
              endmodule          

随机约束、随机分布、随机数组等-systemverilog

五、随机化常见错误

  • 在随机变量中使用了有符号变量导致随机化错误;
  • 变量可能会溢出导致高位去除而随机化出错;
    避免随机化错误的技巧:
  • 避免使用乘除和取模的操作,如果要使用就通过左移和右移代替乘除;
  • 使用AND mask代替取模;
sig1:12345678
sig 1 &000FF 000
class test;
  rand bit[7:0]sig1;
  rand int unsigned sig1;
constrain c
{
  sig2>1000;
  sig1>sig2;//sig1的最大值256,无法达到1000,需要注意位宽、符号
rand bit[7:0]sig1;
rand bit[7:0]sig2;
constraint c1{sig1>5;}
constraint c2{sig2>10;}
constraint c2{sig1+sig2 ==13;}//这里也是不对的,多随机变量的出现形式可能会出现相互矛盾的;

所以我们在使用随机化时需要注意

  • 数据类型
  • 数据位宽(精确些)
  • 多个约束块中的变量之间的关系
  • 约束块中表达式的结果
  • 约束块中各约束冲突
上一篇:约束式编程学习笔记[8] 简单的不完全求解器


下一篇:C# generic "where constraint" with "any generic type" definition?