目录
Setting policy using uvm_comparer
Using factory overrides
在SV中我们是通过new()来创建类的对象的,这种方法也可以在UVM中使用.但是UVM引入了工厂(factory)的概念,这使我们可以修改在工厂中注册的组件的属性而不必去修改testbench。假设存在这样一个情形,工厂中注册了两个driver,而environment中只使用了一个,这时候你可以利用工厂用未使用的driver替换在environment中正在使用的driver,替换语句可以写在test的build_phase中。
UVM验证环境分为两部分,一部分构成环境的层次,这部分代码通过uvm_component完成;另一部分构成环境的属性和数据传输,这部分代码通过uvm_object完成。UVM工厂存在的意义就是为了更加方便的替换验证环境中的实例或已注册的类型,同时工厂的注册机制带来配置的灵活性。这里实例或类型的替代在UVM中称为覆盖(override),用来替换的类型必须满足注册(registration)和多态(polymorphism)的要求。
UVM使用宏在factory中注册组件和环境属性,注意两种注册使用的宏是不同的。组件使用的宏是`uvm_component_utils,而环境属性属性使用的宏是`uvm_object_utils。这里的组件包括:sequencer、driver、monitor、agent、environment和test,而环境属性主要是sequence和sequence item。来看下面例子:
class ABC extends uvm_object;
// 在factory中注册该自定义类
`uvm_object_utils(ABC)
function new(string name = "ABC");//第一个string参数都是类名
super.new(name);
endfunction
endclass
class DEF extends uvm_component;
// 来源于uvm_component的类在factory注册
`uvm_component_utils(DEF)
function new(string name = "DEF", uvm_component parent=null);
super.new(name, parent);
endfunction
endclass
注意观察,上面注册环境属性和组件的不同,除了使用的宏不同之外,它们的构造方法的参数个数也不相同。组件的注册中,构造方法多了一个组件的父类参数"uvm_component parent=null",这是因为组件会在最终的UVM层次中显示,而多出来的一个父类参数,是使用了类似“钩子”的方法,将组件一层往上勾住一层,这样整个UVM的层次就构建起来了。而环境属性只是作为传递数据或配置属性的类,它们会成为uvm_component的成员,所以不会显示在UVM层次中,所以就不需要父类参数!
在工厂中注册环境属性和组件后,可以在上层层次中创建组件的对象,例如在agent中创建monitor、sequencer和driver的对象,这是在agent中的build_phase中完成的。创建对象在SV中使用new(),但是对于在工厂中注册的组件,UVM推荐使用creat()方法,具体格式如下例所示:
class my_driver extends uvm_driver;//注册driver
`uvm_component_utils (my_driver)
...
endclass
class my_env extends uvm_env;
`uvm_component_utils (my_env)
my_driver drv0;
...
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
drv0 = my_driver::type_id::create ("drv0", this);//在environment中创建driver对象
//通用格式:组件对象名=组件名::type_id::create("组件对象名", this);//this表示当前路径下创建
endfunction
endclass
可以通过调用方法来覆盖environment/test中已经实例化组件的对象,组件覆盖包括全部覆盖和部分路径覆盖,如下:
// 覆盖所有某一类型的组件
static function void set_type_override_by_type (
uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
bit replace = 1
);//通过组件类型覆盖
//第一个参数是被覆盖的类,第二个参数是覆盖的类
等价于:factory.set_type_override_by_type(original_type, override_type,replace);//factory是一个全局变量
function void set_inst_override_by_type(
string relative_inst_path,
uvm_object_wrapper original_type,
uvm_object_wrapper override_type
);
//第一个参数是相对路径,第二个参数是被覆盖的类,第三个参数是覆盖的类
等价于:factory.set_inst_override_by_type( original_type,
override_type,
{get_full_name(),".",relative_inst_path});
上面的写法比较复杂,一般用下面两个简化版:
static function void set_type_override(
string original_type_name,
string override_type_name,
bit replace = 1);
function void set_inst_override(
string relative_inst_path,
string original_type_name,
string override_type_name
);
对于前两种方法,覆盖方法的参数类型都是uvm_object_wrapper,所以要结合下面的方法来获取类名:
static function uvm_object_wrapper get_type ();
//返回调用该方法的类名
//该方法为静态方法,需要使用作用域操作符来调用
//静态成员属于类,不属于类的对象
例子如下:假设存在两个类,bird和parrot,parrot派生自bird,现在要用parrot来覆盖bird,则对于前两种覆盖方法 可以写为:
set_type_override_by_type(bird::get_type(),parrot::get_type());
set_inst_override_by_type(路径名,bird::get_type(),parrot::get_type());
对于后两种简化的方法,其参数类型为string,所以只需要写出类名即可,如下:
set_type_override(bird,parrot);
set_inst_override(路径名,bird,parrot);
并不是任意两个类之间都能够进行覆盖,这两个类必须满足下面的关系:
(1)被覆盖的类与覆盖的类首先都需要在factory中注册;
(2)被覆盖的类的实例化要使用creat()方法;
(3)覆盖的类与被覆盖的类之间存在派生的关系,覆盖的类必须派生自被覆盖的类,即被覆盖的类是父类,覆盖的类是子类(第一个参数类是父类,第二个参数类是子类);
(4)uvm_component与uvm_object之间不能重载;
以上的四个覆盖方法都属于uvm_component,所以只有组件可以调用;对于一个无法使用组件的地方,如top_tb的intial块中就无法调用,因此UVM提供了下面四个方法来替换上面的方法,这四个方法属于uvm_factory类,在UVM中是通用的,如下:
pure virtual function void set_type_override_by_type (
uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
bit replace = 1
);//该方法与uvm_component中的同名方法类似,传递的参数相同
pure virtual function void set_type_override_by_name (
string original_type_name,
string override_type_name,
bit replace = 1
);//该方法与uvm_component中set_type_override类似,传递的参数相同
pure virtual function void set_inst_override_by_type (
uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
string full_inst_path
);//该方法与uvm_component中的同名方法类似,路径要使用get_full_name()来获取完整路径
pure virtual function void set_inst_override_by_name (
string original_type_name,
string override_type_name,
string full_inst_path
);
注意,在uvm_factory中,存在一个全局变量factory,一般使用factory.set...的覆盖语句,如下:
factory.set_type_override_by_type(original_type, override_type,replace);//factory是一个全局变量
factory.set_inst_override_by_type( original_type,
override_type,
{get_full_name(),".",relative_inst_path});
来看一个例子:
上图的environment中,组件的类型分为多种driver、sequence和sequence item。主要包括:
首先来看sequence item:
//------------------ eth_packet-----------------------------------
class eth_packet extends uvm_sequence_item;
`uvm_object_utils (eth_packet)
...
function new (string name = "eth_packet");
super.new (name);
`uvm_info (get_type_name(), "Packet created", UVM_MEDIUM)
endfunction
endclass
//------------------ eth_v2_packet-----------------------------------
class eth_v2_packet extends eth_packet;
`uvm_object_utils (eth_v2_packet)
...
function new (string name = "eth_v2_packet");
super.new (name);
endfunction
endclass
再来看driver:
//------------------ base_driver-----------------------------------
class base_driver #(type T=eth_packet) extends uvm_driver;
`uvm_component_utils (base_driver #(T))
T pkt;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
pkt = T::type_id::create ("pkt0");
endfunction
virtual task run_phase (uvm_phase phase);
super.run_phase (phase);
`uvm_info (get_type_name(), $sformatf("Driver running ...with packet of type : %s", pkt.get_type_name()), UVM_MEDIUM)
endtask
endclass
//----------------- eth_driver-----------------------------------
class eth_driver #(type T=eth_packet) extends base_driver #(T);
`uvm_component_utils (eth_driver #(T))
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
endclass
//----------------- spi_driver-----------------------------------
class spi_driver #(type T=eth_packet) extends base_driver #(T);
`uvm_component_utils (spi_driver #(T))
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
endclass
再看sequence:
//----------------- base_sequence-----------------------------------
class base_sequence extends uvm_sequence;
`uvm_object_utils (base_sequence)
endclass
//----------------- seq1 -------------------------------------------
class seq1 extends base_eth_sequence;
`uvm_object_utils (seq1)
...
endclass
//----------------- seq2 -------------------------------------------
class seq2 extends base_eth_sequence;
`uvm_object_utils (seq2)
...
endclass
//----------------- seq3 -------------------------------------------
class seq3 extends base_eth_sequence;
`uvm_object_utils (seq3)
...
endclass
再看agent:
//----------------- my_agent -------------------------------------------
class my_agent extends uvm_agent;
`uvm_component_utils (my_agent)
base_driver m_drv0;
my_sequencer m_seqr0;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_drv0 = base_driver::type_id::create ("m_drv0", this);
m_seqr0 = my_sequencer::type_id::create ("m_seqr0", this);
endfunction
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
endclass
//----------------- my_agent_v2 -------------------------------------------
class my_agent_v2 extends uvm_agent;
`uvm_component_utils (my_agent_v2)
eth_driver m_drv0;
my_sequencer m_seqr0;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_drv0 = eth_driver::type_id::create ("m_drv0", this);
m_seqr0 = my_sequencer::type_id::create ("m_seqr0", this);
endfunction
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
endclass
driver和sequencer对象的创建都是在agent的build_phase中,而两者的连接在connect_phase中。这里使用了默认的sequencer,也就是sequence在默认的sequencer上执行。
我们再创建顶层的environment:
class my_env extends uvm_env ;
`uvm_component_utils (my_env)
my_agent m_agnt0;
my_agent m_agnt1;
my_agent_v2 m_agnt2;
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction : new
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_agnt0 = my_agent::type_id::create ("m_agnt0", this);
m_agnt1 = my_agent::type_id::create ("m_agnt1", this);
m_agnt2 = my_agent_v2::type_id::create ("m_agnt2", this);
endfunction : build_phase
endclass : my_env
这样,我们就完成了对上图中组件的组装。现在我们可以调用工厂中的方法用其他的组件在bulid_phase中来覆盖environment中的组件.
class feature_test extends base_test;
`uvm_component_utils (feature_test)
function new (string name, uvm_component parent = null);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
`ifdef PKT_OVERRIDE//如果存在PKT_OVERRIDE则执行下面的操作
// 用eth_v2_packet替换全部的eth_packets get_type()方法获得类名
set_type_override_by_type (eth_packet::get_type(), eth_v2_packet::get_type());
`endif
// These are the three different styles to override something
`ifdef DRV_STYLE1
// 用spi_driver替换所有的base_driver
set_type_override_by_type (base_driver::get_type(), spi_driver::get_type());
`elsif DRV_STYLE2
// 仅仅替换m_top_env.m_agnt2.m_drv0路径下的driver,用spi_driver替换base_driver
eth_driver::type_id::set_inst_override (spi_driver::get_type(), "m_top_env.m_agnt2.m_drv0", this);
`elsif DRV_STYLE3
//调用factory方法,用eth_driver替换agent0中的base_driver
factory.set_inst_override_by_type (base_driver::get_type(), eth_driver::get_type(), {get_full_name(), ".m_top_env.m_agnt0.*"});
`endif
// Trying to override a sequence
`ifdef SEQ_TYPE
// Substitute seq1 with seq2
set_type_override_by_type (seq1::get_type(), seq3::get_type());
`elsif SEQ_INST
// Substitute seq1 with seq2 only for agnt1
set_inst_override_by_type ("m_top_env.m_agnt1.m_seqr0.*", seq1::get_type(), seq2::get_type());
`else
`endif
factory.print();
endfunction
// Enter test code for feature here
endclass
下面是在定义了 PKT_OVERRIDE、DRV_STYLE3、SEQ_INST这几个宏的执行情况:
How to use uvm_printer
在随机验证环境中,数据对象是连续不断地产生的,并在不同的组件之间流通。如果数据对象的内容是可见的话,debug就很容易找出来了。
传统的方法是调用$display,如下面:
class Packet;
bit [31:0] addr;
int count_id;
bit [3:0] length;
...
function display ();
$display ("addr=0x%0h count_id=0x%0h length=0x%0h", p1.addr, p1.count_id, p1.length);
endfunction
endclass
module tb;
initial begin
Packet p1 = new();
p1.display();
end
endmodule
但是在uvm_object中有一个内建的print()方法,继承uvm_object的子类可以调用该方法以标准格式直接打印出类的内容,内建的print()方法如下:
virtual class uvm_object extends uvm_void;
...
function void print (uvm_printer printer=null);
if (printer==null)
printer = uvm_default_printer;
...
endfunction
...
endclass
print()方法的打印格式主要有以下三种:
(1)table print(默认格式)
(2)tree print
(3)line print
c1: (container@1013) { d1: (mydata@1022) { v1: 'hcb8f1c97 e1: THREE str: hi } value: 'h2d }
print()在默认的情况下使用表格的形式打印对象.你也可以在print()方法的参数中通过修改参数来改变方法的打印格式,如下:
class my_data extends uvm_sequence_item;
bit [2:0] mode;
endclass
my_data obj0;
obj0.print (); // Calls table printer by default
obj0.print (uvm_default_line_printer); // Calls line printe
使用print()方法打印类的内容,该类必须满足以下其中一个条件:
(1)将需要打印的内容放入宏`uvm_object_utils_begin
和 `uvm_object_utils_end 中,如下:
class my_data extends uvm_sequence_item;
rand bit [7:0] data;
rand bit [7:0] addr;
constraint c_addr { addr > 0; addr < 8;}
`uvm_object_utils_begin (my_data)//将需要打印的内容放入宏中
`uvm_field_int (data, UVM_ALL_ON)
`uvm_field_int (addr, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "my_data");
super.new (name);
endfunction
endclass
(2)定义do_print()方法,该方法为回调方法;当调用print()时会自动调用执行
class derivative extends my_data;
rand bit [2:0] mode;
rand bit [1:0] format;
`uvm_object_utils_begin (derivative)
`uvm_field_int (format, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name="derivative");
super.new (name);
endfunction
virtual function void do_print (uvm_printer printer);//注意do_print()的格式
printer.print_int ("mode", mode, $bits(mode));
`uvm_info ("DVR", "do_print called", UVM_MEDIUM)
endfunction
endclass
其次,还可以修改输出表格的格式,如下:
uvm_default_printer.knobs.size = 0; // Will not display the size column
uvm_default_printer.knobs.indent = 4; // Indents to the right by 4 spaces instead of the default 2
uvm_default_printer.hex_radix = "0x"; // Replaces the hex radix of 'h with 0x
来看下面一个例子:
//---------------------- colors --------------------------
class colors extends uvm_sequence_item;
rand bit [3:0] color;
rand bit enable;
`uvm_object_utils_begin (colors)
`uvm_field_int (color, UVM_ALL_ON)
`uvm_field_int (enable, UVM_ALL_ON)
`uvm_object_utils_end
endclass
//---------------------- format --------------------------
class format extends uvm_sequence_item;
rand bit [3:0] header;
rand bit [2:0] footer;
rand bit enable;
rand bit [1:0] body;
rand colors m_color;//colors在format中例化
`uvm_object_utils_begin (format)
`uvm_field_int (header, UVM_ALL_ON)
`uvm_field_int (footer, UVM_ALL_ON)
`uvm_field_int (enable, UVM_ALL_ON)
`uvm_field_int (body, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "format");
super.new (name);
m_color = colors::type_id::create ("m_color");
endfunction
virtual function void do_print (uvm_printer printer);
printer.print_object ("m_color", m_color);
endfunction
endclass
class my_data extends uvm_sequence_item;
rand format m_format0;//format在my_data中例化
rand bit [7:0] data;
rand bit [7:0] addr;
constraint c_addr { addr > 0; addr < 8;}
`uvm_object_utils_begin (my_data)
`uvm_field_int (data, UVM_ALL_ON)
`uvm_field_int (addr, UVM_ALL_ON)
`uvm_field_object (m_format0, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "my_data");
super.new (name);
m_format0 = format::type_id::create ("m_format0");
endfunction
endclass
//---------------------- derivative --------------------------
class derivative extends my_data;
`uvm_object_utils (derivative)
rand bit [2:0] mode;
function new (string name="derivative");
super.new (name);
endfunction
virtual function void do_print (uvm_printer printer);
printer.knobs.depth=0;
printer.print_int ("mode", mode, $bits(mode));
`uvm_info ("DVR", "do_print called", UVM_MEDIUM)
endfunction
endclass
例化上面的一系列的sequence item,并打印:
class base_test extends uvm_test;
`uvm_component_utils (base_test)
my_data obj0;
derivative dv0;
uvm_table_printer tprinter;
function new (string name = "base_test", uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
obj0 = my_data::type_id::create ("obj0");
dv0 = derivative::type_id::create ("dv0");
tprinter = new();
cfg_printer();
endfunction
virtual task run_phase (uvm_phase phase);
void'(obj0.randomize());
`uvm_info ("TST", "print() called with no arguments", UVM_MEDIUM)
obj0.print (); // uses uvm_default_printer
`uvm_info ("TST", "Calling print (uvm_default_line_printer)", UVM_MEDIUM)
obj0.print (uvm_default_line_printer);
`uvm_info ("TST", "Calling print (uvm_default_tree_printer)", UVM_MEDIUM)
obj0.print (uvm_default_tree_printer);
`uvm_info ("TST", "Calling print (uvm_default_table_printer)", UVM_MEDIUM)
obj0.print (uvm_default_table_printer);
`uvm_info ("TST", "Calling print (tprinter)", UVM_MEDIUM)
obj0.print (tprinter);
`uvm_info ("TST", "Print derivative (tprinter)", UVM_MEDIUM)
dv0.print (uvm_default_table_printer);
endtask
// Configuration to change display settings for the printer
virtual function void cfg_printer ();
tprinter.knobs.full_name = 1; // Show full name of the variable relative to class instance
tprinter.knobs.size = 0; // Do not show size column
tprinter.knobs.depth = 1; // Only show upto 1 level of the nested hierarchy
tprinter.knobs.reference = 2; // Do not print object ID handles
tprinter.knobs.type_name = 0; // Do not show Type column
tprinter.knobs.indent = 4; // Indent from the left by 4
tprinter.knobs.hex_radix = "0x"; // Replace 'h radix by 0x - observe Value field to see this
endfunction
endclass
依次输出的结果如下:
Reporting Functions
在前面我们介绍过,所有的组件都是继承于uvm_report_component,所以它们存在内建的方法来显示信息。
在UVM中存在四种报告函数(report function),它们可以带有不同的复杂度(verbosity level),格式如下:
uvm_report_* ("TAG", $sformatf ("[Enter the display message]"), VERBOSITY_LEVEL);
其中"*"可以是 info, error, warning, fatal 其中一种。UVM有6种复杂度,每一种代表一个整数值,如下:
typedef enum {
UVM_NONE = 0,
UVM_LOW = 100,
UVM_MEDIUM = 200,
UVM_HIGH = 300,
UVM_FULL = 400,
UVM_DEBUG = 500
} uvm_verbosity;
上面的格式语句中,只有 info才要求带复杂度选项,并且使用fatal语句会退出仿真环境。如下面的例子:
uvm_report_info (get_type_name (), $sformatf ("None level message"), UVM_NONE);
uvm_report_info (get_type_name (), $sformatf ("Low level message"), UVM_LOW);
uvm_report_info (get_type_name (), $sformatf ("Medium level message"), UVM_MEDIUM);
uvm_report_info (get_type_name (), $sformatf ("High level message"), UVM_HIGH);
uvm_report_info (get_type_name (), $sformatf ("Full level message"), UVM_FULL);
uvm_report_info (get_type_name (), $sformatf ("Debug level message"), UVM_DEBUG);
uvm_report_warning (get_type_name (), $sformatf ("Warning level message"));
uvm_report_error (get_type_name (), $sformatf ("Error level message"));
uvm_report_fatal (get_type_name (), $sformatf ("Fatal level message"));
也可以显示文件名和显示语句所在的行数,这需要用到宏`__FILE__和 `__LINE__,语法如下:
uvm_report_info (get_type_name (), $sformatf ("None level message - Display File/Line"), UVM_NONE, `__FILE__, `__LINE__);
这种方式对于找出debug是非常有效的;也可以在命令行窗口中输入+UVM_REPORT_DISABLE_FILE_LINE阻止该显示。
上面调用函数的方法显得太过复杂,在UVM中一般调用宏来显示,宏会自动显示文件和行数信息,而不用调用`__FILE__和 `__LINE__;看下面的例子:
`uvm_info (get_type_name (), $sformatf ("[Driver] None level message"), UVM_NONE)
`uvm_info (get_type_name (), $sformatf ("[Driver] Low level message"), UVM_LOW)
`uvm_info (get_type_name (), $sformatf ("[Driver] Medium level message"), UVM_MEDIUM)
`uvm_info (get_type_name (), $sformatf ("[Driver] High level message"), UVM_HIGH)
`uvm_info (get_type_name (), $sformatf ("[Driver] Full level message"), UVM_FULL)
`uvm_info (get_type_name (), $sformatf ("[Driver] Debug level message"), UVM_DEBUG)
`uvm_warning (get_type_name (), $sformatf ("[Driver] Warning level message"))
`uvm_error (get_type_name (), $sformatf ("[Driver] Error level message"))
`uvm_fatal (get_type_name (), $sformatf ("[Driver] Fatal level message"))
关于复杂度,默认情况下是 UVM_MEDIUM;如果你将复杂度设为UVM_HIGH,则只有低于该复杂度的显示语句才会显示。
Setting policy using uvm_comparer
uvm_compare是一个单独的类,用来设定比较的标准(policy) 以及决定误判(miscompare)的计数(count)。uvm_object带有内建的compare()方法,通过uvm_compare设定比较的标准如比较的深度、复杂度、最大误判数等。误判数是以一个叫做"result"的整数值变量存放的,当每一个误判发生后,它自动加1.所以建议在每次比较之前,将误判数清零,也就是将标准重新例化一次。看下面例子:
class base_test extends uvm_test;
`uvm_component_utils (base_test)
my_data obj0, obj1;
derivative dv0, dv1;
uvm_comparer uc0;
function new (string name = "base_test", uvm_component parent);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
obj0 = my_data::type_id::create ("obj0");
obj1 = my_data::type_id::create ("obj1");
uc0 = new();
endfunction
virtual task run_phase (uvm_phase phase);
cfg_comparer();
// Do not use the same uvm_compare object to do multiple comparisons, like shown below
// The example is a demonstration of the different methods of uvm_compare
// NOTE: There's an internal variable "result" that stores the number of miscompares.
// This variable will continue to be incremented for every mismatch, until cleared manually
`uvm_info ("COMPARE", "Trying out compare_field", UVM_MEDIUM)
uc0.compare_field ("compare_field1", 7, 7, 2); // pass
uc0.compare_field ("compare_field2", 8'd45, 8'd8, 2); // fail
`uvm_info ("COMPARE", "Trying out field_int", UVM_MEDIUM)
uc0.compare_field_int ("field_int1", 64'hface_deaf_feed_cafe, 64'hfabe_deaf_feed_cafe, 64); // fail
uc0.compare_field_int ("field_int2", 64'habcd_ef12_3456_7890, 64'habcd_ef12_3456_7890, 64); // pass
uc0.compare_field_int ("field_int3", 64'hface_deaf_feed_cafe, 64'h7ace_deaf_feed_cafe, 63); // pass
uc0.compare_field_int ("field_int4", 64'hface_deaf_feed_cafe, 64'h8abe_deaf_feed_cafe, 64); // fail
uc0.compare_field_int ("field_int5", 68'hbbbb_face_deaf_feed_cafe, 68'haaaa_8abe_deaf_feed_cafe, 68); // won't work; nothing happens
uc0.compare_field ("field", 68'hb_face_deaf_feed_cafe, 68'haa_8abe_deaf_feed_cafe, 68); // fail: will work with field, because size > 64
`uvm_info ("COMPARE", "Trying out compare_object", UVM_MEDIUM)
void'(obj0.randomize());
void'(obj1.randomize());
uc0.compare_object ("object1", obj0, obj1); // fail
obj1.copy (obj0);
uc0.compare_object ("object2", obj0, obj1); // pass
`uvm_info ("COMPARE", "Trying out compare_string", UVM_MEDIUM)
uc0.compare_string ("string1", "Hello", "World"); // fail
uc0.compare_string ("string2", "Hello", "Hello"); // pass
// Proper Usage: Set the configuration for uvm_comparer and pass it to compare()
// The "result" variable is cleared before comparison starts within uvm_object::compare()
obj0.name = "Apple";
obj0.m_format0.m_color.fav = "magenta";
obj0.m_format0.m_color.unfav = "yellow";
obj1.name = "Orange";
obj1.m_format0.m_color.fav = "magenta";
obj1.m_format0.m_color.unfav = "green";
cfg_comparer();
void'(obj0.randomize());
obj1.compare (obj0, uc0);//重新例化uvm_compare
// randomize and compare again
void'(obj0.randomize());
obj1.compare (obj0, uc0);//重新例化uvm_compare
endtask
virtual function cfg_comparer();//比较标准
uc0.show_max = 20; // total number of miscompares to be printed
uc0.verbosity = UVM_MEDIUM;
endfunction
endclass
结果如下:
HDL access routines
UVM提供了在testbench中通过DPI/PLI接口访问HDL代码的途径.考虑下面一个简单的设计:
module B;
reg [3:0] cfg;
endmodule
module A;
B b;
endmodule
// Testbench module
module tb;
A a();
initial
run_test("base_test");
endmodule
访问分为以下几种:
uvm_hdl_check_path
class base_test extends uvm_test;
...
virtual function void build_phase (uvm_phase phase);
if (uvm_hdl_check_path ("tb.a.b.cfg"))//如果给定的该路径存在,则返回1;否则返回0
`uvm_info ("TEST", "Path tb.a.b.cfg exists", UVM_MEDIUM)
if (!uvm_hdl_check_path ("tb.a.def"))
`uvm_info ("TEST", "Path tb.a.def does not exist", UVM_MEDIUM)
endfunction
endclass
uvm_hdl_deposit
class base_test extends uvm_test;
...
virtual task run_phase (uvm_phase phase);
if (! uvm_hdl_deposit("tb.a.b.cfg", 4'h9))//如果对给定路径下的变量赋值成功则返回1,否则返回0
`uvm_error ("TEST", "Deposit on tb.a.b.cfg failed", UVM_MEDIUM)
endtask
endclass
uvm_hdl_force
class base_test extends uvm_test;
...
virtual task run_phase (uvm_phase phase);
if (! uvm_hdl_force("tb.a.b.cfg", 4'h9))
//对给定路径的变量强制赋值,如果成功则返回1,否则返回0
`uvm_error ("TEST", "Force on tb.a.b.cfg failed", UVM_MEDIUM)
endtask
endclass
uvm_hdl_force 与uvm_hdl_deposit 的不同在于,前者在一定的时间点所赋予的值会被释放,以便使原始的线网可以驱动这个信号;而后者一旦被赋值,就会一直等待直到有线网重新给该变量赋值.
uvm_hdl_force_time
class base_test extends uvm_test;
...
virtual task run_phase (uvm_phase phase);
if (! uvm_hdl_force_time("tb.a.b.cfg", 4'h9, 30))//给给定路径下的变量在一定的时间点赋值
//操作成功则返回1,否则返回0
`uvm_error ("TEST", "Force on tb.a.b.cfg failed", UVM_MEDIUM)
endtask
endclass
uvm_hdl_release_and_read
class base_test extends uvm_test;
...
virtual task run_phase (uvm_phase phase);
if (! uvm_hdl_release_and_read("tb.a.b.cfg", 4'hE))//将原值释放重新赋值,成功返回1,否则返回0
`uvm_error ("TEST", "Release on tb.a.b.cfg failed", UVM_MEDIUM)
endtask
endclass
uvm_hdl_release
class base_test extends uvm_test;
...
virtual task run_phase (uvm_phase phase);
if (! uvm_hdl_release("tb.a.b.cfg"))
`uvm_error ("TEST", "Release on tb.a.b.cfg failed", UVM_MEDIUM)
endtask
endclass
uvm_hdl_read
class base_test extends uvm_test;
...
virtual task run_phase (uvm_phase phase);
int rdata;
if (! uvm_hdl_read("tb.a.b.cfg", rdata))
`uvm_error ("TEST", "Read from tb.a.b.cfg failed", UVM_MEDIUM)
endtask
endclass
UVM Pool
uvm_pool是一个基于类(class based)的参数化的动态关联数组,它可以根据需求动态分配存储空间.它会返回一个全局句柄,以便在各个组件之间分享item.声明格式如下:
class uvm_pool #(type KEY = int, T = uvm_void) extends uvm_object;
uvm_pool中的方法如下表:
看下面例子:
class base_test extends uvm_test;
...
uvm_pool #(string, int) my_pool;
string key;
virtual task run_phase (uvm_phase phase);
my_pool = new ("age");//创建关联数组
my_pool.add("Ross", 28);//添加元素到数组,两个参数分为为元素名和元素值
my_pool.add("Rachel", 26);
`uvm_info ("TEST", $sformatf ("my_pool : %0d", my_pool.num()), UVM_MEDIUM)//获得数组元素数目
`uvm_info ("TEST", $sformatf ("my_pool [Ross] : %0d", my_pool.get("Ross")), UVM_MEDIUM)//获得数组元素值
`uvm_info ("TEST", $sformatf ("Rachel exists ? %0d", my_pool.exists("Rachel")), UVM_MEDIUM)//判定元素是否存在
`uvm_info ("TEST", $sformatf ("Joey exists ? %0d", my_pool.exists("Joey")), UVM_MEDIUM)
my_pool.delete("Ross");//删除数组元素
`uvm_info ("TEST", $sformatf ("Delete Ross, my_pool : %0d", my_pool.num()), UVM_MEDIUM)
my_pool.add("Chandler", 28);
my_pool.add("Phoebe", 27);
my_pool.add("Monica", 26);
my_pool.first(key);
`uvm_info ("TEST", $sformatf ("first = %s", key), UVM_MEDIUM)
my_pool.last(key);
`uvm_info ("TEST", $sformatf ("last = %s", key), UVM_MEDIUM)
key = "Monica";
my_pool.next(key);
`uvm_info ("TEST", $sformatf ("next = %s", key), UVM_MEDIUM)
my_pool.prev(key);
`uvm_info ("TEST", $sformatf ("prev = %s", key), UVM_MEDIUM)
endtask
endclass
结果: