UVM factory机制源码探微

1. UVM factory机制的使用

UVM 的factory(工厂)模式是其提供的最核心的功能之一,利用factory机制可以实现强大的代码重载和验证平台扩展功能。uvm_factory就像是uvm的相关管理部门,每一个新的class类型类似于人类的新生儿,在class中需要调用宏 `uvm_component_utils定义一个参数化的代理类并在factory中注册,类似于新生儿出生后需要报户口拿到身份证并在人口系统登记;对class来说代理类就是他的身份证明,可以通过其参数类名和对应字符串找到它;对一个人来说,也可以通过身份证上的身份证号和照片来找到他。

uvm最主要的两大类型包括object和component,本文主要以component为例来看uvm_factory功能的底层代码实现。假如我们的验证平台现在有一个component class my_driver, 若我们想要用factory以其扩展类new_driver来重载它,需要以下三个步骤:

  1. 将被重载的类my_driver和重载类new_driver分别通过在各自class中调用宏 `uvm_component_utils(T)在factory中注册:
class my_driver extends uvm_driver;
	`uvm_component_utils(my_driver)
	...
endclass: my_driver

class new_driver extends my_driver;
	`uvm_component_utils(new_driver)
	...
endclass: new_driver
  1. 通过override 函数给出需要重载的类/某一实例和被重载的类的信息(可以是class type/name)。
  2. 以及使用uvm factory特定的实例化方式xx::type_id::creat(“name”, parent); 就可以实现my_driver到new_driver的factory重载功能:
function void case_xx::build_phase(uvm_phase phase);
	set_type_override_by_type(my_driver::get_type(), new_driver::get_type()); //用new_driver来重载 my_driver
	driver = my_driver::type_id::creat("driver", this); //此时creat会创建一个new_driver类型的实例,而不是my_driver类型的
endfunction: build_phase

2. class 在factory 中的注册过程

2.1. `uvm_component_utils()

我们先来看一下这个宏,这个宏包含另外两个宏定义m_uvm_component_registry_internal(T,T)和m_uvm_get_type_name_func(T), 后者比较简单,就是返回T同名的字符串类型,我们重点来分析前者。

//uvm_object_defines.svh
`define uvm_component_utils(T) \
   `m_uvm_component_registry_internal(T,T) \
   `m_uvm_get_type_name_func(T) \

//This is needed due to an issue in of passing down strings
//created by args to lower level macros.
`define m_uvm_component_registry_internal(T,S) \
   typedef uvm_component_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

`define m_uvm_get_type_name_func(T) \
   const static string type_name = `"T`"; \
   virtual function string get_type_name (); \
     return type_name; \
   endfunction 

2.1.1. m_uvm_component_registry_internal(T,T)

这个宏做了两件事情,第一件是用typedef 定义了一个uvm_component_registry#(T, S)类型的句柄type_id, 注意这个宏的两个参数T和S分别以传入的class type和同名字符串来定义参数化类uvm_component_registry#(T, S),在我们的例子中就是分别定义了uvm_component_registry#(my_driver, “my_driver”) 和uvm_component_registry#(new_driver, “new_driver”);
另外一件事是定义了两个函数,其中的一个静态函数get_type(),就是我们在override时会调用的my_driver::get_type(), 这里其实就是返回参数化类uvm_component_registry#(T, S)的唯一实例句柄,我们下文会提到。

2.1.1.1. uvm_component_registry #(T, Tnane)

先来看这个class的uvm源码:

//uvm_registry.svh
class uvm_component_registry #(type T=uvm_component, string Tname="<unknown>")
                                           extends uvm_object_wrapper;
  typedef uvm_component_registry #(T,Tname) this_type;
	
  local static this_type me = get();
  
  // Returns the singleton instance of this type. Type-based factory operation
  // depends on there being a single proxy instance for each registered type. 
  static function this_type get();
    if (me == null) begin
  	  uvm_coreservice_t cs = uvm_coreservice_t::get();                                                     
  	  uvm_factory factory=cs.get_factory();
      me = new;
      factory.register(me);
    end
    return me;
  endfunction
	...
endclass

virtual class uvm_object_wrapper;
	...
endclass

这个类派生自uvm_object_wrapper, uvm_object_wrapper是一个纯虚类,是不能直接实例化的,相当于只是提供了一个壳(wrapper)。对于每一个class T, 在声明uvm_component_registry #(T, Tname)的时候,其中的静态变量me为uvm_component_registry #(T, Tname)类型的一个句柄,在me初始化的时候都会通过get()函数指向该类在系统中的唯一一个实例(所谓的sigleton单例模式,在第一次调用的时候,me是没有指向任何实例的,所以会调用new()来分配一个实例对象,此后如果再次调用get()的时候,因为静态变量是由同类型的所有实例共享,而系统中已经有了一个该类型的实例,所以会直接返回之前的实例句柄),并且将该实例句柄在系统唯一的factory实例中调用register()进行注册,uvm_factory类的实例化也是sigleton模式,整个系统只会有一个factory的实例,所有的component/object类型都注册在这一个实例中(感兴趣的同学可以自行查阅uvm_coreservice_t和uvm_factory相关源码,这里不再赘述)。

2.1.1.2. register()

register()函数定义在uvm_default_factory中:

