约束(constraint)是将合法值分配给随机变量的方法。通过指定范围,约束帮助我们限制变量的随机性。创建有效的测试配置的方法是使用约束。
要启用随机化,我们使用 rand() 和 randc() 函数。 要使用约束,首先需要使用关键字 rand 或 randc 分配随机变量。然后声明约束语句。
所有约束块同时处于活动状态。
语法
constraint constraint_name{random_variable[range];}
注意- 如果变量在没有任何约束的情况下随机化,则范围内的任何值都将以相等的概率分配给变量。
序号 | 约束 |
---|---|
1. | Random Function System |
2. | Random Variables |
3. | std::randomize |
4. | Constraint Block |
5. | Array Randomization |
6. | Inside Constraint |
7. | Inverted Inside Constraint |
8. | Weighted Distribution |
9. | Bidirectional Constraint |
10. | Implication Constraint |
11. | if-else constraint |
12. | foreach |
13. | Solve before constraint |
14. | Static Constraint |
15. | Randomization methods |
16. | Inline Constraint |
17. | Soft Constraint |
18. | Disable Constraint |
19. | Disable Randomization |
20. | randcase |
21. | constraint memory examples |
随机化有两种方式。
- 随机函数系统
- 随机变量
1. 随机函数系统#
这个系统函数用于产生伪随机数。这些函数通常在 “initial begin” 块内部使用。通常使用两个系统函数,它们是 -
$urandom() | $random() | $urandom_range() |
---|---|---|
返回32位无符号随机数,但该数字在整个仿真过程中保持不变,除非我们改变种子数字。对于特定的种子数字,随机数再次固定,并且在整个仿真过程中不会改变。语法- $urandom() | 返回32位有符号随机数,与 $urandom() 相同,在仿真过程中不会改变值,除非种子数字发生变化。语法- $random() | 对于给定的指定范围返回无符号值,并且在仿真时间内不会改变值。 语法 - $urandom(max,min); |
示例: -
下面的示例显示了随机函数的代码。
a = $random();
b = $urandom();
c= $urandom_range(4,2); //GIVING RANGE (MAX,MIN)
d = $random(23); // assign some seed value
e = $urandom(4); // assign seed value
$display ("a=$random() // Return 32 bit signed random variable");
$display("Random Value of a = %0d",a);
$display("b = $urandom() // Return 32 bit unsigned random value .");
$display("Random Value of b = %0d",b);
$display ("c = $random_range(4,2) // Return the unsigned random number") ;
$display(" by giving the range to the variable");
$display("Random value of c = %0d",c);
$display(" $random(seed); // assign some seed value, it will display 32 bit ");
$display (" signed random value for the given seed value ");
$display ("d = $random(23); // Seed value =23");
$display ("Random value of d = %0d",d );
$display ("$urandom(seed); // assign the seed value , it will display 32 bit ");
$display (" unsigned random value for the given seed value ");
$display ("e = $urandom(4); // Seed value = 4;");
$display ("Random value of e = %0d", e);
end
输出快照
下面的输出,图 -1 显示了 $random(),$urandom() 的值,并显示了种子值的影响。
注意:
seed
是在随机化时给定的值,表示对于特定的种子,随机变量的值不应更改,并且使用-sv_seed<value>
在 CMD 行中给出。- 无论编译和运行文件多少次,都会再次给出相同的随机值。
- 如果希望在不同的时间获得不同的值,则更改种子。
2. 随机变量#
通常,随机变量是值未知的变量,或者是将值分配给每个实验结果的函数。 在随机化时获得随机值的类变量称为随机变量。随机变量的值在范围内均匀分布。
随机变量的目的
当进行直接测试时,我们需要一些时间来考虑测试的可能条件或场景,并且有可能我们会漏掉一些测试用例。为了解决这个问题,引入了随机变量的概念。在随机变量中,将生成分配范围内的随机值。
随机函数的缺点是,它们不能在整个仿真时间内更改值。
要使用随机变量,需要使用 rand 和 randc 类型修饰符关键字声明类变量。
rand#
rand 的性质是非循环的。它会随机给出任何值,并且可以在完成循环之前重复该值。 语法
rand data_type variable_name;
示例:
下面的示例是随机变量的例子。
class rand_function;
rand logic [2:0] a ;
endclass
rand_function raf;
module rand_var;
initial begin
// rand_function ra_f;
raf = new();
$display ("rand - Randomizing the value of the variable in the non-cycling form ");
for (int i =0;i <= 10;i++)begin
void'(raf.randomize ());
$display("Iteration = %0d Random value of a = %0d",i, raf.a);
end
end
在上述代码中,类被声明为 rand_function,在其中声明了变量 ‘a’。随机变量 rand 用于随机化变量 ‘a’ 的值。在模块内部,使用 randomize() 函数对类进行随机化。for 循环用于11次获取变量的随机值。
输出快照
这里,输出显示了使用 rand 进行变量的随机化。
randc#
randc 是随机循环的,在重复任何特定值之前会循环遍历其范围内的所有值。 randc 的性质是循环的。它会给出一个随机值,并在完成循环后重复它。
语法
randc data_type variable_name;
示例
下面的示例是为 randc 变量。
class pack;
randc bit [2:0]a;
endclass
module randc_var;
pack pk=new();
initial begin
$display (" randc - It is cyclic in nature . It will repeat ");
$display (" it's value after completing one cycle .");
for (int i =0; i<=12;i++)begin
void'(pk.randomize ());
$display("Iteration = %0d Random Value = %0d ", i ,pk.a);
end
end
以上代码中,声明的类是包在其中声明的类变量是 a。使用 randc,变量 a 被随机化。在模块内,使用 randomize() 来随机化类,for 循环从 0 到 12 进行迭代。
输出快照
下面的输出显示了使用 randc 进行变量的随机化。
3.std::randomize#
std::randomize()
也被称为 Scope-Randomize 函数。作用域随机化使您能够随机化不是类成员的变量,甚至可以使用下面所示的任一方法同时随机化多个变量。
作用域随机化通常用于没有使用 rand 函数声明的局部变量,用户还可以为其指定随机约束。甚至可以使用 “with” 子句接受内联约束。以下是声明 std::randomize 函数的一些语法。
语法:
std::randomize (variable);
std::randomize (variable) with { constraint's; };
std::randomize (variable-1, variable-2 ... variable-n);
std::randomize (variable-1, variable-2 ... variable-n) with { constraint's; };
示例:
module std_random;
bit [3:0]a;
bit [3:0]b;
bit [3:0]c;
bit [3:0]d;
bit [3:0]e;
initial begin
repeat(4)begin
void'( std ::randomize(a));
$display("value of a %d",a);
void'(std::randomize(b) with {b>6; a<12;});
$display("value of b :%d",b);
void'(std::randomize(c,d) with {c<4; d<3;});
$display("value of c :%d and d %d ",c,d);
void'(std::randomize(e) with {e inside {[1:4]};});
$display("value of e :%d ",e);
end
end
endmodule
在上面的例子中,我们声明了5个局部变量。这些变量使用 std::randomize()
进行了随机化,甚至可以通过将多个变量作为参数传递给 randomize()
函数,一次性随机化所有变量。我们还可以为随机化变量指定自己的内联约束条件。
这是随机化局部变量的一个优势,甚至在模块中,我们也可以像 class_handle.randomize();
一样,同时随机化多个变量。
输出:
在类中使用 std::randomize:#
如果我们使用 class_handle.randomize
来随机化变量,只有那些类型为 rand
和 randc
的变量会被随机化。为了克服这一点,我们可以使用 std::randomize(this)
,它将随机化该类的所有变量。但是,属于任何函数的局部变量不会被随机化,这是 std::randomize()
函数的主要缺点。对于这些局部变量,我们需要再次使用 std
函数进行随机化。
示例:
class test;
int a;
rand bit [2:0]b;
constraint c1 { a >= 2;a <= 9; }
function void display();
int c;
if (std::randomize(this));
$display("using std_randomize %0d , %d",a,b);
if (randomize(a))
$display("randomize(a) %0d",a);
if (std::randomize(a));
$display("using std_randomize(a) %0d",a);
if(std::randomize(b))
$display("using std_randomize b: %0d",b);
if (std::randomize(c) with {c>1;
c<4;})
$display("using std_randomize with constraint,c: %0d",c);
if (this.randomize());
$display("this randomize %0d %d",a,b);
endfunction
endclass
module tb;
test t;
initial begin
t=new();
repeat (3)
t.display();
end
endmodule
在这个例子中,我们在类 test
中声明了整型变量 a
和 rand
位类型变量 b
,并在显示函数中声明了局部变量 c
。所以,当我们使用 std::randomize(this)
时,只有 a
和 b
变量被随机化,而局部变量 c
没有被随机化。我们甚至可以使用 “with” 子句随机化局部变量并给出内联约束。
输出:
从上面的输出中,我们可以看到 std:randomize()
的确切操作,这个函数严格适用于局部变量。尽管在类中为变量 a
定义了约束,std::randomize(a)
并不考虑那个全局约束,因为它完全依赖于其内联约束。由于 std::randomize(a)
没有内联约束,仿真器考虑了 a
的整个范围。
而对于变量 c
,我们声明了一个范围在 1 到 4 之间的内联约束。因此,c
的输出在这个范围内。
局限性:
std::randomize(variable)
只考虑其内联约束,否则将考虑其声明的默认范围进行随机化。std::randomize(this)
仅适用于随机化类变量。但这个函数不会考虑在任何函数中声明的局部变量,因此在随机化过程中有更大的可能遗漏某些数据。std::randomize(variable)
仅适用于局部声明的变量。它不能在其他函数或类中访问。
4. 约束块#
约束块是类似于函数和任务的类方法。约束在类中有一个唯一的名称。
语法
constraint [constraint_name] {
expression 1;
expression 2;
... expression N;
}
约束块使用花括号括起来,而不是使用 begin
和 end
块。
约束冲突 约束块中的冲突在以下情况下出现:
- 我们声明了多个具有相同名称的约束。
- 给定约束的范围不匹配。
我们可以在类内和类外声明约束。要在类外声明约束,请使用 “extern” 关键字。
在类块外声明约束
如果在声明约束时不使用 extern
关键字,那么在编译时会显示警告。
- 在类外声明约束- 语法
class class_name;
extern constraint constraint_name;
endclass
constraint class_name::constraint_name {
condition;
}
示例
下面的示例将展示如何使用 extern
关键字声明约束。在这里,声明了两个约束:cons_name1
和 cons_name2
。cons_name1
在类的内部声明,而 cons_name2
使用 extern
关键字在类的外部声明。
class class_a;
rand byte a;
rand byte x;
constraint const_name1{a<6;
a>2;}
extern constraint cons_name2;
endclass
constraint class_a:: cons_name2{x>7;}
module mod;
class_a pack;
initial begin
pack = new;
for (int i =0;i<=5;i++)begin
void'(pack.randomize());
$display ( "Iteration = %0d Value of a = %0d Value of x = %0d " , i,pack.a,pack.x);
end
end
输出截图
输出图显示了使用外部约束对变量进行随机化的结果。
约束重写#
约束重写
在这种约束重写中,如果在父类和子类中有相同的约束名称,那么我们可以说我们的约束在子类中使用子类句柄进行了重写。
示例:
Let us understand through an example:
class parent;
rand byte a;
rand byte b;
constraint cons{a==0;b==5;}
endclass:parent
class child extends parent;
constraint cons{a==5;b==8;}
endclass:child
module top;
initial begin
child t1;
t1= new;
for(int i=0;i<3;i++)
begin
if (!t1.randomize()) begin
$display("Randomization failed");
end
else begin
$display("Iteration = %0d value of the a=%0d value of b=%0d",i,t1.a,t1.b);
end
end
end
endmodule:top
如果在父类和子类中使用了不同的约束名称,那么随机化会失败。因为子类是从父类继承而来的,所以我们不能在类中使用不同的约束名称。
如果我们想要重写约束,我们必须在父类和子类中使用相同的约束名称。然后我们可以轻松地从子类中覆盖父类的内容。
在上面的例子中,我们声明了一个名为父类的类,然后我们声明了一个扩展自父类的类。在父类内部,我们写了约束,即 a=0 和 b=5。然后在子类中,我们给变量 a 和 b 赋值为 a=5 和 b=8,两者都使用了相同的约束名称。然后我们创建了子类的句柄,并进行了随机化,此时父类的约束值被覆盖了。子类约束值的输出为 a=5 和 b=8。
输出
在上述例子中,在3次迭代中,只有子类约束的值被执行,即 a=5 和 b=8。
5. 数组随机化#
随机化也可以在数组数据类型中进行,比如静态数组、动态数组和队列。变量必须使用 rand
或 randc
类型声明,以启用变量的随机化。
静态数组随机化#
在静态数组中,只能对数组元素进行随机化。由于大小是固定的,无法更改它。
将数组声明为关键字 rand
或 randc
;在随机化时,数组的元素将获得随机值。
示例 -1 下面的示例展示了在不使用约束的情况下对一维静态数组进行随机化。
class static_array;
randc byte a[5];
endclass
module stat_array;
static_array stat_arr;
initial begin
stat_arr = new();
$display ("静态数组 - 大小已经声明。因此,我们只能对其元素进行随机化。");
$display ("在随机化数组 'a' 的元素之前");
$display ("通过数组数据类型的默认值进行赋值。");
$display ("%0p", stat_arr.a);
void '(stat_arr.randomize ());
$display ("在随机化数组 'a' 的元素之后");
$display ("输出 = %0p ", stat_arr.a);
end
endmodule
以上代码声明了一个 byte 数据类型的数组 ‘a[5]’。随机化是通过使用随机化函数完成的。在不使用 void 的情况下,编译后,编译器会显示警告。在对类进行随机化后,将显示随机化的数组。
输出截图
下图显示了在不使用约束的情况下对一个数组进行随机化的输出。
示例: -2-
以下示例展示了在不使用约束的情况下对二维静态数组进行随机化。
class class_1;
rand bit [3:0]a[2][4];
endclass
module mod;
class_1 pack;
initial begin
pack = new;
$display ("The value elements of array before randomization = %0p",pack.a);
for (int i =0;i<=5;i++)begin
void'(pack.randomize());
$display ("The value of elements of array after randomization = %0p",pack.a);
end
end
以上代码中,声明的数组是 a[2][4]
,每个元素大小为 4 位。在模块内部,使用 for 循环来进行更多次迭代。
输出截图
下图显示了一个二维数组的随机化输出。
示例 -3
以下示例展示了使用约束对多维静态数组进行随机化。在约束内部,使用 foreach 循环为变量提供条件。在这里,数组随机化进行了6次。正如我们所看到的,所有元素的值都小于12。
class class_1;
rand bit [4:0]a[2:0][3:0];
constraint cons_name1{foreach (a[i,j]) // standard way to represent multidimensional array using
a[i][j]<12;} //foreach conditional statement
endclass
module mod;
class_1 pack;
initial begin
$display ("Randomization of multidimensional array");
$display ("----------------------------------------");
pack=new();
$display ("Before randomization");
$display (" Array = %0p",pack.a); // gives default value of data types .
$display ("After randomization");
void'(pack.randomize());
for (int i =0;i<=5;i++)begin
void'(pack.randomize());
$display (" Iteration = %0d, Array = %0p",i,pack.a);
end
end
输出截图
以下图显示了使用约束对多维数组进行随机化。
动态数组#
动态数组在数组声明期间没有预定义的大小。通常,使用 array_name.new()
关键字来为动态数组分配大小。
约束可以通过两种方式使用 -
- 使用约束块中的关键字
size
限制动态数组的大小。 - 使用约束块中的 “foreach” 条件语句为动态数组的元素赋值。
以下示例将展示如何随机化动态数组。
如果未约束大小,则随机化后的输出将是一个空数组。
示例:
class class_1;
randc bit [7:0] dyn_arr[];
// 声明一个动态数组,每个元素为 8 位。
constraint dyn_arr_size { dyn_arr.size() > 3; dyn_arr.size() < 7; }
// 声明 dyn_arr 的大小在 3 到 7 之间
constraint dyn_arr_ele { foreach (dyn_arr[i]) // 每个元素的值是索引号的平方。
dyn_arr[i] == i*i; }
endclass
module mod;
class_1 pack;
initial begin
pack = new();
for (int i = 0; i <= 2; i++) begin
void'(pack.randomize());
$display ("迭代次数 = %0d 数组 = %0p", i, pack.dyn_arr[i]);
end
end
endmodule
以上代码首先声明了一个动态数组 dyn_arr[]
。在类内部声明一个用于给出大小范围的约束。在模块内部,使用 randomize()
函数来随机化类。使用约束将数组的大小限制在 3 到 7 之间。在随机化后,将显示输出 (数组大小 + 1)
次。
输出截图
下面的输出是动态数组的随机化结果。
队列#
队列的大小将根据大小约束进行随机化,并且队列元素将获得随机值。
以下示例将展示如何随机化队列的元素。
示例:
以下示例展示了对队列元素进行随机化,并使用约束声明队列的大小。
class class_1;
rand bit [3:0] que[$];
constraint que_const { que.size() == 5; }
endclass
module mod;
class_1 pack;
initial begin
pack = new;
for (int i = 0; i <= pack.que.size(); i++) begin
void'(pack.randomize());
$display ("迭代次数 = %0d 数组的值 = %0p", i, pack.que);
end
end
endmodule
6.约束内部#
inside 关键字用于检查给定值是否在范围内。
inside 块中包含的值可以是变量、常量或范围。inside 结构包括上限和下限,并收集所有值,并以相等的概率选择值。
在 inside 块中,我们使用 inside 关键字,后跟大括号 {}
。
语法
constraint const_name { variable inside { values or range }; }
- 示例:
让我们以 inside 约束的示例来更好地理解。
// 类声明
class PQR;
// 使用 rand 关键字声明随机变量
rand bit [3:0] var1;
// 约束块
constraint C1 { (var1 inside {[3:9]}); }
endclass
module top;
initial begin
int i;
// 在此,我们需要创建一个句柄
// 句柄名为 pkt
PQR pqr;
// 为句柄分配内存
pqr = new();
$display("------- inside 约束的输出 -------");
$display("-----------------------------------");
for (int i = 1; i < 7; i++) begin
// 使用 .randomize() 函数随机化类对象的属性
void'(pqr.randomize());
$display("[%0t] @ 迭代次数:%0d -----> var1=%0d", $time, i, pqr.var1);
end
$display("-----------------------------------");
end
endmodule
在上面的例子中,我们声明了一个类,类名为 PQR,在其中声明了变量 var1。在约束块中,我们使用 inside 关键字声明了一个范围,范围位于 3 和 9 之间。 在这里,随机值将在 3 和 9 之间打印。
输出截图:
下图显示了 inside 约束的输出。
在上面的示例中,我们声明了一个类,其中一个随机变量被声明为 var1。 在这里,我们使用 inside 关键字声明了一个范围,范围位于 3 和 9 之间。随机值将在 3 和 9 之间打印。
7.反转 inside 约束#
反转 inside 是 inside 操作符的相反操作。只需在 inside 关键字前加上一个取反符号 ! 即可实现。
如果我们想生成一个不应该在一系列值范围内的值,那么我们可以使用带有否定的 inside 操作符。
语法
constraint const_name { !(variable inside { values or range }); }
示例:
让我们以反转 inside 约束的示例来更好地理解。
// 类声明
class PQR;
// 使用 rand 关键字声明随机变量
rand bit [3:0] var2;
// 约束块
// 在 inside 关键字前使用否定符号
constraint C1 { !(var2 inside {[3:9]}); }
endclass
module top;
initial begin
int i;
// 在此,我们需要创建一个句柄
// 句柄名为 pkt
PQR pqr;
// 为句柄分配内存
pqr = new();
$display("----- 反转 inside 约束的输出 -----");
$display("----------------------------------");
for (int i = 1; i < 7; i++) begin
// 使用 .randomize() 函数随机化类对象的属性
void'(pqr.randomize());
$display("[%0t] @ 迭代次数:%0d -----> var2=%0d", $time, i, pqr.var2);
end
$display("----------------------------------");
end
endmodule
在上面的示例中,我们声明了一个类,类名为 PQR,在其中声明了变量 var2。在约束块中,我们使用带有否定的 inside 关键字声明了一个范围,范围位于 3 和 9 之间。如果我们想生成一个不应该在一系列值范围内的值,那么我们可以使用带有否定的 inside 操作符。在这里,由于使用了带有否定的 inside 关键字,将打印不在范围内的随机值。
输出截图:
下图显示了反转 inside 约束的输出。
在上面的示例中,我们声明了一个类,在其中声明了一个随机变量 var2。在这里,由于使用了带有否定的 inside 关键字,将打印不在范围内的随机值。
8.加权分布#
dist 操作符允许您创建加权分布。dist 是一个操作符,它接受一组值和权重,用 := 或 :/ 操作符分隔。 权重较大的值,在随机化中出现的频率更高。dist 操作符在需要加权分布的随机化时非常有用。
有两种类型的分布运算符
1. := 操作符#
:= 操作符将指定的权重分配给项目,或者如果项目是范围,则将指定的值分配给范围中的每个值。
语法
value := weightage
- 示例:
让我们以 := 操作符的示例来更好地理解。
class myWorld;
// 使用 rand 关键字声明随机变量
rand bit [3:0] value1;
// 约束块
// 在这里,1 的权重为 30,6 的权重为 70,7 的权重为 20
// 而 2 到 5 的权重为 40
constraint m_var { value1 dist { 1:=30, [2:5]:=40, 6:=70, 7:=20 }; }
endclass
module top;
initial begin
int i;
// 在此,我们需要创建一个句柄
// 句柄名为 world
myWorld world;
// 为句柄分配内存
world = new();
$display("----- := 操作符的输出 -----");
$display("6 的出现次数更多,因为 6 的权重更大");
for(int i = 0; i < 10; i++) begin
void'( world.randomize());
$display("[%0t] @ 迭代次数 %0d -----> value1=%0d", $time, i, world.value1);
end
end
endmodule
在这个例子中,1 的权重为 30,6 的权重为 70,7 的权重为 20,2 到 5 的权重为 40,总共为 280。 因此选择 1 的概率为 30/280,选择 6 的概率为 70/280,选择 7 的概率为 20/280,而选择 2 到 5 之间的值的概率为 40/280。在这个例子中,6 出现的次数更多,因为它的权重更高,被选择的概率更大。
- 输出截图:
下图显示了使用 := 操作符的加权分布的输出。
在上面的示例中,我们使用了 := 操作符。在这里,6 的权重比其他值更大。因此,6 出现的次数更多,因为 6 的权重更大。
2. :/ 操作符#
:/ 操作符将指定的权重分配给项目,或者如果项目是范围,则将指定的权重分配给整个范围。如果范围中有 n 个值,则每个值的权重为 range_weight / n。
语法
value :/ weightage
示例:
让我们以 :/ 操作符的示例来更好地理解。
// 类声明
class myWorld;
// 使用 rand 关键字声明随机变量
rand bit [3:0] value1;
// 约束块
// 在这里,1 的权重为 30,6 的权重为 40,7 的权重为 20
// 而 2 到 5 共享总权重为 60,因此每个值的权重为 60/4
constraint m_var { value1 dist { 1:/30, [2:5]:/60, 6:/40, 7:/20 }; }
endclass
// 模块名为 top
module top;
initial begin
int i;
// 在此,我们需要创建一个句柄
// 句柄名为 world
myWorld world;
// 为句柄分配内存
world = new();
$display("----- :/ 操作符的输出 -----");
$display("6 的出现次数更多,因为 6 的权重更大");
$display("--------------------------------");
for(int i = 0; i < 10; i++) begin
void'( world.randomize());
$display("[%0t] @ 迭代次数 %0d -----> value1=%0d", $time, i, world.value1);
end
end
endmodule
在这个例子中,1 的权重为 30,6 的权重为 40,7 的权重为 20,而 2 到 5 共享总权重为 60,因此每个值的权重为 60/4。
因此选择 1 的概率为 30/150,选择 6 的概率为 40/150,选择 7 的概率为 20/150,而选择 2 到 5 之间的值的概率为 60/150。
在这个例子中,6 出现的次数更多,因为它的权重更高。
- 输出截图:
下图显示了使用 :/ 操作符的加权分布的输出。
在上面的示例中,我们使用了 :/ 操作符,其中 6 的权重最高。在这里,‘6’ 的出现次数更多,因为 ‘6’ 的权重比其他值更高。
9.双向约束#
约束是双向解决的,这意味着所有随机变量的约束将并行解决。
约束会并行解决所有随机变量,并确保没有约束失败。
示例:
让我们以双向约束的示例来更好地理解。
// 类声明
class items;
// 使用 rand 关键字声明随机变量
rand bit [3:0] value1;
// 约束块
constraint addr_mode1 { value1 > 5; value1 < 12; }
constraint addr_mode2 { value1 > 6; }
endclass
// 模块名为 constraint_top
module constraint_top;
initial begin
int i;
// 在此,我们需要创建一个句柄
// 句柄名为 item
items item;
// 为句柄分配内存
item = new();
$display("----- 双向约束的输出 -----");
$display("----- 约束 1 & 2 限制值为 7、8、9、10 和 11 -----");
$display("----------------------------------------------");
for (int i = 1; i < 10; i++) begin
void'(item.randomize());
$display("[%0t] @ 迭代次数 %0d -----> value1 = %0d", $time, i, item.value1);
end
$display("----------------------------------------------");
end
endmodule
在上面的示例中,我们声明了一个类,类名为 item,其中声明了变量 value1。在约束块中,我们对两个约束都给出了一些条件。所有随机变量的约束将并行解决。
在这里,约束 1 和 2 限制值为 7、8、9、10 和 11。
输出截图:
下图显示了双向约束的输出。
在上面的示例中,我们声明了一个类,在其中声明了一个随机变量 value1。
在这里,约束 1 和 2 限制值为 7、8、9、10 和 11。
10.蕴涵约束#
蕴涵运算符用于声明两个变量之间的条件关系。蕴涵运算符位于表达式和约束之间。
蕴涵运算符用符号 -> 表示
蕴涵运算符#
蕴涵运算符 -> 用于约束表达式中显示两个变量之间的条件关系。蕴涵运算符位于表达式和约束之间。
如果蕴涵运算符 -> 左侧的表达式为真,则将满足右侧的约束表达式。如果左侧不为真,则不考虑右侧表达式。
语法:
constraint const_name { (variable1) -> (variable2) }
- 示例:
让我们以蕴涵运算符的示例来更好地理解。
// 类声明
class ABC;
// 使用 rand 关键字声明随机变量
rand bit [2:0] value1;
rand bit [3:0] value2;
// 约束块
// 蕴涵运算符用于指定两个变量 value1 和 value2 之间的条件
constraint c_mode { (value1 inside {[1:5]}) -> (value2 < 8); }
endclass
// 模块名为 top
module top;
initial begin
int i;
// 在此,我们需要声明一个句柄
// 句柄名为 abc
ABC abc;
// 为句柄分配内存
abc = new();
$display("----- 蕴涵约束的输出 -----");
$display("---------------------------------");
for(int i = 0; i < 10; i++) begin
void'(abc.randomize());
$display("[%0t] @ 迭代次数 %0d -----> value1=%0d , value2=%0d", $time, i, abc.value1, abc.value2);
end
$display("---------------------------------");
end
endmodule
在上面的示例中,我们声明了一个类,类名为 ABC,在其中声明了两个变量 value1 和 value2。在约束块中,使用 inside 关键字为 value1 声明了范围。
在这里,如果 value1 处于 1 和 5 的范围内,则 value2 总是小于 8。
如果 value1 不处于 1 和 5 的范围内,则 value2 总是大于 8。
- 输出截图:
下图显示了蕴涵约束的输出。
在上面的示例中,使用了蕴涵运算符,其中定义了两个随机变量 value1 和 value2。
在这里,如果 value1 处于 1 和 5 的范围内,则 value2 总是小于 8。
如果 value1 不处于 1 和 5 的范围内,则 value2 总是大于 8。
11.if-else Constraint#
if-else 块允许约束的条件执行。如果表达式为真,则必须满足第一个约束块中的所有约束;否则,必须满足可选的 else 约束块中的所有约束。
示例:
让我们通过一个 if-else 约束的例子来更好地理解这一点。
// class declaration
class basic;
//value1 & value2 are the variables
//random variables are created by rand keyword
rand bit [3:0] value1;
rand bit [2:0] value2;
constraint c_var {
if(value1 inside {[4'h3:4'h9]})
value2 == 1;
else {
value2 == 0;}
}
endclass
//module name is top
module top;
int i=1;
initial begin
//here, we need to create a handle
//handle name is pkt
basic pkt;
// memory allocation to the handle
pkt = new();
$display("-----Output for if else constraint-----");
$display("If the value1 lies between the 3 to 9, ");
$display(" then value2 will be 1 otherwise 0 ");
$display("-----------------------------------------------");
for (int i=0;i<5;i++)begin
void'( pkt.randomize());
$display("[%0t] @ iteration %0d -----> value1=%0d, value2 = %0d",$time,i,pkt.value1,pkt.value2);
end
$display("-----------------------------------------------");
end
endmodule
在上面的例子中,我们声明了一个名为 basic
的类,其中声明了两个变量 value1
和 value2
。在约束块中,我们使用了 if-else 条件。
在 if 语句中,使用 inside
关键字为 value1
声明了一个范围,如果该范围在 3 到 9 之间,则 value2
将始终为 1;如果该范围不在 3 到 9 之间,则 value2
将始终为 0。
输出截图:
下图显示了 if-else 约束的输出结果。
在上面的例子中,我们声明了一个类,其中声明了两个随机变量 value1
和 value2
。
如果范围在 3 到 9 之间,则 value2
将始终为 1;如果范围不在 3 到 9 之间,则 value2
将始终为 0。
12.foreach constraint#
foreach 构造遍历数组的元素,并将每个元素的所有值分配给其索引。
foreach 循环遍历数组的元素,因此带有 foreach 循环的约束称为迭代约束。
语法:
constraint constraint_name { foreach ( variable[iterator] ) variable[iterator] <..conditions..> }
示例:
让我们通过一个 foreach 约束的例子来更好地理解这一点。
//class declaration
class value;
//random variable declared using rand keyword
rand bit [3:0] s_array1[4];
rand bit [3:0] s_array2[5];
//constraint block
//standard way to represent fixed array using
//foreach conditional statement
constraint cons {foreach(s_array1[i])
s_array1[i]==i;
foreach (s_array2[i])
s_array2[i] == i;}
endclass
// module name is top
module top;
initial begin
//here,we need to declare handle
//handle name is val
value val;
//memory allocation to the handle
val = new();
$display("-------------------------------");
void'(val.randomize());
$display("\t s_array1=%0p" , val.s_array1);
$display("\t s_array2=%0p" , val.s_array2);
$display("-------------------------------");
end
endmdule
在上面的例子中,我们声明了一个类,类名为 value
,其中定义了两个固定数组。
在类内部声明了两个数组 s_array1
和 s_array2
。这个约束将遍历数组中的每个元素,并将每个元素设置为其特定索引的值。
输出截图:
下图显示了 foreach 约束的输出结果。
13.Solve before Constraint#
这个约束属性在约束块中用于指定约束求解。如果变量是依赖的,因为约束是双向的,一个变量的值将影响另一个变量。这个 solve-before 可以通过强制约束求解器选择解决约束的顺序来改变概率分布。
语法:
constraint constraint_name {variable_1==1 -> variable_2==1;solve variable_1 before variable_2}
让我们通过一个示例来更好地理解 solve-before 约束。
示例:
class without_solve_before;
rand bit value1;
rand bit [3:0] value2;
constraint val {value1==1 -> value2==1;}
endclass
class with_solve_before;
rand bit value1;
rand bit [3:0] value2;
constraint valu {value1==1 -> value2==1;
solve value1 before value2;}
endclass
module solve_before();
without_solve_before gen1 = new();
with_solve_before gen2 = new();
initial
begin
$write("%c[1;31m \t------Without solve before----------\n",27);
for(int i=1;i<=10;i++)
begin
void'(gen1.randomize());
#1 $display("\t[%0t] @ iteration: %0d -----> value1: %0d \t value2: %0d",$time,i,gen1.value1,gen1.value2);
end
$write("\n%c[1;34m\t -----with solve before--------\n",27);
for(int i=1;i<=10;i++)
begin
void'(gen2.randomize());
#1 $display("\t[%0t] @ iteration: %0d -----> value1: %0d \t value2: %0d",$time,i,gen2.value1,gen2.value2);
end
$write("%c[0m",27);
end
endmodule : solve_before
在上面的例子中,我们创建了两个类,分别命名为 without_solve_before
和 with_solve_before
,它们分别拥有对象 gen1
和 gen2
。它们都具有相同的约束,即如果 value1
为 1,则 value2
应该为 1。由于约束求解器是双向的,选择 value2
的概率也会影响选择 value1
的概率。
单独选择的概率为:
value1
为 1 的概率是 1/2。value2
为 1 的概率是 1/16。
输出:
在上述输出中,前 32 次迭代是在没有使用 solve-before 的情况下进行的,接下来的 10 次迭代是在使用 solve-before 的情况下进行的。
没有 solve-before:同时考虑了 value1 和 value2 的约束,即 value1 取 1 的概率是 1/2,value2 取 1 的概率是 1/16,但要同时满足 value1 为 1 和 value2 为 1 的概率是 (1/2) * (1/16) = 1/32。
有 solve-before:在这种情况下,约束求解器首先随机确定 value1,然后根据 value1 来解决 value2。因此,获得 value1 为 1 的概率是 1/2。
因此,在前 32 次迭代中,只有 1 次迭代的 value1 为 1,即约为 0.03%,但使用 solve-before,则每隔一次迭代 value1 就会为 1,概率达到了 50%。
限制:
不能使用
randc
变量,因为它们总是被允许首先解决。如果使用randc
变量,则在没有先解决的情况下不会被考虑。不应有循环依赖,即不能存在解决
value1
之前解决value2
,又要在解决value2
之前解决value1
的情况。如果存在这种情况,则会抛出如下错误。
14. 静态约束#
静态约束在该类的所有对象中共享。
可以通过该类的任何对象句柄启用或禁用静态约束模式,这将在所有对象中反映出来。
语法:
static constraint constraint_name {constraint1;
constraint2;
........
constraintN;}
示例:
class class1;
rand bit [2:0] value;
constraint cons {value==1;}
endclass
class class2;
rand bit [2:0] value;
static constraint cons {value==1;}
endclass
initial
begin
object_1.cons.constraint_mode(0);
for(int i=1;i<=3;i++)
begin
void'(object_1.randomize());
void'(object_2.randomize());
#1 $display("\t[%0t] @ iteration: %0d -----> value in object_1: %0d value in object_2: %0d",$time,i,object_1.value,object_2.value);
end
object_3.cons.constraint_mode(0);
for(int i=1;i<=3;i++)
begin
void'(object_3.randomize());
void'(object_4.randomize());
#1 $display("\t[%0t] @ iteration: %0d -----> value in object_3: %0d value in object_4: %0d",$time,i,object_3.value,object_4.value);
end
end
在上述示例中,有两个类,分别命名为 class1 和 class2,class1 有对象 1 和 2,class2 有对象 3 和 4。这里两个类的约束是相同的,因此如果约束开启,则值应该始终为 1。
如果约束关闭,则值将不为 1。
从上面的 GIF 可以看出,如果通过单个对象句柄关闭/开启非静态约束,则该约束仅适用于该特定对象;而对于静态约束,如果关闭/开启约束,则该约束适用于该类的所有对象。
输出:
类1有两个对象,分别是1和2,类2有对象3和4。在类1中使用了非静态约束,虽然在对象1中关闭了约束,但在对象2中约束依然开启。然而,在类2中使用了静态约束,当在对象3中关闭约束时,对象4中的约束也被关闭了。
15. 随机化方法#
通常,会有三种随机化方法:
- randomize();
- pre_randomize();
- post_randomize();
GIF 2: 随机化方法
当调用随机化函数时,会根据约束为声明为 rand
/randc
的变量生成随机值。这是一个不可重写的虚拟函数,在随机化成功完成时返回1,否则返回0。
实际上,在执行实际随机化之前,模拟器首先进入 pre_randomize
,然后在随机化成功后,模拟器进入 post_randomize
函数,该函数也是可重写的。如果由于约束冲突等问题导致随机化失败,则模拟器不会进入 post_randomize
,并返回0。
randomize:#
randomize
是一个返回 int 类型的虚拟函数,成功完成时返回1,否则返回0。
示例:
class generator;
rand bit [2:0] value;
constraint cons {value==5;}
endclass
module randomization();
generator gen = new();
initial
begin
$display("\tCalling Randomize....");
if(gen.randomize())
$display("\tvalue: %0d \tRandomization successful",gen.value);
else
$display("\tvalue: %0d \tRandomization Failed",gen.value);
$display("\tCalling Randomize....");
if(gen.randomize()with{value==2;})
$display("\tvalue: %0d \tRandomization successful",gen.value);
else
$display("\tvalue: %0d \tRandomization Failed",gen.value);
end
endmodule
输出:
在上述示例中,调用 randomize
函数并在随机化完成后,约束要求值必须为 5。如果随机化成功,则打印“随机化成功”;否则打印“随机化失败”,并保留变量中的先前随机值。
在第一次随机化中,没有冲突,因此随机化成功完成并返回 1,所以满足条件,打印“随机化成功”。在下一次调用中,由于约束之间的冲突,随机化未能完成,因此返回 0,打印“随机化失败”,变量保留之前的随机值,即 5。
注意: 如果第一次迭代中的随机化失败,则变量的值将是变量的默认值。如果在一些成功的随机化之后随机化失败,则变量中将保留先前的随机值。
pre_randomize:#
这是一个可重写的 void 类型函数,在实际随机化执行之前调用。
语法:
function void pre_randomize();
示例:
class generator;
rand bit [2:0] value;
constraint exp { value>5;}
function void pre_randomize();
if(i%2==0)
begin
rand_mode(0);
$display("\tstopping randomization");
end
else
rand_mode(1);
i++;
endfunction
endclass
module pre_randomization();
generator gen = new();
initial
begin
for(int i=1;i<=4;i++)
begin
$display("\t[%0t]Calling Randomize....",$time);
void'(gen.randomize());
#1 $display("\t[%0t] @ iteration: %0d -----> value: %0d ",$time,i,gen.value);
end
end
endmodule
输出:
在示例中,调用 pre_randomize
函数,如果内部条件满足,则随机化将在 pre_randomize
中停止,因此模拟器退出 randomize
函数而不执行随机化,这意味着 rand
变量将保留先前的值。在第1和第3次迭代中,随机化被停止,因此变量保留了先前的值;但在第2和第4次迭代中,随机化完成。
post_randomize:#
这也是一个可重写的 void 类型函数,在随机化执行之后运行。
语法:
function void post_randomize();
示例:
function void pre_randomize();
$display("\tI'm in pre_randomize function");
check=0;
endfunction
function void post_randomize();
$display("\tI'm in post_randomize function");
check=1;
endfunction
initial
begin
$display("\t[%0t]Calling Randomize....",$time);
check=gen.randomize();
#1 $display("\t[%0t] @ iteration: 1 -----> value: %0d ",$time,gen.value);
if(check==1)
$display("%c[1;32m\tRandomization is performed%c[0m",27,27);
else
$display("%c[1;31m\tRandomization is not performed%c[0m",27,27);
$display("\t[%0t]Calling Randomize....",$time);
check=gen.randomize()with{value<5;};
#1 $display("\t[%0t] @ iteration: 2 -----> value: %0d ",$time,gen.value);
if(check==1)
$display("%c[1;32m\tRandomization is performed%c[0m",27,27);
else
$display("%c[1;31m\tRandomization is not performed%c[0m",27,27);
end
输出:
在示例的第一次迭代中,调用 randomize
函数时没有冲突,因此在随机化之后进入了 pre_randomize
和 post_randomize
,打印了“随机化成功”。但在第二次迭代中,由于存在冲突,随机化未能完成,返回并打印“随机化失败”。
16. 内联约束#
假设某些变量需要在 100 次迭代中进行随机化,但在前 95 次迭代中用户需要使用通用约束,而在最后 5 次迭代中用户希望在前面的约束基础上添加一些额外的约束。这时,用户可以在调用随机化函数时使用 with
关键字添加这些额外的约束,而不必在不同的类中编写。
语法:
obj_hndl.randomize() with {extra_constraints};
这些内联约束作为额外的约束进行处理,同时考虑先前的约束,但不会覆盖先前的约束。例如,如果类中声明了 variable > 5
,并且添加了一个内联约束 variable < 10
,那么变量的最终值将介于 5 和 10 之间。
- 仅内联约束
- 内联约束与类约束一起使用
- 内联约束与类约束冲突
仅内联约束#
示例:
class generator;
rand bit [2:0] value;
endclass
module only_inline();
generator gen = new();
initial
begin
for(int i=1;i<=2;i++)
begin
void'(gen.randomize()with{value==3;});
#1 $display("\t[%0t] @ iteration: %0d -----> value: %0d",$time,i,gen.value);
end
end
endmodule
在上面的例子中,类中没有约束,因此值可以在 0 到 7 之间任意取值,因为它是一个 3 位的值,但是调用随机化函数时,同时加上内联约束,要求值必须是 3。
输出:
根据内联约束,值必须为 3,因此在两次迭代中,值都相同。
内联约束与类约束一起使用#
示例:
class generator;
rand bit [2:0] value;
constraint exp { value>2;}
endclass
module inline_constraint();
generator gen = new();
initial
begin
void'(gen.randomize());
#1 $display("\t[%0t] @ iteration: %0d -----> value: %0d",$time,i,gen.value);
void'(gen.randomize()with{value==7;});
#1 $display("\t[%0t] @ iteration: %0d -----> value: %0d",$time,i,gen.value);
end
endmodule
在上面的例子中,类中有一个约束,要求值必须大于 2,并且通过内联方式给出了另一个约束,要求值必须是 7。
输出:
类中的初始约束是值大于 2,因此第一次迭代中的值范围在 2 到 7 之间,但在第二次迭代中,通过内联约束给出了值必须为 7,所以值为 7。
内联约束与类约束冲突#
示例:
class generator;
rand bit [2:0] value;
constraint cons {value==5;};
endclass
module conflict_with_inline();
generator gen = new();
int check;
initial
begin
for(int i=1;i<=2;i++)
begin
check=gen.randomize()with{value==3;};
if(check)
$display("\tRandomization Done \t @ iteration: %0d -----> value: %0d",i,gen.value);
else
$display("\tRandomization Failed \t @ iteration: %0d -----> value: %0d",i,gen.value);
end
end
endmodule
在上述示例中,类中有一个约束,要求值必须为 5,并且从内联约束中提到了另一个约束,要求值必须为 3。
输出:
在这种情况下,约束求解器考虑了这两个约束,这意味着这些约束之间存在冲突,此时将不会发生随机化,并返回值 0。因此,根据检查显示消息被打印。
注意:
- 在 Questa sim 10.6 版本的模拟器中没有抛出任何错误
- 在 Mentor Graphics 2021 版本中,抛出了约束冲突错误,如下所示
17. 软约束#
使用关键字 soft 声明的约束称为软约束。
如果类约束和内联约束之间存在冲突导致随机化失败,则不可能通过使用内联约束来覆盖类约束。因此,一些测试案例需要覆盖约束,这可以通过在类约束中使用 soft 关键字来实现。
语法:
constraint constraint_name { soft variable_name > range ; }
示例:
为了更好地理解,我们将执行两段代码。
首先,让我们以一个例子来更好地理解不使用软约束的普通约束。
class pack;
rand bit [0:3]a;
constraint addr_a{a>5;}
endclass
module soft_without_conflict;
pack pkh;
initial begin
pkh = new;
$display("without using soft constraint output");
for(int i =0; i<5;i++)
begin
void'(pkh.randomize());
$display("\n \t a=%0d value =%0d",i,pkh.a);
end
pkh = new;
$display("\n \t output of without conflict");
for(int i =0; i<5;i++)
begin
void'(pkh.randomize()with {a<5;});
$display("\n \t a=%0d value =%0d",i,pkh.a);
end
end
endmodule
在上述示例中,我们使用了一个名为 pack 的类,其中包含一个名为 pkh 的对象。该类有一个名为 addr_a 的约束,在该约束中,值从 0 到 5 被显示。在模块内部,我们使用了内联约束,它将显示从 0 到 10 的值,但我们在这里使用了内联约束,因此这个约束 addr_a 将被覆盖。
输出截图
在上述示例中,开始的 5 次迭代执行了普通约束,因此显示值从 5 到 15,另外 5 次迭代执行了内联约束,因此显示值从 10 到 0。
让我们以一个例子来更好地理解软约束。
class pack;
rand bit [0:3]a;
constraint addr_a{a>5;}
endclass
module soft_with_conflict;
pack pkh;
initial begin
pkh = new;
$display("output of soft with conflict ");
for(int i =0; i<5;i++)
begin
void'(pkh.randomize()with {a<5;});
$display("\n \t a=%0d value =%0d",i,pkh.a);
end
pkh = new;
$display("\n \t using soft constraint to solve conflict issue");
for(int i =0; i<5;i++)
begin
void'(pkh.randomize()with {soft a<10;});
$display("\n \t a=%0d value =%0d",i,pkh.a);
end
end
endmodule
在上述示例中,我们使用了一个名为 pack 的类,其中包含一个名为 pkh 的对象。该类有一个名为 addr_a 的约束,在该约束中,值应该显示为从 0 到 5,但没有显示任何内容。在模块内部,我们使用了内联约束,但也没有显示任何内容,因为这里会发生冲突,所以为了解决这个冲突,我们使用了软约束。这样就显示了值从 10 到 0。
输出截图
在上述示例中,开始的 5 次迭代执行了普通约束,因此显示值为零,因为在内部代码中已经发生了冲突。另外 5 次迭代使用软约束执行代码,显示值从 10 到 0。
18. 禁用约束#
可以使用 constraint_mode 方法禁用类中的约束。
默认情况下,所有约束都将被启用,在随机化约束期间,用户将不考虑被禁用的约束。
constraint_mode(1) 表示约束块已启用
constraint_mode(0) 表示约束块已禁用
constraint_mode 的默认值为 1,即一旦约束块被禁用,就需要将 constraint_mode(1) 恢复为启用约束块。
语法:
object_handle.constraint_block_name.constraint_mode(enable);
enable == 1,启用约束块
enable == 0,禁用约束块
示例:
让我们以一个示例来更好地理解禁用的约束。
class packet;
rand bit [3:0] data;
constraint data_range { data inside {5,10,15}; }
endclass
module constraint_mode;
packet pkt = new();
initial begin
$display("If constraint is mode is 1 it will display the 5,10,15");
$display("If constraint is mode is 0 it will display the random values");
$display("\t Before Constraint disable");
$display("\t Value of constraint mode = %0d",pkt.data_range.constraint_mode());
void'( pkt.randomize());
$display("\tdata = %0d",pkt.data);
pkt.data_range.constraint_mode(0);
$display("After Constraint disable");
$display("Value of constraint mode = %0d",pkt.data_range.constraint_mode());
repeat(5) begin
void'(pkt.randomize());
$display("\tdata = %0d",pkt.data);
end
end
endmodule
在上述示例中,我们使用了一个名为 packet 的类,其中包含一个名为 pkt 的对象。该类有一个名为 data_range 的约束,在该约束中,将显示值 5、10 和 15。约束模式已启用 (1),它显示值 5、10 和 15,如果约束模式被禁用 (0),则显示任意值。
输出截图
在上述输出中,如果约束模式为 1,则显示 5、10、15;如果使用约束模式禁用了约束,即约束模式为 0,则显示任意随机值。
19. 禁用随机化#
rand_mode() 方法用于禁用使用 rand/randc 关键字声明的变量的随机化。
rand_mode(1) 表示随机化已启用
rand_mode(0) 表示随机化已禁用
rand_mode 的默认值为 1,即已启用
一旦禁用了随机化,就需要通过 rand_mode(1) 重新启用随机化。rand_mode 可以作为 SystemVerilog 方法调用,可以通过调用 variable.rand_mode() 来获取变量的随机化启用/禁用状态。rand_mode 方法如果随机化已启用则返回1,否则返回0。
语法:
object_hanlde.variable_name.rand_mode(enable);
//enable = 1, randomization enable
//enable = 0, randomization disable
示例:
让我们以一个例子来更好地理解禁用随机化。
class packet;
rand bit [2:0]data1;
randc bit [1:0]data2;
int state;
function rand_mode1(int a);
if(a==0)
begin
rand_mode(a);
state = a;
end
else if(a==1)
begin
rand_mode(a);
state = a;
end
endfunction
endclass
module randomization_mode;
packet pkt = new();
int c;
initial begin
$display("Before Randomization data1 = %0d data2= %0d",pkt.data1,pkt.data2);
if(pkt.data1.rand_mode())
if (pkt.data2.rand_mode())
pkt.state = 1;
$display("randomization of all variables are enabled");
for(int i = 0;i<5;i++)
begin
c = pkt.randomize();
$display("[%0d] After enable the randomizations data1 =%0d, data2 = %0d",i,pkt.data1,pkt.data2);
$display("[%0d] state = %0d",i,pkt.state);
if(i==3)
begin
pkt.rand_mode1(0);
$display("[%0d] state = %0d",i,pkt.state);
end
$display("[%0d] After disable in the randomization data1 =%0d, data2 = %0d",i,pkt.data1,pkt.data2);
end
end
endmodule
在上述示例中,我们使用了一个名为 packet 的类,其中包含一个名为 pkt 的对象。在类内部,我们声明了一个名为 rand_mode1 的函数,其中有一个名为 state 的变量。如果 state = 1,则随机化已启用;如果 state = 0,则随机化已禁用。
我们声明了输出值从 0 到 5 变化,在 0 到 3 的范围内,同时启用和禁用随机化都发生了随机化。在剩下的 4 和 5 变化中,只发生了随机化。
输出截图
在上述输出中,使用随机化模式为 1 来启用随机化,则会显示随机数据。使用随机化模式为 0 来禁用随机化,则显示零或先前的随机化数据。在我们的输出截图中,迭代从 0 到 3 中同时启用和禁用随机化,在第 4 次迭代中只禁用随机化。
20. randcase#
关键字 randcase 引入了一个 case 语句,随机选择其中的一个分支。
case 项表达式是表示与每个项关联的权重的正整数值。
选择一个项的概率是通过将该项的权重除以所有权重之和来推导的。
语法:
randcase
item: statement
endcase
示例
让我们以一个例子来更好地理解 randcase。
module r_case;
initial begin
$display("Random data will be generated by simulator");
$display("data from 0 to 3 then it will display randcase output");
for(int i =0; i<6;i++)
begin
randcase
0:$display("\t \n output of randcase 0");
1:$display("\t \n output of randcase 1");
2:$display("\t \n output of randcase 2");
3:$display("\t \n output of randcase 3");
endcase
end
end
endmodule
在上述示例中,我们使用了 randcase,如果其表达式匹配,则显示结果。每次调用 randcase 都会在 0 到权重总和的范围内检索一个随机数。然后,按照小随机数对应于第一个(顶部)权重语句的声明顺序选择权重。
输出截图
在上述输出中,使用 randcase 生成 0 到 3 的值,如果任何表达式匹配,则显示输出。
在上述输出截图中,所有权重的总和为 6;因此,选择第一个分支的概率为 (1/6) 即 0.166。
选择第二个分支的概率为 (2/6) 即 0.33,选择第三个分支的概率为 (3/6) 即 0.5。每次调用 randcase 语句将返回一个在 0 到 3 范围内的随机数。
21. 使用约束进行内存分区#
内存块随机化#
假设您在设计中有一个大小为 2048 即 2KB 的内存,用于存储一些数据,然后如果我们需要将该 2KB 内存的某些块用于某种目的,我们可以使用约束来对内存进行分区。
示例:
class memory_block;
bit [31:0] mem_ram_start,mem_ram_end;
rand bit [31:0] mem_start_addr,mem_end_addr;
rand int mem_block_size;
constraint mem {mem_start_addr>=mem_ram_start;
mem_start_addr<mem_ram_end;
mem_start_addr%4==0;
mem_end_addr==mem_start_addr+mem_block_size-1;}
constraint block_size {mem_block_size inside {32,64};}
function void display();
$display("\t----memory block----");
$display("\t RAM start addr : %0d",mem_ram_start);
$display("\t RAM end addr : %0d",mem_ram_end);
$display("\t BLOCK start addr : %0d",mem_start_addr);
$display("\t BLOCK end addr : %0d",mem_end_addr);
$display("\t BLOCK SIZE:%0d",mem_block_size);
endfunction
endclass
module single_memory_block();
memory_block memb =new();
initial
begin
memb.mem_ram_start=32'h0;
memb.mem_ram_end=32'h7ff;
void'(memb.randomize());
memb.display();
end
endmodule
在上述示例中,我们取起始地址为 0,结束地址为 2047,并给出约束,使得块大小应为 32 或 64(整数)。块应从 4 的倍数开始,并根据块大小结束。
输出:
上面的示例输出如上图所示,对于块大小的约束,求解器给出了 64。根据此块大小,求解器将给出一个起始地址,使得内存块位于 2KB 的范围内。这里的起始地址为 1896,是 4 的倍数,从那里到 64 的结束地址为 1959。
内存 n 等分区#
在这里,我们将尝试将给定的内存划分为 n 个等分区。
示例:
class memory_block;
bit [31:0] mem_ram_start,mem_ram_end;
rand int mem_num_parts,mem_part_size;
rand bit [31:0] mem_part_start[];
constraint parts {mem_num_parts>=2;
mem_num_parts<=8;}
constraint part_size {mem_part_size==(mem_ram_end-mem_ram_start)/mem_num_parts+1;}
constraint patition {mem_part_start.size()==mem_num_parts;
foreach(mem_part_start[i])
if(i)
mem_part_start[i]==mem_part_start[i-1]+mem_part_size;
else
mem_part_start[i]==mem_ram_start;}
function display();
$display("\t RAM start addr : %0d ",mem_ram_start);
$display("\t RAM end addr : %0d ",mem_ram_end);
$display("\t No of Partitions : %0d ",mem_num_parts);
$display("\t Size of each partition : %0d ",mem_part_size);
$display("\n\t -----------partitions----------- ");
foreach(mem_part_start[i])
begin
if(i==mem_num_parts-1)
$display("\t Partition : %0d from %0d to %0d "\
,i+1,mem_part_start[i],mem_ram_end);
else
$display("\t Partition : %0d from %0d to %0d "\
,i+1,mem_part_start[i],mem_part_start[i+1]-1);
end
endfunction
endclass
module memory_n_equal_partitions();
memory_block memb=new();
initial
begin
memb.mem_ram_start=32'h0;
memb.mem_ram_end=32'h7ff;
void'(memb.randomize());
void'(memb.display());
end
endmodule
在上述示例中,我们取了一个大小为 2KB 的内存,并尝试将其分成 n 个等分区。
输出:
所有约束都并行解决,这里假设首先取了 2 到 8 之间的一个数字,即 7,在这种情况下,然后将整个内存分成 7 部分,即 293,这几乎等于 2KB。在这里,我们尝试使用数组分配每个部分的起始地址,并且每个部分的空间为 293。
内存 n 变量分区#
在以下示例中,我们尝试将一个内存块分成 n 个分区,但不一定相等。
Example:
class memory_block;
bit [31:0] mem_ram_start,mem_ram_end;
rand int mem_num_parts,mem_part_size[];
rand bit [31:0] mem_part_start[];
constraint parts {mem_num_parts>=2;
mem_num_parts<=8;}
constraint part_sizes {mem_part_size.size()==mem_num_parts;
mem_part_size.sum()==mem_ram_end-mem_ram_start+1;
foreach(mem_part_size[i])
mem_part_size[i] inside {16,32,64,128,256,512,1024,2048,4096};
}
constraint partition {mem_part_start.size()==mem_num_parts;
foreach(mem_part_start[i])
if(i)
mem_part_start[i]==mem_part_start[i-1]+mem_part_size[i-1];
else
mem_part_start[i]==mem_ram_start;}
function display();
$display("\t RAM start addr : %0d ",mem_ram_start);
$display("\t RAM end addr : %0d ",mem_ram_end);
$display("\t No of Partitions : %0d ",mem_num_parts);
$display("\n\t -----------partitions-----------");
foreach(mem_part_start[i])
begin
if(i==mem_num_parts-1)
$display("\t Partition : %0d with size :%0d from %0d to %0d "\
,i,mem_part_size[i],mem_part_start[i],mem_ram_end);
else
$display("\t Partition : %0d with size :%0d from %0d to %0d "\
,i,mem_part_size[i],mem_part_start[i],mem_part_start[i+1]-1);
end
endfunction
endclass
module memory_n_var_partitions();
memory_block memb=new();
initial
begin
memb.mem_ram_start=32'h0;
memb.mem_ram_end=32'h3fff;
void'(memb.randomize());
void'(memb.display());
end
endmodule
在这个例子中,给定的内存大小为 16KB,然后取一个约束将其分成 n 部分,即在这个例子中为 8 部分。还给出了另一个约束,即所有 n 个变量分区的总和,并使用 foreach 约束为分区分配了特定的大小。分区的划分如下图所示。
输出:
带有间隔的内存分区#
在这个例子中,类似于前面的例子,将 n 个变量分区分成了变量,但是在分区之间也添加了空间部分。
示例:
class memory_block;
bit [31:0] mem_ram_start,mem_ram_end;
rand int mem_num_parts,mem_part_size[],mem_space[];
rand bit [31:0] mem_part_start[];
constraint parts {mem_num_parts>4;
mem_num_parts<10;}
constraint part_sizes {mem_part_size.size()==mem_num_parts;
mem_space.size()==mem_num_parts-1;
mem_part_size.sum()+mem_space.sum()==mem_ram_end-mem_ram_start+1;
foreach(mem_part_size[i])
{
mem_part_size[i] inside {256,512,1024,2048};
if(i<mem_space.size())
mem_space[i] inside {64,256,512,1024};
}
}
constraint partition {mem_part_start.size()==mem_num_parts;
foreach(mem_part_start[i])
if(i)
mem_part_start[i]==mem_part_start[i-1]+mem_part_size[i-1];
else
mem_part_start[i]==mem_ram_start;}
function display();
$display("\tRAM start addr : %0d ",mem_ram_start);
$display("\tRAM end addr : %0d ",mem_ram_end);
$display("\tNo of Partitions : %0d ",mem_num_parts);
$display("\tmem_part_size : %0p",mem_part_size);
$display("\tmem_space : %0p",mem_space);
$display("\n\t %c[1;32m-----------%c[1;34mpartitions%c[1;32m------------%c[0m",27,27,27,27);
foreach(mem_part_start[i])
begin
if(i==mem_num_parts-1)
$display("\t%c[0;34m Partition : %0d with size :%0d from %0d to %0d %c[0m\n"
,27,i,mem_part_size[i],mem_part_start[i]+mem_space[i-1],mem_ram_end,27);
else if(i==0)
$display("\t%c[0;34m Partition : %0d with size :%0d from %0d to %0d %c[1;31m
\n\t\tspace_part : %0d bytes ",27,i,mem_part_size[i],mem_part_start[i],
mem_part_start[i+1]-1,27,mem_space[i]);
else
$display("\t%c[0;34m Partition : %0d with size :%0d from %0d to %0d %c[1;31m
\n\t\tspace_part : %0d bytes",27,i,mem_part_size[i],
mem_part_start[i]+mem_space[i-1],mem_part_start[i+1]-1,27,mem_space[i]);
end
endfunction
endclass
module memory_partition_with_spaces();
memory_block memb=new();
initial
begin
memb.mem_ram_start=32'h0;
memb.mem_ram_end=32'h23ff;
void'(memb.randomize()with{mem_num_parts==5;});
void'(memb.display());
end
endmodule
在上述示例中,内存大小为 9KB,我们取了 5 个内存部分作为内联约束,并在每两个部分之间添加了空间分区,这意味着总共有 9 部分。在这里,我们要求所有部分的总和应该等于 9215。内存分区的起始地址存储在一个数组中,并使用内部约束分配不同的大小,空间部分也是如此。最终的内存块如下图所示。如果任何约束不满足,则不会进行随机化,每个值都将具有默认值,即 0。
输出:
程序和数据的分区#
在下面的示例中,将内存分成了 n 个程序和数据的分区,并在它们之间添加了间隔。
示例:
class memory_block;
int total_mem;
rand int pgm[],data[],space[];
rand int max_pgms,max_pgm_size,max_data_size;
rand int num_pgm,num_data,num_space;
constraint nums {num_pgm inside {[1:max_pgms]};
num_data inside {[1:10]};
num_space inside {[1:10]};}
constraint maxs {max_pgms==10;
max_pgm_size==512;
max_data_size==128;}
constraint arrays {pgm.size()==num_pgm;
data.size()==num_data;
space.size()==num_space;}
constraint ram {foreach(pgm[i])
{
pgm[i] dist {[128:512]:=75,[32:64]:/20};
pgm[i]%4 ==0;
}
foreach(data[i])
{
data[i] inside {64};
}
foreach(space[i])
{
space[i] inside {64,128,512};
}
total_mem == pgm.sum()+data.sum()+space.sum();
}
function void display();
$display("\tTotal_RAM : %0d ",total_mem);
$display("\tNo.of Programs : %0d ",num_pgm);
$display("\tNo.of data's : %0d ",num_data);
$display("\tNo.of space's : %0d ",num_space);
$display("\tTotal_program_size : %0d , Total_data_size :\
%0d Total_space_size : %0d \n",pgm.sum(),data.sum(),space.sum());
foreach(pgm[i])
$display("\t %c[1;32m Program_%0d is of %0d bytes %c[0m",27,i,pgm[i],27);
foreach(data[i])
$display("\t %c[1;33m data_%0d is of %0d bytes %c[0m",27,i,data[i],27);
foreach(space[i])
$display("\t %c[1;34m space_%0d is of %0d bytes %c[0m",27,i,space[i],27);
endfunction
endclass
module memory_pgm_data();
memory_block mb;
initial
begin
mb =new();
mb.total_mem=6144;
void'(mb.randomize());
mb.display();
end
endmodule
在这个示例中,我们使用约束使得所有程序、数据和空间的总大小应该等于 6KB 的内存块大小。我们给出了选择程序块、数据块和空间块数量的约束。这里得到了 10 个程序块、2 个数据块和 5 个空间块。程序、空间和数据的大小也是使用 foreach 和内部约束进行分配的。如果任何约束失败,则整个随机化过程将停止。
输出: