《UVM实战》——3.1节uvm_component与uvm_object

简介:

本节书摘来自华章社区《UVM实战》一书中的第3章,第3.1节uvm_component与uvm_object,作者 张 强,更多章节内容可以访问云栖社区“华章社区”公众号查看

第3章
UVM 基 础
3.1 uvm_component与uvm_object
component与object是UVM中两大最基本的概念,也是初学者最容易混淆的两个概念。本节将介绍uvm_object与uvm_component的区别和联系。

3.1.1 uvm_component派生自uvm_object
通过对第2章搭建的验证平台的学习,读者应对UVM有了较直观的认识,不少读者会认为uvm_component与uvm_object是两个对等的概念。当创建一个类的时候,比如定义一个sequence类,一个driver类,要么这个类派生自uvm_component(或者uvm_component的派生类,如uvm_driver),要么这个类派生自uvm_object(或者uvm_object的派生类,如uvm_sequence),似乎uvm_object与uvm_component是对等的概念,其实不然。
uvm_object是UVM中最基本的类,读者能想到的几乎所有的类都继承自uvm_object,包括uvm_component。uvm_component派生自uvm_object这个事实会让很多人惊讶,而这个事实说明了uvm_component拥有uvm_object的特性,同时又有自己的一些特质。但是uvm_component的一些特性,uvm_object则不一定具有。这是面向对象编程中经常用到的一条规律。
uvm_component有两大特性是uvm_object所没有的,一是通过在new的时候指定parent参数来形成一种树形的组织结构,二是有phase的自动执行特点。图3-1列出了UVM中常用类的继承关系。
从图中可以看出,从uvm_object派生出了两个分支,所有的UVM树的结点都是由uvm_component组成的,只有基于uvm_component派生的类才可能成为UVM树的结点;最左边分支的类或者直接派生自uvm_object的类,是不可能以结点的形式出现在UVM树上的。


9917f269e7ac3c4d1478e4e905ef6891a7dbae2b

3.1.2 常用的派生自uvm_object的类
既然uvm_object是最基本的类,那么其能力恰恰也是最差的,当然了,其扩展性也是最好的。恰如一个婴儿,其能力很差,但是可以把其尽量培养成书法家、艺术家等。
到目前为止uvm_object依然是一个相当抽象的类。验证平台中用到的哪些类会派生自uvm_object?答案是除了派生自uvm_component类之外的类,几乎所有的类都派生自uvm_object。换个说法,除了driver、monitor、agent、model、scoreboard、env、test之外的几乎所有的类,本质上都是uvm_object,如sequence、sequence_item、transaction、config等。
如果读者现在依然对uvm_object很迷茫的话,那么举一个更加通俗点的例子,uvm_object是一个分子,用这个分子可以搭建成许许多多的东西,如既可以搭建成动物,还可以搭建成植物,更加可以搭建成没有任何意识的岩石、空气等。uvm_component就是由其搭建成的一种高级生命,而sequence_item则是由其搭建成的血液,它流通在各个高级生命(uvm_component)之间,sequence则是众多sequence_item的组合,config则是由其搭建成的用于规范高级生命(uvm_component)行为方式的准则。
在验证平台中经常遇到的派生自uvm_object的类有:
uvm_sequence_item:读者定义的所有的transaction要从uvm_sequence_item派生。transaction就是封装了一定信息的一个类,本书中的my_transaction就是将一个mac帧中的各个字段封装在了一起,包括目的地址、源地址、帧类型、帧的数据、FCS校验和等。driver从sequencer中得到transaction,并且把其转换成端口上的信号。从图3-1中可以看出,虽然UVM中有一个uvm_transaction类,但是在UVM中,不能从uvm_transaction派生一个transaction,而要从uvm_sequence_item派生。事实上,uvm_sequence_item是从uvm_transaction派生而来的,因此,uvm_sequence_item相比uvm_transaction添加了很多实用的成员变量和函数/任务,从uvm_sequence_item直接派生,就可以使用这些新增加的成员变量和函数/任务。
uvm_sequence:所有的sequence要从uvm_sequence派生一个。sequence就是sequence_item的组合。sequence直接与sequencer打交道,当driver向sequencer索要数据时,sequencer会检查是否有sequence要发送数据。当发现有sequence_item待发送时,会把此sequence_item交给driver。
config:所有的config一般直接从uvm_object派生。config的主要功能就是规范验证平台的行为方式。如规定driver在读取总线时地址信号要持续几个时钟,片选信号从什么时候开始有效等。这里要注意config与config_db的区别。在上一章中已经见识了使用config_db进行参数配置,这里的config其实指的是把所有的参数放在一个object中,如10.5节所示。然后通过config_db的方式设置给所有需要这些参数的component。
除了上面几种类是派生自uvm_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之间依次运转。
除了这些之外,其实还有很多。不过其他的一些并不那么重要,这里不再一一列出。

