【UVM】factory机制源代码分析

factory机制源代码解析



0、写在前面

uvm源代码的第二篇,工厂机制是整个uvm的基石,用到的所有组件和sequence都需要在工厂中注册。前面第一篇的component源代码也使用到了工厂机制注册uvm_test


1、根据类的名字来创建实例

这个话听起来是不是很摸不着头脑,为什么要根据名字创建实例,直接要用什么创建什么不可以嘛,大不了在new的时候传一个名字

1.1、为什么要根据名字创建实例

试想一个这样的场景:

class A ;
	...
endclass

class B;
	string type_string;+
	type_string = $value$plusarg("type_string=%s",type_string);
	function new();
		
		//to create A;
	endfunction

endclass

怎么样这个场景是不是很熟悉,我们上一节在讲run_test的时候,是不是就是这样的, v a l u e value valueplusagr传进来一个名字,然后工厂直接根据名字就创建这个类型。现在是不是有点明白工厂机制的作用了。工厂在根据名字创建类型的时候,有一个前提,这个名字必须要在工厂里面注册,否则工厂怎么知道要创建什么样的实例呢,下面我们讲怎么实现工厂里面的注册。

1.2、工厂如何实现注册

工厂的注册流程比较固定,其实我们能预想到的就是向factory里面的关联数组添加一条记录,因此uvm用宏帮我们简化了一系列流程。
工厂机制里面主要有两大类的宏,分别是:uvm_component_utils和uvm_object_utils
(1) uvm_object_utils宏展开

`define uvm_object_utils
	`uvm_object_utils_begin(T)
	`uvm_object_utils_end
//重点看uvm_object_utils_begin
`define uvm_object_utils_begin(T)
	`m_uvm_object_registry_internal(T,T) 
	`m_uvm_object_create_func(T)
	`m_uvm_get_type_name_func(T)
	`uvm_field_utils_begin(T)

1)m_uvm_object_registry_internal

`define m_uvm_object_registry_internal //看名字就知道 要向工厂中注册
	typedef uvm_object_registry#(T,`"S`") type_id;//工厂的核心,向类里面添加了一个成员变量。
	static function type_id get_type();
		return type_id::get();
	endfunction
	virtual function uvm_object_wrapper get_object_type();
		return type_id::get();
	endfunction
	

我们继续看一下uvm_object_registry这个类

class uvm_object_registry #(type T=uvm_object,string Tname="<unknown>") 
	typedef uvm_object_registry #(T,Tname) this_type;
	...
	loacal staic this_type me = get();
	...
	static function this_type get();
		if(me == null)begin
			uvm_factory f =uvm_factory::get();
			me =new;
			f.registry(me);
		end
	endfunction
	

uvm_object_wrapper是一个代理类,我们在使用uvm_config_db#(uvm_object_warpper)::set或者::get的时候会讲这个类作为类型。
uvm_object_registry 通过参数化的方式,根据传入的class_name名字不同会定义出不同的类型,例如

typedef uvm_object_registry#(T1,“S1”) this_type1;
typedef uvm_object_registry#(T!,“S2”) this_type2;
这是两个不同的类型,因此在使用过程中,uvm_object_utils(classname)根据clas name的不同注册的是不同的类型,虽然真正注册进工厂的是me,也就是这个type_id 的类型,但是也相当于间接把T注册了。
我们来梳理一下,工厂如何实现注册,
1、通过宏,类的类型和名字用于参数化。这样uvm_object_registry类获得了类的类型和类的名字;
2、通过一个typedef uvm_object_registry#(T,Tname) type_id; 给该类留下了一个接口(class_name::type_id:);
3、type_id 干了什么事情,type_id 里面声明了一个staic 的uvm_object_registry 类型的me ,并自动调用get完成向工厂里面的注册。

2、uvm_factory 类

上面用到了f.registry();那么factory是什么样的呢。仅仅只能注册嘛?

class uvm_factory;
	extern `_protected function new();
	extren static function uvm_factory get();

endclass
function uvm_factory uvm_factory::get();
	if(m_inst == null)begin
		m_inst =new();
	end
	return m_inst;
endfunction
//从这里可以看出来factory也是单实例的
function void uvm_factory::register(uvm_object_warpper obj);
	if(obj == null)begin
	//report error
	end
	if(obj.get_type_name() || obj.get_type_name() == "<unknown>" )begin
	//report warning
	end
	else begin
		if(m_type_name.exist(obj.get_type_name()))begin
		//report error
		end
		else begin
			m_type_name[obj.get_type_name()] = obj;
		end
	end
endfunction

这里最重要的就是,向联合数组里面插入一条记录,联合数组定义如下:uvm_object_wrapper m_type_names[sting];
同时也会向另一个联合数组 bit m_types[uvm_object_wrapper] 用于指示,该类型是否已经被注册过了。

3、factory机制的应用

3.1 根据类名创建类的一个实例

class AAA extends uvm_object;
	`uvm_object_utils(AAA)
...
endclass

class test;
	uvm_object aaa;
	function new();
		uvm_factory f =uvm_factory::get();
		aaa = f.create_object_by_name("AAA")

 这里我们分析一下 create_object_by_name是如何工作的:

function uvm_object uvm_factory::create_object_by_name(string requested_type_name,
													   string parent_inst_path	= "",
													   string name			   	= "",);
	uvm_object_wrapper wrapper;
	string inst_path;

	...
	if (wrpper == null)begin
		wrapper  = m_type_names[requestew_type_name];
	end
	return wrapper.create_object(name);
endfunction

function uvm_object_registry#(type T=uvm_object ,string Tname="<unkonwn>")::create_object(name);
	T obj;
	obj =new();
	if(name !="")
		obj.set_name(name);
	return obj;
endfucntion

因此工厂机制核心就是参数化的类,类T被当作类型参数传入uvm_object_registry类,这样将这个uvm_object_registry类注册进工厂,间接也就是将T注册进工厂,最后调用 create_object完成类型创建。

3.2白皮书对于factory机制的反思

factory 机制的核心就是一个联合数组,m_type_names。这个联合数组的索引是string 类型的,其存储的内容是 uvm_object_wrapper 类型的。想像一下,在 systemverilog 中,我们要往任何的数组或者 queue 中存放东西,存放的永远是值,而不可能是一个类型。我们只能说其中存放了某个类型的值,而不能说存放了一个类型。形象点说,假如我们定义好了一个 A 类,这个类的名字就是 A,我们要把这个类存放在联合数组中,那么应该存什么?第一,我们可以存放"A"这个字符串,但是这只是字符串,而不是类。第二,我们可以声明如下的一个数组:

A testarray[3];

这样,testarray 中就可以存放 A 类型的实例指针了,注意,存放的是 A 类型的 实例的指针,而不可能是 A这个类。类是一个抽象的概念,是不可能存放在一个数 组里面的!我们只有先把这个类实例化了,然后才能把实例的指针放入数组里面。基于这样的一种考虑,那么我们可以把一个 A 类的指针直接放入 m_type_names 中,干吗非要引入uvm_object_registry#(A,“A”)这样的一个类呢?