//uvm_factory.svh
function void uvm_default_factory::register (uvm_object_wrapper obj);
  if (obj == null) begin
    uvm_report_fatal ("NULLWR", "Attempting to register a null object with the factory", UVM_NONE);
  end
  if (obj.get_type_name() != "" && obj.get_type_name() != "<unknown>") begin
    if (m_type_names.exists(obj.get_type_name()))
      uvm_report_warning("TPRGED", {"Type name '",obj.get_type_name(),
        "' already registered with factory. No string-based lookup ",
        "support for multiple types with the same type name."}, UVM_NONE);
    else 
      m_type_names[obj.get_type_name()] = obj;
  end

  if (m_types.exists(obj)) begin
    if (obj.get_type_name() != "" && obj.get_type_name() != "<unknown>")
      uvm_report_warning("TPRGED", {"Object type '",obj.get_type_name(),
                         "' already registered with factory. "}, UVM_NONE);
  end
  else begin
    m_types[obj] = 1;
    // If a named override happens before the type is registered, need to copy
    // the override queue.
    // Note:Registration occurs via static initialization, which occurs ahead of
    // procedural (e.g. initial) blocks. There should not be any preexisting overrides.
    if(m_inst_override_name_queues.exists(obj.get_type_name())) begin
       m_inst_override_queues[obj] = new;
       m_inst_override_queues[obj].queue = m_inst_override_name_queues[obj.get_type_name()].queue;
       m_inst_override_name_queues.delete(obj.get_type_name());
    end
    if(m_wildcard_inst_overrides.size()) begin
       if(! m_inst_override_queues.exists(obj)) 
            m_inst_override_queues[obj] = new;
       foreach (m_wildcard_inst_overrides[i]) begin
         if(uvm_is_match( m_wildcard_inst_overrides[i].orig_type_name, obj.get_type_name()))
            m_inst_override_queues[obj].queue.push_back(m_wildcard_inst_overrides[i]);
       end
    end

  end

endfunction

这里出现了两个数组m_type_names[]和m_types[],它们都是定义在uvm_default_factory类中的联合数组, 前者的索引是字符串类型,值是uvm_object_wrapper类型的句柄,后者是以uvm_object_wrapper类型句柄为索引,值为一个bit。因为系统全局只有唯一的factory实例,所以这个m_type_names[]也是全局唯一。在上面的代码中,若传入的uvm_component_registry #(T, Tname) 尚未被注册,则以Tname为索引将其对应句柄存入m_type_names[],在我们的例子中即m_type_names[“my_driver”] = me, 这个me指向的是uvm_component_registry #(my_driver, “my_driver”) 唯一实例,与此同时m_types[me]这个bit被置为1,表明该类型已经在factory中注册过了。此外register()中还出现了诸如m_inst_override_name_queues和m_wildcard_inst_overrides, 这些数组或者队列类型跟下文介绍的override机制有关,这里主要是用来处理当使用字符串name来重载某些类型的inst的时候,factory之前的注册信息并没有该字符串对应类型的相关记录,而本次注册的时候恰巧匹配到这些字符串,这里会对这些重载信息进行重新调整和记录。

//uvm_factory.svh
 class uvm_default_factory extends uvm_factory;
 	protected bit                  m_types[uvm_object_wrapper];
  	protected uvm_object_wrapper   m_type_names[string];
  ...
 endclass

2.2. 总结

  1. 在class my_driver中调用宏`uvm_component_utils(my_driver)之后,uvm做了两件事情,
    a, 第一件是声明uvm_component_registry#(my_driver, “my_driver”)类型的句柄type_id,在初始化时实例化该类型的全局唯一实例对象,顺便定义了get_type()/get_object_type()/get_type_name ()这三个函数,前两个函数都是返回指向uvm_component_registry#(my_driver, “my_driver”)类型的实例句柄me, 第三个函数返回字符串类型的"my_driver"。
    b, 第二件是将uvm_component_registry#(my_driver, “my_driver”)注册在系统唯一factory实例中,所谓注册,就是以该类同名字符串"my_driver"为索引,把me存进联合数组m_type_names[], 即m_type_names[“my_driver”] = me, 并且把联合数组m_types[me]置为1,表明该类型已经完成factory注册,同理,对于new_driver的注册也是如此:
    m_type_names[“my_driver”] = me1 >>> 指向uvm_component_registry#(my_driver, “my_driver”)类型的实例
    m_types[me1] = 1
    m_type_names[“new_driver”] = me2 >>> 指向uvm_component_registry#(new_driver, “new_driver”)类型的实例
    m_types[me2] = 1
  2. 由此可见,对于class my_driver的注册,实际上并不是注册的这个类本身,而是他的一个替身,官方叫做代理类(proxy class),类似于它的身份证。在我们的例子中即uvm_component_registry#(my_driver, “my_driver”),对于my_driver这个类来说,系统仅有这个类与其对应,所以把这个类注册也就相当于my_driver已经被注册。为什么不直接注册my_driver而大费周章的创造出一个proxy来呢,这个proxy本质是一个参数化的类,其中的第二个参数就是其所代表的类的同名字符串,通过这个代理类可以实现factory另一个强大的功能 ---- 根据字符串来实例化此字符串所代表的类

3. uvm_factory实例化过程

3.1 creat()

在实例化一个类的时候,uvm推荐调用xx::type_id::creat(name, parent) 而不是简单的new(),实际上这跟其factory的override机制有关,本例中我们这样来实例化my_driver: driver = my_driver::type_id::creat(“driver”, this); 这里的create是定义在my_driver的唯一代理类type_id(即uvm_component_registry#(my_driver, “my_driver”))作用域下的函数,其定义如下:

//uvm_registry.svh
class uvm_component_registry #(type T=uvm_component, string Tname="<unknown>")
                                           extends uvm_object_wrapper;
  ...
  static function T create(string name, uvm_component parent, string contxt="");
    uvm_object obj;
    uvm_coreservice_t cs = uvm_coreservice_t::get();                                                     
    uvm_factory factory=cs.get_factory();
    if (contxt == "" && parent != null)
      contxt = parent.get_full_name();
    obj = factory.create_component_by_type(get(),contxt,name,parent);
    if (!$cast(create, obj)) begin
      string msg;
      msg = {"Factory did not return a component of type '",type_name,
        "'. A component of type '",obj == null ? "null" : obj.get_type_name(),
        "' was returned instead. Name=",name," Parent=",
        parent==null?"null":parent.get_type_name()," contxt=",contxt};
      uvm_report_fatal("FCTTYP", msg, UVM_NONE);
    end
  endfunction
  ...
endclass

这个函数会返回一个T类型的实例句柄,而T就是type_id对应的class类型,即my_driver或其扩展类,其中最关键的是在11行调用uvm_factory的create_component_by_type()函数来拿到这个实例对象的句柄,该函数定义如下,

//uvm_factory.svh
function uvm_component uvm_default_factory::create_component_by_type (uvm_object_wrapper requested_type,  
                                                            string parent_inst_path="",  
                                                            string name, 
                                                            uvm_component parent);
  string full_inst_path;

  if (parent_inst_path == "")
    full_inst_path = name;
  else if (name != "")
    full_inst_path = {parent_inst_path,".",name};
  else
    full_inst_path = parent_inst_path;

  m_override_info.delete();

  requested_type = find_override_by_type(requested_type, full_inst_path);

  return requested_type.create_component(name, parent);

endfunction

它的参数有四个,第一个是在creat()函数中通过get()拿到的uvm_component_registry#(my_driver, “my_driver”)的实例对象的句柄,第二和第三个参数分别是字符串类型的parent_inst_path和要创建实例类型的名字,最后一个参数是parent句柄,这个parent_inst_path和16行的m_override_info以及18行的函数find_override_by_type()都跟override机制有关,这里暂且按下不表,其实find_override_by_type()就是根据factory记录的重载信息来返回重载前/后的类型,在本例中我们用new_driver类型来重载my_driver类型,该函数会返回uvm_component_registry#(new_driver, “new_driver”)的实例句柄。最后,调用uvm_component_registry#(new_driver, “new_driver”)的creat_component()函数来创建new_driver的实例。

//uvm_registry.svh
class uvm_component_registry #(type T=uvm_component, string Tname="<unknown>")
                                           extends uvm_object_wrapper;
  ...
  virtual function uvm_component create_component (string name,
                                                   uvm_component parent);
    T obj;
    obj = new(name, parent);
    return obj;
  endfunction
  ...
endclass

creat_component()很简单,两个参数分别是字符串类型的name和parent,现在我们发现creat()最终其实还是调用new()来创建出对应的实例对象,对于uvm_component类型来说,理所当然的需要一个parent参数,creat_component()只需要传入字符串类型的name(这里即"driver",当然也可以是"aa")就可以实例化new_driver类型的一个对象,关键就在于proxy类uvm_component_registry #(T, Tname)是参数化的类,一旦find_override_by_type()返回合适的uvm_component_registry #(T, Tname)类(对应重载后的类或者未被重载过的类),则creat_component()只能创建T类型的对象,跟我们在creat(name, parent)中传入的字符串name无关。

3.2. 总结

其实creat()就是把new()包装了一下,增加了一个主要功能:根据factory的override信息来创建对应的实例。

4. factory override过程

uvm提供多种形式的override功能,比如根据type或者name来重载,重载系统中所有同类型的实例对象或者仅重载某类型的某一实例。在uvm_component中提供下面四种override函数:
set_type_override_by_type/set_type_override/set_inst_override_by_type/set_inst_override,实际上这些函数还是分别通过调用uvm_factory的函数set_type_override_by_type/set_type_override_by_name/set_inst_override_by_type/set_inst_override_by_name来实现其功能,我们来看下uvm_factory中这些函数的定义。

4.1. set_type_override_by_type()

在本例中我们通过调用set_type_override_by_type(my_driver::get_type(), new_driver::get_type())来实现new_driver对my_driver的重载,这里的两个参数传入的分别是这两个类型对应的proxy类实例对象的句柄。

//uvm_factory.svh
function void uvm_default_factory::set_type_override_by_type (uvm_object_wrapper original_type,
                                                      uvm_object_wrapper override_type,
                                                      bit replace=1);
  bit replaced;
  ...
  // register the types if not already done so, for the benefit of string-based lookup
  if (!m_types.exists(original_type))
    register(original_type); 

  if (!m_types.exists(override_type))
    register(override_type); 

  // check for existing type override
  foreach (m_type_overrides[index]) begin
    if (m_type_overrides[index].orig_type == original_type ||
        (m_type_overrides[index].orig_type_name != "<unknown>" &&
         m_type_overrides[index].orig_type_name != "" &&
         m_type_overrides[index].orig_type_name == original_type.get_type_name())) begin
      string msg;
      msg = {"Original object type '",original_type.get_type_name(),
             "' already registered to produce '",
             m_type_overrides[index].ovrd_type_name,"'"};
      if (!replace) begin
        msg = {msg, ".  Set 'replace' argument to replace the existing entry."};
        uvm_report_info("TPREGD", msg, UVM_MEDIUM);
        return;
      end
      msg = {msg, ".  Replacing with override to produce type '",
                  override_type.get_type_name(),"'."};
      uvm_report_info("TPREGR", msg, UVM_MEDIUM);
      replaced = 1;
      m_type_overrides[index].orig_type = original_type; 
      m_type_overrides[index].orig_type_name = original_type.get_type_name(); 
      m_type_overrides[index].ovrd_type = override_type; 
      m_type_overrides[index].ovrd_type_name = override_type.get_type_name(); 
    end
  end

  // make a new entry
  if (!replaced) begin
    uvm_factory_override override;
    override = new(.orig_type(original_type),
                   .orig_type_name(original_type.get_type_name()),
                   .full_inst_path("*"),
                   .ovrd_type(override_type));

    m_type_overrides.push_back(override);
  end

endfunction

首先,若重载类和被重载类尚未在factory中注册,这个函数会先分别对其进行注册(因此理论上说就算之前这两个类型都没有在factory中注册过,也可以直接调用这个函数来进行重载,因为该函数内部会对传入类型进行补注册)。这里出现了一个新的uvm_factory_override类型的队列m_type_overrides[$], uvm_factory_override类的一个实例会储存一条override信息诸如original_type/override_type以及他们对应的字符串name(用于以字符串name索引来寻找重载类型)和full_inst_path(用于对某一特定实例重载),而队列m_type_overrides[$]则用来存储factory中当前所有的type重载信息。

//uvm_factory.svh
 class uvm_factory_override;
  string full_inst_path;
  string orig_type_name;
  string ovrd_type_name;
  bit selected;
  int unsigned used;
  uvm_object_wrapper orig_type;
  uvm_object_wrapper ovrd_type;
  function new (string full_inst_path="",
                string orig_type_name="",
                uvm_object_wrapper orig_type=null,
                uvm_object_wrapper ovrd_type);
    if (ovrd_type == null) begin
      uvm_report_fatal ("NULLWR", "Attempting to register a null override object with the factory", UVM_NONE);
    end
    this.full_inst_path= full_inst_path;
    this.orig_type_name = orig_type == null ? orig_type_name : orig_type.get_type_name();
    this.orig_type      = orig_type;
    this.ovrd_type_name = ovrd_type.get_type_name();
    this.ovrd_type      = ovrd_type;
  endfunction
endclass

 class uvm_default_factory extends uvm_factory;
  	protected uvm_factory_override m_type_overrides[$];
  ...
 endclass

回到上面的set_type_override_by_type()函数中,在该函数中会遍历队列m_type_overrides[$]来寻找当前传入的被重载类型(original_type)是否已经存在重载信息,若找到且第三个参数replace=1(default1, 在调用set_type_override_by_type()来控制是否真的要进行重载),则将被重载和重载类重新写入该uvm_factory_override类来更新这条重载信息;若当前该队列中并未找到被重载的类型(original_type),则创建一个新的uvm_factory_override实例对象并记录相关重载信息,然后将其存入队列m_type_overrides[$]。
本例中调用此函数set_type_override_by_type(my_driver::get_type(), new_driver::get_type())时,会创建一个新的uvm_factory_override实例"override"(该实例的句柄,也可以叫"aa")来记录这条重载信息:
override.full_inst_path = “*”
override.orig_type_name = “my_driver”
override.orig_type = my_driver
override.ovrd_type_name = “new_driver”
override.ovrd_type = new_driver
假设当前factory中只有这一条重载信息,则m_type_overrides[0] = override;

4.2. set_type_override_by_name()

这个函数跟set_type_override_by_type类似,只是它通过传入的字符串参数type_name在factory中搜寻注册信息,因为factory中储存各个类的注册信息的数组m_type_names[]的索引正是各个类的同名字符串,先通过字符串索引到各自对应的类型,然后记录相关重载信息。该函数最终也会将通过参数传入的重载信息用uvm_factory_override的一个实例来记录并存入队列m_type_overrides[$]。注:若重载类尚未在factory中注册,该函数会直接返回。使用该函数进行重载之前,重载类和被重载类必须事先已经在factory中注册过,否则无法实现重载功能

//uvm_factory.svh
function void uvm_default_factory::set_type_override_by_name (string original_type_name,
                                                      string override_type_name,
                                                      bit replace=1);
  bit replaced;
  
  uvm_object_wrapper original_type;
  uvm_object_wrapper override_type;

  if(m_type_names.exists(original_type_name))
    original_type = m_type_names[original_type_name];

  if(m_type_names.exists(override_type_name))
    override_type = m_type_names[override_type_name];

  // check that type is registered with the factory
  if (override_type == null) begin
      uvm_report_error("TYPNTF", {"Cannot register override for original type '",
      original_type_name,"' because the override type '",
      override_type_name, "' is not registered with the factory."}, UVM_NONE);
    return;
  end

  // check that old and new are not the same
  if (original_type_name == override_type_name) begin
      uvm_report_warning("TYPDUP", {"Requested and actual type name ",
      " arguments are identical: ",original_type_name,". Ignoring this override."}, UVM_NONE);
    return;
  end

  foreach (m_type_overrides[index]) begin
    if (m_type_overrides[index].orig_type_name == original_type_name) begin
      if (!replace) begin
        uvm_report_info("TPREGD", {"Original type '",original_type_name,
          "' already registered to produce '",m_type_overrides[index].ovrd_type_name,
          "'.  Set 'replace' argument to replace the existing entry."}, UVM_MEDIUM);
        return;
      end
      uvm_report_info("TPREGR", {"Original object type '",original_type_name,
        "' already registered to produce '",m_type_overrides[index].ovrd_type_name,
        "'.  Replacing with override to produce type '",override_type_name,"'."}, UVM_MEDIUM);
      replaced = 1;
      m_type_overrides[index].ovrd_type = override_type; 
      m_type_overrides[index].ovrd_type_name = override_type_name; 
    end
  end

  if (original_type == null)
    m_lookup_strs[original_type_name] = 1;

  if (!replaced) begin
    uvm_factory_override override;
    override = new(.orig_type(original_type),
                   .orig_type_name(original_type_name),
                   .full_inst_path("*"),
                   .ovrd_type(override_type));

    m_type_overrides.push_back(override);
//    m_type_names[original_type_name] = override.ovrd_type;
  end

endfunction

4.3. set_inst_override_by_type()

对于某类型的某个inst的重载操作,uvm引入了一个新的数组m_inst_override_queues[],它是定义在uvm_default_factory类中的以uvm_object_wrapper类型为索引,内容为uvm_factory_queue_class类型的联合数组,这个uvm_factory_queue_class其实就是uvm_factory_override类型的一个队列,它可以储存多条重载信息,类似于我们之前看到的队列m_type_overrides[$]。 在整个uvm验证平台中,某一个class的实例可能有多个,而同一类型的不同实例之间可能会有不同的重载需求,所以uvm采用uvm_factory_queue_class.queue[$]这样一个队列来储存同一类型的所有不同实例的重载信息,然后以这些类型(proxy class)为索引放进联合数组uvm_factory_queue_class[]来记录当前平台中所有类型的所有实例间的相关重载信息。

uvm_factory.svh
//Instance overrides by requested type lookup
class uvm_factory_queue_class;
  uvm_factory_override queue[$];
endclass

class uvm_default_factory extends uvm_factory;
  ...
  protected uvm_factory_queue_class m_inst_override_queues[uvm_object_wrapper];
  protected uvm_factory_queue_class m_inst_override_name_queues[string];
  protected uvm_factory_override    m_wildcard_inst_overrides[$];
  ...
endclass

set_inst_override_by_type()这个函数的三个参数除了前两个是被重载和重载类的句柄之外,第三个参数尤为重要,我们需要给出被重载的实例的具体路径path。同样若这两个类尚未在factory中注册,在函数内部会先分别对其进行注册。

//uvm_factory.svh
function void uvm_default_factory::set_inst_override_by_type (uvm_object_wrapper original_type,
                                                      uvm_object_wrapper override_type,
                                                      string full_inst_path);
  
  uvm_factory_override override;

  // register the types if not already done so
  if (!m_types.exists(original_type))
    register(original_type); 

  if (!m_types.exists(override_type))
    register(override_type); 

  if (check_inst_override_exists(original_type,override_type,full_inst_path))
    return;

  if(!m_inst_override_queues.exists(original_type))
    m_inst_override_queues[original_type] = new;

  override = new(.full_inst_path(full_inst_path),
                 .orig_type(original_type),
                 .orig_type_name(original_type.get_type_name()),
                 .ovrd_type(override_type));
                 
  m_inst_override_queues[original_type].queue.push_back(override);
endfunction

函数内部调用check_inst_override_exists()检查这个具体路径下的特定实例是否已经被重载过了,这个函数主要就是遍历m_inst_override_queues[]来检查当前我们想要重载的这个实例的相关重载信息是否已经被记录在案,若已经被记录,则直接返回。否则就以被重载类型(proxy class)为索引创建一条新的重载记录信息放入该类型(proxy class)重载信息队列并存入m_inst_override_queues[];

//uvm_factory.svh
function bit uvm_default_factory::check_inst_override_exists (uvm_object_wrapper original_type,
                                      uvm_object_wrapper override_type,
                                      string full_inst_path);
  uvm_factory_override override;
  uvm_factory_queue_class qc;

  if (m_inst_override_queues.exists(original_type))
    qc = m_inst_override_queues[original_type];
  else
    return 0;

  for (int index=0; index<qc.queue.size(); ++index) begin

    override = qc.queue[index]; 
    if (override.full_inst_path == full_inst_path &&
        override.orig_type == original_type &&
        override.ovrd_type == override_type &&
        override.orig_type_name == original_type.get_type_name()) begin
    uvm_report_info("DUPOVRD",{"Instance override for '",
       original_type.get_type_name(),"' already exists: override type '",
       override_type.get_type_name(),"' with full_inst_path '",
       full_inst_path,"'"},UVM_HIGH);
      return 1;
    end
  end
  return 0;
endfunction

4.4. set_inst_override_by_name()

使用字符串name来对某类型的某个inst重载操作时,uvm引入了一个新的联合数组m_inst_override_name_queues[],这个数组以字符串为索引储存的是该字符串相关的一组重载信息(以uvm_factory_queue_class.queue[$]为载体)。此外还有一个队列m_wildcard_inst_overrides[$]储存的是字符串中有wildcard通配符(例如"*", “?”)时对应的重载信息。

uvm_factory.svh
class uvm_default_factory extends uvm_factory;
  ...
  protected uvm_factory_queue_class m_inst_override_name_queues[string];
  protected uvm_factory_override    m_wildcard_inst_overrides[$];
  ...
endclass

set_inst_override_by_name()函数传入字符串name对应的重载类和被重载类型需要事先在factory中注册,若重载字符串对应的重载类尚未在factory中注册,函数会直接返回。

//uvm_factory.svh
function void uvm_default_factory::set_inst_override_by_name (string original_type_name,
                                                      string override_type_name,
                                                      string full_inst_path);
  
  uvm_factory_override override;
  uvm_object_wrapper original_type;
  uvm_object_wrapper override_type;

  if(m_type_names.exists(original_type_name))
    original_type = m_type_names[original_type_name];

  if(m_type_names.exists(override_type_name))
    override_type = m_type_names[override_type_name];

  // check that type is registered with the factory
  if (override_type == null) begin
    uvm_report_error("TYPNTF", {"Cannot register instance override with type name '",
    original_type_name,"' and instance path '",full_inst_path,"' because the type it's supposed ",
    "to produce, '",override_type_name,"', is not registered with the factory."}, UVM_NONE);
    return;
  end

  if (original_type == null)
      m_lookup_strs[original_type_name] = 1;

  override = new(.full_inst_path(full_inst_path),
                 .orig_type(original_type),
                 .orig_type_name(original_type_name),
                 .ovrd_type(override_type));

  if(original_type != null) begin
    if (check_inst_override_exists(original_type,override_type,full_inst_path))
      return;
    if(!m_inst_override_queues.exists(original_type))
      m_inst_override_queues[original_type] = new;
    m_inst_override_queues[original_type].queue.push_back(override);
  end 
  else begin
    if(m_has_wildcard(original_type_name)) begin
       foreach(m_type_names[i]) begin
         if(uvm_is_match(original_type_name,i)) begin
           this.set_inst_override_by_name(i, override_type_name, full_inst_path);
         end
       end
       m_wildcard_inst_overrides.push_back(override);
    end
    else begin
      if(!m_inst_override_name_queues.exists(original_type_name))
        m_inst_override_name_queues[original_type_name] = new;
      m_inst_override_name_queues[original_type_name].queue.push_back(override);
    end
  end

endfunction

uvm会先在factory已注册类型中搜寻与函数传入字符串匹配的类型(proxy类),然后创建一条新的重载信息。若被重载类型已经被注册,则调用check_inst_override_exists()来查找是否已经有相同实例的重载信息,若有,直接返回,否则将该条重载信息存入记录被重载类型所有实例重载信息的队列m_inst_override_queues[].queue中。
此外,这个函数还支持传入字符串通配符进行匹配,若根据传入字符串original_type_name没有在factory注册表中查找到对应的类型,则
1,若字符串original_type_name中包含通配符(如"“, “?”),将该条重载信息存入队列m_wildcard_inst_overrides[$]。然后遍历uvm_factory的注册表m_type_names[]来查找是否有跟original_type_name匹配的字符串索引,若匹配,则使用被查找到的字符串name为参数再次调用set_inst_override_by_name()进行重载。(第二次调用一定可以找到)
2, 若字符串original_type_name中不包含通配符(如”
", “?”),则创建以该字符串为索引的m_inst_override_name_queues[]的实例并把该条重载信息放入队列m_inst_override_name_queues[“original_type_name”].queue中。

4.5. 总结

  1. 我们在调用各种override函数的时候,uvm_factory会在其内部的联合数组或队列中记录这些信息。uvm_factory_override类是最基础的用来记录某一条重载信息的类,其中分别记录了重载类和被重载类的class type(proxy class)和字符串name以及被重载的inst具体路径。
  2. 队列m_type_overrides[$]用来存放factory当前以class type(proxy class)来重载某 class type(proxy class)的所有重载信息, 即uvm_factory_override的实例句柄(指向的实例对象中记录了相关重载信息)。
  3. 联合数组m_inst_override_queues[]以被重载类(proxy class)为索引,每条索引对应的内容是一个uvm_factory_queue_class类的实例句柄,其对象中的队列存储了该类型的所有inst的重载信息。
  4. 队列m_wildcard_inst_overrides[$]中储存了所有以含有通配符的被重载类字符串name且在当前factory未被注册的类型的相关重载信息。
  5. 联合数组m_inst_override_name_queues[]以某不含有通配符且在当前的factory中未被注册的被重载类字符串name为索引,每条索引对应的内容是一个uvm_factory_queue_class类的实例句柄,其对象中的队列为该字符串相关的所有重载信息。

5. override机制下的实例化过程

其实uvm_default_factory中还有一个uvm_factory_override类型的队列m_override_info[$],它储存的是在当前创建实例过程中需要重载的信息,主要是用来检查当前的重载信息中被重载类是否与函数被递归调用时传入的重载类型相同,防止代码陷入递归循环重载。

//uvm_factory.svh
class uvm_default_factory extends uvm_factory;
  ...
  local uvm_factory_override     m_override_info[$];
  ...
endclass

5.1. find_override_by_type()

之前我们在分析creat()函数的时候,曾经遇到过该函数, 这个函数定义如下:

//uvm_factory.svh
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type,
                                                               string full_inst_path);

  uvm_object_wrapper override;
  uvm_factory_override lindex;
  
  uvm_factory_queue_class qc;
  qc = m_inst_override_queues.exists(requested_type) ?
       m_inst_override_queues[requested_type] : null;

  foreach (m_override_info[index]) begin
    if ( //index != m_override_info.size()-1 &&
       m_override_info[index].orig_type == requested_type) begin
      uvm_report_error("OVRDLOOP", "Recursive loop detected while finding override.", UVM_NONE);
      if (!m_debug_pass)
        debug_create_by_type (requested_type, full_inst_path);

	  m_override_info[index].used++;
      return requested_type;
    end
  end

  // inst override; return first match; takes precedence over type overrides
  if (full_inst_path != "" && qc != null)
    for (int index = 0; index < qc.queue.size(); ++index) begin
      if ((qc.queue[index].orig_type == requested_type ||
           (qc.queue[index].orig_type_name != "<unknown>" &&
            qc.queue[index].orig_type_name != "" &&
            qc.queue[index].orig_type_name == requested_type.get_type_name())) &&
          uvm_is_match(qc.queue[index].full_inst_path, full_inst_path)) begin
        m_override_info.push_back(qc.queue[index]);
        if (m_debug_pass) begin
          if (override == null) begin
            override = qc.queue[index].ovrd_type;
            qc.queue[index].selected = 1;
            lindex=qc.queue[index];
          end
        end
        else begin
	      qc.queue[index].used++;	        
          if (qc.queue[index].ovrd_type == requested_type)
            return requested_type;
          else
            return find_override_by_type(qc.queue[index].ovrd_type,full_inst_path);
        end
      end
    end

  // type override - exact match
  foreach (m_type_overrides[index]) begin
    if (m_type_overrides[index].orig_type == requested_type ||
        (m_type_overrides[index].orig_type_name != "<unknown>" &&
         m_type_overrides[index].orig_type_name != "" &&
         requested_type != null &&
         m_type_overrides[index].orig_type_name == requested_type.get_type_name())) begin
      m_override_info.push_back(m_type_overrides[index]);
      if (m_debug_pass) begin
        if (override == null) begin
          override = m_type_overrides[index].ovrd_type;
          m_type_overrides[index].selected = 1;
          lindex=m_type_overrides[index];
        end
      end
      else begin
	    m_type_overrides[index].used++;	      
        if (m_type_overrides[index].ovrd_type == requested_type)
          return requested_type;
        else
          return find_override_by_type(m_type_overrides[index].ovrd_type,full_inst_path);
      end
    end
  end

  // type override with wildcard match
  //foreach (m_type_overrides[index])
  //  if (uvm_is_match(index,requested_type.get_type_name())) begin
  //    m_override_info.push_back(m_inst_overrides[index]);
  //    return find_override_by_type(m_type_overrides[index],full_inst_path);
  //  end

  if (m_debug_pass && override != null) begin
    lindex.used++;	  
    if (override == requested_type) begin
      return requested_type;
    end	
    else
      return find_override_by_type(override,full_inst_path);
  end	
  
  return requested_type;

endfunction

接下来分析一下这个函数,

  1. 假如现在我们想要用new_driver来重载my_driver类型,调用set_type_override_by_type()或者set_type_override_by_name()后,队列m_type_overrides[$]会有这样一条重载信息。此后在调用driver = my_driver::type_id::creat(“driver”, this)时其会调用find_override_by_type(),这个函数传入的第一个参数是class my_dirver的proxy class句柄,函数会先检查一下该类型是否存在任何inst被重载的信息并将其存入队列qc,这里显然为空。然后检查m_override_info[$], 在find_override_by_type()被调用之前,这个队列也会先被清空。所以函数会直接遍历m_type_overrides[$]来寻找其中是否有new_driver的重载信息,若有,将其存入m_override_info[$],然后第二次调用set_type_override_by_type(), 传入的第一个参数为根据重载信息记录拿到的重载类new_driver(proxy class),第二次函数调用后除了检查m_override_info[$]的内容来防止递归循环之外,会继续遍历m_type_overrides[$]的重载信息,这里m_type_overrides[$]已经没有关于new_driver作为original_type的重载信息,所以函数直接返回new_driver(proxy class)类型从而创建一个new_driver类型的实例。
  2. 若使用new_driver调用set_inst_override_by_type()/set_inst_override_by_name()来对my_driver的某路径下的某个实例进行重载的话,例如
    set_inst_override_by_type(my_driver::get_type(), new_driver::get_type(), “env.agt.drv”);该条信息所在的记录重载类my_driver(proxy class)所有inst重载信息的队列句柄会被放到数组m_inst_override_queues[]中,在调用creat()时函数自动把当前要创建实例的full_inst_path传入find_override_by_type()中,在find_override_by_type()中会首先拿到与被重载类相关的的所有inst的重载信息的队列,然后遍历这个队列,根据传入的full_inst_path进行检查,如果当前是在env.agt中创建一个my_driver的实例,则跟该实例路径匹配,以重载信息中的重载类new_driver(proxy calss)和full_inst_path作为参数递归调用set_inst_override_by_type(),显然第二次调用set_inst_override_by_type()找不到与new_driver相关的重载信息,则直接返回该类型用于创建实例对象。

5.2. override函数的多次调用

  1. 考虑下面两种情况:
    a, class new_driver和new_driver1都是class my_driver的扩展类且在uvm_factory中完成注册,在平台中前后两次调用函数设置以下重载信息:
      set_type_override_by_type(my_driver::get_type(), new_driver::get_type());
      set_type_override_by_type(my_driver::get_type(), new_driver1::get_type());
    当调用driver = my_driver::type_id::creat(“driver”, this)的时候,若第二次调用set_type_override_by_type()时传入的第三个参数bit replace = 1, 则创建的是new_driver1的实例,若bit replace = 0, 则创建的是new_driver的实例。在第一次调用这个函数时,m_type_overrides[$]为空,函数内部另一个变量replaced 为0,此时函数会创建一个uvm_factory_override类型实例来记录第一条重载信息。第一条重载信息被储存在m_type_overrides[$]后,第二次调用find_override_by_type()函数时首先会遍历这个队列,通过相同的被重载类的索引找到第一条重载信息后,若参数replace为0,函数会直接返回,此时m_type_overrides[$]中的这条重载信息记录的重载类就是new_driver。若参数replace为1,函数会以第二次调用时传入的重载类重写这条信息,同时把变量replaced置为1,函数不会再创建关于这条重载信息的新纪录,最终m_type_overrides[$]中保留的原有的重载信息记录的重载类就是new_driver1。当多次调用set_type_override_by_type()函数对同一个类型进行多次重载时,若重复调用时的参数replace为1,则会创建最终重载类的实例,若重复调用时的参数replace为0,则创建第一次调用时的重载类实例
    b, class new_driver和new_driver1都是class my_driver的扩展类且在uvm_factory中完成注册,在平台中前后两次调用函数设置以下重载信息:
      set_type_override_by_type(my_driver::get_type(), new_driver::get_type());
      set_type_override_by_type(new_driver::get_type(), new_driver1::get_type());
    当调用driver = my_driver::type_id::creat(“driver”, this)时,其实创建的是new_driver1的实例,当第一条重载信息被找到,函数find_override_by_type()把new_driver(proxy class)当做参数递归调用自身时,new_driver(proxy class)作为被重载类也可以与第二条重载信息匹配,所以在第三次递归调用时将第二条重载信息的重载类作为参数传入并返回。当调用set_type_override_by_type()函数进行连续重载时,只有最后一次调用的重载类对象会被创建。例如用new_driver来重载my_driver类型,再用new_driver1来重载new_driver类型,对my_driver调用creat()时会创建一个new_driver1的实例,且“中间类”new_driver不必扩展自my_driver,但new_driver1必须扩展自my_driver
  2. 假设每次调用set_inst_override_by_type/name()函数传入的full_inst_path都相同,则多次调用该函数对同一类型进行多次重载和连续重载时,情况与set_type_override_by_type()函数被多次调用时相同,这里不再赘述。
  3. 当set_inst_override_by_type/name()和set_type_override_by_type/name()被同时调用时(无所谓先后),inst override有较高优先级。
    假设new_driver和new_driver1都从my_driver扩展而来且均已在factory进行注册。
    a, 在平台中调用以下函数(无所谓先后):
      set_type_override_by_type(my_driver::get_type(), new_driver1::get_type());
      set_inst_override_by_type(my_driver::get_type(), new_driver::get_type(), “env.agt.drv”);
    则在env.agt下调用driver = my_driver::type_id::creat(“driver”, this)时会创建new_driver的实例对象,在别的path如env.agt1下调用会创建new_driver1的实例对象;
    b, 在平台中调用以下函数(无所谓先后):
      set_type_override_by_type(new_driver::get_type(), new_driver1::get_type());
      set_inst_override_by_type(my_driver::get_type(), new_driver::get_type(), “env.agt.drv”);
    则在env.agt下调用driver = my_driver::type_id::creat(“driver”, this)时会创建new_driver1的实例对象,在别的path如env.agt1下调用会创建my_driver的实例对象;
    c, 在平台中调用以下函数(无所谓先后):
      set_type_override_by_type(my_driver::get_type(), new_driver::get_type());
      set_inst_override_by_type(new_driver::get_type(), new_driver1::get_type(), “env.agt.drv”);
    则在env.agt下调用driver = my_driver::type_id::creat(“driver”, this)时会创建new_driver1的实例对象,在别的path如env.agt1下调用会创建new_driver的实例对象;

5.3. find_override_by_name()

前已述及在调用creat()时uvm_factory会默认调用creat_component_by_type()函数来创建实例,其实在uvm_default_factory中还提供了另外一个函数creat_component_by_name()来创建一个实例,其中会调用find_override_by_name()函数来根据传入的字符串类型和uvm_factory中记录的重载信息返回合适的类型用于创建实例,这些函数的实现跟我们上面分析的部分大同小异,这里不再赘述。
uvm_factory之所以可以做到通过字符串来创建其所对应的类型实例,就在于参数化的proxy类和定义在其中的静态变量。利用参数化的类uvm_component_registry#(type T, string Tname)作为代理类,代理类与其所代表的类型是一一对应的,利用字符串参数可以索引到代理类从而创建其背后所代表的类型。factory机制的核心就是将这些代理类统统存进一个数组(所谓的注册过程),要用到哪个类只要用字符串索引就行了,听起来似乎顺理成章。然鹅关键问题是,我们没有办法把一个类型class存进数组,因为类型class是一个抽象的概念,数组里面的内容只能是某类型class的一个实例对象的句柄,所以uvm采用静态变量和静态函数的方式使得系统中每一个新的class(proxy class)在诞生之初就会随着静态变量初始化方式创建一个自身的实例句柄同时在factory中进行注册(sigleton模式保证factory中有且仅有一个该类型的句柄)

6. 其它

  1. 以上主要以component类型为例来分析uvm factory源码的运行机制,object类型的factory机制也有一系列的对应函数来实现,二者大同小异(例如object类型在最终调用new()函数创建实例时不需要parent参数),感兴趣的同学可以自行查阅相关源码。
  2. 对于参数化类在factory的注册,uvm提供了宏uvm_component/object_param_utils(T)来进行,这个宏做的事情其实跟普通类在注册时调用的uvm_component/object_utils(T)一样,只不过它的代理类 uvm_object_registry#(T, Tname)第二个参数采用了默认值"<unknown>",最终在factory的m_types[]中放入的句柄索引为uvm_object_registry#(T, “<unknown>”)类型的,因而我们不能通过调用creat_component_by_name()来创建一个参数化的类的实例。此外uvm中还有另外的factory注册宏uvm_component_utils_begin, 这个主要用于实现filed_automation机制,这里不多赘述。
  3. 在上面的源码中还有一些uvm_factory提供的debug功能,例如多次出现的m_debug_pass相关语句等等,debug相关功能可以打印出当前平台中所有已被注册的类以及被重载的类和重载类的信息等,这部分内容这里暂且略过,留待后补。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值