UVM源码分析之factory机制详解

前言

作者在学习了一段时间的UVM factory源码之后写下此文,旨在记录自己的学习成果,毕竟好记性不如烂笔头嘛,当然如果能帮助到对这部分有疑惑的同仁就更好了。作者是在笔记本电脑上的windows环境下使用source insight软件分析UVM 源码的。感兴趣的同仁可以试试用source insight分析源码,个人认为还是比在纯编辑器(比如vim)方便很多。source insight支持systemverilog的配置文件可以通过 sourceinsightsystemverilog语言CLF配置文件_SystemVerilogCLF-其它文档类资源-CSDN下载 下载。接下来,本文将尝试循序渐进地介绍UVM factory的实现。

1 什么是工厂?

工厂是软件工程中的一种常见的设计模式,工厂模式(factory pattern)。其核心理念是创建不同但相似的类型时,使用统一的工厂类接口。工厂模式的好处是,客户创建对象时无需知道内部细节,只需要传入参数即可。提供创建对象接口的就是工厂,创建的对象就是产品。

比如,有一家汽车公司叫Benz,它可以生产c200,e300,s400等不同类型的汽车。在没有工厂模式的情况下,创建对象的方法就是:benz_c200.new(), benz_e300.new(), benz_s400.new(). 使用工厂模式时,创建对象的接口就是:Benz_factory.creat_car(string type_name).

2 工厂最简单的实现方式

比如上面那个奔驰汽车的例子,如果有人要你实现这个工厂系统,你会怎么实现呢?这3种汽车类型,他们具有很多相似的属性,因此给他们搞个基类是理所当然的。那么在工厂类中如何根据不同的汽车类型生产不同的产品呢?最简单的就是if,else if 判断type_name, 针对不同的type_name创建不同的对象。如下:

class benz_car;
endclass

class benz_c200 extends benz_car;
    function new();
        $display("I am benz c200.\n");
    endfunction
endclass

class benz_e300 extends benz_car;
    function new();
        $display("I am benz e300.\n");
    endfunction
endclass

class benz_s400 extends benz_car;
    function new();
        $display("I am benz s400.\n");
    endfunction
endclass

class benz_factory;
    static function benz_car create_car(string type_name);
        if ("c200" == type_name) return benz_c200.new();
        else if ("e300" == type_name) return benz_e300.new();
        else if ("s400" == type_name) return benz_s400.new();
        else return null;            
    endfunction
endclass

上述这种实现方式的可扩展性实在太差,当产品类型变多之后,那段if else的代码长度可以绕办公室两圈。

3 改进的工厂

进一步地思考,为了便于扩展,我们必须建立一种type_name与类型的联系。那么,我们能否在factory中设置一个用string类型索引的关联数组呢?关联数组就存放产品类型的对象。在用户实现一种新的产品后,就把产品名字和类型注册到factory中。试试看:

class benz_factory;
    // 注册产品的接口
    static function void registry(benz_car obj, string type_name);
        m_type_names[type_name] = obj;
    endfunction

    // 创建产品的接口
    static function benz_car create_car(string type_name);
        if (m_type_names.exists(type_name))
            return m_type_names[type_namen];
        else
            return null;
    endfunction
    
    protected static benz_car  m_type_names[string];
endclass

这种实现方式看起来要好很多,最起码的新增产品类型时不用再写一串else if。我们再来看看用户如何注册自己的产品?

benz_c200 obj = new();
benz_factory::registry(obj, "benz_c200");

在注册之前,先要通过new的方式创建一个对象,这有点多此一举,用户都已经通过new的方式创建对象了,那还要工厂干嘛?况且对于这种实现方式,用户想要创建多个同一产品时,得到的是多个指向同一对象的handle, 这违背了初衷。因此,这种方式也不是我们想要的实现方式。

4 进一步改进

我们在factory中不能直接存储产品类型的对象,可以存储"产品加工器",这个产品加工器可以作为产品类型的一个静态成员,而且这个加工器必须具有create car的功能用户在实现了一种产品后,可以将产品的加工器注册到factory里面。factory在创建特定产品时,可以通过字符串索引,先找到对应的加工器,然后调用加工器的create_car函数,完美。如下:

