(1)认识UVM世界的版图(类库)和核心机制
(2)学习核心的UVM组件间和层次构建方式
(3)了解常见的UVM组件间的通信方式
(4)深入UVM测试场景的构成
(5)UVM寄存器模型的应用

1.核心基类:核心基类提供最底层的支持,包括一些基本的方法,如复制、创建、比较和打印。
2.工厂(factory)类:工厂类提供注册环境组件、创建组件和覆盖组件类型的方法。
3.事务(transaction)和序列(sequence)类:事务类和序列来用来规定在TLM传输管道中的数据类型和数据生成方式。
4.结构创建(structure creation)类;
5.环境组件(environment component)类:环境组件类则是构成环境结构的主要部分。
6.通信管道(channel)类:事务接口类和通信管道类则共同实现组件之间的通信和存储。
7.信息报告(message report)类;
8.寄存器模型(register model)类:用来完成对寄存器和存储的建模、访问和验证。
9.线程同步(thread synchronization)类:线程同步则比sv自身的同步方法更方便,发生同步时可传递更多的信息更多。
10.事务接口(transaction interface)类;
UVM工厂的存在就是为了更方便地替换验证环境中的实例或已注册的类型,同时工厂的注册机制带来配置的
灵活性。这里的实例或类型替代在UVM中称为覆盖(override),用来替换的对象或者类型应满足注册
(registeration)和多态(polymorphism)的要求。UVM的验证环境分为两部分,一部分构成环境的层
次,这部分代码通过uvm_component类完成,另一部分构成环境的属性(例如配置)和数据传输,这一部分
通过uvm_object类完成。
1.一般运用factory的步骤为:
(1)定义
(2)注册
(3)构建函数
class comp1 extends uvm_component;//(1)定义
`uvm_component_utils(comp1) //宏定义:(2)注册
function new(string name="comp1",uvm_component parent=null);//(3)构建函数,当uvm_component的时候两个固定参数name和parent,name为class名,parent为上一层,即例化comp1的那一层。
super.new(name,parent);//对父类里面的new函数做了继承
$display($sformatf("%s is created",name));
endfunction:new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction:build_phase
endclass
class obj1 extends uvm_object;//(1)定义
`uvm_object_utils(obj1)//(2)注册
function new(string name = "obj1");//(3)构建函数,当uvm_object时候只有一个参数name
super.new(name);
$display($sformatf("%s is create",name));
endfunction: new
endclass
comp1 c1,c2; //句柄
obj1 o1,o2; //句柄
initial begin
c1 = new("c1"); //sv方式:new函数创建对象
o1 = new("o1");
c2 = comp1::type_id::create("c2",null); //uvm_component对象创建方式:工厂的方式来创建对象,要求使用工厂这种方式来创建对象
o2 = obj1::type_id::create("o2");//uvm_object对象创建方式
end
2.uvm_coreservice_t类
该类内置了UVM世界核心的组件和方法,它们主要包括
(1)唯一的uvm_factory,该组件用来注册、覆盖和例化
(2)全局的report_server,该组件用来做消息统筹和报告
(3)全局的tr_database,该组件用来记录transaction记录
(4)get_root()方法用来返回当前UVM环境的结构顶层对象
(1)在UVM1.2中,明显的变化是通过uvm_coreservice_t将最重要的机制(也是必须做统一例化处理的组件)都放置在了uvm_coreserice_t类中。
(2)该类并不是uvm_component或者uvm_object,它也并没有例化在UVM环境中,而是独立于UVM环境之外的。uvm_coreservice_t产生factory,factory可以创建component和object类型,所以该类型不是component和object类型。
3.工厂创建component/object的方法
create_component_by_name()
create_component_by_type()
create_object_by_name()
create_object_by_type()
1.覆盖机制可以将原来所属的类型替换为另外一个新的类型
2.在覆盖之后,原本用来创建原属性类型的请求,将由工厂来创建新的替换类型。
(1)无需再修改原始代码,继而保证了原来代码的封装性。
(2)新的替换类型必须与被替换类型相兼容,否则稍后的句柄赋值将失效,所以使用继承。
3.做顶层修改时,非常方便!
(1)允许灵活的配置,例如可使用子类来覆盖原本的父类
(2)可使用不同的对象来修改其代码行为
4.要想实现覆盖特性,原有类型和新类型均需要注册
5.当使用create()来创建对象时:
(1)工厂会检查,是否原有类型被覆盖
(2)如果是,那么它会创建一个新类型的对象
(3)如果不是,那么它会创建一个原有类型的对象
6.覆盖发生时,可以使用“类型覆盖”或者“实例覆盖”
(1)类型覆盖指,UVM层次结构下的所有原有类型都被覆盖类型所替换
(2)实例覆盖指,在某些位置中的原有类型会被覆盖类型所替换
7.覆盖方法
(1)类型覆盖:set_type_override()
static_function void set_type_override(uvm_object_wrapper
override_type,bit replace=1);
- uvm_object_wapper override_type
这是什么?并不是某一个具体实例的句柄,实际上是注册过后的某一个类在
工厂中注册时的句柄。怎么找到它呢?就使用new_type::get_type()。
- bit replace = 1
1:如果已经有覆盖的存在,那么新的覆盖会替代旧的覆盖。
0:如果已经有覆盖的存在,那么该覆盖将不会生效。
- set_type_override是一个静态函数
orig_type::type_id::set_type_override(new_type::get_type())
(2)实例名覆盖:set_inst_override()
static function void set_inst_override(uvm_objext_wrapper
override_type,string inst_path,uvm_component parnet=null);
- string inst_path指向的是组件结构的路径字符串
- uvm_component parent = null
如果缺省,表示使用inst_path的内容为绝对路径,
如果有值传递,则使用{parent.get_full_name(),'.',inst_path}
来作为目标路径
- set_type_override是一个静态函数
- orig_type::type_id::set_inst_override(new_type::get_type(),"orig_inst_path")
8.如何使用覆盖相关的函数
- 首先需要知道,有不止一个类提供与覆盖相关的函数,然而名称与参数列表可能各不相同:
- uvm_component::set_(type,inst)_override{_by_type}
- uvm_component_registry::set_{type,inst}_override
- uvm_object_registery::set_{type,inst}_override
- uvm_factory::set_{type,inst}_override
-因此,想要实现类型替换,也有不止一种方式。包括上述给的例子中通过orig_type::type_id来调用覆盖函数,还可以在uvm_component的域中直接调用,或者使用uvm_factory来做覆盖。
9.例子
目的:comp2替代comp1
module factory_override;
import uvm_pkg::*; //vcs中需要编译
`include "uvm_macros.svh" //vcs中需要编译
class comp1 extends uvm_component;//定义
`uvm_component_utils(comp1)//注册
function new(string name= "comp1,uvm_component parnet=null");//构造函数
super.new(name,parnet);
$display($sformatf("comp1::%s is created",name));
endfunction:new
virtual function void hello(string name);
$display($sformatf("comp1::%s said hello!",name));
endfunction
endcalss
class comp2 extends comp1;//定义
`uvm_component_utils(comp2)//注册
function new(string name="comp2",uvm_component parent=null);//构建函数
super.new(name,parent);
$display($sformatf("comp2::%s is created",name));
endfunction:new
function void hello(string name);
$display($sformatf("comp2::%s said hello!",name));
endfunction
endclass
//注意,需要先做覆盖的配置,再做类型的变化
comp1 c1,c2;
initial begin
comp1::type_id::set_type_override(comp2::get_type());//set_type_override覆盖方法
c1 = new("c1");//没有通过工厂创建所以没有调换
c2 = comp1::type_id::create("c2",null);//使用工厂的创建方法有利于使用覆盖
c1.hello("c1");
c2.hello("c2");
end
endmodule
打印信息
comp1::c1 is created
comp1::c2 is created
comp2::c2 is created
comp1::c1 said hello!
comp2::c2 said hello!
10.结果分析
(1)在例化c2之前,首先应该用comp2来替换comp1的类型,只有先完成了类型替换,才可以在后面的例化时由factory选择正确的类型。
(2)上面的例码中较好地反映了一些实际情况,首先在声明c2时,由于验证工程师不知道今后可能存在着覆盖,所以类型为comp1。在后面发生了类型的替换以后,如果原有的代码不做更新 ,那么c2句柄的类型仍然为comp1,却指向了comp2类型的对象。这就要求,comp2应该是comp1的子类,只有这样,句柄指向才是安全合法的。
(3)c2在调用hello()方法时,由于首先是comp1类型,那么会查看comp1::hello(),又由于该方法定义是被指定为虚函数,这就通过了多态性的方法调用,转而调用了comp2::hello()函数。因此,显示的结果也是“comp2::c2 said hello”。
- 从UVM通过域的自动化,使得用户在注册UVM类的同时也可以声明今后会参与到对象拷贝、克隆、打印等操
的成员变量。
- 域的自动化解放了verifier的双手,这使得在使用uvm_object提供的一些预定义方法时,非常便捷,而
无需再实现自定义方法。
- 在了解了域的自动化常用的宏之后,用户需要考虑那些成员变量在注册UVM类(`uvm_{component
,object}_utils)的时候,也一并将它们归置到对应的域列表中,以便为稍后的域方法提供可以自动实现
基础。
class box extends uvm_object;//定义
int volume = 120;//变量定义
color_t color = WHITE;
string name = "box";
`uvm_object_utils_begin(box) //注册+域的自动化的声明,使用了宏定义
`uvm_field_int(volume,UVM_ALL_ON)
`uvm_field_enum(color_t,color,UVM_ALL_ON)
`uvm_field_string(name,UVM_ALL_ON)
`uvm_object_utils_end
...//省略了构建函数:new函数
endclass
box b1,b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b2 = new();
b2.copy(b1);//复制
b2.name = "box2";
end
- 在uvm的数据操作中,需要对copy和clone加以区分。前者默认已经创建好了对象,只需要对数据进行
拷贝;后者则会自动创建对象并对source object进行数据拷贝,再返回target object句柄。
- 无论是copy或者clone,都需要对数据进行复制。
- 但是如果数据成员包括句柄,那么拷贝的时候,是否拷贝该成员句柄本身,还是也额外创建新的对象,拷
贝该句柄指向的对象?
- 以示例可以看到,在进行copy时,默认进行的是深拷贝(deep copy),即会执行copy()和do_copy()。
class ball extends uvm_object;//定义
int diameter = 10;//变量定义
color_t color = RED
`uvm_object_utils_begin(ball)//注册+域的自动化
`uvm_filed_int(diameter,UVM_DEFAULT)
`uvm_filed_enum(color_t,color,UVM_NOCOPY)
`uvm_object_utils_end
... //省略了构建函数:new函数
function void do_copy(uvm_object rhs);
ball b;
$cast(b,rhs);
$display("ball::do_copy enterd..");
if(b.diameter <= 20)begin
diameter = 20;
end
endfunction
endclass
calss box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
ball b;
`uvm_object_utils_begin(box)
`uvm_filed_int(volume,UVM_ALL_ON)
`uvm_filed_enum(color_t,color,UVM_ALL_ON)
`uvm_filed_string(name,UVM_ALL_ON)
`uvm_filed_object(b,UVM_ALL_ON)
`uvm_object_utils_end
...
endclass
box b1,b2;
initial begin
b1 = new("box1");//创建
b1.volume = 80;//修改值
b1.color = BLACK;
b1.b.color = WHITE;
b2 = new();
b2.copy(b1);//拷贝
b2.name = "box2";
$display("%s",b1.sprint());
$display("%s",b2.sprint());
end

分析:
- 新添加的一个类ball,并且在box中例化了一个ball的对象。在拷贝过程中,box的其他成员都正常拷贝
了,但对于box::b拷贝则通过了ball的深拷贝方式进行。
- 即先执行自动拷贝copy(),来拷贝运行拷贝的域,由于ball::color不允许拷贝,所以只拷贝了ball::
diameter。
- 接下来,再执行do_copy()函数,这个函数是需要用户定义的回调函数(callback function),即在
copy()执行完后会执行do_copy()。
- 如果用户没有定义该函数,那么则不会执行额外的数据操作。
- 从ball::do_copy()函数可以看到,如果被拷贝对象的diameter小于20,那么则将自身的diameter设置
因此,最后对象b2.b的成员与b1.b的成员数值不同。
浅拷贝与深拷贝的理解:
原文链接:https://blog.csdn.net/Bunny9__/article/details/123438753
浅拷贝即句柄拷贝,只拷贝对象中的数据变量,比如数组、句柄,但是它不复制对象,只复制了句柄,对于对象中的数据操作(一般为任务和函数)和其中定义的其他类的句柄,采用类似“引用”的方式,浅拷贝前后共用同一内存空间;
深拷贝即对象拷贝,对对象中的所有成员变量(包括数据变量、数据操作和其他句柄)统一分配新的内存空间
function bit compare(uvm_object rhs,uvm_comparer comparer = null);
- 默认情况下,如果不对比较的情况作出额外配置,用户可以在调用compare()方法时,省略第二项参数,
即采用默认的比较配置。
- 比较方法经常会在两个数据中进行。例如从generator产生的一个transaction(数据类),和在设计输
出上捕捉的transaction(数据类),如果他们为同一种类型,除了可以自定义数据来比较之外,也可以直接
使用uvm_object::compare()函数来实现数据比较和消息打印。
class box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
`uvm_object_utils_begin(box)
...
`uvm_object_utils_end
...
endclass
box b1,b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b2 = new("box2");
b2.volume = 90;
if(!b2.compare(b1))begin
`uvm_info("COMPARE","b2 compared with b1 failure",UVM_LOW)
end
else begin
`uvm_info("COMPARE","b2 compare with b1 success",UVM_LOW)
end
end

输出结果显示volume的值不一样,但是b1和b2不仅仅是volume不一样,color也不一样,但是没有显示,原因是:uvm的比较发生错误即刻返回。
在uvm_pkg中例话了不少全局对象,而在本节中我们会使用到的全局配置对象包括uvm_default_comparer,uvm_default_pinter和uvm_default_packer。
如果用户不想使用默认的比较配置,而是想直接对比较进行设定,可以考虑创建一个uvm_comparer对象,或者修改全局的uvm_comparer对象。
但是是不可以去自己创建uvm_factory的。
打印方法是核心基类提供的另外一种便于开发和调试的功能。
通过field automatic,使得声明之后的各个成员域会在调用uvm_object::print()函数时自动打印出来
相比于在仿真中设置断点,逐步调试,打印是另外一种调试方式。它的好处在于可以让仿真继续进行,会在最终回顾执行过程中,从全局理解执行的轨迹和逻辑。
代码例子
class box extends uvm_object;
int volume = 120;
color_t color = WHTTE;
string name = "box";
`uvm_object_utils_begin(box)
...
endclass
box b1;
uvm_table_printer local_printer;
initial begin
b1 = new("box1");
local_printer = new();
$display("default table printer format");//不同的打印形式
b1.print();
$display("default line printer format");//不同的打印形式
uvm_default_printer = uvm_default_line_printer;
b1.print();
$display("default tree printer format");//不同的打印形式
uvm_default_printer = uvm_default_line_printer;
b1.print();
$display("customized pinter format");//不同的打印形式
local_printer.knobs.full_name = 1;
b1.print(local_printer);
end
function int pack (ref bit bitstream[],input uvm_packer packer=null);
function int unpack (ref bit bitstream[],input uvm_packer packer=null);
SV的验证环境构建中,我们可以发现,传统的硬件设计模型在仿真开始前,已经完成例化和连接了;而SV的软件部分对象例化则需要在仿真开始后执行。虽然对象例化通过调用结构函数new()来实现,但是单单通过new()函数无法解决一个重要问题,那就是验证环境在实现层次化时,如何保证例化的先后关系,以及各个组件在例化后的连接。引入phase机制,通过该机制我们清晰地将UVM仿真阶段层次。
phase机制理解起来比较简单,如果把验证平台的不同的companont理解成一个班级里上课的同学,那么虽然不同的同学每节课写作业有快有慢,但是他们都整齐划一的按照上课的时间,下课的时间,共享一个作息表。UVM极简教程

