(5)UVM工厂机制


工厂机制也是软件的一种典型设计模式(design pattern)

关于工厂机制的介绍将从下面几点展开:

  • 工厂的意义
  • 工厂提供的便利
  • 覆盖方法
  • 确保正确覆盖的代码要求

工厂的意义

UVM工厂的存在就是为了更方便地替换验证环境中的实例或者注册了的类型同时工厂的注册机制也带来了配置的灵活性

这里的实例或者类型替代,在UVM中称作覆盖(override),而被用来替换的对象或者类型,应该满足注册(registration)和多态(polymorphism)的要求

UVM的验证环境构成可以分为两部分,一部分构成了环境的层次,这部分代码是通过uvm_component类完成另外一部分构成了环境的属性(例如配置)和数据传输,这一部分通过uvm_object类完成

工厂的注册和创建

//创建uvm_component对象时
comp_type::type id::create(string name,uvm_component parent);
//创建uvm_object对象时
object type::type id::create(string name);
//注册:宏,固定的范式;此举注册到工厂里
uvm_component_utils(comp1)

UVM_coreservice_t类

该类内置了UVM世界核心的组件和方法,它们主要包括:
      1、唯一的uvm_factory,该组件用来注册、覆盖和例化
      2、全局的report_server,该组件用来做消息统筹和报告
      3、全局的tr_database,该组件用来记录transaction记录
      4、get_root()方法用来返回当前UVM环境的结构顶层对象
该类并不是uwm_component或者uvm_object,它也并没有例化在UVM环境中,而是独立于UVM环境之外的。

uvm_coreservice_t只会被UVM系统在仿真开始时例化一次。用户无需,也不应该自行再额外例化该核心服务组件。
这个核心组件如同一个随时待命的仆人,做好服务的准备。理论上,用户可以获取核心服务类中的任何一个对象,例如uvm_default_factory对象,继而直接利用factory来实现创建和覆盖。当然,创建和覆盖也可以由其它方式完成。

注册宏`uvm_{component,object}_utils

uvm_component_utils用来注册组件类uwm_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()来实现。

创建对象时,需要结合工厂的注册和覆盖机制来决定,应该使用哪一个类型来创建。
在这里插入图片描述
覆盖类型就是将工厂图纸替换掉;创建的类型没有被覆盖掉,就可以直接拿到对象做一个创建。

工厂提供的便利——覆盖(override)

覆盖机制可以将其原来所属的类型替换为另外一个新的类型。在覆盖之后,原本用来创建原属类型的请求,将由工厂来创建新的替换类型。

  • 无需再修改原始代码,继而保证了原有代码的封装性。
  • 新的替换类型必须与被替换类型相兼容,否则稍后的句柄赋值将失败,所以使用继承。

做顶层修改时,允许灵活的配置例如可使用子类来覆盖原本的父类可使用不同的对象来修改其代码行为

要想实现覆盖特性,原有类型和新类型均需要注册。

当使用create()来创建对象时:
      1、工厂会检查,是否原有类型被覆盖。
      2、如果是,那么它会创建一个新类型的对象。
      3、如果不是,那么它会创建一个原有类型的对象。

覆盖发生时,可以使用“类型覆盖”或者“实例覆盖”

  • 类型覆盖指,UVM层次结构下的所有原有类型都被覆盖类型所替换,
  • 实例覆盖指,在某些位置中的原有类型会被覆盖类型所替换。

覆盖方法

set_type_override()

static function void set_type_override(uvm_object_wrapper override_type, bit replace=1);

uvm_object_wrapper override_type并不是某一个具体实例的句柄,实际上是注册过后的某一个类在工厂中注册时的句柄。使用new_type::get_type()找到它

bit replace=1
1:如果已经有覆盖存在,那么新的覆盖会替代旧的覆盖。
0:如果已经有覆盖存在,那么该覆盖将不会生效。
在这里插入图片描述
用新类型即new_type 替换原有类型orig_type

set_inst_override()

static function void set_inst_override(uvm_object_wrapper override_type,string inst_path,uvm_component parent=nul1);

string inst_path指向的是组件结构的路径字符串
uvm_component parent=null如果缺省,表示使用inst_path内容为绝对路径
如果有值传递,则使用{parent.get_full_name(),inst_path}来作为目标路径。
在这里插入图片描述
有不止一个类提供与覆盖有关的函数,然而名称与参数列表可能各不相同:

uvm_component::set_{type,inst]_override(_by_type}
uvm_component_registry::set_{type,inst}_override 
uvm_object_registry::set_{type,inst]_override 
uvm_factory::set_{type,inst]_override

想要实现类型替换,有不止一种方式。包括通过orig_type::type id来调用覆盖函数,还可以在uvm_component的中直接调用,或者使用uvm_factory来做覆盖

例:

module factory_override; 
	import uvm_pkg::*; 
	`include "uvm_macros.svh"
	class comp1 extends uvm_component; 
		uvm_component_utils(comp1)
		function new(string name="comp1", uvm_component parent=null); 
			super.new(name, parent);
			$display($sformatf("comp1::%s is created", name)); 
		endfunction: new 
		virtual function void hello(string name);
			$display($sformatf("comp1:: %s said hello!", name)); 
		endfunction 
	endclass
	class comp2 extends comp1;
		`uvm_component_uti1s(comp2)
		function new(string name="comp2", uvm_component parent=null); 
			super.new(name, parent);
			$display($sformatf("comp2::%s is created", name)); 
		endfunction: new 
		function void hello(string name);
			$display($sformatf("comp2::%s said hello!", name)); 
		endfunction 
	endclass
	comp1 cl,c2; 
	initial begin 
		comp1::type_id::set_type_override(comp2::get_type()); 
		c1=new("cl"); 
		c2=comp1::type_id::create("c2",null);
		c1.hello("c1");
		c2.hello("c2");
	end
endmodule

输出结果:

comp1::cl is created
comp1::c2 is created
comp2::c2 is created 
comp1::cl said hello!
comp2::c2 said hello!

这里出现了5条结果,尤其是第二条和第三条,为什么会打印两次呢?这就涉及到在comp2中的new函数中,存在super.new(name, parent);这条语句,然后将comp1中的new里面也打印了一遍,但因为覆盖过,所以两个%s打印的都是c2,但因为继承关系,所以前面分别是comp1comp2。这点需要特别注意

确保正确覆盖的代码要求

将UVM环境中所有的类都注册到工厂中,并通过工厂来创建对象。在使用某些类的时候,确保该类已经被导入(import)到当前域(scope)中。
通过工厂创建对象时,句柄名称应该同传递到create()方法中的字符串名称相同。无论是通过层次路径名称来覆盖还是配置,将例化组件的句柄名称同创建时create()方法中的字符串名称保持一致
由于覆盖是采用parent wins模式,因此要注意在同一个顶层build_phase()中覆盖方法应发生在对象创建之前。为了尽量保证运行时覆盖类可以替换原始类,覆盖类最好是原始类的子类,而调用成员方法也应当声明为虚方法
另外一种确保运行时覆盖类型句柄正确使用的方式,需要通过$cast()进行动态类型转换

整个UVM世界的构建,离不开factory的三个核心要素:注册、创建和覆盖。

`uvm_{component,object]_utils
uvm_{component,object]::type_id::create()
set_{type,inst]_override{,_by_type}()

这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字ic攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值