2.3 module
前言
本文以uvm-1.2/examples/simple/basic_examples/module为例,主要介绍1、组件的层级关系构建;2、package的用法;3、config_db的用法。
一、基本介绍
这个测试用例初始化构建的组件层次结构如下:
-------------------------------
Name Type Size Value
-------------------------------
mu myunit - @335
l1 lower - @352
data integral 32 'h30
str string 0 ""
l2 lower - @361
data integral 32 'h40
str string 0 ""
a array 5 -
[0] integral 32 'h0
[1] integral 32 'h1
[2] integral 32 'h4
[3] integral 32 'h9
[4] integral 32 'h10
-------------------------------
后面利用config_db对这里面的参数进行配置,得到的结果如下,改变了l1.data,l1.str,l2.data,l2.str。
-------------------------------
Name Type Size Value
-------------------------------
mu myunit - @335
l1 lower - @352
data integral 32 'h37
str string 2 hi
l2 lower - @361
data integral 32 'h65
str string 2 hi
a array 5 -
[0] integral 32 'h0
[1] integral 32 'h1
[2] integral 32 'h4
[3] integral 32 'h9
[4] integral 32 'h10
-------------------------------
二、代码分析
这个测试用例只有一个文件test.sv
1、test.sv
test.sv的代码如下所示。
package user_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
//----------------------------------------------------------------------
// lower
//----------------------------------------------------------------------
class lower extends uvm_component;
int data;
string str;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
#10 $display("%0t: %s HI", $time, get_full_name());
phase.drop_objection(this);
endtask
function string get_type_name();
return "lower";
endfunction
function void build_phase(uvm_phase phase);
void'(uvm_config_int::get(this, "", "data", data));
void'(uvm_config_string::get(this, "", "str", str));
endfunction
function void do_print(uvm_printer printer);
printer.print_field("data", data, 32);
printer.print_string("str", str);
endfunction
endclass
//----------------------------------------------------------------------
// myunit
//----------------------------------------------------------------------
class myunit extends uvm_component;
lower l1;
lower l2;
int a[];
function new (string name, uvm_component parent);
super.new(name, parent);
uvm_config_string::set(this, "l1", "str", "hi");
uvm_config_int::set(this, "*", "data", 'h100);
l1 = new ("l1", this);
l2 = new ("l2", this);
l1.data = 'h30;
l2.data = 'h40;
a = new[5]; for(int i=0; i<5;++i) a[i] = i*i;
endfunction
task run_phase(uvm_phase phase);
//Check config from initial block
if(l1.data != 55)
`uvm_error("BADCFG", $sformatf("Expected l1.data = 55, got %0d", l1.data))
if(l2.data != 101)
`uvm_error("BADCFG", $sformatf("Expected l2.data = 101, got %0d", l2.data))
if(l1.str != l2.str && l1.str != "hi")
`uvm_error("BADCFG", $sformatf("Expected l1.str = \"hi\" and l2.str = \"hi\", got l1.str = \"%s\" and l2.str = \"%s\"", l1.str, l2.str))
phase.raise_objection(this);
#10 $display("%0t: %s HI", $time, get_full_name());
phase.drop_objection(this);
endtask
function string get_type_name();
return "myunit";
endfunction
function void do_print(uvm_printer printer);
printer.print_array_header("a", a.size());
for(int i=0; i<a.size(); ++i)
printer.print_field( $sformatf("a[%0d]", i), a[i], 32, UVM_HEX, "[");
printer.print_array_footer();
endfunction
endclass
//----------------------------------------------------------------------
// mydata
//
//----------------------------------------------------------------------
class mydata extends uvm_object;
`uvm_object_registry(mydata,"mydata")
function uvm_object create(string name="mydata");
mydata d; d=new; d.set_name(name);
return d;
endfunction
virtual function string get_type_name();
return "mydata";
endfunction
function new(string name="");
super.new(name);
endfunction
endclass
endpackage:user_pkg
//----------------------------------------------------------------------
// top
//----------------------------------------------------------------------
module top;
import uvm_pkg::*;
import user_pkg::*;
myunit mu = new("mu", null);
mydata bar = new;
initial begin
uvm_top.finish_on_completion = 0;
uvm_config_int::set(null, "mu.*", "data", 101);
uvm_config_string::set(null, "mu.*", "str", "hi");
uvm_config_int::set(null, "mu.l1", "data", 55);
uvm_config_object::set(null, "mu.*", "obj", bar);
mu.print();
run_test();
mu.print();
end
endmodule
第1行和103行,通过package对top中用到的组件进行了包封装。
第9到36行,定义了一个从uvm_component继承过来的类lower;
第10和11行,定义了整型和字符串型的类变量data和str;
第17到21行,在run_phase中通过“举手”和“放手”打印了当前的仿真时间,以及这个组件在系统中的层次结构,get_full_name()返回的是当前组件的系统层次结构;
第27到30行,在build_phase中调用config_db中的get函数,获取从top通过put配置过来的参数;
第32到35行,设置打印的格式,具体介绍参考如下。
do_print
virtual function void do_print (
uvm_printer printer
)
The do_print method is the user-definable hook called by print and sprint that allows users to customize what gets printed or sprinted beyond the field information provided by the `uvm_field_* macros, Utility and Field Macros for Components and Objects.
The printer argument is the policy object that governs the format and content of the output. To ensure correct print and sprint operation, and to ensure a consistent output format, the printer must be used by all do_print implementations. That is, instead of using $display or string concatenations directly, a do_print implementation must call through the printer’s API to add information to be printed or sprinted.
An example implementation of do_print is as follows
class mytype extends uvm_object;
data_obj data;
int f1;
virtual function void do_print (uvm_printer printer);
super.do_print(printer);
printer.print_field_int("f1", f1, $bits(f1), UVM_DEC);
printer.print_object("data", data);
endfunction
Then, to print and sprint the object, you could write
mytype t = new;
t.print();
uvm_report_info("Received",t.sprint());
virtual function void print_field (
string name,
uvm_bitstream_t value,
int size,
uvm_radix_enum radix = UVM_NORADIX,
byte scope_separator = “.”,
string type_name = “”
)
Prints an integral field (up to 4096 bits).
name The name of the field.
value The value of the field.
size The number of bits of the field (maximum is 4096).
radix The radix to use for printing. The printer knob for radix is used if no radix is specified.
scope_separator is used to find the leaf name since many printers only print the leaf name of a field. Typical values for the separator are . (dot) or [ (open bracket).
第41到81行,定义了一个从uvm_component继承过来的类myunit;
第42和43行,分别声明了两个lowe的类l1和l2;
第44行,声明了一个动态数组a;
第45到55行,是myunit的构造函数,初始化一些变量;其中,48和49行调用config_db的set函数进行赋值,但是这个时候l1和l2都没有实体化,所以这个赋值不成功,50和51行对l1和l2进行实体化,52个53行对l1和l2的data进行赋初值,54行初始化动态数组a的值。
第57到68行,在run_phase中检查顶层通过config_db配置下来的值,并且通过“举手”和“放手”打印当前的时间和模块在系统中层次。
第74到79行,设置动态数组a的打印格式。其中,print_array_header和print_array_footer函数分别设置打印的头和尾。
第89到101行,定义了一个继承自uvm_object的类mydata,这个测试用例中没有用到,这里不具体介绍。
第108到125行,是顶层top模块;第109和110行,分别导入uvm_pkg和上面封装的user_pkg;第112行,声明并实体化myunit,命名为mu;第113和120行,声明例化并传递mydata,但是没有接收方,所以并没有什么作用;第116行,设置finish_on_completion=0,是为了保证仿真的phase完了后不结束(If set finish_on_completion = 1, then run_test will call $finish after all phases are executed.);第117到119行,调用config_db机制,将值传递进去;第121行先打印一下执行仿真前的值;第122行执行仿真;第123行再打印一下执行仿真后的值;
2、仿真结果
仿真log如下所示,通过观察log可以看到,除了仿真前后,l1和l2的data与str的值,通过config_db改变了之外,仿真执行过程中,不管是mu,还是mu.l1,mu.l2都是在10ns时刻打印了HI,这说明不同组件的run_phase都是在同一时刻开始执行,与模块的层级关系并没有关系。
-------------------------------
Name Type Size Value
-------------------------------
mu myunit - @335
l1 lower - @352
data integral 32 'h30
str string 0 ""
l2 lower - @361
data integral 32 'h40
str string 0 ""
a array 5 -
[0] integral 32 'h0
[1] integral 32 'h1
[2] integral 32 'h4
[3] integral 32 'h9
[4] integral 32 'h10
-------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test ...
10: mu.l1 HI
10: mu.l2 HI
10: mu HI
UVM_INFO ../../../../src/base/uvm_objection.svh(1270) @ 10: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO ../../../../src/base/uvm_report_server.svh(847) @ 10: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 3
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
-------------------------------
Name Type Size Value
-------------------------------
mu myunit - @335
l1 lower - @352
data integral 32 'h37
str string 2 hi
l2 lower - @361
data integral 32 'h65
str string 2 hi
a array 5 -
[0] integral 32 'h0
[1] integral 32 'h1
[2] integral 32 'h4
[3] integral 32 'h9
[4] integral 32 'h10
-------------------------------
V C S S i m u l a t i o n R e p o r t
Time: 10 ns
总结
通过这个测试用例,主要可以理解config_db传递参数的用法;其次,可以学习到利用package对组件进行封装,然后导入到其他模块中去使用;最后,一个组件可以在另外一个组件中例化,构成一定的层次关系,另外,通过仿真结果分析,可以看出run_phase是并行同时执行的,与组件的系统层次没有关系。