uvm_component与uvm_object
1.
几乎所有的类都派生于uvm_object
,包括uvm_component
。
uvm_component有两大特性是uvm_object所没有的:
- 一是通过在new的时候指定parent参数来形成一种树形的组织结构;
- 二是有phase的自动执行特点。
下图是常用的UVM继承关系:
从图中可以看出,从uvm_object派生出了两个分支,所有的UVM树的结点都是由uvm_component组成的,只有基于uvm_component派生的类才可能成为UVM树的结点;最左边分支的类或者直接派生自uvm_object的类,是不可能以结点的形式出现在UVM树上的。
2.
常用的派生自uvm_object
类的有:
- uvm_sequence_item:读者定义的所有的transaction要从uvm_sequence_item派生。
- uvm_sequence:所有的sequence要从uvm_sequence派生。
- config:所有的config一般直接从uvm_object派生。config的主要功能就是规范验证平台的行为方式。之前我们已经见识了使用config_db进行参数配置,这里的config其实指的是把所有的参数放在一个object中。
- uvm_reg_item:它派生自uvm_sequence_item,用于register model中。
- uvm_reg_map、uvm_mem、uvm_reg_field、uvm_reg、uvm_reg_file、uvm_reg_block等与寄存器相关的众多的类都是派生自uvm_object,它们都是用于register model。
- uvm_phase:它派生自uvm_object,其主要作用为控制uvm_component的行为方式,使uvm_component平滑地在各个不同的phase之间依次运转。
常用的派生自uvm_component
类的有:
- uvm_driver:所有的driver都要派生自uvm_driver。driver的功能主要就是向sequencer索要sequence_item(transaction),并且将sequence_item里的信息驱动到DUT的端口上,这相当于完成了从transaction级别到DUT能够接受的端口级别信息的转换。
- uvm_monitor:所有的monitor都要派生自uvm_monitor。monitor做的事情与driver相反,driver向DUT的pin上发送数据,而monitor则是从DUT的pin上接收数据,并且把接收到的数据转换成transaction级别的sequence_item,再把转换后的数据发送给scoreboard,供其做比较。
- uvm_scoreboard:一般的scoreboard都要派生自uvm_scoreboard。scoreboard的功能就是比较reference model和monitor分别发送来的数据,根据比较结果判断DUT是否正确工作。
- reference model:UVM中并没有针对reference model定义一个类。所以通常来说,reference model都是直接派生自uvm_component。reference model的作用就是模仿DUT,完成与DUT相同的功能。
- uvm_agent:所有的agent要派生自uvm_agent。与前面几个比起来,uvm_agent的作用并不是那么明显。它只是把driver和monitor封装在一起,根据参数值来决定是只实例化monitor还是要同时实例化driver和monitor。
- uvm_env:所有的env(environment的缩写)要派生自uvm_env。env将验证平台上用到的固定不变的component都封装在一起。这样,当要运行不同的测试用例时,只要在测试用例中实例化此env即可。
3.
在UVM中与uvm_object相关的factory宏有如下几个:
- uvm_object_utils:它用于把一个直接或间接派生自uvm_object的类注册到factory中。
- uvm_object_param_utils:它用于把一个直接或间接派生自uvm_object的参数化的类注册到factory中。
- uvm_object_utils_begin:这个宏在第2章介绍my_transaction时出现过,当需要使用field_automation机制时,需要使用此宏。
- uvm_object_param_utils_begin:与uvm_object_utils_begin宏一样,只是它适用于参数化的且其中某些成员变量要使用field_automation机制实现的类。
- uvm_object_utils_end:它总是与uvm_object_*_begin成对出现,作为factory注册的结束标志。
在UVM中与uvm_component相关的factory宏有如下几个:
- uvm_component_utils:它用于把一个直接或间接派生自uvm_component的类注册到factory中。
- uvm_component_param_utils:它用于把一个直接或间接派生自uvm_component的参数化的类注册到factory中。
- uvm_component_utils_begin:这个宏与uvm_object_utils_begin相似,它用于同时需要使用factory机制和field_automation机制注册的类。注意主要为了可以自动地使用config_db来得到某些变量的值,而不是在component中使用field_automation机制。
- uvm_component_param_utils_begin:与uvm_component_utils_begin宏一样,只是它适用于参数化的,且其中某些成员变量要使用field_automation机制实现的类。
- uvm_component_utils_end:它总是与uvm_component_*_begin成对出现,作为factory注册的结束标志。
4.
虽说uvm_component是从uvm_object派生来的,但作为UVM的结点,这使得其失去了某些uvm_object的特性。
比如在uvm_object中有clone函数,它用于分配一块内存空间,并把另一个实例复制到这块新的内存空间中。clone函数的使用方式如下:
class A extends uvm_object;
…
endclass
class my_env extends uvm_env;
virtual function void build_phase(uvm_phase phase);
A a1;
A a2;
a1 = new("a1");
a1.data = 8'h9;
$cast(a2, a1.clone());
endfunction
endclass
上述的clone函数无法用于uvm_component中,因为一旦使用后,新clone出来的类,其parent参数无法指定。
虽然uvm_component无法使用clone函数,但是可以使用copy函数。因为在调用copy之前,目标实例已经完成了实例化,其parent参数已经指定了。
UVM的树形结构
1.
一般在使用时,parent通常都是this。假设A和B均派生自uvm_component,在A中实例化一个B:
class B extends uvm_component;
…
endclass
class A extends uvm_component;
B b_inst;
virtual function void build_phase(uvm_phase phase);
b_inst = new("b_inst", this);
endfunction
endclass
在b_inst实例化的时候,把this指针传递给了它,代表A是b_inst的parent。一种常见的观点是,b_inst是A的成员变量,自然而然的,A就是b_inst的parent了。
当b_inst实例化的时候,指定一个parent的变量,同时在每一个component的内部维护一个数组
m_children,当b_inst实例化时,就把b_inst的指针加入到A的m_children数组中。只有这样才能让A知道b_inst是自己的孩子,同时也才能让b_inst知道A是自己的父母。当b_inst有了自己的孩子时,即在b_inst的m_children中加入孩子的指针。
2.
树根应该就是uvm_test。在测试用例里实例化env,在env里实例化scoreboard、reference model、agent、在agent里面实例化sequencer、driver和monitor。
UVM中真正的树根是一个称为uvm_top
的东西,完整的UVM树如下图所示。
uvm_top是一个全局变量,它是uvm_root的一个实例。而uvm_root派生自uvm_component,所以uvm_top本质上是一个uvm_component。uvm_test_top的parent是uvm_top,而uvm_top的parent则是null。
如果一个component在实例化时,其parent被设置为null,那么这个component的parent将会被系统设置为系统中唯一的uvm_root的实例uvm_top。如下图所示:
可见,uvm_root的存在可以保证整个验证平台中只有一棵树,所有结点都是uvm_top的子结点。
而在之前我们的验证平台中这个parent被设置为null的节点为my_casen,因此其被设置为系统中唯一的uvm_root的实例uvm_top,而我们可以在仿真编译时通过UVM_TESTNAME来指定选择哪个case来进行仿真测试。
另外这里my_casen派生自base_test,但并不因此增加UVM的层级,因此其parent为null。
在验证平台中,有时候需要得到uvm_top,由于uvm_top是一个全局变量,可以直接使用uvm_top。除此之外,还可以使用如下的方式得到它的指针:
uvm_root top;
top=uvm_root::get();
3.
UVM提供了一系列的接口函数用于访问UVM树中的结点。这其中最主要的是以下几个:
- get_parent函数,用于得到当前实例的parent。
extern virtual function uvm_component get_parent ();
- 与get_parent相对的就是get_child函数。与get_parent不同的是,get_child需要一个string类型的参数name,表示此child实例在实例化时指定的名字。因为一个component只有一个parent,所以get_parent不需要指定参数;而可能有多个child,所以必须指定name参数。
extern function uvm_component get_child (string name);
- 为了得到所有的child,可以使用get_children函数:
extern function void get_children(ref uvm_component children[$]);
使用方法为:
uvm_component array[$];
my_comp.get_children(array);
foreach(array[i])
do_something(array[i]);//自定义函数
- 除了一次性得到所有的child外,还可以使用get_first_child和get_next_child的组合依次得到所有的child:
string name;
uvm_component child;
if (comp.get_first_child(name))
do begin
child = comp.get_child(name);
child.print();
end while