This page covers the classes that define the UVM factory facility.
UVM Factory | This page covers the classes that define the UVM factory facility. |
uvm_factory | As the name implies, uvm_factory is used to manufacture (create) UVM objects and components. |
uvm_object_wrapper | The uvm_object_wrapper provides an abstract interface for creating object and component proxies. |
As the name implies, uvm_factory is used to manufacture (create) UVM objects and components. Only one instance of the factory is present in a given simulation (termed a singleton). Object and component types are registered with the factory using lightweight proxies to the actual objects and components being created. The uvm_object_registry #(T,Tname) and uvm_component_registry #(T,Tname) class are used to proxy uvm_objects and uvm_components.
The factory provides both name-based and type-based interfaces.
type-based | The type-based interface is far less prone to errors in usage. When errors do occur, they are caught at compile-time. |
name-based | The name-based interface is dominated by string arguments that can be misspelled and provided in the wrong order. Errors in name-based requests might only be caught at the time of the call, if at all. Further, the name-based interface is not portable across simulators when used with parameterized classes. |
See Usage section for details on configuring and using the factory.
uvm_factory | ||
As the name implies, uvm_factory is used to manufacture (create) UVM objects and components. | ||
Class Declaration | ||
| ||
Registering Types | ||
register | Registers the given proxy object, obj, with the factory. | |
Type & Instance Overrides | ||
set_inst_override_by_type | ||
set_inst_override_by_name | Configures the factory to create an object of the override’s type whenever a request is made to create an object of the original type using a context that matches full_inst_path. | |
set_type_override_by_type | ||
set_type_override_by_name | Configures the factory to create an object of the override’s type whenever a request is made to create an object of the original type, provided no instance override applies. | |
Creation | ||
create_object_by_type | ||
create_component_by_type | ||
create_object_by_name | ||
create_component_by_name | Creates and returns a component or object of the requested type, which may be specified by type or by name. | |
Debug | ||
debug_create_by_type | ||
debug_create_by_name | These methods perform the same search algorithm as the create_* methods, but they do not create new objects. | |
find_override_by_type | ||
find_override_by_name | These methods return the proxy to the object that would be created given the arguments. | |
Prints the state of the uvm_factory, including registered types, instance overrides, and type overrides. | ||
Usage | Using the factory involves three basic operations |
function void register ( uvm_object_wrapper obj )
Registers the given proxy object, obj, with the factory. The proxy object is a lightweight substitute for the component or object it represents. When the factory needs to create an object of a given type, it calls the proxy’s create_object or create_component method to do so.
When doing name-based operations, the factory calls the proxy’s get_type_name method to match against the requested_type_name argument in subsequent calls to create_component_by_name and create_object_by_name. If the proxy object’s get_type_name method returns the empty string, name-based lookup is effectively disabled.
function void set_inst_override_by_type ( uvm_object_wrapper original_type, uvm_object_wrapper override_type, string full_inst_path )
function void set_inst_override_by_name ( string original_type_name, string override_type_name, string full_inst_path )
Configures the factory to create an object of the override’s type whenever a request is made to create an object of the original type using a context that matches full_inst_path. The original type is typically a super class of the override type.
When overriding by type, the original_type and override_type are handles to the types’ proxy objects. Preregistration is not required.
When overriding by name, the original_type_name typically refers to a preregistered type in the factory. It may, however, be any arbitrary string. Future calls to any of the create_* methods with the same string and matching instance path will produce the type represented by override_type_name, which must be preregistered with the factory.
The full_inst_path is matched against the contentation of {parent_inst_path, “.”, name} provided in future create requests. The full_inst_path may include wildcards (* and ?) such that a single instance override can be applied in multiple contexts. A full_inst_path of “*” is effectively a type override, as it will match all contexts.
When the factory processes instance overrides, the instance queue is processed in order of override registrations, and the first override match prevails. Thus, more specific overrides should be registered first, followed by more general overrides.
function void set_type_override_by_type ( uvm_object_wrapper original_type, uvm_object_wrapper override_type, bit replace = 1 )
function void set_type_override_by_name ( string original_type_name, string override_type_name, bit replace = 1 )
Configures the factory to create an object of the override’s type whenever a request is made to create an object of the original type, provided no instance override applies. The original type is typically a super class of the override type.
When overriding by type, the original_type and override_type are handles to the types’ proxy objects. Preregistration is not required.
When overriding by name, the original_type_name typically refers to a preregistered type in the factory. It may, however, be any arbitrary string. Future calls to any of the create_* methods with the same string and matching instance path will produce the type represented by override_type_name, which must be preregistered with the factory.
When replace is 1, a previous override on original_type_name is replaced, otherwise a previous override, if any, remains intact.
function uvm_object create_object_by_type ( uvm_object_wrapper requested_type, string parent_inst_path = "", string name = "" )
function uvm_component create_component_by_type ( uvm_object_wrapper requested_type, string parent_inst_path = "", string name, uvm_component parent )
function uvm_object create_object_by_name ( string requested_type_name, string parent_inst_path = "", string name = "" )
function uvm_component create_component_by_name ( string requested_type_name, string parent_inst_path = "", string name, uvm_component parent )
Creates and returns a component or object of the requested type, which may be specified by type or by name. A requested component must be derived from the uvm_component base class, and a requested object must be derived from the uvm_object base class.
When requesting by type, the requested_type is a handle to the type’s proxy object. Preregistration is not required.
When requesting by name, the request_type_name is a string representing the requested type, which must have been registered with the factory with that name prior to the request. If the factory does not recognize the requested_type_name, an error is produced and a null handle returned.
If the optional parent_inst_path is provided, then the concatenation, {parent_inst_path, “.”,~name~}, forms an instance path (context) that is used to search for an instance override. The parent_inst_path is typically obtained by calling the uvm_component::get_full_name on the parent.
If no instance override is found, the factory then searches for a type override.
Once the final override is found, an instance of that component or object is returned in place of the requested type. New components will have the given name and parent. New objects will have the given name, if provided.
Override searches are recursively applied, with instance overrides taking precedence over type overrides. If foo overrides bar, and xyz overrides foo, then a request for bar will produce xyz. Recursive loops will result in an error, in which case the type returned will be that which formed the loop. Using the previous example, if bar overrides xyz, then bar is returned after the error is issued.
function void debug_create_by_type ( uvm_object_wrapper requested_type, string parent_inst_path = "", string name = "" )
function void debug_create_by_name ( string requested_type_name, string parent_inst_path = "", string name = "" )
These methods perform the same search algorithm as the create_* methods, but they do not create new objects. Instead, they provide detailed information about what type of object it would return, listing each override that was applied to arrive at the result. Interpretation of the arguments are exactly as with the create_* methods.
function uvm_object_wrapper find_override_by_type ( uvm_object_wrapper requested_type, string full_inst_path )
function uvm_object_wrapper find_override_by_name ( string requested_type_name, string full_inst_path )
These methods return the proxy to the object that would be created given the arguments. The full_inst_path is typically derived from the parent’s instance path and the leaf name of the object to be created, i.e. { parent.get_full_name(), “.”, name }.
function void print ( int all_types = 1 )
Prints the state of the uvm_factory, including registered types, instance overrides, and type overrides.
When all_types is 0, only type and instance overrides are displayed. When all_types is 1 (default), all registered user-defined types are printed as well, provided they have names associated with them. When all_types is 2, the UVM types (prefixed with uvm_) are included in the list of registered types.
Using the factory involves three basic operations
1 | Registering objects and components types with the factory |
2 | Designing components to use the factory to create objects or components |
3 | Configuring the factory with type and instance overrides, both within and outside components |
We’ll briefly cover each of these steps here. More reference information can be found at Utility Macros, uvm_component_registry #(T,Tname), uvm_object_registry #(T,Tname), uvm_component.
When defining uvm_object and uvm_component-based classes, simply invoke the appropriate macro. Use of macros are required to ensure portability across different vendors’ simulators.
Objects that are not parameterized are declared as
class packet extends uvm_object; `uvm_object_utils(packet) endclass class packetD extends packet; `uvm_object_utils(packetD) endclass
Objects that are parameterized are declared as
class packet #(type T=int, int WIDTH=32) extends uvm_object; `uvm_object_param_utils(packet #(T,WIDTH)) endclass
Components that are not parameterized are declared as
class comp extends uvm_component; `uvm_component_utils(comp) endclass
Components that are parameterized are declared as
class comp #(type T=int, int WIDTH=32) extends uvm_component; `uvm_component_param_utils(comp #(T,WIDTH)) endclass
The `uvm_*_utils macros for simple, non-parameterized classes will register the type with the factory and define the get_type, get_type_name, and create virtual methods inherited from uvm_object. It will also define a static type_name variable in the class, which will allow you to determine the type without having to allocate an instance.
The `uvm_*_param_utils macros for parameterized classes differ from `uvm_*_utils classes in the following ways:
It is worth noting that environments that exclusively use the type-based factory methods (*_by_type) do not require type registration. The factory’s type-based methods will register the types involved “on the fly,” when first used. However, registering with the `uvm_*_utils macros enables name-based factory usage and implements some useful utility functions.
Having registered your objects and components with the factory, you can now make requests for new objects and components via the factory. Using the factory instead of allocating them directly (via new) allows different objects to be substituted for the original without modifying the requesting class. The following code defines a driver class that is parameterized.
class driverB #(type T=uvm_object) extends uvm_driver; // parameterized classes must use the _param_utils version `uvm_component_param_utils(driverB #(T)) // our packet type; this can be overridden via the factory T pkt; // standard component constructor function new(string name, uvm_component parent=null); super.new(name,parent); endfunction // get_type_name not implemented by macro for parameterized classes const static string type_name = {"driverB #(",T::type_name,")"}; virtual function string get_type_name(); return type_name; endfunction // using the factory allows pkt overrides from outside the class virtual function void build_phase(uvm_phase phase); pkt = packet::type_id::create("pkt",this); endfunction // print the packet so we can confirm its type when printing virtual function void do_print(uvm_printer printer); printer.print_object("pkt",pkt); endfunction endclass
For purposes of illustrating type and instance overrides, we define two subtypes of the driverB class. The subtypes are also parameterized, so we must again provide an implementation for uvm_object::get_type_name, which we recommend writing in terms of a static string constant.
class driverD1 #(type T=uvm_object) extends driverB #(T); `uvm_component_param_utils(driverD1 #(T)) function new(string name, uvm_component parent=null); super.new(name,parent); endfunction const static string type_name = {"driverD1 #(",T::type_name,")"}; virtual function string get_type_name(); ...return type_name; endfunction endclass class driverD2 #(type T=uvm_object) extends driverB #(T); `uvm_component_param_utils(driverD2 #(T)) function new(string name, uvm_component parent=null); super.new(name,parent); endfunction const static string type_name = {"driverD2 #(",T::type_name,")"}; virtual function string get_type_name(); return type_name; endfunction endclass // typedef some specializations for convenience typedef driverB #(packet) B_driver; // the base driver typedef driverD1 #(packet) D1_driver; // a derived driver typedef driverD2 #(packet) D2_driver; // another derived driver
Next, we’ll define a agent component, which requires a utils macro for non-parameterized types. Before creating the drivers using the factory, we override driver0’s packet type to be packetD.
class agent extends uvm_agent; `uvm_component_utils(agent) ... B_driver driver0; B_driver driver1; function new(string name, uvm_component parent=null); super.new(name,parent); endfunction virtual function void build_phase(uvm_phase phase); // override the packet type for driver0 and below packet::type_id::set_inst_override(packetD::get_type(),"driver0.*"); // create using the factory; actual driver types may be different driver0 = B_driver::type_id::create("driver0",this); driver1 = B_driver::type_id::create("driver1",this); endfunction endclass
Finally we define an environment class, also not parameterized. Its build method shows three methods for setting an instance override on a grandchild component with relative path name, agent1.driver1, all equivalent.
class env extends uvm_env; `uvm_component_utils(env) agent agent0; agent agent1; function new(string name, uvm_component parent=null); super.new(name,parent); endfunction virtual function void build_phase(uvm_phase phase); // three methods to set an instance override for agent1.driver1 // - via component convenience method... set_inst_override_by_type("agent1.driver1", B_driver::get_type(), D2_driver::get_type()); // - via the component's proxy (same approach as create)... B_driver::type_id::set_inst_override(D2_driver::get_type(), "agent1.driver1",this); // - via a direct call to a factory method... factory.set_inst_override_by_type(B_driver::get_type(), D2_driver::get_type(), {get_full_name(),".agent1.driver1"}); // create agents using the factory; actual agent types may be different agent0 = agent::type_id::create("agent0",this); agent1 = agent::type_id::create("agent1",this); endfunction // at end_of_elaboration, print topology and factory state to verify virtual function void end_of_elaboration_phase(uvm_phase phase); uvm_top.print_topology(); endfunction virtual task run_phase(uvm_phase phase); #100 global_stop_request(); endfunction endclass
In the previous step, we demonstrated setting instance overrides and creating components using the factory within component classes. Here, we will demonstrate setting overrides from outside components, as when initializing the environment prior to running the test.
module top; env env0; initial begin // Being registered first, the following overrides take precedence // over any overrides made within env0's construction & build. // Replace all base drivers with derived drivers... B_driver::type_id::set_type_override(D_driver::get_type()); // ...except for agent0.driver0, whose type remains a base driver. // (Both methods below have the equivalent result.) // - via the component's proxy (preferred) B_driver::type_id::set_inst_override(B_driver::get_type(), "env0.agent0.driver0"); // - via a direct call to a factory method factory.set_inst_override_by_type(B_driver::get_type(), B_driver::get_type(), {get_full_name(),"env0.agent0.driver0"}); // now, create the environment; our factory configuration will // govern what topology gets created env0 = new("env0"); // run the test (will execute build phase) run_test(); end endmodule
When the above example is run, the resulting topology (displayed via a call to uvm_root::print_topology in env’s uvm_component::end_of_elaboration_phase method) is similar to the following:
# UVM_INFO @ 0 [RNTST] Running test ... # UVM_INFO @ 0 [UVMTOP] UVM testbench topology: # ---------------------------------------------------------------------- # Name Type Size Value # ---------------------------------------------------------------------- # env0 env - env0@2 # agent0 agent - agent0@4 # driver0 driverB #(packet) - driver0@8 # pkt packet - pkt@21 # driver1 driverD #(packet) - driver1@14 # pkt packet - pkt@23 # agent1 agent - agent1@6 # driver0 driverD #(packet) - driver0@24 # pkt packet - pkt@37 # driver1 driverD2 #(packet) - driver1@30 # pkt packet - pkt@39 # ----------------------------------------------------------------------
The uvm_object_wrapper provides an abstract interface for creating object and component proxies. Instances of these lightweight proxies, representing every uvm_object-based and uvm_component-based object available in the test environment, are registered with the uvm_factory. When the factory is called upon to create an object or component, it finds and delegates the request to the appropriate proxy.
uvm_object_wrapper | ||
The uvm_object_wrapper provides an abstract interface for creating object and component proxies. | ||
Class Declaration | ||
| ||
Methods | ||
create_object | Creates a new object with the optional name. | |
create_component | Creates a new component, passing to its constructor the given name and parent. | |
get_type_name | Derived classes implement this method to return the type name of the object created by create_component or create_object. |
virtual function uvm_object create_object ( string name = "" )
Creates a new object with the optional name. An object proxy (e.g., uvm_object_registry #(T,Tname)) implements this method to create an object of a specific type, T.
virtual function uvm_component create_component ( string name, uvm_component parent )
Creates a new component, passing to its constructor the given name and parent. A component proxy (e.g. uvm_component_registry #(T,Tname)) implements this method to create a component of a specific type, T.
pure virtual function string get_type_name()
Derived classes implement this method to return the type name of the object created by create_component or create_object. The factory uses this name when matching against the requested type in name-based lookups.
As the name implies, uvm_factory is used to manufacture (create) UVM objects and components.
class uvm_factory
The uvm_object_wrapper provides an abstract interface for creating object and component proxies.
virtual class uvm_object_wrapper
Registers the given proxy object, obj, with the factory.
function void register ( uvm_object_wrapper obj )
function void set_inst_override_by_type ( uvm_object_wrapper original_type, uvm_object_wrapper override_type, string full_inst_path )
Configures the factory to create an object of the override’s type whenever a request is made to create an object of the original type using a context that matches full_inst_path.
function void set_inst_override_by_name ( string original_type_name, string override_type_name, string full_inst_path )
function void set_type_override_by_type ( uvm_object_wrapper original_type, uvm_object_wrapper override_type, bit replace = 1 )
Configures the factory to create an object of the override’s type whenever a request is made to create an object of the original type, provided no instance override applies.
function void set_type_override_by_name ( string original_type_name, string override_type_name, bit replace = 1 )
function uvm_object create_object_by_type ( uvm_object_wrapper requested_type, string parent_inst_path = "", string name = "" )
function uvm_component create_component_by_type ( uvm_object_wrapper requested_type, string parent_inst_path = "", string name, uvm_component parent )
function uvm_object create_object_by_name ( string requested_type_name, string parent_inst_path = "", string name = "" )
Creates and returns a component or object of the requested type, which may be specified by type or by name.
function uvm_component create_component_by_name ( string requested_type_name, string parent_inst_path = "", string name, uvm_component parent )
function void debug_create_by_type ( uvm_object_wrapper requested_type, string parent_inst_path = "", string name = "" )
These methods perform the same search algorithm as the create_* methods, but they do not create new objects.
function void debug_create_by_name ( string requested_type_name, string parent_inst_path = "", string name = "" )
function uvm_object_wrapper find_override_by_type ( uvm_object_wrapper requested_type, string full_inst_path )
These methods return the proxy to the object that would be created given the arguments.
function uvm_object_wrapper find_override_by_name ( string requested_type_name, string full_inst_path )
Prints the state of the uvm_factory, including registered types, instance overrides, and type overrides.
function void print ( int all_types = 1 )
The uvm_object_registry serves as a lightweight proxy for an uvm_object of type T and type name Tname, a string.
class uvm_object_registry #( type T = uvm_object, string Tname = "<unknown>" ) extends uvm_object_wrapper
The uvm_component_registry serves as a lightweight proxy for a component of type T and type name Tname, a string.
class uvm_component_registry #( type T = uvm_component, string Tname = "<unknown>" ) extends uvm_object_wrapper
The uvm_object class is the base class for all UVM data and hierarchical classes.
virtual class uvm_object extends uvm_void
The uvm_component class is the root base class for UVM components.
virtual class uvm_component extends uvm_report_object
Returns the full hierarchical name of this object.
virtual function string get_full_name ()
This function returns the type name of the object, which is typically the type identifier enclosed in quotes.
virtual function string get_type_name ()
Print the verification environment’s component topology.
function void print_topology ( uvm_printer printer = null )
The Pre-Defined Phases::uvm_end_of_elaboration_phase phase implementation method.
virtual function void end_of_elaboration_phase( uvm_phase phase )
Creates a new object with the optional name.
virtual function uvm_object create_object ( string name = "" )
Creates a new component, passing to its constructor the given name and parent.
virtual function uvm_component create_component ( string name, uvm_component parent )
Derived classes implement this method to return the type name of the object created by create_component or create_object.
pure virtual function string get_type_name()