[转]UVM工厂机制

一、工厂机制的意义

  • 工厂机制也是软件的一种典型设计模式,UVM工厂的存在就是为了更方便地替换验证环境中的实例或者注册了的类型,同时工厂的注册机制也带来了配置的灵活性。
  • 这里的实例或者类型替代,在UVM中称作覆盖(override),而被用来替换的对象或者类型,应满足注册多态的要求。
  • UVM的验证环境构成可以分为两个部分。一部分构成了环境的层次,这部分代码是通过uvm_component类完成,另一部分构成了环境的属性(例如配置)和数据传输,这一部分通过uvm_object类完成。uvm_component类继承于uvm_object类,这两种类也是进出工厂的主要模具和生产对象。之所以成为模具,是因为通过注册,可以利用工厂完成对象创建,而之所以对象由工厂生成,也是利用了工厂生产模具可灵活替代的好处,这使得在不修改原有验证环境层次和验证包的同时,实现了对环境内部组件类型或者对象的覆盖。

二、uvm_component类和uvm_object

参考SV中的组件的概念,即验证环境的不动产,uvm_component的子类中对应的组件大致包括:

  • generator
  • stimulator
  • monitor
  • agent
  • checker/reference model
  • environment
  • test

SV中的非固定资产即那些TLM transaction,从generator流向stimulator的数据包,而这些类在UVM中统一由uvm_object表示。

三、uvm_component类和uvm_object类的例化

每一个uvm_component类和uvm_object类在例化的时候都应该给予一个名字(string)。“full name”指的是component所处的完成层次结构,在每一个层次中例化的组件名称,应该独一无二的。

创建component或者object的方法

//创建uvm_component对象
comp_type::type_id::create(string name, uvm_component parent);

//创建uvm_object对象
object_type::type_id::create(string name);

四、工厂机制——创建(create)

comp1

class comp1 extends uvm_component;	//定义
	'uvm_component_utils(comp1)		//注册

	function new(string name="comp1", uvm_component parent=null);		//构建函数
		super.new(name, parent);
		$display($sformatf("%s is created", name));
	endfunction

	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
	endfunction
	
endclass

obj1

class obj1 extends uvm_object;	//定义
	'uvm_object_utils(obj1)		//注册
	
	function new(string name="obj1");		//构建函数
		super.new(name);
		$display($sformatf("%s is created", name));
	endfunction	

endclass

例化

comp1 c1, c2;
obj1  o1, o2;

initial begin
	//SV创建方式
	c1 = new("c1");
	o1 = new("o1");
	
	//UVM工厂创建方式
	c2 = comp1::type_id::create("c2", null);
	o2 = obj1::type_id::create("o2");
end

上述代码分别定义了两个类comp1obj1,通过对比new()函数进行例化和create()进行例化。可以看出,工厂创建方式也是最后通过调用new()函数实现的,对于任何对象的例化,最终都要通过new()构建函数来实现。

一般来说运用factory的步骤可分为:将类注册到工厂 → 在例化前设置覆盖对象和类型 → 对象创建。在上述的代码中,使用UVM宏'uvm_component_utils()'uvm_object_utils()来进行注册,这两个宏将类注册到factory中,在整个仿真中,factory是独有的,即有且只有一个,这个保证了所有类的注册都在 一个“机构”中。

五、uvm_coreservice_t

该类内置了UVM核心的组件和方法,主要包括:

  • 唯一uvm_factory,该组件用来注册、覆盖、例化。
  • 全局report_server,该组件用来消息统筹报告
  • 全局tr_database,该组件用来记录transaction记录。
  • get_root()方法用来返回当前UVM环境的结构顶层对象。

在UVM-1.2中,明显的变化是通过uvm_coreservice_t将最重要的机制(也是必须做统一例化处理的组件)都放置在了uvm_coreservice_t类中。而该类并不是uvm_component或者uvm_object,它也并没有例化在UVM环境中,而是独立于UVM环境之外的。uvm_coreservice_t只会被UVM系统在仿真开始时例化一次,用户无需也不应该自行再额外例化该核心服务组件。这个核心组件如同一个随时待命的仆人,做好服务的准备。理论上,用户可以获取核心服务类中的任何一个对象,例如uvm_default_factory对象,继而直接利用factory来实现创建和覆盖,当然,创建和覆盖也可以由其他方式完成。

六、注册宏'uvm_component_utils()'uvm_object_utils()

  • 'uvm_component_utils()用来注册组件类uvm_component
  • 'uvm_object_utils()用来注册核心基类uvm_object
  • 在宏调用的过程中,实现类型定义typedef uvm_component_registry #(T,“S”) type_id
  • uvm_factory::register()来注册type_id并且得到实例。
  • 一旦发生注册,type_id::create()函数就可以最终通过uvm_factory::create_component_by_type()来实现。
  • 对于注册,并不是真正地将一个抽象的类型放置在什么地方,而是通过例化该类的对象来完成。
  • 由于一种类型在通过宏调用时只注册一次,那么在不考虑覆盖的情况下,uvm_default_factory就将每一个类对应的对象都放置到了factory的字典当中。
  • uvm_default_factory::create_component_by_type()首先检查处在该层次路径中需要被例化的对象,是否收到了“类型覆盖”或者“实例覆盖”的影响,进而将最终类型对应的对象句柄(正确的产品模板)交给工厂。有了正确的产品模板,就可以通过uvm_component_registry::create_component()来完成例化。

七、注册后的对象创建

uvm_componentuvm_object在创建时虽然都需要调用create()函数,但最终创建出来的uvm_component是会表示在UVM层次结构中的,而uvm_object则不会显示在层次中。这一点可以从uvm_component::new(name,parent)uvm_object::new(name)中可以看得出来,uvm_component::new(name,parent)保留两个参数,就是为了通过类似“钩子”的做法,一层层由底层勾住上一层,这样就可以将整个UVM结构串接起来了。uvm_object::new(name)则没有parent参数,因此也不会显示在UVM层次中,只能作为configuration或者transaction等用来做传递的配置结构体或者抽象数据传输的数据结构体,成为uvm_component的成员变量。

创建对象时,需要结合工厂的注册和覆盖机制来决定,应该使用哪一个类型来创建。
在这里插入图片描述

八、component/object与工厂有关的方法

配合工厂的注册、创建、覆盖的相关方法:

  • create()
  • create_component()
  • get()
  • get_type_name()
  • set_inst_override()
  • set_type_override()

每一个uvm_component的类在注册时,会定义一个新的uvm_component_registry类, 其如同一个外壳,一个包装模板的纸箱,在factory中注册时,该纸箱中容纳的是被注册类的“图纸”,并没有一个“实例”。

九、工厂创建component/object的方法

除了使用component/object来创建实例,也可以利用factory来创建。

create_component_by_name()
create_component_by_type()
create_object_by_name()
create_object_by_type()

为了避免不必要的麻烦,在使用宏注册类型时,宏内部就将类型T作为类型名Tname=‘T’注册到factory中去,这就使得通过上面的任何一种方法在创建对象时,不会受困于类型与类型名不同的苦恼。

---------------------
作者:煎丶包
来源:CSDN
原文:https://blog.csdn.net/qq_39794062/article/details/113829953
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件