UVM Tutorial for Candy Lovers – 10. Inside Candy Factory

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.

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 the jelly_bean_transaction::type_id::create() (steps 1 and 2)
  • The create() static function calls the create_object_by_type() of uvm_factory class (step 3)
  • The uvm_factory calls find_override_by_type() to check whether the jelly_bean_transaction type is overridden by another class (step 4)
  • If the jelly_bean_transaction type is not overridden, then the uvm_factory calls the create_object() of the uvm_object_registry of the jelly_bean_transaction class (step 5). We will look at an overridden case later.
  • The uvm_object_registry class creates a jelly_bean_transaction object by calling new() (step 6)
  • The jelly_bean_transaction object is returned to the one_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 the sugar_free_jelly_bean_transaction::get_type() to get the uvm_object_registry of the sugar_free_jelly_bean_transaction (steps 1, 2, and 3)
  • Secondly, the jelly_bean_test calls the set_type_override(), which in turn calls the set_type_override_by_type() of the uvm_factory (steps 4 and 5)
  • Lastly, the uvm_factory creates a uvm_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 the jelly_bean_transaction::type_id::create() (steps 8 and 9)
  • The create() function calls the create_object_by_type() of the uvm_factory (step 10)
  • The uvm_factory recursively calls find_override_by_type() to determine the final type of the jelly_bean_transaction (step 11)
  • In our scenario, the jelly_bean_transaction was overridden by the sugar_free_jelly_bean_transaction. Therefore, the uvm_factory calls the create_object() of the uvm_object_registry of the sugar_free_jelly_bean_transaction (step 12)
  • The uvm_object_registry creates a sugar_free_jelly_bean_transaction by calling new() (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

13 thoughts on “UVM Tutorial for Candy Lovers – 10. Inside Candy Factory”

  1. 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?

  2. 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..!

    1. In UVM, we usually don’t call new to create a new object.

      jelly_bean_transaction jb_tx = new( "jb_tx" ); // we don't do this
      

      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, the jelly_bean_transaction::get_type() returns the singleton object of type_id type, which is uvm_object_registry#(jelly_bean_transaction, "jelly_bean_transaction") type.

      The get_object_type also returns the wrapper instance. The difference between the get_type and the get_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 asking get_type_name because I cannot find a function called get_object_name). In our case, it returns "jelly_bean_transaction".

  3. 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

    1. Make sure you create an object of B by calling create (see line 9 below).

      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
      
      typedef class B;
       
      class A extends uvm_object;
        `uvm_object_utils(A)
        B b;
        function new( string name = "" );
          super.new( name );
          set_name( "A" );
          b = B::type_id::create();  endfunction: new
      endclass: A
       
      class AA extends A;
        `uvm_object_utils(AA)
        function new( string name = "" );
          super.new( name );
          set_name( "AA" );
        endfunction: new
      endclass: AA
       
      class B extends uvm_object;
        `uvm_object_utils(B)
        function new( string name = "" );
          super.new( name );
          set_name( "B" );
        endfunction: new
      endclass: B
       
      class BB extends B;
        `uvm_object_utils(BB)
        function new( string name = "" );
          super.new( name );
          set_name( "BB" );
        endfunction: new
      endclass: BB
       
      class test extends uvm_test;
        `uvm_component_utils(test)
       
        function new( string name = "", uvm_component parent = null );
          super.new( name, parent );
        endfunction: new
       
        task run_phase( uvm_phase phase );
          A a;
       
          A::type_id::set_type_override( AA::get_type() );
          B::type_id::set_type_override( BB::get_type() );
          a = A::type_id::create();
          `uvm_info( "run_phase", { "I am ", a.get_name(), " and I have ", a.b.get_name() }, UVM_NONE )
        endtask: run_phase
      endclass: test

      You should get this when you run:

      UVM_INFO testbench.sv(50) @ 0: uvm_test_top [run_phase] I am AA and I have BB

      You can try it on this EDA Playground.

  4. 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

  5. `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?

Leave a Reply to Keisuke Shimizu Cancel reply

Your email address will not be published. Required fields are marked *