systemverilog中的随机化
随机化指的是使得某些事情随机的过程。在systemverulog中的随机化指的是给变量赋值一个随机的值。在verilog中,有$random的方法来产生随机的int数值。但是这仅仅适用于变量,很难适用于类对象的实例的随机化。因此systemverilog引入关键字rand声明随机化,randomize()方法用于产生随机数值。数据随机化能够避免设计人员的主观想法,提高验证的客观性,增加可信度。
为了声明一个变量是一个随机化的变量,需要使用rand关键字或者randc.以下类型的数据可以被声明为rand或者randc:
- 整型变量
- 数组
- 数组大小
- 对象的句柄
对于rand修饰的变量,其值出现概率呈现均匀分布,如rand bit [3:0] addr,addr变量共16种取值,出现0-15的概率都是1/16。而randc,即random-cyclic,它要求所有可能出现的取值都赋值过,才会重复赋以前赋过的值。即randc bit a,变量a假设首先随机化为0,那么下一次一定是1,不能在没赋1前重复赋值0.当然,我们通过rand和randc只是做了声明,要随机化变量,还需要调用randomize方法,即"类句柄.randomize();"
// class
class packet;
rand bit [2:0] addr1;
randc bit [2:0] addr2;
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
repeat(10)begin
pkt.randomize();
$display("\t addr1 = %0d \t addr2 = %0d", pkt.addr1, pkt.addr2);
end
end
endmodule
执行结果如下,randc的随机值会遍历一遍所有的取值,再来一轮新的遍历。要注意的是,使用句柄来调用randomize方法,该方法会对类中所有的随机化变量赋值。
disable randomization
对于不让随机化变量随机化的要求,systemverilog中是支持的,通过使用随机化的方法rand_mode来disable掉一个变量的随机化。
rand_mode是disable用rand/randc关键字声明的随机变量。rand_mode可以称为systemverilog的方法,一个变量的使能与否可以通过调用“变量名.rand_mode()”方法,即此时可以用变量命调用,而非类句柄。当随机化使能时,rand_mode方法返回1,否则返回0。
- rand_mode(1)意味着随机化被开启
- rand_mode(0)意味着随机化北关掉
- rand_mode中默认的值是1
声明形式为:
<object_hanlde>.<variable_name>.rand_mode(enable);
//enable = 1, randomization enable
//enable = 0, randomization disable
在下面这个没有把变量的随机化给关掉,因此变量会得到随机值。
class packet;
rand byte addr;
rand byte data;
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
//calling random method
pkt.randomize();
$display("\t addr = %0d \t data = %0d", pkt.addr, pkt.data);
end
endmodule
其输出结果是:
但是若把addr的随机化disable掉。
输出结果就是:
如果想让类中所有变量都不使能随机化,一个一个太麻烦了,我们可以直接"句柄.rand_mode(0)",就能一次性不使能所有变量。
Randomiztion Methods
我们的randomize方法,随机化成功会返回1,否则返回0。该方法还会配合pre_randomize和post_randoize回调函数callback。调用randomize方法前会调用pre_randomize方法,调用完randomize 方法后会调用post_randomize方法。程序设计者可以覆pre_randomize和post_randomize方法,即改写以适合自己的需求,如通过pre_randmize使能或不使能随机化,通过post_randomize打印随机化后的值。
class packet;
rand bit [7:0] addr;
rand bit [7:0] data;
//pre randomization function
function void pre_randomize();
$display("Inside pre_randomize");
endfunction
//post randomization function
function void post_randomize();
$display("Inside post_randomize");
$display("Value of addr = %0d data = %0d", addr, data);
endfunction
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
pkt.randomize();
end
endmodule
例中,randomize以类为基本单位,在类中定义回调函数,然后调用randomize方法就会自动调用pre_randomize和post_randomize方法
其输出结果是:
//class
class packet;
rand bit [31:0] addr ;
randc bit wr_rd ;
bit tmp_wr_rd ;
//pre randomization function - disabling randomization of addr
//if the prevoious 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) begin
pkt.randomize();
end
end
endmodule
其输出结果是:
有约束的随机化(Constrained)
前面提到,rand类型随机值出现的概率是均匀分布的,我们会有需求如排除一个或多个值,改变概率分布等。sv允许我们写约束,用于控制随机值。
约束块:
- 由类的成员组成,比如任务、函数和变量
- 在一个类中,约束块都有自己独一无二的名字
- 约束块中包括条件或者表达式来约束或者控制一个随机变量的值
- 约束块使用大括号{}
- 约束块可以被定义在类内,也可以被定义在类外作为外部方法。
声明的形式如下:
constraint <constraint_block_name> { <condition/expression>;
...
<condition/expression>; }
class packet;
rand bit [3:0] addr;
constraint add_range {addr > 5;}
endclass
module constr_blocks;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\t addr = %0d", pkt.addr);
end
end
endmodule
在上面的例子中将addr的约束设置为大于5,可以从下图看出所有的输出结果都是大于5的。
要在类外写约束块,需要用类名和作用域解析操作符,并在类中给出约束块名字。
值得注意的是,约束块也是可以继承的,如果子类有同名的约束块,还可以覆盖父类的约束块。下例中,父类packet1的addr需要大于5,而子类的addr则小于5。
class packet;
rand bit [3:0] addr;
constraint addr_range { addr > 5; }
endclass
class packet2 extends packet;
constraint addr_range { addr < 5; } //overriding constraint of parent class
endclass
module const_inhe;
initial begin
packet pkt1;
packet2 pkt2;
pkt1 = new();
pkt2 = new();
$display("------------------------------------");
repeat(5) begin
pkt1.randomize();
$display("\tpkt1:: addr = %0d",pkt1.addr);
end
$display("------------------------------------");
repeat(5) begin
pkt2.randomize();
$display("\tpkt2:: addr = %0d",pkt2.addr);
end
$display("------------------------------------");
end
endmodule
其输出结果是: