您当前的位置: 首页 > 

lu-ming.xyz

暂无认证

  • 0浏览

    0关注

    115博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

SystemVerilog的随机约束(Random constraints)

lu-ming.xyz 发布时间:2022-04-28 13:32:43 ,浏览量:0

文章目录
  • 0 概述
  • 1 随机变量
    • 1.1 rand关键字
    • 1.2 randc关键字
  • 2 Constraint block
    • 2.1 设置成员(membership)
    • 2.2 分布(distribution)
    • 2.3 蕴涵词 (implication)
    • 2.4 if...else 约束
    • 2.5 迭代(iterative)约束
    • 2.6 Global 约束
    • 2.7 变量排序
    • 2.8 静态约束块
    • 2.9 约束中的函数
    • 2.10 约束保护(Constraint guard)

0 概述

SystemVerilog允许用户以一种紧凑的、声明式的方式指定约束。然后由求解器处理约束,生成满足约束的随机值。

随机约束通常是在面向对象的数据抽象之上指定的,该抽象将要随机化的数据建模为包含随机变量和用户定义约束的对象。这些约束决定了可以分配给随机变量的合法值。

例子:

class Bus;
    rand bit[15:0] addr;
    rand bit[31:0] data;
    constraint word_align {addr[1:0] == 2’b0;}
endclass

Bus类用两个随机变量对简化的总线建模:addr和data,它们代表总线上的地址和数据值。word_align约束声明addr的随机值必须是按字对齐的(低2位为0,4byte=1word)。

例化:

Bus bus = new;
repeat (50) begin
    if ( bus.randomize() == 1 )
        $display ("addr = %16h data = %h\n", bus.addr, bus.data);
    else
        $display ("Randomization failed.\n");
end

调用 randomize() 会为所有随机变量产生约束范围内的随机值。

在上面的程序测试中,创建一个总线对象,然后随机化50次。检查每个随机化的结果是否成功。如果随机化成功,则打印addr和data的新随机值;如果随机化失败,就会打印一条错误消息。本例中,只有addr值受约束,而data值不受约束。无约束变量在其声明范围内被赋任何值。

约束编程是一种功能强大的方法,它允许用户构建通用的、可重用的对象,稍后可以对这些对象进行扩展或约束,以执行特定的功能。这种方法既不同于传统的过程式编程,也不同于传统的面向对象编程,如下例所示,它扩展了Bus类:

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

MyBus类继承了Bus类的所有随机变量和约束,并添加了一个名为atype的随机变量,该随机变量用于使用另一个约束控制地址范围。addr_range约束使用 蕴涵词(implication,->) 来根据atype的随机值从三个范围约束中选择一个。 当一个MyBus对象被随机化时,addr、data和type的值将被计算出来,以便满足所有的约束。使用继承来构建分层约束系统使通用模型的开发成为可能,这些模型可以被约束来执行特定于应用程序的功能。

调用:

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  b等价的布尔值是(!a || b)。这表示如果表达式为真,则生成的随机数受约束(或约束集)的约束。否则,生成的随机数是无约束的。

constraint_set表示任何有效的约束或未命名的约束集。如果表达式为真,则约束集中的所有约束也必须满足。

mode == little -> len  len > 100;

在这个例子中,mode的值意味着len的值应该被限制在小于10 (mode == little),大于100 (mode == big),或不受约束(mode != little和mode != big)。

bit [3:0] a, b;
constraint c { (a == 0) -> (b == 1); }

a和b都是4位;因此,a和b有256种组合。约束c说,a == 0意味着b == 1,因此消去了15种组合:{0,0},{0,2},…{0,15}。因此,a == 0的概率是1/(256-15)或1/241。

2.4 if…else 约束

if…else 约束的语法:

在这里插入图片描述

if (mode == little)
    len  100;

等效于

mode == little -> len  len > 100 ;
2.5 迭代(iterative)约束

iterative 约束允许使用循环变量和索引表达式以参数化的方式约束数组变量。

语法: 在这里插入图片描述

foreach构造指定对数组元素的迭代。它的参数是一个标识符,它指定任何类型的数组(fixed-size, dynamic, associative,或 queue),后跟一个用方括号括起来的循环变量列表。每个循环变量对应于数组的一个维度。

例子:

class C;
    rand byte A[] ;
    constraint C1 { foreach ( A [ i ] ) A[i] inside {2,4,8,16}; }
    constraint C2 { foreach ( A [ j ] ) A[j] > 2 * j; }
endclass