// 奔驰加工器的基类
virtual class benz_car_wrapper;
    pure virtual function benz_car create_car();
        return null;
    endfunction
endcalss

// 特定车型的加工器
class benz_c200_wrapper extends benz_car_wrapper;
    function benz_car create_car();
        benz_c200 obj;
        obj = new(); // 这里new的是benz_c200类型的对象
        return obj;
    endfunction
endclass

// 产品类型benz_c200的实现
class benz_c200 extends benz_car;
    function new();
        $display("I am benz c200.\n");
    endfunction

    local static benz_c200_wrapper my_wrapper = get_wrapper();

    static function benz_c200_wrapper get_wrapper();
        if (my_wrapper == null) begin
            my_wrapper = new();
            benz_factory::registry(my_wrapper); // 将c200加工器注册到工厂
        end
        return my_wrapper
    endfunction
endclass

class benz_factory;
    // 注册产品的接口
    static function void registry(benz_car_wrapper obj, string type_name);
        m_type_names[type_name] = obj;
    endfunction

    // 创建产品的接口
    static function benz_car create_car(string type_name);
        if (m_type_names.exists(type_name)) begin
            benz_car_wrapper wrapper = m_type_names[type_namen];
            return wrapper.create_car();
        end else
            return null;
    endfunction
    
    protected static benz_car_wrapper  m_type_names[string];
endclass

其实,UVM的factory机制的核心就是类似于上面的方式,只是UVM巧妙的使用了一些宏来实现,而且加上了override的功能,所以看起来要比这复杂很多。但其本质就是用上面的理念和方法去实现的。

5 言归正传,UVM factory机制的源码解析

有了前文生动形象的例子,我们再来看UVM的源码可能就要轻松一些了……

熟悉UVM的读者一定知道,我们在实现一个class的时候,如果想使用工厂机制,就必须要用UVM提供的宏对你实现的类进行注册。对于uvm_object家族的,我们要使用uvm_object_utils进行注册,对于uvm_component家族的,我们要使用uvm_component_utils进行注册。接下来,我们选择uvm_object_utils进行解析,看看这个宏到底干了啥?通过前文奔驰的例子,可以大胆猜测一下,这个宏可能包含加工器类的实现,以及将加工器注册到factory的内容。事实也的确是这样的…...

uvm_object_utils的实现在uvm_object_defines.svh中,用source insight应该很容易看这部分代码。如上图,勤学好思的作者已经将这个宏的内容通过一个简洁明了的图展现出来了,方便读者了解这个宏的内容。这个大宏一共由4个小宏组成,这里我们先重点关注第一个uvm_object_registry(T,S),看名字就知道它是用来注册产品的。在uvm_object_registry(T,S)中,typedef了一个type_id类型,type_id类型实质上是一个模板类uvm_object_registry#(T,S),它的功能和地位就有点类似于前文奔驰例子中的benz_c200_wrapper, 只是这里UVM用模板类来实现了,用模板参数T来区分到底是c200,e300还是s400...

我们来看看uvm_object_registry(T,S)这个模板类的实现,对此作者又画了个简图,方便大家理解。源码在文件uvm_registry.svh中。(源码太长,况且跳来跳去不方便展示,简图中在不影响源码灵魂的情况下进行了精简)

请再次回忆奔驰车的例子,如上,这个模板类的实现与benz_c200_wrapper的实现基本是一致的,就是加上了模板参数而已。该模板类继承自uvm_object_wrapper,这里也可以大胆猜测下,factory中可能就有个string类型为索引,存储类型为uvm_object_wrapper的关联数组,用来记录各个产品类型的加工器(也就是不同类型的wrapper)。大家注意下这个static成员变量me,它的初始化是通过调用static函数get()来实现的,也就是说,当我们使用这个模板定义一个新的wrapper的时候会自动调用这个get函数。而在这个get函数中,me作为具体产品的加工器被注册到了factory中. 下面是uvm_object_wrapper的实现,uvm_object_wrapper是一个抽象类,定义了一些虚函数,源码在uvm_factory.svh中,此处只展示纲要:

