一 概要
与传统的定向测试相比,随机测试可能更为有效。通过指定约束条件,用户可以轻松创建能够发现难以触及的边界情况的测试。SystemVerilog允许用户以简洁、声明式的方式指定约束条件。然后,这些约束条件会由一个求解器进行处理,该求解器会生成满足约束条件的随机值。
随机约束,通常是在面向对象的数据抽象之上指定的(这里的意思,通常对于面向对象的类对象而言)。该抽象,是通过将待随机化的数据建模为包含随机变量和用户定义约束的对象而实现。这些约束决定了可以分配给随机变量的合法值。该类方法,非常适合表示复杂的聚合数据类型和协议,如以太网数据包。
后面的章节,将会详细讲解关于随机变量、约束块以及用于操作它们的机制。
二 使用规则
SystemVerilog采用面向对象的方法为对象的成员变量分配随机值,同时遵循用户定义的约束条件。
例如:在SystemVerilog中,你可以定义一个类,并为这个类的成员变量指定随机值。这些随机值可以受到一些约束条件的限制,以确保生成的随机数据符合特定的测试需求或设计规格。
规则1 : 约束变量和变量控制
下面是一个简单的例子:
class Bus;
rand bit[15:0] addr;
rand bit[31:0] data;
constraint word_align {addr[1:0] == 2'b0;} // 对地址 addr 的低位 进行约束
endclass
Bus bus = new;
repeat (50) begin
if ( bus.randomize() == 1 ) // randomize 成功,返回值为 1; 否则为 0
$display ("addr = %16h data = %h\n", bus.addr, bus.data);
else
$display ("Randomization failed.\n");
end
注意:
这里,如果 bus.randomize() 随机失败,将会执行else 分支,但是不会仿真停止;
这里,与求解器失败场景不一样,如果是约束求解器求解失败,将会遇到编译错误
这里,仅对addr 进行了约束,而 data没有,那么data 将会取值声明范围内的任意数值
规则2 : 约束遵从继承
typedef enum {low, mid, high} AddrType;
class MyBus extends Bus; // 随机变量及其约束控制,均可继承
rand AddrType atype;
constraint addr_range {
(atype == low ) -> addr inside { [0 : 15] };
(atype == mid ) -> addr inside { [16 : 127]};
(atype == high) -> addr inside {[128 : 255]};
}
endclass
规则3 : randomize with 结构
task exercise_bus (MyBus bus);
int res;
// EXAMPLE 1: restrict to low addresses
res = bus.randomize() with {atype == low;};
// EXAMPLE 2: restrict to address between 10 and 20
res = bus.randomize() with {10 <= addr && addr <= 20;};
// EXAMPLE 3: restrict data values to powers-of-two
res = bus.randomize() with {(data & (data - 1)) == 0;};
endtask
规则4 : 约束几个重要特性
具体如下:
--- 约束可以是包含整型变量和常量的任何SystemVerilog表达式(例如,bit、reg、logic、integer、enum、packed struct)。
--- 约束求解器能够处理广泛的方程,如代数因式分解、复杂布尔表达式以及混合整数和位表达式。在之前的例子中,2的幂约束是以算术方式表达的。它也可以使用移位运算符来表达,例如1 << n,其中n是一个5位的随机变量。
--- 如果存在解,约束求解器将找到它。求解器只有在问题过度约束且没有随机值组合能满足约束时才会失败。
--- 约束是双向交互的。在这个例子中,为addr选择的值取决于atype及其约束方式,而为atype选择的值则取决于addr及其约束方式。所有表达式运算符都是双向处理的,包括蕴含运算符(->)。
--- 约束仅支持2态值。4态值(X或Z)或4态运算符(例如,===、!==)是非法的,并会导致错误。
规则5 : 约束block功能的禁用
task exercise_illegal(MyBus bus, int cycles);
int res;
// Disable word alignment constraint.
bus.word_align.constraint_mode(0); // 禁用
repeat (cycles) begin
// CASE 1: restrict to small addresses.
res = bus.randomize() with {addr[0] || addr[1];};
...
end
// Reenable word alignment constraint
bus.word_align.constraint_mode(1);// 打开
endtask
规则6 : 约束变量功能的禁用
变量的属性 rand_mode() 可以对变量启用和禁用随机功能。禁用后,像其它非随机变量表现的一样。
规则7 : 内置 pre_randomize()
和post_randomize() 函数
有时候,我们希望在随机化之前或之后立即执行某些操作。这是通过两个内置方法pre_randomize()
和post_randomize()
来实现的,它们分别在随机化之前和之后自动被调用。这些方法可以被覆盖以实现所需的功能。
class XYPair;
rand integer x, y;
endclass
class MyXYPair extends XYPair
function void pre_randomize();
super.pre_randomize();
$display("Before randomize x=%0d, y=%0d", x, y);
endfunction
function void post_randomize();
super.post_randomize();
$display("After randomize x=%0d, y=%0d", x, y);
endfunction
endclass
默认情况下,pre_randomize()
和 post_randomize()
会调用它们被重写时的基类方法。当重写 pre_randomize()
或 post_randomize()
时,必须小心调用基类的这些方法,除非该类是基类(即没有父类)。否则,基类的这些方法将不会被调用。