Factory Overrides by Context

I would like to be able to set the context of a uvm_object when creating it, then override only the objects with that context with a new type.

Here is my attempt, which does not work as expected:

class CatObject extends uvm_object;

protected rand int size;

// UVM field macros
`uvm_object_utils_begin(CatObject)
   `uvm_field_int       (size,         UVM_ALL_ON | UVM_UNSIGNED)
`uvm_object_utils_end

// Constructor function
function new (string name = "");
   super.new(name);
endfunction : new

function int getSize();
  return size;
endfunction

endclass
class KittenObject extends CatObject;

protected rand int age;

// UVM field macros
`uvm_object_utils_begin(KittenObject)
   `uvm_field_int       (age,         UVM_ALL_ON | UVM_UNSIGNED)
`uvm_object_utils_end

// Constructor function
function new (string name = "");
   super.new(name);
endfunction : new

function int getAge();
  return age;
endfunction

endclass
class contxt_override_exp extends uvm_test;

   `uvm_component_utils(contxt_override_exp)

   CatObject tom;
   CatObject jenny;
      
   function new (string name = "contxt_override_exp", uvm_component parent=null);
      super.new(name, parent);
   endfunction
      
   // UVM Component Build Phase
   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);

      // Override only one of the two objects.
      CatObject::type_id::set_inst_override(KittenObject::get_type(), "House", this);

      // Create our cat objects
      tom = CatObject::type_id::create(.name("tom"), .contxt("House"));
      jenny = CatObject::type_id::create(.name("jenny"), .contxt("Barn"));

   endfunction

   // UVM Component Run Phase
   task run_phase(uvm_phase phase);
      // Raise object to make sure the test runs
      phase.raise_objection(this);
   
      // Generate some necessary test strings
      `uvm_info("TEST", "START OF TEST", UVM_LOW)

      // Check that the context is set correctly.
      `uvm_info("EXP", $psprintf("Tom full_name is %s", tom.get_full_name()), UVM_LOW)
      `uvm_info("EXP", $psprintf("Tom object_type is %s", tom.get_type_name()), UVM_LOW)

      `uvm_info("EXP", $psprintf("Jenny full_name is %s", jenny.get_full_name()), UVM_LOW)
      `uvm_info("EXP", $psprintf("Jenny object_type is %s", jenny.get_type_name()), UVM_LOW)

      `uvm_info("TEST", "PASS", UVM_LOW)
         
      phase.drop_objection(this);
   endtask : run_phase
   
endclass : contxt_override_exp

This is what I expected the output to be (some things omitted for brevity):

UVM_INFO @ 0: reporter [RNTST] Running test contxt_override_exp...
UVM_INFO @ 0: uvm_test_top [TEST] START OF TEST
UVM_INFO @ 0: uvm_test_top [EXP] Tom full_name is House.tom
UVM_INFO @ 0: uvm_test_top [EXP] Tom object_type is KittenObject
UVM_INFO @ 0: uvm_test_top [EXP] Jenny full_name is Barn.jenny
UVM_INFO @ 0: uvm_test_top [EXP] Jenny object_type is CatObject
UVM_INFO @ 0: uvm_test_top [TEST] PASS
UVM_INFO @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

This is what I actually got:

UVM_INFO @ 0: reporter [RNTST] Running test contxt_override_exp...
UVM_INFO @ 0: uvm_test_top [TEST] START OF TEST
UVM_INFO @ 0: uvm_test_top [EXP] Tom full_name is tom
UVM_INFO @ 0: uvm_test_top [EXP] Tom object_type is CatObject
UVM_INFO @ 0: uvm_test_top [EXP] Jenny full_name is jenny
UVM_INFO @ 0: uvm_test_top [EXP] Jenny object_type is CatObject
UVM_INFO @ 0: uvm_test_top [TEST] PASS
UVM_INFO @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

First of all, why isn’t this working as expected? Am I setting contxt incorrectly? Am I trying to check it (with get_full_name()) incorrectly?

Additionally, I’ve been looking into factory overrides and there are a number of things that confuse me. The documentation I’m referring to in these questions is here: UVM Factory

  1. When I look at the UVM factory information in the documentation, I see create_object_by_type and create_object_by_name.
    1a. Is create_object_by_type equivalent to <type_name>::type_id::create(“instance_name”)?
    1b. How is that ‘translated’? Is there an equivalent for create_object_by_name?

  2. Secondly, I also see in the create_object_by_name and create_object_by_type that the arguments are requested_type, parent_inst_path, and name. But when I tried to pass something to parent_inst_path, it gave a syntax error. However, contxt works as an argument to create, even though I only see as an argument in the uvm_object_registry functions. This makes no sense because it is clear from other experiments that <type_name>::type_id::create(“instance_name”) gives an actual instance of that uvm_object, not a lightweight proxy as the uvm_object_registry describes.

Thanks very much for reading that long question. I’m very confused by this and appreciate your time.

I solved my own problem (mostly)!

First of all, by messing with the override syntax I was able to successfully override the CatObject with a KittenObject based on context.

Here’s the build phase (you can assume everything not included is the same as the previous code):

   // UVM Component Build Phase
   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);

      // Note: parent argument defaults is null, so I just leave it out.
      // Override only one of the two objects.
      CatObject::type_id::set_inst_override(.override_type(KittenObject::get_type()), .inst_path("House.*"));

      // Note: parent argument defaults is null, so leave it out.
      // Create our scan chains
      tom = CatObject::type_id::create(.name("tom"), .contxt("House"));
      jenny = CatObject::type_id::create(.name("jenny"), .contxt("Barn"));

   endfunction

Now the output of this is:

UVM_INFO @ 0: uvm_test_top [TEST] START OF TEST
UVM_INFO @ 0: uvm_test_top [EXP] Tom full_name is tom
UVM_INFO @ 0: uvm_test_top [EXP] Tom object_type is KittenObject
UVM_INFO @ 0: uvm_test_top [EXP] Jenny full_name is jenny
UVM_INFO @ 0: uvm_test_top [EXP] Jenny object_type is CatObject
UVM_INFO @ 0: uvm_test_top [TEST] PASS

I still don’t know why get_full_name() won’t print contxt though. If anyone knows this, please let me know.

I also was able to answer my questions from the end of the post. Apparently the type_id is actually a typedef for a wrapper in the uvm_object/component_registry, so when we call create() or set_inst_override/ set_type_override, we actually are getting a wrapper for that object in the registry, and then calling those tasks on that.

These blog posts were really helpful in understanding how the factory works:

http://cluelogic.com/2012/11/uvm-tutorial-for-candy-lovers-10-inside-candy-factory/
http://cluelogic.com/2016/06/uvm-tutorial-for-candy-lovers-component-override/