Class inheritance questoin - Overriding a parent class

I am trying to solve a problem and I wanted to see if anyone on the forum has any ideas for this -
There are 3 classes.

  1. BaseClass
  2. BaseClassOverride extends BaseClass
  3. ChildClass extends BaseClass

If I override the BaseClass by the BaseClassOverride, then the ChildClass is still an extension of the BaseClass instead of the BaseClassOverride. So, even if there is a factory override, ChildClass is extended from BaseClass instead of BaseClassOverride.

How can I make the ChildClass extend from BaseClassOverride using the factory (or any other way without changing the ChildClass code).

Here is the compilable example -
In the below example - After the factory override, the ChildClass::func1() should in turn call BaseClassOverride::func1() instead of BaseClass::func1().
How can I use the factory to resolve this problem?

package ExpTest_pkg;
   
`include "ovm_macros.svh"
   import ovm_pkg::*;
   
class Txn extends ovm_sequence_item;
   `ovm_object_utils(Txn)
   function new(string name="");
      super.new(name);
   endfunction // new
endclass // Txn

class BaseClass extends ovm_sequence #(Txn, Txn);
   `ovm_object_utils(BaseClass)
   function new(string name = "");
      super.new(name);
   endfunction // new
   function void func1();
      $display("############ Inside BaseClass ############");
   endfunction // func1
   task body();
      func1();
   endtask // body
endclass // BaseClass

class BaseClassOverride extends BaseClass;
   `ovm_object_utils(BaseClassOverride)
   function new(string name = "");
      super.new(name);
   endfunction // new
   function void func1();
      $display("******** Inside BaseClass Override *******");
   endfunction // func1
   task body();
      func1();
   endtask // body
endclass // BaseClassOverride

class ChildClass extends BaseClass;
   `ovm_object_utils(ChildClass)

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

   task body();
      func1();
   endtask // body
endclass // ChildClass
   
class ExpTest extends ovm_test;
   `ovm_component_utils(ExpTest)
   string report_id = "ExpTest";
   
   ovm_sequencer #(Txn) txn_seqr;

   ChildClass child_class;
   
   function new (string name="ExpTest", ovm_component parent=null);
      super.new(name, parent);
   endfunction // new
   
   function void build();
      super.build();
      factory.set_type_override_by_type(BaseClass::get_type(), BaseClassOverride::get_type());
      factory.print();
      
      txn_seqr = new("txn_seqr", this);
      base_class = BaseClass::type_id::create("base_class");
      child_class = ChildClass::type_id::create("child_class");
   endfunction // build

   function void end_of_elaboration();
      super.end_of_elaboration();
   endfunction // end_of_elaboration
      
   task run();
      ovm_test_done.raise_objection(this);
      `ovm_info(report_id, $psprintf("Running test"), OVM_LOW);
      child_class.start(txn_seqr);
      ovm_test_done.drop_objection(this);
   endtask // run
endclass // ExpTest

endpackage // ExpTest_pkg
   
module ExpTB;
   import ovm_pkg::*;
   import ExpTest_pkg::*;
   
   initial begin
      run_test("ExpTest");
   end
endmodule // ExpTB

You can’t override the middle of a class inheritance hierarchy.

I’m assuming you meant to call super.body() in your sequence classes, otherwise you question does not make much sense. You should explain what are ultimately trying to achieve. I’ll make an assumption you want the functionality of this chain of method calls:

BaseClass::body()
ChildClass ::body();

to become

BaseClass::body()
BaseClassOverride::body()
ChildClass ::body();

There are a number of other ways to achieve this depending on what you are ultimately trying to do. One way is to use a hierarchy of sequence objects and override the virtual sequence to insert another sequence in the hierarchy. Another way is to manipulate an array or queue of sequences to contain the order of sequences you want to execute

Hi Dave

Really appreciate your response. I have given a more detailed explanation below to avoid any confusion and to address some of your questions.

I am trying to reuse two sequences - BaseClass and ChildClass. These sequences were defined by another developer for block level testbenches and I am re-using these sequences at a system level testbench. One thing to keep in mind is I cannot change the code for these sequences.

BaseClass : This block-level sequence generates transactions. BaseClass::process_txn() processes the transaction and finally is sent to the driver. I have to modify these transactions to work properly at the system level. There is a hook function BaseClass::customize_txn() that can be used by at the system level to customize the transaction. I cannot change the code for this sequence.

ChildClass : This block-level sequence also generates transactions similar to BaseClass. ChildClass is an extension of BaseClass. ChildClass::process_txn() is different from BaseClass::process_txn(). I cannot change the code for this sequence.

BaseClassOverride : I have created a this class to override the BaseClass::customize_txn() and make it work at system level. BaseClassOverride is an extension of BaseClass.

Problem : At the system level, I am overriding BaseClass with BaseClassOverride in the factory. So when I call ChildClass::process_txn(). This will in turn call - BaseClass::customize_txn() instead of BaseClassOverride::customize_txn() in-spite of the factory override.

Here is a compilable code. I have simplified process_txn() and customize_txn() functions a LOT and made them trivial but they are extremely complicated functions. My code and sequences look pretty much exactly the same now except for simplified functionality. As I have mentioned before, I dont really have the option to change the code in BaseClass and ChildClass.


package ExpTest_pkg;
   
`include "ovm_macros.svh"
   import ovm_pkg::*;
   
