SV — Randomization 随机化

@(SV)

目录SV — Randomization 随机化0. 基础1. 随机化方法2. Constraint3. inside4. dist5. 条件约束6. foreach 约束7. disable constraint7. static constraint8. inline constraint9. soft constraint10. unique11. 约束的双向性质12. Solve before13. 随机系统函数

0. 基础

下面几种类型可以随机化:

单个变量或整形数
数组
数组长度
对象句柄

语法:

rand bit [3:0] addr;生成0-15的随机数
randc bit [3:0] addr; 生成0-15的随机数,完全遍历完16个数之后才会开始开始下一轮,每一轮随机的数不重样
object.randomize();对象内变量随机化命令

rand_mode:
<object_hanlde>.<variable_name>.rand_mode(enable); 打开或关闭某个变量的随机化,或者

<object_hanlde>.rand_mode(enable);打开或关闭某个类内的所有变量的随机化。

class packet;
  rand byte addr;
  rand byte data;  
endclass
 
module rand_methods;
  initial begin
    packet pkt;
    pkt = new();
     
    //disable rand_mode of addr variable of pkt
    pkt.addr.rand_mode(0);
     
    //calling randomize method
    pkt.randomize();
     
    $display("	addr = %0d 	 data = %0d",pkt.addr,pkt.data);
     
    $display("	addr.rand_mode() = %0d 	 data.rand_mode() = %0d",pkt.addr.rand_mode(),pkt.data.rand_mode());
  end
endmodule

输出:

addr = 0 data = 110
addr.rand_mode() = 0 data.rand_mode() = 1

1. 随机化方法

randomize()
pre_randomize()

可以为类设置随机化的先决条件,例如rand_mode()

post_randomize()

用来在随机化后进行检查

例如下面的例子,如果在wr_rd为1的时候,address需要保持不变,定义一个pre_randomize方法来控制变量是否随机化。

//class
class packet;
  rand  bit [7:0] addr;
  randc bit       wr_rd;
        bit       tmp_wr_rd;    
 
  //pre randomization function - disabling randomization of addr,
  //if the prevoius operation is write.
  function void pre_randomize();
    if(tmp_wr_rd==1) addr.rand_mode(0);
    else                 addr.rand_mode(1);
  endfunction
 
  //post randomization function - store the wr_rd value to tmp_wr_rd
  //and display randomized values of addr and wr_rd
  function void post_randomize();
    tmp_wr_rd = wr_rd;
    $display("POST_RANDOMIZATION:: Addr = %0h,wr_rd = %0h",addr,wr_rd);
  endfunction
endclass
 
module rand_methods;
  initial begin
    packet pkt;
    pkt = new();
 
    repeat(4) pkt.randomize();
  end
endmodule

输出:

POST_RANDOMIZATION:: Addr = 6e,wr_rd = 1
POST_RANDOMIZATION:: Addr = 6e,wr_rd = 0
POST_RANDOMIZATION:: Addr = 88,wr_rd = 1
POST_RANDOMIZATION:: Addr = 88,wr_rd = 0

2. Constraint

为随机变量设置约束:
第一种,在内部设置:

class packet;
  rand  bit [3:0] addr;
 
  constraint addr_range { addr > 5; }
endclass

第二种,在外部设置:

class packet;
 
  rand  bit [3:0] addr;
  //constraint block declaration
  constraint addr_range;
endclass
 
//constraint implementation outside class body
constraint packet::addr_range { addr > 5; }

同样,constraint也会被子类继承。

3. inside

让随机值约束在inside指定的范围内:
constraint addr_range { addr inside {1,3,[5:10],12,[13:15]}; }

constraint addr_range { addr !(inside {[5:10]}); }
也可以将上下界指定为随机值(上下界都可以取到):

class packet;
  rand bit [3:0] addr;
  rand bit [3:0] start_addr;
  rand bit [3:0] end_addr;
   
  constraint addr_1_range { addr inside {[start_addr:end_addr]}; }
endclass

4. dist

可以使得随机化的数据

constraint addr_1_range {   addr_1 dist { 2 := 5, [10:12] := 8 }; }

上面的权重是这样的:
addr == 2 , weight 5
addr == 10, weight 8
addr == 11, weight 8
addr == 12, weight 8

  constraint addr_2_range {   addr_2 dist { 2 :/ 5, [10:12] :/ 8 }; }

上面的权重是这样的:
addr == 2 , weight 5
addr == 10, weight 8/3
addr == 11, weight 8/3
addr == 12, weight 8/3

5. 条件约束

class packet;
  rand bit [3:0] addr;
       string    addr_range;
 
  constraint address_range { if(addr_range == "small")
                                addr < 8;
                             else
                                addr > 8;
                           }
endclass

6. foreach 约束

class packet;
  rand byte addr [];
  rand byte data [];
 
  constraint avalues { foreach( addr[i] ) addr[i] inside {4,8,12,16}; }
  constraint dvalues { foreach( data[j] ) data[j] > 4 * j; }
  constraint asize   { addr.size < 4; }
  constraint dsize   { data.size == addr.size; }
endclass

7. disable constraint

跟randomize_mode一样,constraint也可以设置mode控制是否添加约束:
pkt.addr_range.constraint_mode(0); 0表示关闭约束

7. static constraint

静态约束跟普通约束不同的是,静态约束的使能和关闭对所有实例都生效:

class packet;
  rand  bit [7:0] addr;
   
  static constraint addr_range { addr == 5; }
endclass

如果下面实例化了两个packet:a1和a2,如果对a1的addr变量设置constraint_mode(0),则a2的addr的约束也会关闭。

