Last Updated: September 9, 2013
UVM factory is used to create UVM objects and components. This post will explain the UVM factory using jelly beans (as you expected) and reveal what happens behind the scenes in the factory.
::type_id
In Transactions and Sequences, we defined the jelly_bean_transaction
class. Then the one_jelly_bean_sequence
created a jelly_bean_transaction
object as follows:
jb_tx = jelly_bean_transaction::type_id::create( .name( "jb_tx" ) ); |
This is a well-known idiom of the UVM to create an object, but where does this ::type_id
thing come from? The type_id
is defined by `uvm_object_utils(jelly_bean_transaction)
macro used in the jelly_bean_transaction
class. The `uvm_object_utils()
macro is further broken down into the following sub-macros:
`uvm_object_utils_begin()
`m_uvm_object_registry_internal()
`m_uvm_object_create_func()
`m_uvm_get_type_name_func()
`m_uvm_field_utils_begin()
`uvm_object_utils_end()
The following pseudo code shows how the macro is expanded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | // This pseudo code shows how `uvm_object_utils macro is expanded. // // Assuming UVM_NO_DEPRECATED is defined. // Consequently assuming UVM_NO_REGISTERED_CONVERTER is defined. // Assuming UVM_OBJECT_MUST_HAVE_CONSTRUCTOR is defined. class jelly_bean_transaction extends uvm_sequence_item; // `uvm_object_utils(T) // | // +--> `uvm_object_utils_begin(T) // | | // | +--> `m_uvm_object_registry_internal(T,T) // | | // V V typedef uvm_object_registry#( jelly_bean_transaction, "jelly_bean_transaction" ) 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 // | | // | +--> `m_uvm_object_create_func(T) // | | // V V function uvm_object create( string name = "" ); jelly_bean_transaction tmp; if ( name == "" ) tmp = new(); else tmp = new( name ); return tmp; endfunction // | | // | +--> `m_uvm_get_type_name_func(T) // | | // V V const static string type_name = "jelly_bean_transaction"; virtual function string get_type_name(); return type_name; endfunction // | | // | +--> `uvm_field_utils_begin(T) // | // V function void __m_uvm_field_automation( uvm_object tmp_data__, int what__, string str__ ); begin jelly_bean_transaction local_data__; /* Used for copy and compare */ typedef jelly_bean_transaction ___local_type____; string string_aa_key; /* Used for associative array lookups */ uvm_object __current_scopes[$]; if ( what__ inside { UVM_SETINT, UVM_SETSTR, UVM_SETOBJ } ) begin if ( __m_uvm_status_container.m_do_cycle_check( this ) ) begin return; end else __current_scopes=__m_uvm_status_container.m_uvm_cycle_scopes; end super.__m_uvm_field_automation( tmp_data__, what__, str__ ); /* Type is verified by uvm_object::compare() */ if ( tmp_data__ != null ) /* Allow objects in same hierarchy to be copied/compared */ if ( ! $cast( local_data__, tmp_data__ ) ) return; // | // +--> `uvm_object_utils_end end endfunction endclass: jelly_bean_transaction |
As you see on the line 17, the type_id
is nothing but a uvm_object_registry
type.
::create()
Now we know what the type_id
is, so let’s look at the ::create()
that follows the type_id
. If you look at the UVM source code, src/base/uvm_registry.svh
, you will find that the create()
is a static function of the uvm_object_registry
class.
The following sequence diagram shows how the one_jelly_bean_sequence
creates a jelly_bean_transaction
.
- The
one_jelly_bean_sequence
calls thejelly_bean_transaction::type_id::create()
(steps 1 and 2) - The
create()
static function calls thecreate_object_by_type()
ofuvm_factory
class (step 3) - The
uvm_factory
callsfind_override_by_type()
to check whether thejelly_bean_transaction
type is overridden by another class (step 4) - If the
jelly_bean_transaction
type is not overridden, then theuvm_factory
calls thecreate_object()
of theuvm_object_registry
of thejelly_bean_transaction
class (step 5). We will look at an overridden case later. - The
uvm_object_registry
class creates ajelly_bean_transaction
object by callingnew()
(step 6) - The
jelly_bean_transaction
object is returned to theone_jelly_bean_sequence
(step 7)
The class diagram of the factory-related classes is shown below.
Type Override
In Tasting, the jelly_bean_test
class overrode the jelly_bean_transaction
with a sugar_free_jelly_bean_transaction
by doing:
jelly_bean_transaction::type_id::set_type_override(sugar_free_jelly_bean_transaction::get_type()); |
As we saw earlier, the jelly_bean_transaction::type_id
is a uvm_object_registry
type. The set_type_override()
is another static function of the uvm_object_registry
. The sequence diagram below shows how the set_type_override()
overrides a type. Overriding a type involves the following steps:
- Firstly, the
jelly_bean_test
calls thesugar_free_jelly_bean_transaction::get_type()
to get theuvm_object_registry
of thesugar_free_jelly_bean_transaction
(steps 1, 2, and 3) - Secondly, the
jelly_bean_test
calls theset_type_override()
, which in turn calls theset_type_override_by_type()
of theuvm_factory
(steps 4 and 5) - Lastly, the
uvm_factory
creates auvm_factory_override
object and put it in an override queue (steps 6 and 7)
Then, when the one_jelly_bean_sequence
creates a jelly_bean_transaction
, the following steps will be executed:
- The
one_jelly_bean_sequence
calls thejelly_bean_transaction::type_id::create()
(steps 8 and 9) - The
create()
function calls thecreate_object_by_type()
of theuvm_factory
(step 10) - The
uvm_factory
recursively callsfind_override_by_type()
to determine the final type of thejelly_bean_transaction
(step 11) - In our scenario, the
jelly_bean_transaction
was overridden by thesugar_free_jelly_bean_transaction
. Therefore, theuvm_factory
calls thecreate_object()
of theuvm_object_registry
of thesugar_free_jelly_bean_transaction
(step 12) - The
uvm_object_registry
creates asugar_free_jelly_bean_transaction
by callingnew()
(step 13) - The
sugar_free_jelly_bean_transaction
object created at the above step is returned (step 14)
Note that the one_jelly_bean_sequence
called the jelly_bean_transaction::type_id::create()
, not sugar_free_jelly_bean_transaction::type_id::create()
to create a sugar-free jelly bean. The factory took care of what type of jelly bean to create.
I hope you have a better idea of the UVM factory by now.
How do you know and object received is from a certain type. i.e. how to know the actual object type even the object was type-casted?
I am not sure if I fully understand your question, but you could use
$typename( object_handle )
system function to check the type of an object. The$typename
returns a string.Hi Keisuke,
get_type():
“Returns the type-proxy (wrapper) for this object. The uvm_factory’s type-based override
and creation methods take arguments of uvm_object_wrapper.”
What does it mean type-proxy/wrapper of an object ? What is its return value here?
Similary,
get_object_type() & get_object_name() ??
I believe,
get_object_name(), returns -> jelly_bean_transaction (which is the name of the class).
Please can you explain/give a simple example.
Thank You in Advance..!
In UVM, we usually don’t call
new
to create a new object.Instead, we ask the wrapper (proxy) of the class to call
new
. The UVM factory selects which wrapper to use based on the type/instance override.The
get_type
returns the wrapper instance. Actually there is only one instance per a wrapper type (it is called the singleton). In our case, thejelly_bean_transaction::get_type()
returns the singleton object oftype_id
type, which isuvm_object_registry#(jelly_bean_transaction, "jelly_bean_transaction")
type.The
get_object_type
also returns the wrapper instance. The difference between theget_type
and theget_object_type
is that the former is a static function and the latter is a non-static function. If you use the`uvm_object_utils
macro, the implementations of the two functions are the same.The
get_type_name
returns the name of an object type (I assume you are askingget_type_name
because I cannot find a function calledget_object_name
). In our case, it returns"jelly_bean_transaction"
.Thank You..!
After some background analysis on wrapper classes, I could understand some of it.
Hi Keisuke,
I want to override two classes that one of them is field of the other class. for example class A includes class B.
And I want to override class A with AA and B with BB.
if I use the override factory to override them, only the class A is override by AA and B would not change.
Do you have any idea how to do that?
Regards,
Yousef
Make sure you create an object of B by calling
create
(see line 9 below).You should get this when you run:
You can try it on this EDA Playground.
Hi Keisuke,
I am not quite understand the difference between set_type_override and set_type_override_by_type. Would you please tell what is the difference of them?
I think we also have set_type_override_by_name, I think it can not be used for parameterized class. then what is the usage of it?
Best,
Kevin Chen
To answer your question, I wrote a new article. You can use the
set_type_override_by_name
for a parameterized class, but you have to define your owntype_name
variable andget_type_name
function.what is the difference between new() and create()?
new
is the SystemVerilog function that creates an object.create
is a UVM function that eventually calls thenew
to create a UVM object.`uvm_object_utils marco includes following function
function uvm_object function create(string name = “”)
When a object is created type_id::create() static function is used. So what is the purpose of above function?
The
create
function implements the virtual function,create
, defined in theuvm_object
class. The default implementation ofuvm_object::clone
function calls thiscreate
, for example.