class Txn extends ovm_sequence_item;
   `ovm_object_utils(Txn)
   rand int value;
   constraint c_constrint_value {value inside {[10:15]}; }
   function new(string name="");
      super.new(name);
   endfunction // new
endclass // Txn
   
class BaseClass extends ovm_sequence #(Txn, Txn);
   `ovm_object_utils(BaseClass)
   string report_id = "BaseClass";

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

   virtual function void process_txn(Txn input_txn);
      `ovm_info(report_id, $psprintf("Inside BaseClass::process_txn"), OVM_LOW);
      input_txn.value += 10;
      customize_txn(input_txn);
   endfunction // print

   virtual function void customize_txn(Txn input_txn);
      `ovm_info(report_id, $psprintf("Inside BaseClass::customize_txn"), OVM_LOW);
   endfunction // func1
   
   task body();
      Txn txn;
      txn = Txn::type_id::create("txn");
      start_item(txn);
      assert(txn.randomize());
      process_txn(txn);
      finish_item(txn);
   endtask // body
endclass // BaseClass

class BaseClassOverride extends BaseClass;
   `ovm_object_utils(BaseClassOverride)
   string report_id = "BaseClassOverride";

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

   function void customize_txn(Txn input_txn);
      `ovm_info(report_id, $psprintf("Inside BaseClassOverride::customize_txn"), OVM_LOW);
      input_txn.value = -input_txn.value;
   endfunction // func1
   
endclass // BaseClassOverride

class ChildClass extends BaseClass;
   `ovm_object_utils(ChildClass)
   string report_id = "ChildClass";

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

   function void process_txn(Txn input_txn);
      `ovm_info(report_id, $psprintf("Inside ChildClass::process_txn"), OVM_LOW);
      input_txn.value += 100;
      customize_txn(input_txn);
   endfunction // print
endclass // ChildClass

class ExpDriver extends ovm_driver #(Txn, Txn);
   `ovm_component_utils(ExpDriver)
   string report_id = "ExpDriver";

   function new(string name, ovm_component parent);
      super.new(name, parent);
   endfunction // new

   task run();
      Txn txn;
      seq_item_port.get_next_item(txn);
      seq_item_port.item_done();
   endtask // run
endclass // ExpDriver
   
class ExpTest extends ovm_test;
   `ovm_component_utils(ExpTest)
   string report_id = "ExpTest";
   
   ovm_sequencer #(Txn) txn_seqr;
   
   ChildClass child_class;

   ExpDriver exp_driver;
   
   function new (string name="ExpTest", ovm_component parent=null);
      super.new(name, parent);
   endfunction // new
   
   function void build();
      super.build();
      factory.set_type_override_by_type(BaseClass::get_type(), BaseClassOverride::get_type());
      factory.print();
      
      txn_seqr = new("txn_seqr", this);
      child_class = ChildClass::type_id::create("child_class");
      exp_driver = ExpDriver::type_id::create("exp_driver", this);
   endfunction // build

   function void connect();
      exp_driver.seq_item_port.connect(txn_seqr.seq_item_export);
   endfunction // connect
   
   function void end_of_elaboration();
      super.end_of_elaboration();
   endfunction // end_of_elaboration
      
   task run();
      ovm_test_done.raise_objection(this);
      `ovm_info(report_id, $psprintf("Running test"), OVM_LOW);
      child_class.start(txn_seqr);
      ovm_test_done.drop_objection(this);
   endtask // run
   
endclass // ExpTest

endpackage // ExpTest_pkg
   
module ExpTB;

   import ovm_pkg::*;
   import ExpTest_pkg::*;
   
   initial begin
      
      run_test("ExpTest");
      
   end

endmodule // ExpTB

Output of the above code using Cadence (same with Questa)-

OVM_INFO @ 0: reporter [RNTST] Running test ExpTest…

Factory Configuration (*)

No instance overrides are registered with this factory

Type Overrides:

Requested Type Override Type


BaseClass BaseClassOverride

All types registered with the factory: 14 total
(types without type names will not be printed)

Type Name

BaseClass
BaseClassOverride
ChildClass
ExpDriver
ExpTest
Txn
(*) Types with no associated type name will be printed as

OVM_INFO experiment.sv(123) @ 0: ovm_test_top [ExpTest] Running test
OVM_INFO experiment.sv(68) @ 0: reporter [ChildClass] Inside ChildClass::process_txn
OVM_INFO experiment.sv(31) @ 0: reporter [BaseClass] Inside BaseClass::customize_txn
OVM_INFO @ 0: ovm_test_done [TEST_DONE] All end_of_test objections have been dropped. Calling global_stop_request()

The solution I am looking for is to make it come in this order without changing ChildClass -

OVM_INFO experiment.sv(68) @ 0: reporter [ChildClass] Inside ChildClass::process_txn
OVM_INFO experiment.sv(53) @ 0: reporter [BaseClassOverride] Inside BaseClassOverride::customize_txn

In reply to kiranks:

I think cut & paste is your only available re-use option given the constraints you have specified. It seems there is never enough time to do things the right way first, but you are supposed to have the time to fix the code later.

In reply to dave_59:

Hi Dave

My problem will be solved if there I could implement multiple inheritance in system verilog.

Until then, cut-paste it is… at the risk of missing updates to ChildClass.

Thanks again Dave.