In SystemVerilog, inheritance allows a base class handle to point to an instance of a subclass. However, there is a key detail to understand: if a method in the base class is invoked, it might execute the base class’s method, not the one in the child class. To ensure that the child class’s method is called, you need to declare the method in the base class as virtual. This article will explain how SystemVerilog virtual methods work and demonstrate their use with examples.
Inheritance in SystemVerilog
In our previous discussion on inheritance, we saw how a method call through a base class handle to a subclass instance ends up executing the base class’s method instead of the subclass’s method. The exception to this behavior happens when the base class method is declared as virtual.
Let’s break this down further with an example.
Example 1: Without Declaring the Function as Virtual
Consider the following code where we have a base class Packet
and a subclass ExtPacket
. In this case, the display()
function is not declared as virtual.
// Base class Packet
class Packet;
int addr;
function new (int addr);
this.addr = addr;
endfunction
// Function is not declared as virtual
function void display ();
$display ("[Base] addr=0x%0h", addr);
endfunction
endclass
// Testbench module
module tb;
Packet bc;
ExtPacket sc;
initial begin
// Create an instance of ExtPacket
sc = new (32'hfeed_feed, 32'h1234_5678);
// Assign the base class handle to point to the subclass instance
bc = sc;
// Calling the display function
bc.display ();
end
endmodule
Simulation Log Output
ncsim> run
[Base] addr=0xfeedfeed
ncsim: *W,RNQUIE: Simulation is complete.
Explanation: Since the display()
function is not declared as virtual, even though bc
is a base class handle pointing to a subclass instance, the base class’s display()
method gets executed.
Example 2: Declaring the Function as Virtual
Now, let’s modify the Packet
class to declare the display()
function as virtual. This will allow the subclass to override this method.
// Base class Packet
class Packet;
int addr;
function new (int addr);
this.addr = addr;
endfunction
// Declare display() function as virtual
virtual function void display ();
$display ("[Base] addr=0x%0h", addr);
endfunction
endclass
// Testbench module
module tb;
Packet bc;
ExtPacket sc;
initial begin
// Create an instance of ExtPacket
sc = new (32'hfeed_feed, 32'h1234_5678);
// Assign the base class handle to point to the subclass instance
bc = sc;
// Calling the display function
bc.display ();
end
endmodule
Simulation Log Output
ncsim> run
[Child] addr=0xfeedfeed data=0x12345678
ncsim: *W,RNQUIE: Simulation is complete.
Explanation: In this case, since the display()
method is declared as virtual, the child class’s display()
method is called instead of the base class’s method. This shows how the virtual method mechanism works.
Key Takeaway: Use Virtual Methods in Base Classes
The key lesson here is simple: always declare methods in the base class as virtual. This allows existing base class handles to refer to the method overrides in the child class. By using virtual methods, you ensure that the correct method (from the child class) is executed, even when the method is invoked through a base class handle.
Benefits of Using Virtual Methods
Benefit | Description |
---|---|
Polymorphism | Virtual methods enable polymorphism, allowing subclasses to define their own behavior. |
Flexibility | It allows a base class handle to refer to methods in derived classes, providing more flexibility. |
Code Reusability | Base class methods can be reused in multiple child classes, avoiding code duplication. |
Dynamic Dispatch | Virtual methods enable dynamic method dispatch at runtime, providing more control over method execution. |
Conclusion: Why Use SystemVerilog Virtual Methods?
Using SystemVerilog virtual methods is crucial in object-oriented design. It ensures that the correct method from the child class is invoked, even when using a base class handle. By declaring methods as virtual, you leverage polymorphism, which is a core feature of object-oriented programming. Always keep this in mind when designing your classes to ensure that child class behavior is executed as intended.