3.1.3 常用的派生自uvm_component的类
与uvm_object相比,派生自uvm_component的类比较少,且在上一章的验证平台中已经全部用到过。
uvm_driver:所有的driver都要派生自uvm_driver。driver的功能主要就是向sequencer索要sequence_item(transaction),并且将sequence_item里的信息驱动到DUT的端口上,这相当于完成了从transaction级别到DUT能够接受的端口级别信息的转换。与uvm_component相比,uvm_driver多了如下几个成员变量:
代码清单 3-1
来源:UVM源代码

  uvm_seq_item_pull_port #(REQ, RSP) seq_item_port;
  uvm_seq_item_pull_port #(REQ, RSP) seq_item_prod_if; // alias
  uvm_analysis_port #(RSP) rsp_port;
  REQ req;
  RSP rsp;
在函数/任务上,uvm_driver并没有做过多的扩展。
uvm_monitor:所有的monitor都要派生自uvm_monitor。monitor做的事情与driver相反,driver向DUT的pin上发送数据,而monitor则是从DUT的pin上接收数据,并且把接收到的数据转换成transaction级别的sequence_item,再把转换后的数据发送给scoreboard,供其比较。与uvm_component相比,uvm_monitor几乎没有做任何扩充。uvm_monitor的定义如下:
代码清单 3-2
来源:UVM源代码
 34 virtual class uvm_monitor extends uvm_component;
 …
 42   function new (string name, uvm_component parent);
 43     super.new(name, parent);
 44   endfunction
 45
 46   const static string type_name = "uvm_monitor";
 47
 48   virtual function string get_type_name ();
 49     return type_name;
 50   endfunction
 51
 52 endclass
虽然从理论上来说所有的monitor要从uvm_monitor派生。但是实际上如果从uvm_component派生,也没有任何问题。
uvm_sequencer:所有的sequencer都要派生自uvm_sequencer。sequencer的功能就是组织管理sequence,当driver要求数据时,它就把sequence生成的sequence_item转发给driver。与uvm_component相比,uvm_sequencer做了相当多的扩展,具体的会在第6章中介绍。
uvm_scoreboard:一般的scoreboard都要派生自uvm_scoreboard。scoreboard的功能就是比较reference model和monitor分别发送来的数据,根据比较结果判断DUT是否正确工作。与uvm_monitor类似,uvm_scoreboard也几乎没有在uvm_component的基础上做扩展:
代码清单 3-3
来源:UVM源代码
 36 virtual class uvm_scoreboard extends uvm_component;
 …
 44   function new (string name, uvm_component parent);
 45     super.new(name, parent);
 46   endfunction
 47
 48   const static string type_name = "uvm_scoreboard";
 49
 50   virtual function string get_type_name ();
 51     return type_name;
 52   endfunction
 53
 54 endclass
所以,当定义自己的scoreboard时,可以直接从uvm_component派生。
reference model:UVM中并没有针对reference model定义一个类。所以通常来说,reference model都是直接派生自uvm_component。reference model的作用就是模仿DUT,完成与DUT相同的功能。DUT是用Verilog写成的时序电路,而reference model则可以直接使用SystemVerilog高级语言的特性,同时还可以通过DPI等接口调用其他语言来完成与DUT相同的功能。
uvm_agent:所有的agent要派生自uvm_agent。与前面几个比起来,uvm_agent的作用并不是那么明显。它只是把driver和monitor封装在一起,根据参数值来决定是只实例化monitor还是要同时实例化driver和monitor。agent的使用主要是从可重用性的角度来考虑的。如果在做验证平台时不考虑可重用性,那么agent其实是可有可无的。与uvm_component相比,uvm_agent的最大改动在于引入了一个变量is_active:
代码清单 3-4
来源:UVM源代码
 39 virtual class uvm_agent extends uvm_component;
 40   uvm_active_passive_enum is_active = UVM_ACTIVE;
 …
 58   function void build_phase(uvm_phase phase);
 59     int active;
 60     super.build_phase(phase);
 61    if(get_config_int("is_active", active)) is_active = uvm_active_passive_enum' (active);
 62   endfunction
