Extend a uvm_component whose member is another inherited uvm_component class

I created an example code for my question: parent_class is inherited by child_class, where it wants to replace type for a member slv.


class pmem_class extends uvm_component;
  int a;
  `uvm_component_utils_begin(pmem_class)
  `uvm_field_int(a, UVM_ALL_ON)
  `uvm_component_utils_end
  function new(string name = "pmem_class", uvm_component parent = null);
    super.new(name, parent);
    a = 1;
  endfunction
endclass: pmem_class

class cmem_class extends pmem_class;
  int b;
  `uvm_component_utils_begin(cmem_class)
  `uvm_field_int(b, UVM_ALL_ON)
  `uvm_component_utils_end
  function new(string name = "cmem_class", uvm_component parent = null);
    super.new(name, parent);
    b = 2;
  endfunction
endclass: cmem_class

class parent_class extends uvm_component;
 
  bit [31:0] addr;
  pmem_class slv;
  
  `uvm_component_utils_begin(parent_class)
  `uvm_field_object(slv, UVM_ALL_ON)
  `uvm_component_utils_end
  
  function new(string name = "parent_class", uvm_component parent = null);
    super.new(name, parent);
    
    slv = pmem_class::type_id::create("slv", this);
  endfunction
  
  function void display();
    $display("Addr = %0d",addr);
    $display("slv.a = %0d", slv.a);
  endfunction
endclass

class child_class#(type T=cmem_class) extends parent_class;
  
  bit [31:0] data;
  
  T slv;
  
  `uvm_component_param_utils_begin(child_class)
  `uvm_component_utils_end
  
  function new(string name = "child_class", uvm_component parent = null);
//    super.new(name, parent);
    slv = T::type_id::create("slv", this);
  endfunction
  
  function void display();
    $display("Data = %0d",data);
    $display("slv.a = %0d, slv.b = %0d", slv.a, slv.b);
  endfunction
endclass

module inheritence;
  initial begin
    child_class c=child_class#()::type_id::create("child_class", null);
    c.addr = 10;
    c.data = 20;
    c.display();
  end
endmodule


The above code will generate a UVM_FATAL error:

UVM_FATAL /apps/vcsmx/vcs/S-2021.09//etc/uvm-1.2/src/base/uvm_component.svh(1892) @ 0: slv [CLDEXT] Cannot set ‘slv’ as a child of ‘parent_class’, which already has a child by that name.

Here is the edaplayground link: EDA Playground Login

If the member class ‘pmem_class’ is a uvm_object then it can work. I somehow understand why the error happens, but is there any decent solution for the issue. The real issue happens when I try to extend an in-house vip and try to update one of it member object (of type inherited from uvm_component).

Any suggestion?

In reply to David Peng:

You can only override a method member in a class. So you’d solve the problem by renaming the slv variable in your child class.

In reply to sylvainb:

Thanks sylvainb.

I did a little bit more search on this. It seems systemverilog/uvm supports this, you CAN override a member/property in a base class. As I indicated above, if the class member is of type uvm_object, above code can work. However per expert like Dave Rich @dave_59, this is a very bad programming practice. https://verificationacademy.com/forums/systemverilog/setting-variables-same-name-child-class. And moving to uvm_component member, it doesn’t work at all.

But I think this kind of inheritance does provide value. It is something like factory but can’t be resolved by factory. I want to keep the same name slv so that any code manipulating slv in base class will be reused. If I use a different name, same logic need be implemented again.

In reply to David Peng:

I did not know it was possible.

Also in the example of Dave R uses the redefinition of a variable with the same type. It is not your case.

Good luck for finding a solution to your problem

In reply to David Peng:

You are confusing the concepts of child and parent objects of UVM components with inheritance. It would have been much clearer if you named your classes c1_class for parent_class and c2_class for child_class.

Your error is because you are constructing a UVM parent component c2 (which you called child_class) and trying create 2 child uvm_component class objects with the same string name “slv”. The types of those components are irrelevant. The UVM does not allow 2 child components to have the same string name under the same parent. Another problem you did not get far enough into to encounter is the uvm_field macros do not work correctly when you have fields with the same name in both the base and extended class. And finally you cannot comment out the call to super.new, the compiler will insert it for if you leave it out. Maybe that’s why you thought you were only constructing one “slv” child object.

I believe once you understand what I wrote above you might want to reconsider rethinking your approach to the entire problem. But there is one way you could proceed by making sure only one child object gets constructed using the build_phase instead of constructing components in new().

class c1_class extends uvm_component;
 
  bit [31:0] addr;
  pmem_class slv;
 
  `uvm_component_utils(c1_class)
 
  function new(string name = "c1_class", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  function void build_phase(uvm_phase phase);
    slv = pmem_class::type_id::create("slv", this);
  endfunction
 
  virtual function void display();
    $display("Addr = %0d",addr);
    $display("slv.a = %0d", slv.a);
  endfunction
endclass

class c2_class#(type T=cmem_class) extends c1_class;
 
  bit [31:0] data;
 
  T slv; // this is a very bad practice 
 
  `uvm_component_param_utils(c2_class)
 
  function new(string name = "child_class", uvm_component parent = null);
    super.new(name, parent);
  function void build_phase(uvm_phase phase);
    // do not call super.build();
    slv = T::type_id::create("slv", this);
    super.slv = slv; // both member have a handle to the same object
  endfunction
 
  function void display();
    $display("Data = %0d",data);
    $display("slv.a = %0d, slv.b = %0d", slv.a, slv.b);
  endfunction
endclass
 
module inheritence;
  initial begin
    child_class c=c2#()::type_id::create("c", null);
    c.addr = 10;
    c.data = 20;
    c.display();
  end
endmodule

In reply to dave_59:

Thanks Dave.

Using super.slv handle is a smart idea.