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

BenefitDescription
PolymorphismVirtual methods enable polymorphism, allowing subclasses to define their own behavior.
FlexibilityIt allows a base class handle to refer to methods in derived classes, providing more flexibility.
Code ReusabilityBase class methods can be reused in multiple child classes, avoiding code duplication.
Dynamic DispatchVirtual 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.

Scroll to Top