get_config_int是uvm_config_db#(int)::get的另一种写法,这种写法最初出现在OVM中,本书将在3.5.9节详细地讲述这种写法。由于is_active是一个枚举变量,从代码清单2-35可以看出,其两个取值为固定值0或者1。所以在上面的代码中可以以int类型传递给uvm_agent,并针对传递过来的数据做强制类型转换。
uvm_env:所有的env(environment的缩写)要派生自uvm_env。env将验证平台上用到的固定不变的component都封装在一起。这样,当要运行不同的测试用例时,只要在测试用例中实例化此env即可。uvm_env也并没有在uvm_component的基础上做过多扩展:
代码清单 3-5
来源:UVM源代码
 33 virtual class uvm_env extends uvm_component;
 …
 41   function new (string name="env", uvm_component parent=null);
 42     super.new(name,parent);
 43   endfunction
 44
 45   const static string type_name = "uvm_env";
 46
 47   virtual function string get_type_name ();
 48     return type_name;
 49   endfunction
 50
 51 endclass
uvm_test:所有的测试用例要派生自uvm_test或其派生类,不同的测试用例之间差异很大,所以从uvm_test派生出来的类各不相同。任何一个派生出的测试用例中,都要实例化env,只有这样,当测试用例在运行的时候,才能把数据正常地发给DUT,并正常地接收DUT的数据。uvm_test也几乎没有做任何扩展:
代码清单 3-6
来源:UVM源代码
 62 virtual class uvm_test extends uvm_component;
 …
 70   function new (string name, uvm_component parent);
 71     super.new(name,parent);
 72   endfunction
 73
 74   const static string type_name = "uvm_test";
 75
 76   virtual function string get_type_name ();
 77     return type_name;
 78   endfunction
 79
 80 endclass

3.1.4 与uvm_object相关的宏
在UVM中与uvm_object相关的factory宏有如下几个:
uvm_object_utils:它用于把一个直接或间接派生自uvm_object的类注册到factory中。
uvm_object_param_utils:它用于把一个直接或间接派生自uvm_object的参数化的类注册到factory中。所谓参数化的类,是指类似于如下的类:
代码清单 3-7

class A#(int WIDTH=32) extends uvm_object;
参数化的类在代码可重用性中经常用到。如果允许,尽可能使用参数化的类,它可以提高代码的可移植性。
uvm_object_utils_begin:这个宏在第2章介绍my_transaction时出现过,当需要使用field_automation机制时,需要使用此宏。如果使用了此宏,而又没有把任何字段使用uvm_field系列宏实现,那么会出现什么情况呢?
代码清单 3-8
`uvm_object_utils_begin(my_object)
`uvm_object_utils_end

答案是不会出现任何问题,这样的写法完全正确,可以尽情使用。
uvm_object_param_utils_begin:与uvm_object_utils_begin宏一样,只是它适用于参数化的且其中某些成员变量要使用field_automation机制实现的类。
uvm_object_utils_end:它总是与uvm_object_*_begin成对出现,作为factory注册的结束标志。

3.1.5 与uvm_component相关的宏
在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机制注册的类。在类似于my_transaction这种类中使用field_automation机制可以让人理解,可是在component中使用field_automation机制有必要吗?uvm_component派生自uvm_object,所以对于object拥有的如compare、print函数都可以直接使用。但是filed_automation机制对于uvm_component来说最大的意义不在于此,而在于可以自动地使用config_db来得到某些变量的值。具体的可以参考3.5.3节的介绍。
uvm_component_param_utils_begin:与uvm_component_utils_begin宏一样,只是它适用于参数化的,且其中某些成员变量要使用field_automation机制实现的类。
uvm_component_utils_end:它总是与uvm_component_*_begin成对出现,作为factory注册的结束标志。