接下来,我们要看看uvm_factory里做了些什么,其实,大家去翻翻源码就知道,uvm_factory里基本啥都没做,因为它是一个抽象类,只定义了一些虚函数,让它的子类去真正实现内容。起作用的是uvm_factory的子类,uvm_default_factory。我们只关注两个核心点,一个是类型注册,一个是创建对象,至于override部分下期再详解。

// 代码片段摘自uvm_factory.svh,进行了大量精简,删去了override功能相关代码

protected uvm_object_wrapper   m_type_names[string];

function void uvm_default_factory::register(uvm_object_wrapper obj);
  if (obj == null) begin
    uvm_report_fatal ...
  end
  if (obj.get_type_name() != "" && obj.get_type_name() != "<unknown>") begin
    if (m_type_names.exists(obj.get_type_name()))
      uvm_report_warning ...
    else 
      m_type_names[obj.get_type_name()] = obj; // 这样是注册的重点
  end
endfunction

function uvm_object uvm_default_factory::create_object_by_name (string requested_type_name,  string parent_inst_path="",  string name=""); 
  uvm_object_wrapper wrapper;
  // if no override exists, try to use requested_type_name directly
  if (wrapper==null) begin
    if(!m_type_names.exists(requested_type_name)) begin
      uvm_report_warning ...
      return null;
    end
    wrapper = m_type_names[requested_type_name];
  end
  return wrapper.create_object(name);
endfunction

如上所示,vum_default_factory中有一个索引为string的关联数组m_type_names,关联数组存储类型为uvm_object_wrapper,uvm_object_wrapper为uvm_object_registry(T,S)的基类。uvm_default_factory::register()的实现就是将wrapper放到了这个关联数组中,uvm_default_factory::create_object_by_name()的实现就是先从关联数组中取出wrapper,然后调用wrapper的create_object函数。

  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
UVM factory机制UVM中非常重要的一个机制,它提供了一种灵活的方式来创建和管理UVM组件。在UVM中,每个组件都是通过一个工厂类来创建的,这个工厂类就是UVM factory机制的核心。 UVM factory机制码实现主要包括以下几个部分: 1. `uvm_factory` 类 `uvm_factory` 类是UVM factory机制的核心类,它是所有组件的创建和管理的中心。在这个类中,定义了一些重要的函数,例如 `create_component_by_name`、`create_object_by_name`、`register_component_creator`、`register_object_creator` 等函数。 2. `uvm_component` 类 `uvm_component` 类是所有UVM组件的基类,它包含了一些重要的函数,例如 `create`、`get_type_name`、`get_full_name` 等函数。在 `uvm_component` 类的 `create` 函数中,会通过 `uvm_factory` 来创建组件。 3. `uvm_object` 类 `uvm_object` 类是所有UVM对象的基类,它也包含了一些重要的函数,例如 `create`、`get_type_name` 等函数。`uvm_object` 类的 `create` 函数中,也会通过 `uvm_factory` 来创建对象。 4. `uvm_create_func` 和 `uvm_component_registry` 类 `uvm_create_func` 和 `uvm_component_registry` 类是用来注册和存储组件创建函数的。在 `uvm_factory` 中,会使用 `uvm_component_registry` 来存储所有已注册的组件创建函数。 5. `uvm_coreservice_t` 类 `uvm_coreservice_t` 类是UVM中的一个单例类,它提供了一些全局的服务,例如 `get_factory`、`get_report_server`、`get_phase_scheduler` 等函数。在 `uvm_factory` 中,会使用 `uvm_coreservice_t` 来获取全局的 `uvm_factory` 实例。 总的来说,UVM factory机制码实现比较复杂,涉及到很多UVM的核心类和机制。如果想要深入了解UVM factory机制码实现,需要对UVM的整体架构有比较充分的理解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值