8. inline constraint

inline的约束允许使用with关键字在类外对变量增加新的约束:

class packet;
  rand bit [3:0] addr;
   
  constraint addr_range {addr inside {[6:12]};};
endclass
 
module inline_constr;
  initial begin
    packet pkt;
    pkt = new();
    repeat(2) begin
      pkt.randomize() with { addr == 8;};
      $display("	addr = %0d",pkt.addr);
    end
  end
endmodule

上面pkt.randomize() with { addr == 8;};将给变量添加新的约束,使得输出都为8,但是通过with添加的新约束必须包含在类中定义的范围内,否则会报错。

9. soft constraint

软约束是为了解决inline方式的弊端。比如有这样的场景,我们已经测试了很多正常约束的case,可能会使用with的方式来生成约束的子集,但我们永远无法通过with来生成约束的补集,而这可能在测试异常情况时需要用到。一个方法是修改constraint,但无疑会有很大麻烦。
这时只需要在constraint中为变量增加soft关键字即可:

class packet;
  rand bit [3:0] addr;
  constraint addr_range { soft addr > 6; }
endclass
 
module soft_constr;
  initial begin
    packet pkt;
    pkt = new();
 
    repeat(2) begin
      pkt.randomize() with { addr < 6;};
      $display("	addr = %0d",pkt.addr);
    end
  end
endmodule

10. unique

使用unique关键词加在一组随机变量或者随机数组前,可以使得这些变量在约束范围内各不相同:

rand bit [31:0] array[10];
   
  constraint array_c {unique {array}; foreach(array[i]) array[i] < 10;}

11. 约束的双向性质

上面的例子一般都只涉及单个变量的约束,而实际上sv中约束的变量是并行地设置的,因此约束中如果涉及多个约束变量的约束,则这一约束是双向的。
例如:

constraint c_name { if(a == 0) b == 1;
                    else       b == 0; }

上面的例子b的值依赖a,而实际上a的值也会依赖b,如果将b通过inine的方式约束为1,则a也被约束为0。

class packet;
  rand bit a;
  rand bit b;
 
  constraint a_value { a == 1; }
  constraint b_value { if(a == 0) b == 1;
                       else       b == 0; }
endclass
 
module bidirectional_const;
  initial begin
    packet pkt;
    pkt = new();
    pkt.randomize() with { b == 1; };
    $display("Value of a = %0d 	b = %0d",pkt.a,pkt.b);
  end
endmodule

上面的例子试图用inline的方式约束b,但b的约束会反作用到a,使得a约束为0,但a已经约束为只能为1,所以程序报错。

12. Solve before

solve before可以强制约束中先进行哪个变量的约束。

在上面提到的约束双向性中,多个变量之间有依赖关系时约束是双向的,这可能会导致一些问题,比如下面的例子:

class packet;
  rand bit       a;
  rand bit [3:0] b;
 
  constraint a_b { (a == 1) -> b == 0; }
endclass
 
module inline_constr;
  initial begin
    packet pkt;
    pkt = new();
    repeat(10) begin
      pkt.randomize();
      $display("	Value of a = %0d, b = %0d",pkt.a,pkt.b);
    end
  end
endmodule

b可以取0-15中的值,而只要b不为0,a都会被约束为0,所以这个例子产生的结果是这样的:

Value of a = 0, b = 6
Value of a = 0, b = 3
Value of a = 1, b = 0
Value of a = 0, b = 15
Value of a = 0, b = 7
Value of a = 0, b = 2
Value of a = 0, b = 15
Value of a = 0, b = 4
Value of a = 0, b = 7
Value of a = 0, b = 11

a=1的情况非常少。如果我们希望a的产生不依赖b,可以使用solve before:

class packet;
  rand bit       a;
  rand bit [3:0] b;
 
  constraint sab { solve a before b;}
  constraint a_b { (a == 1) -> b == 0;}
endclass
 
module inline_constr;
  initial begin
    packet pkt;
    pkt = new();
    repeat(10) begin
      pkt.randomize();
      $display("	Value of a = %0d, b = %0d",pkt.a,pkt.b);
    end
  end
endmodule

输出:

Value of a = 0, b = 9
Value of a = 0, b = 14
Value of a = 0, b = 3
Value of a = 0, b = 13
Value of a = 1, b = 0
Value of a = 1, b = 0
Value of a = 1, b = 0
Value of a = 0, b = 5
Value of a = 0, b = 3
Value of a = 0, b = 4

13. 随机系统函数

随机化的系统函数主要有两个:

urandom() 生成无符号随机数,括号内可以填入不同随机种子

urandom_range(max,min) 生成范围内的随机数,上下界可以不按顺序写,默认下届为0

random() 生成有符号随机数。

module system_funcations;
  bit [31:0] addr1;
  bit [31:0] addr2;
  bit [64:0] addr3;
  bit [31:0] data;
  initial begin
    addr1 = $urandom();
    addr2 = $urandom(89);
    addr3 = {$urandom(),$urandom()};
    data  = $urandom * 6;
 
    $display("addr1=%0d, addr2=%0d, addr3=%0d, data=%0d",addr1,addr2,addr3,data);
  
    addr1 = $urandom_range(30,20);
    addr2 = $urandom_range(20); //takes max value as '0'
    addr3 = $urandom_range(20,30); //considers max value as '30' and min value as '20'
    $display("addr1=%0d, addr2=%0d, addr3=%0d",addr1,addr2,addr3);
  end
endmodule

输出:

addr1=303379748, addr2=2153631232, addr3=423959822444962108, data=546103870
addr1=27, addr2=6, addr3=25