UVM实战 卷I学习笔记5——UVM基础(1)uvm_component与uvm_object


uvm_component派生自uvm_object

  • uvm_object是UVM中最基本的类,几乎所有类都继承自uvm_object,包括uvm_component。说明它拥有uvm_object的特性,同时又有自己的一些特质。但是uvm_component的一些特性,uvm_object则不一定具有。这是面向对象编程中常用到的一条规律。
  • uvm_component有两大特性是uvm_object所没有的:一是通过在new时指定parent参数来形成一种树形的组织结构,二是有phase的自动执行特点。
  • 下图列出了UVM中常用类的继承关系:可以看出从uvm_object派生出了两个分支,所有的UVM树的结点都是由uvm_component组成的,只有基于uvm_component派生的类才可能成为UVM树的结点;最左边分支的类或直接派生自uvm_object的类,是不可能以结点的形式出现在UVM树上的。
    在这里插入图片描述

常用的派生自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_component就是由其搭建成的一种高级生命sequence_item是由其搭建成的血液,流通在各个高级生命(uvm_component)之间,sequence是众多sequence_item的组合config是由其搭建成的用于规范高级生命(uvm_component) 行为方式的准则

在验证平台中经常遇到的派生自uvm_object的类:

uvm_sequence_item:定义的所有transaction要从uvm_sequence_item派生。transaction就是封装了一定信息的一个类。driver从sequencer中得到transaction并将其转换成端口上的信号。从上图可以看出虽然UVM中有一个uvm_transaction类,但不能从uvm_transaction派生一个transaction。事实上,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的主要功能是规范验证平台的行为方式。 要注意config与config_db的区别:这里的config指的是把所有参数放在一个object中。然后通过config_db的方式设置给所有需要这些参数的component

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_component相比,uvm_driver多了如下几个成员变量:

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_monitor:所有的monitor都要派生自uvm_monitor。monitor做的事情与driver相反,monitor从DUT的pin上接收数据并转换成transaction级别的sequence_item,再把转换后的数据发送给scoreboard,供其比较。 与uvm_component相比,uvm_monitor几乎没有做任何扩充。uvm_monitor的定义如下:

virtual class uvm_monitor extends uvm_component;
	...
	function new (string name, uvm_component parent);
		super.new(name, parent);
	endfunction
	const static string type_name = "uvm_monitor";
	virtual function string get_type_name ();
		return type_name;
	endfunction
endclass

uvm_sequencer:所有sequencer都要派生自uvm_sequencer。sequencer的功能是组织管理sequence,当driver要求数据时把sequence生成的sequence_item转发给driver。与uvm_component相比,uvm_sequencer做了相当多的扩展,详见后面sequencer部分。

uvm_scoreboard:一般scoreboard都要派生自uvm_scoreboard。scoreboard的功能是比较reference model和monitor分别发送来的数据,根据比较结果判断DUT是否正确工作。uvm_scoreboard也几乎没有在uvm_component的基础上做扩展:

virtual class uvm_scoreboard extends uvm_component;
	...
	function new (string name, uvm_component parent);
		super.new(name, parent);
	endfunction
	const static string type_name = "uvm_scoreboard";
	virtual function string get_type_name ();
		return type_name;
	endfunction
endclass

reference model:UVM中没有针对reference model定义一个类,通常都是直接派生自uvm_component。它的作用就是模仿DUT,完成与DUT相同的功能。DUT是用Verilog写成的时序电路,而reference model可直接使用SV的高级语言特性,同时还可通过DPI等接口调用其他语言完成与DUT相同的功能。

uvm_agent:所有agent要派生自uvm_agent,其作用并不那么明显,只是把driver和monitor封装在一起根据参数值决定是只实例化monitor还是同时实例化driver和monitor。 agent的使用主要是从可重用性的角度考虑。如果构建验证平台时不考虑可重用性,那么agent可有可无。与uvm_component相比,uvm_agent的最大改动在于引入了一个变量is_active

virtual class uvm_agent extends uvm_component;
	uvm_active_passive_enum is_active = UVM_ACTIVE;
	...
	function void build_phase(uvm_phase phase);
		int active;
		super.build_phase(phase);
		if(get_config_int("is_active", active)) 
			is_active = uvm_active_passive_enum' (active);
	endfunction