class A extends uvm_object;
 `uvm_object_utils(A)
 …
endclass

我们要把一个 A 实例的指针放入 m_type_names 数组中,其索引是字符串"A",create_object_by_name(“A”)时,可以直接到 m_type_names 中搜索"A",找出的是一个 A类型的实例,要想创建一个新的实例, 那么就 m_type_names[“A”].copy()可以了。这样就可以直接省去 uvm_object_registry 这个类的,节省了存储空间,而且变的非常简单了。 看上去确实是挺不错的主意,但是关键是,怎么样在 A定义的时候把 A 的一个 实例的指针放m_type_names 中呢?这是很难的事情,可以用静态成员变量来实 现。

class A extends uvm_object;
 static A m_a = A:;get();
 static function A get();
 if (m_a == null) begin
 uvm_factory f=uvm_factory::get();
 m_a = new();
 f.register(m_a);
 end

 return m_a;
 endfunction
 …
endclass

如上就可以实现我们要的功能。但是上述过程比较复杂,如果作为 UVM 的用 户,每从 uvm_object 类派生一个类的时候,都要这么做,那么我相信每个用户都会 很快放弃 UVM 的。UVM 的伟大之处就在于,把上述过程给标准化了,通过调用一 个uvm_object_utils 的宏来完成上述事情。通过使用 uvm_object_registry#(A,“A”)这样一个中间类,在这个类中做与上面类似的事情,即使用一些静态变量和静态函数, 通过静态变量的方法产生 uvm_object_registry#(A,“A”)的一个实例,把其加入到 factory 的 m_type_names 中去。仔细回味,我们才觉 uvm_object_registry 类的巧妙。

总结:其实注册功能我们可以手撸出来,无非就是声明一个静态的变量自己 staic A ma =A::get(); 然后把ma放到f.registry(ma)就行了,但是这样很麻烦,每次要我uvm_info(get_type_name(),"info",UVM_HIGH)这样打印一个信息我都觉得麻烦,如果每次让我手搓这样的代码,我可能会疯掉。

4、override

这里提一个点,不仅工厂里面提供了override的功能,我们的component组件也提供了override功能。
不过component组件的override功能也是调用了工厂机制。

set_type_override_by_name();
set_type_override();
set_inst_override();
set_inst_override_by_name();
//组件在type_id 内部还定义了两种override方法
uvm_component_registry#(T,Tname)::set_type_override();
uvm_component_registry#(T,Tname)::set_inst_override();

工厂有如下四种override方法

uvm_factory::set_type_override_by_type();
uvm_factory::set_type_override_by_name();
uvm_factory::set_inst_override_by_name();
uvm_factory::set_inst_override_by_type();

4.1如何实现override

前面的component源代码和factory源代码存在大量的关联数组,override功能使用了大量的队列来实现

uvm_factory_override ,_type_overrides[$];

class uvm_facrory_override;
	string full_inst_path;
	string orig_type_name;
	string ovrd_type_name;
	bit selected;
	uvm_object_wrapper orig_type;
	uvm_object_wrapper ovrd_type;
endclass

略,等待后面补充 ,不是特别影响使用,创建之前先去override队列里面找一下有没有override,有的话就用override。核心就是这个点

5、总结

这篇文章在component源码剖析的基础上,对factory机制进行分析,经过对源码的解析,我们可以总结出来以下核心内容:
factory机制主要的两个内容:
 1、根据名字创建实例,我们感受最深的就是,这个在component组件的run_tesr里面被用到了,我们给验证环境传递了一个test_name ,工厂机制就自动帮我们完成了test_case的创建。
 2、override,想复用别人的代码,但是又不能去修改别人的代码,就使用override功能去实现组件的替换,注意哦不仅component类型可以override,object类型也可以override。
这可以用来回答介绍一下factory机制这个问题。
感谢耐心观看,下期再见!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UVM工厂机制是一种用于创建和管理UVM组件和对象的机制。它通过使用UVM工厂类(uvm_factory)来实现。\[1\]该工厂类是一个独特的对象,用于注册和创建UVM组件和对象。在UVM中,组件和对象的注册是通过使用宏来实现的,分别是uvm_component_utils和`uvm_object_utils。\[2\]这些宏将组件和对象注册工厂中,以便在需要时可以通过工厂来创建它们。 使用UVM工厂机制创建组件和对象有两种方法。一种是通过继承uvm_component类或uvm_object类,并在类内重载方法来实现。这种方法本质上是借用工厂对象来创建组件和对象。\[1\]另一种方法是直接使用工厂方法来创建组件和对象。这需要使用全局唯一的uvm_factory类对象factory来调用工厂方法,并使用$cast进行类型转换。\[3\] 总之,UVM工厂机制提供了一种方便的方式来注册和创建UVM组件和对象,使得它们可以在需要时动态地创建和使用。 #### 引用[.reference_title] - *1* *3* [UVMfactory 机制](https://blog.csdn.net/Starry__/article/details/122930959)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [UVM-工厂机制(factory)](https://blog.csdn.net/sinat_41774721/article/details/121763596)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值