约束1:C1将数组A中的每个元素限定在集合[2,4,8,16]内。C2约束数组中的每个元素。 约束2:A大于索引的两倍。

注意:循环变量的数量不能超过数组变量的维数。每个循环变量的作用域是foreach约束结构,包括它的constraint_set。每个循环变量的类型隐式声明为与数组索引的类型一致。空循环变量表示不对数组的该维度进行迭代。与默认参数一样,末尾的逗号列表可以省略;因此,foreach(arr [j])是foreach(arr [j,,,,])的简写。任何循环变量与th具有相同的标识符都将是错误的。

iterative 约束允许包含断言:

class C;
rand int A[] ;
    constraint c1 { A.size inside {[1:10]}; }
    constraint c2 { foreach ( A[ k ] ) (k  A[k + 1] > A[k]; }
endclass

约束1:约束c1将数组A的大小限制在1到10之间。 约束2:约束c2约束每个数组的值大于前一个,即按升序排序的数组。

注意:在foreach中,只涉及常数、状态变量、对象句柄比较、循环变量或迭代数组的大小的断言表达式的行为是防止创建约束,而不是作为逻辑关系。例如,上面约束c2的含义只涉及一个循环变量和正在迭代的数组的大小;因此,它只允许在k < A.size() - 1,在本例中,它阻止了约束中的越界访问。

2.6 Global 约束

当类的对象成员声明为rand时,其所有约束和随机变量与其他类变量和约束同时被随机化。包含来自其他对象的随机变量的约束表达式称为全局约束。

例子:

class A; // leaf node
    rand bit [7:0] v;
endclass
class B extends A; // heap node
    rand A left;
    rand A right;
    constraint heapcond {left.v  d == 0; }
    constraint order { solve s before d; }
endclass

在这种情况下,顺序约束指导解决解决为d s之前解决。年代现在选择的效果是真实的概率为50%,然后选择d s的价值。因此,d = = 0将发生50%的时间,和d != 0应当发生另外的50%

可变排序可以用来迫使所选的corner情况比其他情况更频繁地出现。然而“solve…before…”约束不会改变解空间,因此不会导致求解器失败。

2.8 静态约束块

通过在约束块的定义中包含static关键字,可以将约束块定义为static。

如果一个约束块被声明为静态的,那么对constraint_mode()的调用将影响所有对象中指定约束的所有实例。因此,如果一个静态约束被设置为OFF,那么它对于这个特定类的所有实例都是关闭的。

2.9 约束中的函数

有些属性难以处理,或者不可能在一个表达式中表达。例如,计算已填充数组中1的数量的自然方法是使用循环:

function int count_ones ( bit [9:0] w );
    for( count_ones = 0; w != 0; w = w >> 1 )
    count_ones += w & 1'b1;
endfunction

这样的函数可以用来约束其他随机变量为1位:

constraint C1 { length == count_ones( v ) ; } 

如果不能调用函数,则该约束要求展开循环,并将其表示为单个位的和:

constraint C2
{
    length == ((v>>9)&1) + ((v>>8)&1) + ((v>>7)&1) + ((v>>6)&1) + ((v>>5)&1) +
    ((v>>4)&1) + ((v>>3)&1) + ((v>>2)&1) + ((v>>1)&1) + ((v>>0)&1);
}

与count_ones函数不同,更复杂的属性(需要临时状态或无界循环)可能无法转换为单个表达式。因此,调用函数的能力增强了约束语言的表达能力,并降低了出错的可能性。上面的两个约束,C1和C2,是不完全等价的;C2是双向的(长度可以约束v,反之亦然),而C1不是。

2.10 约束保护(Constraint guard)

约束保护是断言表达式,它的作用是防止约束的创建,而不是作为解算器要满足的逻辑关系。这些断言表达式在约束解出之前被求值,其特征是只涉及以下项:

  • 常量
  • 状态变量
  • 对象句柄比较(两个句柄或一个句柄与常量null的比较)

除了上述,迭代约束也将循环变量和被迭代数组的大小视为状态变量。

将这些断言表达式作为约束保护,可以防止求解器生成求值错误,从而在一些看似正确的约束上失败。这使用户能够编写约束,以避免由于对象句柄不存在或数组下标出界而导致的错误。例如,下面所示的单链列表(SList)的排序约束旨在分配一个按升序排序的随机数字序列。但是,约束表达式在最后一个元素上失败,而在下一个元素上失败。N由于不存在句柄而导致计算错误。

class SList;
    rand int n;
    rand Slist next;
    constraint sort { n             
关注
打赏
1655639048
查看更多评论
0.0396s