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.
// 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.
type_id
::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
.
Sequence diagram of creating 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.
Class diagram of the factory-related classes
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.
Sequence diagram of type override