UVM utility & field macros
UVM uses the concept of a factory where all objects are registered with it so that it can return an object of the requested type when required. The utility macros help to register each object with the factory.
The utils macro is used primarily to register an object or component with the factory and is required for it to function correctly. It is required to be used inside every user-defined class that is derived from
uvm_object which includes all types of sequence items and components.
All classes derived directly from
uvm_transaction require them to be registered using
`uvm_object_utils macro. Note that it is mandatory for the
new function to be explicitly defined for every class, and takes the name of the class instance as an argument.
class ABC extends uvm_object; // Register this user defined class with the factory `uvm_object_utils(ABC) function new(string name = "ABC"); super.new(name); endfunction endclass
All classes derived directly or indirectly from
uvm_component require them to be registered with the factory using
`uvm_component_utils macro. Note that it is mandatory for the
new function to be explicitly defined for every class derived directly or indirectly from
uvm_component and takes the name of the class instance and a handle to the parent class where this object is instantiated.
class DEF extends uvm_component; // Class derived from uvm_component, register with factory `uvm_component_utils(DEF) function new(string name = "DEF", uvm_component parent=null); super.new(name, parent); endfunction endclass
Macro Expansion: Behind the Scenes
`uvm_object_utils eventually gets expanded into its
*_end form with nothing between the begin and end.
*_begin implements other macros required for the correct functionality of UVM factory.
// Empty uvm_object_utils macro `define uvm_object_utils(T) \ `uvm_object_utils_begin(T) \ `uvm_object_utils_end `define uvm_object_utils_begin(T) \ `m_uvm_object_registry_internal(T,T) \ // Sub-macro #1 `m_uvm_object_create_func(T) \ // Sub-macro #2 `m_uvm_get_type_name_func(T) \ // Sub-macro #3 `uvm_field_utils_begin(T) // Sub-macro #4 // uvm_object_utils_end simply terminates a function started // somewhere in the middle `define uvm_object_utils_end \ end \ endfunction \
It can be seen from the code shown below that each macro expanded by
*_begin implements some functions required for factory operation and performs the following steps.
get_typestatic method which basically returns a factory proxy object for the requested type.
create, which instantiates an object of the specified type by calling its constructor with no arguments.
get_type_namemethod which returns the type as a string
- Registers the type with UVM factory
// Sub-macro #1. Implement the functions "get_type()" and "get_object_type()" `define m_uvm_object_registry_internal(T,S) \ 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 // Sub-macro #2. Implement the function "create()" `define m_uvm_object_create_func(T) \ function uvm_object create (string name=""); \ T tmp; \ `ifdef UVM_OBJECT_DO_NOT_NEED_CONSTRUCTOR \ tmp = new(); \ if (name!="") \ tmp.set_name(name); \ `else \ if (name=="") tmp = new(); \ else tmp = new(name); \ `endif \ return tmp; \ endfunction // Sub-macro #3. Implement the function "get_type_name()" `define m_uvm_get_type_name_func(T) \ const static string type_name = `"T`"; \ virtual function string get_type_name (); \ return type_name; \ endfunction // Sub-macro #4. Implement field automation macros `define uvm_field_utils_begin(T) \ function void __m_uvm_field_automation (uvm_object tmp_data__, \ int what__, \ string str__); \ ...
In a similar way,
`uvm_component_utils_* also has equivalent macros and function calls. Note that the
create function for components take two arguments, name and parent, unlike that of an object.
Paratemerized classes should use
`uvm_object_param_utils_* since they do not have to give a type during factory registration.
Creation of class object
It is recommended that all class objects are created by calling the
type_id::create() method which is already defined using the macro
`m_uvm_object_create_func. This makes any child class object to be created and returned using factory mechanism and promotes testbench flexibility and reusability.
class ABC extends uvm_object; `uvm_object_utils(ABC) function new(string name = "ABC"); super.new(name); endfunction endclass class base_test extends uvm_test; `uvm_component_utils(base_test) function new(string name = "base_test", uvm_component parent=null); super.new(name, parent); endfunction virtual function void build_phase(uvm_phase phase); // An object of class "ABC" is instantiated in UVM by calling // its "create()" function which has been defined using a macro // as shown above ABC abc = ABC::type_id::create("abc_inst"); endfunction endclass
`uvm_field_* macros that were used between
*_end utility macros are basically called field macros since they operate on class properties and provide automatic implementations of core methods like copy, compare and print. This helps the user save some time from implementing custom
do_print functions for each and every class. However, since these macros are expanded into general code, it may impact simulation performance and are generally not recommended.
class ABC extends uvm_object; rand bit [15:0] m_addr; rand bit [15:0] m_data; `uvm_object_utils_begin(ABC) `uvm_field_int(m_addr, UVM_DEFAULT) `uvm_field_int(m_data, UVM_DEFAULT) `uvm_object_utils_end function new(string name = "ABC"); super.new(name); endfunction endclass
`uvm_field_* corresponding to the data type of each variable should be used. For example, variables of type
byte should use
`uvm_field_int, while variables of type
string should use
`uvm_field_string and so on. The macro accepts atleast two arguments: ARG and FLAG.
|ARG||Name of the variable, whose type should be appropriate for the macro that is used|
|FLAG||When set to something other than UVM_DEFAULT or UVM_ALL_ON, it specifies which data method implementations will not be included. For example, if FLAG is set to NO_COPY, everything else will be implemented for the variable except copy.|
The field FLAG can take the following values and multiple values can be OR'ed together. A combination of these flags determine the kind of operations that are allowed to be done on the given variable.
|UVM_ALL_ON||All operations are turned on|
|UVM_DEFAULT||Enables all operations and equivalent to UVM_ALL_ON|
|UVM_NOCOPY||Do not copy the given variable|
|UVM_NOCOMPARE||Do not compare the given variable|
|UVM_NOPRINT||Do not print the given variable|
|UVM_NOPACK||Do not pack or unpack the given variable|
|UVM_REFERENCE||Operate only on handles, i.e. for object types, do not do deep copy, etc|
Along with control on operations,
`uvm_field_* macros also provide some control on the radix of the given variable which can take the following values. This value can be OR'ed with the operational flags listed in the table above. UVM_HEX is the default radix if none is specified.
|UVM_BIN||Print/record the given variable in binary format|
|UVM_DEC||Print/record the given variable in decimal format|
|UVM_HEX||Print/record the given variable in hexadecimal format|
|UVM_OCT||Print/record the given variable in octal format|
|UVM_STRING||Print/record the given variable in string format|
|UVM_TIME||Print/record the given variable in time format|