我们要随机什么?
• 器件配置:通过寄存器和系统信号
• 环境配置:随机化验证环境, 例如合理的时钟和外部反馈信号
• 原始输入数据:例如MCDF数据包的长度、 带宽, 数据间的顺序
• 延时:握手信号之间的时序关系,例如valid和ready,req和 ack之间的时序关系
• 协议异常:如果反馈信号给出异常, 那么设计是否可以保持后续数据处理的稳定性呢?
随机化是为了产生更多可能的驱动,因此在软件世界"class"一侧的运用更多,所以我们倾向于将相关数据有机整理在一个类的同时,也用rand关键词来表明它们的随机属性。
randc表示周期随机性,即所有可能的值都赋过值后随机值才可能重复。
随机属性需要配合SV预定义的类随机函数std::randomize()使用。即只有通过声明rand变量,并且在后期通过对象调用randomize()函数才可以随机化变量。
约束constraint也同随机变量一起在类中声明。
- class Packet;//The random variables
- rand bit [31.:0] src, dst, data[8] ;
- randc bit [7:0] kind; // Limit the values for src
- constraint c {src > 10;
- src <15;}
- endclass
-
- Package p;
- initial begin
- p = new();
- assert (p.randomize()) else
- $fatal(0, "Packet::randomize failed");
- transmit(p);
- end
什么是约束?
• 约束表达式的求解是由SV的约束求解器 (constraint solver)完成的。
• 求解器能够选择满足约束的值, 这个值是有SV的PRNG (伪随机数发生器 Pseudo random number generator) 从—个初始值{seed)产生。 只要改变种子的值, 就可以改变CRT的行为。
• sv标准定义了表达式的含义以及产生的合法值, 但没有规定求解器计算约束的准确顺序。 这即是说, 不同仿真器对千同一个约束类和种子值求解出的数值可能是不相同的。
• 什么可以被约束? sv只能随机化2值数据类型, 但位可以是2值或4值。 这即是说, 无法随机化出X值和Z值, 也无法随机化字符串。
- class date;
- rand bit[2:0] month;
- rand bit[4:0] day;
- rand int year;
- constrain c_date {
- month inside {[1:12]};
- day inside {[1:31]};
- year inside {[2010:2030]};
- }
-
- endclass
权重分布:
• 关键词dist可以在约束中用来产生随机数值的权重分布, 这样某些值的选取机会要比其他值更大一些;
• dist操作符带有—个值的列表以及相应的权重, 中间用:=或:/分开。 值或权重可以是常数或者变量。
• 权重不用百分比表示, 权重的和也不必是100。
• : =操作符表示值范围内的每一个值的权重是相同的, :/操作符表示权重要平均分到值范围内的每一个值。
- rand int src, dst;
- constraint c dist {
- src dist {0:=40, [l:3]:=60};
- //src = 0, weight= 40/220
- //src = 1, weight= 60/220
- //src = 2, weight= 60/220
- //src = 3, weight= 60/220
- dst dist {0:/40, [l:3]:/60};
- // dst= 0, weight= 40/100
- // dst= 1, weight= 20/100
- // dst= 2, weight= 20/100
- // dst= 3, weight= 20/100
- end
集合成员的inside运算符:
inside是常见的约束运算符,表示变量应该属于某一个值的集合,除非还存在其他约束,否则随机变量在集合里取值的概率是相等的,集合里也可以使用变量。
rand int c; //随机变量
int lo,hi; //作为上限和下限的非随机变量constraint c_range{ c inside {[lo:hi]}; // lo <= c && c <= hi}
使用"$"指定最大值和最小值
rand bit[6:0] b; // 0 <= b <= 127rand bit[5:0] e; // 0<= e <= 63
constraint c_range {
b inside {[$:4], [20:$}; // 0 <= b <= 4 || 20 <= b <= 127
e inside {[$:4], [20:$}; // 0 <= e <= 4 II 20 <= e <= 63
}
可以通过->或者if-else来让一个约束表达式在特定时刻有效。
- class BusOp;
- ...
- constraint c_io {
- (io_space_mode) ->
- addr[31] == l'bl; }
-
-
- class BusOp;
- ...
- constraint c_en_rw {
- if (op == READ)
- len inside {[BYTE: LWRD]}
- else
- len == LWRD;
- }
• 初学者需要注意的是,约束块不像自上向下的程序性代码。它们是声明性的代码,是并行的,所有的约束表达式同时有效。
• 同时,约束也是双向的,这表示它会同时计算所有的随机变量的约束。增加或删除任何—个变量的约束都会直接或间接影响所有相关的值的选取。
- rand logic [15:0] r, s, t;
- constraint cbidir {
- r
- s==r;
- t<30;
- s>25;}

1.2约束块控制
打开/关闭约束块 :
• —个类可以包含多个约束块。 可以把不同约束块用于不同测试。
• 一般情况下, 各个约束块之间的约束内容是互相协调不违背的,因此通过随机函数产生随机数时可以找到合适的解。
• 对于其它情况,例如根据不同需要,来选择使能哪些约束块,禁止哪些约束块的要求,可以使用内建的constraint_mode()函 数打开或者关闭约束。
- class Packet;
- rand int length;
- constraint c_short{length inside {[1:32]};}
- constraint c_long {lengh inside {[1000:1023]};}
- endclass
- Packet p;
- initial begin
- p = new();
- p.c_short.constraint_mode(O);
- assert(p.randomize ()) ;
- transmit(p);
- p.constraint_mode(0);
- p.c_short.constraint_mode(l);
- assert(p.randomize ());
- transmit(p);
- end
内嵌约束
• 伴随着复杂的约束, 它们之间会相互作用,最终产生难以预测的结果。用来使能和禁止这些约束的代码也会增加测试的复杂性。
• 经常增加或修改类例的约束也可能会影响整个团队的工作 , 这需要考虑类的开放封闭原则(OCP)。
• SV允许使用randomize()with来增加额外的约束,这和在类里增加约束是等效的, 但同时要注意类内部约束和外部约束之间应该是协调的 , 如果出现互相违背的清况, 那么随机数值求解会失败。
- class Transaction;
- rand bit[31:0] addr, data;
- constraint cl { soft addr inside
- { [0: 100], [1000: 2000]};}
- endclass
-
- Transaction t;
- initial begin
- t = new();
- // addr is 50-100,
- // 1000-1500, data < 10
- assert (t.randomize() with {addr >= 50; addr <= 1500;
- data < 10;));
- driveBus(t);
- // force addr to a specific
- // value, data > 10
- assert(t.randomize() with
- {addr = 2000; data > 10;});
- driveBus(t);
- end
1.3 随机函数
pre_randomize()和post_randomize()
• 有时需要在调用randomize()之前或之后立即执行一些操作, 例如在随机前设置类例的一些非随机变量(上下限、 条件值、 权重),或者在随机化之后需要计算随机数据的误差、 分析和记录随机数据等。
• sv提供了两个预定义的void类型函数pre_randomize()和post_randomize()函数,用户可以类中定义这两个函数,分别在其中定义随机化前的行为和随机化后的行为。
• 如果某个类中定义了pre_randomize()或者post_randomize()函数,那么对象在执行了randomize()之前或者之后会分别执行这两个函数。所以,pre_randomize()和post_randomize()可以看做是randomize()函数的回调函数(callback function).
随机数函数
SV提供了—些常用的系统随机函数。 这些随机函数可以直接调用来返回随机数值。
• $random() 平均分布 , 返回32位有符号随机数。
• $urandom()平均分布 , 返回32位无符号随机数。
• $urandom_range() 在指定范围内的平均分布
随机化个别变量
• 在调用randomize()时可以传递变量的—个子集 , 这样只会随机化类里的几个变量。
• 只有参数列表里的变量才会被随机化, 其它变量会被当做状态变量而不会被随机化。
• 所有的约束仍然保持有效。
• 初学者需要注意该种应用针对的是类里所有被指定或者没有被指定rand的变量都可以作为randomize()的参数而被随机化。
- class Rising;
- byte low; // Not random
- rand byte med, hi; // Random variable
- constraint up
- {low< med; med< hi; }
- endclass
- initial begin
- Rising r;
- r = new();
- r. randomize() ; //随机化hi, 但不改变low
- r.randomize(med); //只随机化med
- r.randomize(low); //只随机化low
- end