class subcomp extends uvm_component;
`uvm_compoent_utils(subcomp)
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
`uvm_info("build_phase","",UVM_LOW)
endfunction
function void connect_phase(uvm_phase phase);
`uvm_info("connect_phase","",UVM_LOW)
endfunction
...
task run_phase(uvm_phase phase);
`uvm_info("run_phase","",UV_LOW)
endtask
function void report_phase(uvm_phase phase)
`uvm_info("report_phase","",UVM_LOW)
endfunction
...
endclass
要在仿真开始时建立验证环境,有以下几种方式
无论哪一种方式,都需要在顶层调用全局函数run_test()。
//run_test的源码
task run test(string test_name="");
uvm_root top;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
top.run_test(test_name);
endtask
- uvm_top承担的核心职责包括
- 作为隐形的uvm世界顶层,任何其它的组件实例都在它之下,通过创建组件时指定parent来构成层次。
- 如果parent设定为null,那么它将作为uvm_top的子组件。
- phase控制。控制所有组件的phase顺序
- 索引功能。通过层次名称来索引组件实例。
- 报告配置。通过uvm_top来全局配置报告的繁简度(verbosity)
- 全局报告设备。由于可以全局访问到uvm_top实例,因此uvm报告设备在组件内部和组件外部都可以访问
通过uvm_top调用方法run_test(test_name),uvm_top做了如下的初始化:
- 得到正确的test_name
- 初始化objection机制,控制仿真退出
- 创建uvm_test_op实例
- 调用phase控制方法,安排所有组件的phase方法执行顺序
- 等待所有phase执行结束,关闭phase控制进程
- 报告综合和结束仿真
- UVM-1.1之后,结束仿真的机制有且只有一种,那就是利用objection挂机机制来控制仿真结束。
- uvm_objection类提供了一种所有component和sequence共享的计数器。如果所有组件挂起objection,
那么它还应该记得落下objection
- 参与到objection机制中的参与组件,可以独立的各自挂起objection,来防止run phase退出。但是
只有这些组件都落下objection后,uvm_objection共享的counter共享counter才会变为0,这意味
run phase退出的条件满足,因此可以退出run phase。
- 对于uvm_objection类,用来反停止的控制方法包括:
- raise_objection(uvm_objection obj = null,string description = "",int count = 1)挂机
objection
- drop_objection(uvm_objection obj = null,string description = "",int count = 1)落下
objection
- set_drain_time(uvm_objection obj = null,time drain)设置退出时间
- 对着几种方法,在实际应用中的建议有:
- 对于component()而言,用户可以在run_phase()中使用
- 用户最好为description字符串参数提供说明,这有利于后期的调试。
- 对于uvm_top或者uvm_test_top应该尽可能少地使用set_drain_time()。
- uvm_pkg::uvm_test_done实例会在test1的run_phase()执行完毕之后,才会退出run phase。
- 这得益于test1::run_phase()在仿真一开始就挂起了objection,而在执行完毕之后才落下了objection
这时uvm_pkg::uvm_test_done认为run phase已经可以退出,进而转向下一个extract phase。
- 直到退出所有phase之后,UVM进入了报告总结阶段。
- 那么如果没有挂机objection,UVM仿真会怎么样呢?直接进入下一个phase。
uvm_config_db#(T)::set(uvm_component cntxt,string inst_name,string field_name,T value);
uvm_config_db#(T)::get(uvm_component cntxt,string inst_name,string field_name,inout T value);
//T为参数类表示传递的类型,可能是int或者virtual interface,set为顶层放进去,get为底层拿出来 uvm_component为句柄,cntct是实例,inst_name为实例的名称,field_name为某一个变量 T value为实际值。
interface intf1; //interface的定义
logic enable = 0;
endinterface
class comp1 extends uvm_component; //定义
`uvm_component_utils(comp1) //注册
virtual intf1 vif;
...
function void build_phase(uvm_phase phase);
if(!uvm_config_db#(virtual intf1)::get(this,"","vif",vif))begin//this 表示句柄为comp1,comp1下面没有实例,所以字符串为空,路径为uvm_top/test1/c1/vif,vif表示获得的数值
`uvm_errer("GETVIF","no virtual interface is assgined")
end
`uvm_info("SETVAL",$sformatf("vif.enable is %b before set",vif.enable),UVM_LOW)
vif.enable = 1;
`uvm_info("SETVAL",$sformatf("vif.enable is %b after set",vif.enable),UVM_LOW)
endfunction
endclass
calss test1 extends uvm_test;
`uvm_component_utils(test1)
comp1 c1;
...
endclass
intf1 intf();
initial begin
uvm_config_db#(virtual intf1)::set(uvm_root::get(),"uvm_test_top.c1","vif",intf);//get和set相对应 uvm_root::get()为句柄,需要传递的实例是test下面的c1“uvm_test_top.c1”,c1下面的名称为"vif",set一定要发生在get之前。
run_test("test1");
end
UVM_INFO @ 0:reporter [RNTST] Running test test1...
UVM_INFO @ 0:uvm_test_top.c1 [SETVAL] vif.enable is 0 before set
UVM_INFO @ 0:uvm_test_top.c1 [SETVAL] vif.enable is 1 after set
//set和get的路径,类型都需要保持一致
class comp1 extends uvm_component;
`uvm_component_utils(comp1)
int vall = 1;
string str1 = "null";
...
function void build_phase(uvm_phase phase);
`uvm_info("SETVAL",$sformatf("vall is %d before get",vall),UVM_LOW)
`uvm_info("SETVAL",$sformatf("str1 is %s before get",str1),UVM_LOW)
uvm_config_db#(int)::get(this,"","vall",vall);//get和set类型要保持一致,int对应int,string对应string,前三个表示层次关系。
uvm_config_db#(string)::get(this,"","str1",str1);
`uvm_info("SETVAL".$sformatf("vall is %d after get",vall),UVM_LOW)
`uvm_info("SETVAL",$sformatf("str1 is %s after get",str1),UVM_LOW)
endfunction
endclass
class test1 extends uvm_test;
`uvm_component_utils(test1)
comp1 c1;
...
function void build_phase(uvm_phase phase);
uvm_config_db#(int)::set(this,"c1","vall",100);
uvm_config_db#(string)::set(this,"c1","str1","comp1");
c1 = comp1::type_id::create("c1",this);//先做set再做创建
endfunction
endclass
输出结果
UVM_INFO @ 0 : uvm_test_top.c1 [SETVAL] vall is 1 before get
UVM_INFO @ 0 : uvm_test_top.c1 [SETVAL] str1 is null before get
UVM_INFO @ 0 : uvm_tets_top.c1 [SETVAL] vall is 100 after get
UVM_INFO @ 0 : UVM_TEST_TOP.C1 [setval] str1 is comp1 after get
class config1 extends uvm_object;
int vall = 1;
int str1 = "null";
`uvm_object_utils(config1)
...
endclass
class comp1 extends uvm_component;
`uvm_component_utils(comp1)
config1 cfg;
...
function void build_phase(uvm_phase phase);
`uvm_object tmp;
uvm_config_db# (uvm_object)::get(this,"","cfg",tmp);
void'($cast(cfg,tmp));//父类的句柄转到子类里面
`uvm_info("SETVAL",$sformatf("cfg.vall is %d after get",cfg.vall),UVM_LOW)
`uvm_info("SETVAL",$sformatf("cfg.str1 is %s after get",cfg.str1),UVM_LOW)
endfunction
endclass
class test1 extends uvm_test;
`uvm_component_utils(test1)
comp1 c1,c2;
config1 cfg1,cfg2;
...
function void build_phase(uvm_phase phase);
cfg1 = config1::type_id::create("cfg1");
cfg2 = config1::type_id::create("cfg2");
cfg1.val1 = 30;
cfg1.str1 = "c1";
cfg2.val1 = 50;
cfg2.str1 = "c2";
uvm_config_db#(uvm_object)::set(this,"c1","cfg",cfg1);//先做set再做create
uvm_config_db#(uvm_object)::set(this,"c2","cfg",cfg2);
c1 = comp1::type_id::create("c1",this);
c2 = comp1::type_id::create("c2",this);
endfunction
endclass
输出结果为:
UVM_INFO @ 0:uvm_test_top.c1 [SETVAL] cfg.vall is 30 after get
UVM_INFO @ 0:uvm_test_top.c1 [SETVAL] cfg.str1 is c1 after get
UVM_INFO @ 0:uvm_test_top.c2 [SETVAL] cfg.vall is 50 after get
UVM_INFO @ 0:uvm_test_top.c2 [SETVAL] cfg.str1 is c2 after get
function void uvm_report_info(string id,string message,int verbosity = UVM_MEDIUM, string filename = "",int line = 0);
function void uvm_report_warning(string id,string message,int verbosity = UVM_MEDIUM,string filename = "", int line = 0);
function void uvm_report_error(string id, string message, int verbosity = UVM_LOW, string filename = "", int line = 0);
function void uvm_report_fatal(string id, string message, int verbosity = UVM_NONE, string filename = "", int line = 0);




回调函数
- function bit report_hook(string id, string message, int verbosity, string filename, int line);
- function bit report_info_hook(string id, string message, int verbosity, string filename, int line)
- function bit report_warning_hook(string id, string message, int verbosity, string filename, int line)
- function bit report_error_hook(string if, string message, int verbosity, string filename, int line);
- function bit report_fatal_hook(string id, string message, int verbosity, string filename, int line);
class test1 extends uvm_test;
integer f;
`uvm_componet_utils(test1)
...
function void build_phase(uvm_phase phase);
set_report_severity_action(UVM_ERROR,UVM_DISPLAY | UVM_CALL_HOOK);
set_report_verbosity_level(UVM_LOW);
endfunction
task run_phase(vum_phase phase);
uvm_report_info("RUN","info1",UVM_MEDIUM);
uvm_report_info("RUN","info2",UVM_LOW);
uvm_report_warning("RUN","warning1",UVM_LOW);
uvm_report_error("RUN","error1",UVM_LOW);
uvm_report_error("RUN","error2",UVM_HIGH);
uvm_report_error("RUN","error3",UVM_LOW);
endtask
function void report_phase(uvm_phase phase);
$fclose(f);
endfunction
function it report_hook(string id,string message,int verbosity, string filename, int line);
uvm_report_info("RPTHOOK",$sformatf("%s:%s",id, message),UVM_LOW);
return 1;
endfunction
function bit report_error_hook(string id,string message,int verbosity,string filename,int line);
uvm_report_info("ERRHOOK",$sformatf("%s : %s",id,message),UVM_LOW);
return 1;
endfunction
endclass
