UVM采用树形结构管理验证平台的各个部分。sequencer、driver、monitor、agent、model、scoreboard、env等都是树的一个结点。用树的形式来组织是因为作为一个验证平台,它必须能够掌握自己治下的所有“人口”,只有这样做才利于管理大家统一步伐做事情而不会漏掉谁。树形结构是实现这种管理的一种比较简单的方式。
uvm_component中的parent参数
UVM通过uvm_component实现树形结构。所有UVM树的结点本质上都是一个uvm_component。每个uvm_component都有一个特点:它们在new时需要指定一个类型为uvm_component、名字是parent的变量:function new(string name, uvm_component parent);
以下内容解释为何需要制定parent变量:
一般在使用时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。为什么要指定这么一个parent呢?一种常见的观点是,b_inst是A的成员变量,那么A就是b_inst的parent,无需再在调用new函数的时候指定,即b_inst在实例化时可以这样写:b_inst = new(“b_inst”); 但这样忽略了一点,b_inst是A的成员变量,那么在SV仿真器一级这种关系是确定的、可知的。假定有下面的类:
class C extends uvm_component;
A a_inst;
function void test();
…
a_inst.b_inst = …;
a_inst.d_inst = …;
…
endfunction
endclass
可以在C类的test函数中使用a_inst.b_inst来得到B的值或给B赋值,但不能用a_inst.d_inst来给D赋值,因为D不存在于A里面。SV仿真器会检测这种成员变量的从属关系,只负责检查这些代码的合理性,不会主动发消息给代码,所以A根本就没有办法知道自己有B这个变量。
如果在test中想得到A中所有变量的指针应怎么办? 可能会说因为A是自己写出的,它只有一个变量且它的名字叫b_inst,所以可直接使用a_inst.b_inst。问题是假设要把整棵UVM树遍历一下,即要找到每个结点及结点的孩子的指针,那如何写呢?似乎根本就没有办法实现。
解决这个问题的方法是,当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中加入变量的指针。
总而言之,在实例化变量时指定parent变量,可以形成一个树状结构从而起到建立层次的作用。
UVM树的根
UVM中真正的树根是uvm_top,完整的UVM树如图所示:
uvm_top是一个全局变量,它是uvm_root的唯一实例,且uvm_root派生自uvm_component。uvm_test_top的parent是uvm_top,而uvm_top的parent则是null。
UVM为什么不以uvm_test派生的测试用例,即uvm_test_top作为树根?之前的例子中所有component在实例化时将this指针传递给parent参数,如my_env在base_test中实例化:env=my_env::type_id::create(“env”, this);假如不按上面写法而是将parent参数设为null,那么这个component的parent将会被系统设置为系统中唯一的uvm_root的实例uvm_top,如下图所示:
可见,uvm_root的存在可以保证整个验证平台中只有一棵树,所有结点都是uvm_top的子结点。
在验证平台中,有时候需要得到uvm_top,由于uvm_top是一个全局变量,可直接使用uvm_top。除此之外, 还可以使用如下的方式得到它的指针:
uvm_root top;
top = uvm_root::get();
层次结构相关函数
UVM提供一系列的接口函数用于访问UVM树中的结点。这其中最主要的是以下几个:
get_parent函数:用于得到当前实例的parent
其函数原型为:extern virtual function uvm_component get_parent ();
与get_parent相对的是get_child函数:extern function uvm_component get_child (string name);
与get_parent不同,get_child需要一个string类型的参数name,表示此child实例在实例化时指定的名字。一个component只有一个parent,所以get_parent不需要指定参数;而可能有多个child,所以必须指定name参数。
可使用get_children函数得到所有的child:extern function void get_children(ref uvm_component children[$]);
除了一次性得到所有的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(comp.get_next_child(name));
这两个函数的使用依赖于一个string类型的name。在这两个函数的原型中,name是作为ref类型传递的:
extern function int get_first_child (ref string name);
extern function int get_next_child (ref string name);
name只是用于get_first_child和get_next_child之间及不同次调用get_next_child时互相之间传递信息。无需为name赋任何初始值,也没有必要在使用这两个函数过程中对其做任何赋值操作。
get_num_children函数用于返回当前component所拥有的child的数量:extern function int get_num_children ();