目录
1. RANDOMIZE() VS STD::RANDOMIZE()
2. ADDING CONSTRAINTS WITH STD::RANDOMIZE() WITH
4. pre/post_randomize()在子类和父类中的调用顺序是什么?
零零碎碎地记载一些关于SystemVerilog随机化的问题,结合代码实验进行解析。以下的代码的运行均利用windows版的QuestaSim。关于基本的仿真命令使用方法可以参考:数字电路设计仿真方法入门
本笔记随时动态更新。
RANDOMIZE()是SV的类内建的随机化函数,STD::RANDOMIZE()是独立的随机化函数。
每一个类都内建(built-in)虚方法 randomize(), 其声明如下(注意,如下面所述,回调函数pre/post_randomize()不是虚方法!):
virtual function int randomize();
randomize()用于对类中定义为rand或者randc类型的成员变量进行随机化处理。但是randomize()不会被自动调用。类中的成员定义为rand或者randc类型后,需要显式调用来产生所有随机变量的随机采样值(遵循约束条件)。关于class::randomize(),有以下几点需要注意:
randomize()
fails, then post_randomize()
is not called. 关于post_randomize(),请参考后面章节。randomize()
method is built-in and cannot be overriden. randomize()不能被override!开发者不能自己去重新定义randomize()- 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
不仅是类的内建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
设计一个简单的代码实验如下所示:
- //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了。
这个问题其实有点坑。基于以下实验代码来进行说明。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的类的方法的行为是一致的,即:
答案是:它们不是虚方法(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'.