注:get_config_int是uvm_config_db#(int)::get的另一种写法,由于is_active是枚举变量,取值为固定值0或1。所以上面代码中可以以int类型传递给uvm_agent,并针对传递过来的数据做强制类型转换

uvm_env:所有env要派生自uvm_env。env将验证平台上用到的固定不变的component都封装在一起。当要运行不同测试用例时,只要在测试用例中实例化此env即可。

virtual class uvm_env extends uvm_component;
	...
	function new(string name = "env", uvm_component parent = null);
		super.new(name, parent);
	endfunction
	const static string type_name = "uvm_env";
	virtual function string get_type_name();
		return type_name;
	endfunction
enclass

uvm_test:所有测试用例要派生自uvm_test或其派生类,不同测试用例之间差异很大,所以从uvm_test派生出来的类各不相同。任何一个派生出的测试用例中都要实例化env,只有这样,当测试用例在运行时才能把数据正常发给DUT,并正常接收DUT的数据

virtual class uvm_test extends uvm_component;
	...
	function new(string name, uvm_component parent);
		super.new(name, parent);
	endfunction
	const static string type_name = "uvm_test";
	virtual function string get_type_name();
		return type_name;
	endfunction
endclass

与uvm_object相关的宏

uvm_object_utils:它用于把一个直接或间接派生自uvm_object类注册到factory中

uvm_object_param_utils: 它用于把一个直接或间接派生自uvm_object的参数化的类注册到factory中。参数化的类是指类似右边这个类:class A#(int WIDTH=32) extends uvm_object;(参数化的类在代码可重用性中经常用到。如果允许,尽可能使用参数化的类,提高代码的可移植性。)

uvm_object_utils_begin:使用field_automation机制时需要使用此宏。如果使用了此宏,而又没有把任何字段使用uvm_field系列宏实现也没有问题。例如:

`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注册的结束标志

与uvm_component相关的宏

uvm_component_utils:用于把一个直接或间接派生自uvm_component的类注册到factory中

uvm_component_param_utils:用于把一个直接或间接派生自uvm_component的参数化的类注册到factory中。

uvm_component_utils_begin:用于同时需要使用factory机制和field_automation机制注册的类。uvm_component派生自uvm_object,所以对于object拥有的如compare、print函数都可以直接使用。但是filed_automation机制对于uvm_component最大的意义在于可自动使用config_db来得到某些变量的值

uvm_component_param_utils_begin:用于参数化的且其中某些成员变量要使用field_automation机制实现的类。

uvm_component_utils_end:它总是与uvm_component_*_begin成对出现,作为factory注册的结束标志

uvm_component的限制

uvm_component从uvm_object派生,理论上uvm_component应具有uvm_object的所有行为特征。但由于uvm_component是作为UVM树的结点存在的,这一特性使得它失去了uvm_object的某些特征。

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

uvm_component与uvm_object的二元结构

  • 为什么UVM中会分成uvm_component与uvm_object两大类呢?把有共性的万物分类,按类别来认识万物,才能大大降低人类认识世界的难度。UVM吸收了这种哲学,先分类,然后分别管理。假如UVM中不分uvm_object与uvm_component, 所有东西都是uvm_object,相当于直接与分子打交道,不易于使用。
  • SV作为一门编程语言,相当于提供了最基本的原子,使用起来相当麻烦。为减少这种麻烦,UVM出现了。但假如UVM中全部都是uvm_object的话,即全部都是分子,分子虽然比原子好用一些,但依然处于普通人的承受范围之外。只有把分子组合成一个又一个生命体的时候,用起来才会比较顺手。
  • 那为什么不把所有东西都做成uvm_component的形式呢?因为uvm_component是高级生命体,有其自己鲜明的特征。 验证平台中并不是所有东西都有这种鲜明的特征。例如:uvm_component在整个仿真中是一直存在的,但假如要发送一个transaction给DUT,此transaction可能只需要几毫秒就可以发送完。发送完了,此transaction的生命周期几乎就结束了,根本没有必要在整个仿真中一直持续下去。生命是多样化的,要既允许uvm_component这样的高级生命存在,也要允许transaction这种如流星一闪而逝的东西存在。
  • 细品这几段话,会觉得UVM就是两个字:妙啊!
  • 5
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值