一、工厂的注册,创建和覆盖机制
工厂的注册:为什么需要注册,因为在 uvm 世界中,uvm工厂的存在就是为了更方便地替换验证环境中的实例或者注册了的类型,工厂的注册机制也就带来了配置的灵活。
验证环境构成可以分为两个部分,一部分构成了环境的层次,这部分代码是通过uvm_component 类完成,另外一部构成了环境的属性(例如配置)和数据传输,这一部分通过 uvm_object 类完成。
注册需要注册宏,包括: `uvm_object_utils(my_driver) `uvm_component_utils(my_driver)
这俩宏的作用:就是将 my_driver 登记在uvm 内部的一张表中,这张表是factory 功能实现的基础。只要在定义一个新的类时使用这俩宏,就相当于把这个类注册到了这张表中。
注册的代码如下:
工厂机制的实例创建为:uvm_component new() 比 uvm_object new() 多了一个 parent,便于构成验证层次结构(这也是uvm_component 的两大特性之一,另一个是有phase的自动执行机制),uvm_object 已经是最顶层了,从uvm库中可以看到,uvm_component 也继承于它。
注意不要忘了加上 super.new(name); super.new(name,parent);
类实例的创建:最常见的就是我们 Systemverilog中的 new()。
factory机制中的实例创建有三种:
1)type_name::type_id::create("name", parent);
2)f.create_object_by_type(trans::get_type(), get_full_name(), "name");
f.create_component_by_type(trans::get_type(), get_full_name(), "name", this);
其中, get_type() 是获得类型, get_full_name() 是获得完整的当前component的路径
3)create_object("trans","t4"); create_component("trans","t4");
class object_create extends top;
trans t1, t2, t3, t4;
`uvm_component_utils(object_create)
function new(string name = "object_create", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
uvm_factory f = uvm_factory::get(); // get singleton factory
super.build_phase(phase);
t1 = new("t1"); // direct construction
t2 = trans::type_id::create("t2", this); // common method
void'($cast(t3,f.create_object_by_type(trans::get_type(), get_full_name(), "t3"))); // factory method
void'($cast(t4,create_object("trans", "t4"))); // pre-defined method inside component
endfunction
endclass
class component_create extends top;
unit u1, u2, u3, u4;
`uvm_component_utils(component_create)
function new(string name = "component_create", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
uvm_factory f = uvm_factory::get(); // get singleton factory
super.build_phase(phase);
u1 = new("u1"); // direct construction
u2 = unit::type_id::create("u2", this); // common method
void'($cast(u3,f.create_component_by_type(unit::get_type(), get_full_name(), "u3", this))); // factory method
void'($cast(u4,create_component("unit", "u4"))); // pre-defined method inside component
endfunction
endclass
使用 factory 机制的重载是有前提的,并不是任意的类都可以互相重载,要想使用重载,必须满足以下要求:
- 无论是重载的类还是被重载的类,都要在定义时注册到factory机制中。
- 被重载的类在实例化时,要使用 factory 机制式的实例化方式,而不是能使用传统的new方式。
- 最重要的是,重载的类要与被重载的类有派生关系。重载的类必须派生子被重载的类,被重载的类必须是重载类的父类。
- component 与 object 之间互相不能重载,虽然 uvm_component 是派生自 uvm_object ,但是这两者的血缘关系太远了,远到根本不能重载。从两者的new参数的函数就可以看出,两者互相重载时,多出来的一个parent 参数会使 factory 机制无所适从。
重载的代码:
set_type_override_by_type(uvm_object_wrapper original_type, uvm_object_wrapper override_type);
set_type_override(string original_type_name, string override_type_name);
class object_override extends object_create;
`uvm_component_utils(object_override)
function new(string name = "object_override", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
set_type_override_by_type(trans::get_type(), bad_trans::get_type());
super.build_phase(phase);
endfunction
endclass
class component_override extends component_create;
`uvm_component_utils(component_override)
function new(string name = "component_override", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
set_type_override("unit", "big_unit");
super.build_phase(phase);
endfunction
endclass
二、域自动化(field automation)和 uvm_object 的常用方法
uvm通过域的自动化,使得用户在注册 uvm 类的同时也可以声明今后会参与到对象复制,克隆,打印等操作的成员变量,简化了对象的操作,可以直接使用这些函数。
最简单的 uvm_field 系列宏有如下几种:
`define uvm_field_int(ARG,FLAG)
`define uvm_field_real(ARG,FLAG)
`define uvm_field_enum(ARG,FLAG)
`define uvm_field_object(ARG,FLAG)
`define uvm_field_event(ARG,FLAG)
`define uvm_field_string(ARG,FLAG)
这些用于域的自动化的宏声明应该在 uvm_object 或 uvm_component 注册时发生。即在 `uvm_object_utils_begin 和 `uvm_object_utils_end 之间, 或者在 `uvm_component_utils_begin 和 `uvm_component_utils_end 之间声明要自动化的域。在声明自动化的域时,除了要注意运用正确的宏来匹配域的成员类型(ARG)还应该声明这些域在将来参与的数据操作(FLAG)
目前 FLAG = UVM_ALL_ON 表示该域参与所有的数据操作
以下代码使用了 compare(),print(),copy()
还有全局控制对象变量:uvm_default_comparer.show_max = 10; 可以把比较的数量调大一点,默认是1,这样不会说比较完一个就停下来了
package object_methods_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
typedef enum {WRITE, READ, IDLE} op_t;
class trans extends uvm_object;
bit[31:0] addr;
bit[31:0] data;
op_t op;
string name;
`uvm_object_utils_begin(trans)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_enum(op_t, op, UVM_ALL_ON)
`uvm_field_string(name, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "trans");
super.new(name);
`uvm_info("CREATE", $sformatf("trans type [%s] created", name), UVM_LOW)
endfunction
function bit do_compare(uvm_object rhs, uvm_comparer comparer);
trans t;
do_compare = 1;
void'($cast(t, rhs));
if(addr != t.addr) begin
do_compare = 0;
`uvm_warning("CMPERR", $sformatf("addr %8x != %8x", addr, t.addr))
end
if(data != t.data) begin
do_compare = 0;
`uvm_warning("CMPERR", $sformatf("data %8x != %8x", data, t.data))
end
if(op != t.op) begin
do_compare = 0;
`uvm_warning("CMPERR", $sformatf("op %s != %8x", op, t.op))
end
if(addr != t.addr) begin
do_compare = 0;
`uvm_warning("CMPERR", $sformatf("name %8x != %8x", name, t.name))
end
endfunction
endclass
class object_methods_test extends uvm_test;
`uvm_component_utils(object_methods_test)
function new(string name = "object_methods_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
task run_phase(uvm_phase phase);
trans t1, t2;
bit is_equal;
phase.raise_objection(this);
t1 = trans::type_id::create("t1");
t1.data = 'h1FF;
t1.addr = 'hF100;
t1.op = WRITE;
t1.name = "t1";
t2 = trans::type_id::create("t2");
t2.data = 'h2FF;
t2.addr = 'hF200;
t2.op = WRITE;
t2.name = "t2";
is_equal = t1.compare(t2);
uvm_default_comparer.show_max = 10;
is_equal = t1.compare(t2);
if(!is_equal)
`uvm_warning("CMPERR", "t1 is not equal to t2")
else
`uvm_info("CMPERR", "t1 is equal to t2", UVM_LOW)
`uvm_info("COPY", "Before uvm_object copy() taken", UVM_LOW)
t1.print();
t2.print();
`uvm_info("COPY", "After uvm_object t2 is copied to t1", UVM_LOW)
t1.copy(t2);
t1.print();
t2.print();
`uvm_info("CMP", "Compare t1 and t2", UVM_LOW)
is_equal = t1.compare(t2);
if(!is_equal)
`uvm_warning("CMPERR", "t1 is not equal to t2")
else
`uvm_info("CMPERR", "t1 is equal to t2", UVM_LOW)
#1us;
phase.drop_objection(this);
endtask
endclass
endpackage
三、phase机制
uvm中的phase,按照是否消耗仿真时间分为两大类,一类是 function phase :build_phase, connect_phase 等,另一类是 task phase : run_phase 以及与它并行的右边的12个细分phase。
虽然 run_phase 与细分的12个phase是并行的,但12个phase也是按先后顺序执行的。
并且run_phase 与 12个phase 可以认为是 同时启动,但谁先执行完不一定,故为了不必要的干扰,尽量不要将他们混合起来使用。
四、config机制
在验证环境的创建过程build phase中,除了组件的实例化,配置也是必不可少的,为了验证环境的复用性,通过外部的参数配置,使得环境在创建时可以根据不同参数来选择创建组件的组件类型,组件实例数目,组件之间的连接以及组件的运行模式等。UVM提供了uvm_config_db 配置类以及几种方便的变量设置来实现仿真的环境控制,常见的 uvm_config_db 类的使用方式包括:
- 将virtual interface 传递到环境中
- 设置单一变量值,例如 int,string,enum等
- 传递配置对象(config object)到环境
1. 传递接口
首先,使用这种传递方式很好的解决了连接硬件世界和软件世界的问题。在之前SV的章节中可以看到,虽然SV可以通过层次化的interface的索引完成传递,但是这种方式不利于软件环境的封装和复用。把接口的传递和获取彻底分开,在后台对 virtual interface 的传递立下功劳的便是 uvm_config_db。
set:uvm_config_db#(virtual uvm_config_if)::set(uvm_root::get(), "uvm_test_top.*","vif",if0)
其中第一个参数也可以写为:null , uvm会自动把它替换为 uvm_root::get() 即 root_top
第一个参数加上第二个参数,构成要传递的目标路径,第三个参数表示一个记号,第四个参数是要设置的值。
在组件中获取:get 的参数同set,
一般地,第一个参数如果为this, 那么第二个参数可以是一个空的字符串,第三个参数set 和 get要设置的相同为了匹配,第四个参数还是设置的变量。
注意:
2.传递变量
在各个test中,可以在 build phase 对底层组件的变量加以配置,进而在环境例化之前完成配置,使得环境可以按照预期运行。
uvm_config_test中的 set:
comp1组件中 get:
3.传递配置对象
在 test 配置中,需要配置的参数不只是数量多,可能还分属不同的组件,对这么多层次中的变量做出类似上面的变量设置,需要更多的代码,容易出错且不易于复用,甚至底层组件的变量被删除后也无法通过 uvm_config_db::set() 得知配置是否成功。然而,如果整合每个组件中的变量,首先放置到一个 uvm_object 中, 再对中心化的配置对象进行传递,将更有利于整体环境的修改和维护。
五、消息管理
uvm 通过冗余度级别的设置提高了仿真日志的可读性,在打印信息之前,uvm 会比较要显示信息的冗余度级别与默认的冗余度阈值。如果小于等于阈值,就会显示;否则不会显示。默认的冗余度阈值是 UVM_MEDIUM 。
get_report_verbosity_level() 函数可以得到某个component 的冗余度阈值
set_report_verbosity_level(UVM_HIGH) 函数可以用来设置某个特定component的默认冗余度阈值
set_report_verbosity_level_hier(UVM_NONE) 函数可以把某个component及其下的所有的component 的冗余度阈值设置为UVM_NONE。
set_report_id_verbosity_hier("TOPTB", UVM_NONE) 函数可以把特性id 的component及其下的所有的component的冗余度阈值设置为 UVM_NONE