3.1.6 uvm_component的限制
uvm_component是从uvm_object派生来的。从理论上来说,uvm_component应该具有uvm_object的所有的行为特征。但是,由于uvm_component是作为UVM树的结点存在的,这一特性使得它失去了uvm_object的某些特征。
在uvm_object中有clone函数,它用于分配一块内存空间,并把另一个实例复制到这块新的内存空间中。clone函数的使用方式如下:
代码清单 3-9

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参数无法指定。
copy函数也是uvm_object的一个函数,在使用copy前,目标实例必须已经使用new函数分配好了内存空间,而使用clone函数时,目标实例可以只是一个空指针。换言之,clone = new + copy。
虽然uvm_component无法使用clone函数,但是可以使用copy函数。因为在调用copy之前,目标实例已经完成了实例化,其parent参数已经指定了。
uvm_component的另外一个限制是,位于同一个父结点下的不同的component,在实例化时不能使用相同的名字。如下的方式中都使用名字“a1”是会出错的:
代码清单 3-10
class A extends uvm_component;
…
endclass

class my_env extends uvm_env;
  virtual function void build_phase(uvm_phase phase);
    A a1;
    A a2;
    a1 = new("a1", this);
    a2 = new("a1", this);
  endfunction
endclass

3.1.7 uvm_component与uvm_object的二元结构
为什么UVM中会分成uvm_component与uvm_object两大类呢?从古至今,人类在探索世界的时候,总是在不断寻找规律,并且通过寻找到的规律来把所遇到的事物、所看到的现象分类。因为世界太复杂,只有把有共性的万物分类,从而按照类别来认识万物,这样才能大大降低人类认识世界的难度。比如世界的生命有千万种,但是只有动物和植物两类。遇到一个生命的时候,人们会不自觉地判断它是一个动物还是植物,并且把动物或植物的特性预加到这种生命的身上,接下来用动物或者植物的方法来研究这个生命,从而加快对于这个生命的认知过程。
UVM很明显吸收了这种哲学,先分类,然后分别管理。想像一下,假如UVM中不分uvm_object与uvm_component,所有的东西都是uvm_object,那是多么恐怖的一件事情?这相当于直接与分子打交道!废时废力,不易于使用。
SystemVerilog作为一门编程语言,相当于提供了最基本的原子,其使用起来相当麻烦。为了减少这种麻烦,UVM出现了。但是假如UVM中全部都是uvm_object的话,也就是全部都是分子,分子虽然比原子好用一些,但是依然处于普通人的承受范围之外。只有把分子组合成一个又一个生命体的时候,用起来才会比较顺手。
uvm_component那么好用,为什么不把所有的东西都做成uvm_component的形式呢?因为uvm_component是高级生命体,有其自己鲜明的特征。验证平台中并不是所有的东西都有这种鲜明的特征。一个简单的例子:uvm_component在整个仿真中是一直存在的,但是假如要发送一个transaction(激励)给DUT,此transaction(激励)可能只需要几毫秒就可以发送完。发送完了,此transaction(激励)的生命周期几乎就结束了,根本没有必要在整个仿真中一直持续下去。生命是多样化的,要既允许uvm_component这样的高级生命存在,也要允许transaction这种如流星一闪而逝的东西存在。

相关文章
【UVM源码学习】uvm_object
【UVM源码学习】uvm_object
88 0
【UVM源码学习】uvm_object
【UVM源码学习】uvm_object_globals
【UVM源码学习】uvm_object_globals
99 0
【UVM源码学习】uvm_object_globals
【UVM源码学习】uvm_resource_specializations
【UVM源码学习】uvm_resource_specializations
67 0
|
测试技术 数据库 容器
【UVM源码学习】uvm_resource
【UVM源码学习】uvm_resource
135 0
【UVM源码学习】uvm_resource
【UVM源码学习】uvm_event
【UVM源码学习】uvm_event
229 0
【UVM源码学习】uvm_event
【UVM源码学习】uvm_comparer
【UVM源码学习】uvm_comparer
420 0
【UVM源码学习】uvm_comparer
【UVM源码学习】uvm_coreservice
【UVM源码学习】uvm_coreservice
187 0
|
测试技术
【UVM源码学习】uvm_globals
【UVM源码学习】uvm_globals
468 0
|
安全
【UVM源码学习】uvm_links
【UVM源码学习】uvm_links
111 0
【UVM源码学习】uvm_links
|
存储
【UVM源码学习】uvm_queue
【UVM源码学习】uvm_queue
425 0