目录
1. RANDOMIZE() VS STD::RANDOMIZE()
2. ADDING CONSTRAINTS WITH STD::RANDOMIZE() WITH
4. pre/post_randomize()在子类和父类中的调用顺序是什么?
0. 前言
零零碎碎地记载一些关于SystemVerilog随机化的问题,结合代码实验进行解析。以下的代码的运行均利用windows版的QuestaSim。关于基本的仿真命令使用方法可以参考:数字电路设计仿真方法入门
本笔记随时动态更新。
1. RANDOMIZE() VS STD::RANDOMIZE()
RANDOMIZE()是SV的类内建的随机化函数,STD::RANDOMIZE()是独立的随机化函数。
每一个类都内建(built-in)虚方法 randomize(), 其声明如下(注意,如下面所述,回调函数pre/post_randomize()不是虚方法!):
virtual function int randomize();
randomize()用于对类中定义为rand或者randc类型的成员变量进行随机化处理。但是randomize()不会被自动调用。类中的成员定义为rand或者randc类型后,需要显式调用来产生所有随机变量的随机采样值(遵循约束条件)。关于class::randomize(),有以下几点需要注意:
- If
randomize()
fails, thenpost_randomize()
is not called. 关于post_randomize(),请参考后面章节。 randomize()
method is built-in and cannot be overriden. randomize()不能被override!开发者不能自己去重新定义randomize()- If randomization fails, then the variables retain their original values and are not modified。如过随机化失败(比如说,约束条件无法满足),
class trans;
rand bit [3:0] data1;
randc bit [3:0] data2;
bit [7:0] data3;
constraint c_data1 { data1 >= 8;}
constraint c_data2 { data2 < 8;}
function void pre_randomize();
$display("trans: called before randomization!");
endfunction
function void post_randomize();
$display("trans: called after randomization!");
endfunction
endclass
module test1;
initial begin
int var1;
trans tr;
tr = new();
if(tr.randomize())
$display("Randomization successful ! data1 = %0d \t data2 = %0d \t data3 = %0d",tr.data1,tr.data2, tr.data3);
else
$display("Randomization failed ! ");
void'(std::randomize(var1));
$display("var1 = %0d",var1);
void'(std::randomize(tr.data1));
void'(std::randomize(tr.data3));
$display("after std::randomize(): tr.data1 = %0d, tr.data1 = %0d",tr.data1,tr.data3);
end
endmodule
std::randomize()可以用于对当前作用域中任何变量进行随机化处理,即便该变量并没有被声明为随机化类型。
std::randomize()也可以用于对类的成员进行随机化处理!不管它是否被定义为rand类型。虽然这个并不在SV标准要求的范围以内。
运行以上代码会报告以下编译警告:
# ** Warning: pre_post_randomize.sv(77): (vopt-2961) Argument #1 for std::randomize() function is non-LRM compliant.
# ** Warning: pre_post_randomize.sv(78): (vopt-2961) Argument #1 for std::randomize() function is non-LRM compliant.
但是它的确是能够工作的,运行结果如下所示:
VSIM 1> run -all
# trans: called before randomization!
# trans: called after randomization!
# Randomization successful ! data1 = 14 data2 = 3 data3 = 0
# var1 = 393546790
# after std::randomize(): tr.data1 = 6, tr.data1 = 241
2. ADDING CONSTRAINTS WITH STD::RANDOMIZE() WITH
不仅是类的内建randomize()可以带约束条件运行,std::randomize()也同样可以。当带约束调用std::randomize()时,randomize()的输入参数变量为待随机化变量,其它的变量则当作状态变量使用。如以下例码所示:
module test5;
int x, y, z, status;
int d_length;
initial begin
d_length = $urandom_range(1,1000);
status = std::randomize( x, y, z ) with { x < y ; x + y < d_length ; };
$display("\tx = %0d \t y = %0d \t z = %0d \t d_length = %0d", x,y,z,d_length);
status = std::randomize( x, y ) with { y - x > d_length ; };
$display("\tx = %0d \t y = %0d \t z = %0d \t d_length = %0d", x,y,z,d_length);
end
endmodule
VSIM 1> run -all
# x = -1763570032 y = 31477632 z = 877272384 d_length = 913
# x = -1050698721 y = -438691868 z = 877272384 d_length = 913
3. 随机模式关闭后约束还有效吗?
设计一个简单的代码实验如下所示:
//class
class trans;
rand bit [3:0] data1;
randc bit [3:0] data2;
constraint c_data1 { data1 >= 8;}
constraint c_data2 { data2 < 8;}
endclass
module test3;
initial begin
trans tr;
tr = new();
tr.rand_mode(0);
tr.randomize();
$display("\tdata1 = %0d \t data2 = %0d",tr.data1,tr.data2);
for(int i=0; i<8; i++) begin
tr.data1 = i;
tr.data2 = i + 8;
$display("\tdata1 = %0d \t data2 = %0d",tr.data1,tr.data2);
end
end
endmodule
编译运行以上test3,可以得到结果如下:
# data1 = 0 data2 = 0
# data1 = 0 data2 = 8
# data1 = 1 data2 = 9
# data1 = 2 data2 = 10
# data1 = 3 data2 = 11
# data1 = 4 data2 = 12
# data1 = 5 data2 = 13
# data1 = 6 data2 = 14
# data1 = 7 data2 = 15
由于随机化模式关闭了, 虽然对data1和data2的显式赋值全部违反约束但是并没有引发报错。同样,由于随机化模式关闭了,data1和data2被初始化为0了。
4. pre/post_randomize()在子类和父类中的调用顺序是什么?
这个问题其实有点坑。基于以下实验代码来进行说明。child_trans1/2/3都继承于child_trans,在child_trans中定义了pre/post_randomize()。
child_trans1中显式定义了pre/post_randomize(),但是其中没有显式调用super.pre/post_randomize()。child_trans2中显式定义了pre/post_randomize(),但是其中显式调用super.pre/post_randomize()。child_trans3中没有显式定义了pre/post_randomize()。
//class
class trans;
rand bit [3:0] data1;
randc bit [3:0] data2;
constraint c_data1 { data1 >= 8;}
constraint c_data2 { data2 < 8;}
function void pre_randomize();
$display("trans: called before randomization!");
endfunction
function void post_randomize();
$display("trans: called after randomization!");
endfunction
endclass
class child_trans1 extends trans;
rand bit [3:0] data3;
randc bit [3:0] data4;
constraint c_data3 { data3 >= 8;}
constraint c_data4 { data4 < 8;}
function void pre_randomize();
$display("child_trans1: called before randomization!");
endfunction
function void post_randomize();
$display("child_trans1: called after randomization!");
endfunction
endclass
class child_trans2 extends trans;
rand bit [3:0] data5;
randc bit [3:0] data6;
constraint c_data5 { data5 >= 8;}
constraint c_data6 { data6 < 8;}
function void pre_randomize();
super.pre_randomize();
$display("child_trans2: called before randomization!");
endfunction
function void post_randomize();
super.post_randomize();
$display("child_trans2: called after randomization!");
endfunction
endclass
class child_trans3 extends trans;
rand bit [3:0] data7;
randc bit [3:0] data8;
constraint c_data7 { data7 >= 8;}
constraint c_data8 { data8 < 8;}
endclass
module test1;
initial begin
trans tr;
tr = new();
if(tr.randomize())
$display("Randomization successful ! data1 = %0d \t data2 = %0d",tr.data1,tr.data2);
else
$display("Randomization failed ! ");
end
endmodule
module test2;
initial begin
child_trans1 ctr;
ctr = new();
void'(ctr.randomize());
$display("\tdata1 = %0d \t data2 = %0d \t data3 = %0d \t data4 = %0d",
ctr.data1,ctr.data2,ctr.data3,ctr.data4);
end
endmodule
module test3;
initial begin
child_trans2 ctr;
ctr = new();
void'(ctr.randomize());
$display("\tdata1 = %0d \t data2 = %0d \t data5 = %0d \t data5 = %0d",
ctr.data1,ctr.data2,ctr.data5,ctr.data5);
end
endmodule
module test4;
initial begin
child_trans3 ctr;
ctr = new();
void'(ctr.randomize());
$display("\tdata1 = %0d \t data2 = %0d \t data7 = %0d \t data8 = %0d",
ctr.data1,ctr.data2,ctr.data7,ctr.data8);
end
endmodule
编译以下代码文件并分别运行work.test2,work.test3,work.test4.
work.test2:
# child_trans1: called before randomization!
# child_trans1: called after randomization!
work.test3:
# trans: called before randomization!
# child_trans2: called before randomization!
# trans: called after randomization!
# child_trans2: called after randomization!
work.test4:
# trans: called before randomization!
# trans: called after randomization!
基于以上实验可以看出,pre/post_randomize()在类的继承中的调用行为与普通的systemverilog的类的方法的行为是一致的,即:
- (1) 同名同参数时,子类方法覆盖父类方法,父类方法不会被调用
- (2) 同名同参数时,如果希望父类方法得到调用,需要在子类方法中以super.methodname()的形式对父类方法进行显式调用
- (3) 如果子类没有定义的话,缺省地会调用父类的方法
5. pre/post_randomize()是虚方法吗?
答案是:它们不是虚方法(virtual method),但是它们以虚方法的方式工作(behave as virtual methods)。如果用户自定义类中,将pre/post_randomize()定义为virtual method的话,编译器会报错。比如说,定义类如下的话:
class trans;
rand bit [3:0] data1;
randc bit [3:0] data2;
constraint c_data1 { data1 >= 8;}
constraint c_data2 { data2 < 8;}
virtual function void pre_randomize();
$display("trans: called before randomization!");
endfunction
virtual function void post_randomize();
$display("trans: called after randomization!");
endfunction
endclass
用questasim编译命令vlog编译后报告如下错误:
** Error: pre_post_randomize.sv(13): (vlog-2943) Invalid use of 'virtual' keyword for built-in function 'post_randomize'
.
** Error: pre_post_randomize.sv(9): (vlog-2943) Invalid use of 'virtual' keyword for built-in function 'pre_randomize'.
6. 随机化的层次化
考虑在SV验证平台中,test作为测试的顶层,在test中包含有generator用于生成transaction。
首先,transaction中的成员可以定义为rand或者randc类型;
其次,在generator中,可以根据需要进行数据或者参数的随机化处理,并且进一步根据这个随机化生成的数据或者参数影响或者控制transaction的成员的随机化
最后,在test这一层可以控制generator的随机化处理。
通过这种层次化的随机化处理,提供了灵活的随机化控制机制。比如说,在最初的smoke test中,只需要利用transaction中的随机化处理即可。但是,如果只有transaction中的随机化,就无法区分不同的test所需要的不同的随机化需求。
如以下例子所示,generator在send_trans()中根据自己的成员随机化值控制trans的成员随机化,而generator的成员随机化又可以进一步在test层创建generator对象时进行控制。
class trans;
rand bit[31:0] data[];
rand int ch_id;
rand int pkt_id;
rand int data_nidles;
rand int pkt_nidles;
...
endclass
class generator;
rand int pkt_id = -1;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
...
function new();
...
endfunction
task run();
repeat(ntrans) send_trans();
endtask
// generate transaction and put into local mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
local::pkt_id >= 0 -> pkt_id == local::pkt_id;
local::data_nidles >= 0 -> data_nidles == local::data_nidles;
local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
local::data_size >0 -> data.size() == local::data_size;
})
else $fatal("[RNDFAIL] channel packet randomization failure!");
this.pkt_id++;
$display(req.sprint());
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
endclass: generator
7. 约束的几种形式
-
权重约束 dist:有两种操作符::=n :/n 第一种表示每一个取值权重都是n,第二种表示每一个取值权重为n/num。
-
条件约束 if else 和->(case):if else 就是和正常使用一样;->通过前面条件满足后可以触发后面事件的发生。
-
范围约束inside:inside{[min:max]};范围操作符,也可以直接使用大于小于符号进行,但是不可以连续使用,如 min<wxm<max 这是错误的
8. Rand 和randc的区别
-
rand
修饰符:rand
修饰的变量,每次随机时,都在取值范围内随机取一个值,每个值被随机到的概率是一样的,就想掷骰子一样。 -
randc
修饰符:randc
表示周期性随机,即取值范围内所有可能的值都取到过后,才会重复取值。类似于无放回采样。打个比方说,从一副扑克牌中任取一张,每取完一张后不放回,接着取下一张,这样经过54次采样后这一副牌就取完了,这样构成一轮无放回采样。然后再重置为一副完整的牌,重复以上采样。
相关文章: