Polymorphism in SV

26 Aug 2023
4 mins

We discussed the general concept of polymorphism in our previous article. We have seen to declare a class and create a child class from the parent class. In this article we will focus on the implementation of polymorphism in System Verilog.


In System Verilog we achieve polymorphism through function overriding, which is known as static polymorphism. In function overriding we can change the definition of a function in child class. This is known as static polymorphism as the complier decides which definition of the function to execute at compile time only and definition is not changed in run-time.

Another way to achieve polymorphism in SV is through parameterized classes. In parameterized classed we can declare class with same name but different parameters and thus class can act differently based on parameter. We will investigate this in detail in future article.

There is one more way to achieve polymorphism which is function overloading. Function overloading is not supported in latest version of System Verilog (IEEE 1800-2008). Thus, System Verilog only supports static polymorphism.

virtual keyword

We saw in our previous article that the child class can also have the function with same name and the parent class can be called be through super keyword. This may seem to be polymorphism as we are using the same function name, but this is not.

This is because parent and child function are treated as a part of separate entity (class) and object handle will execute the function definition present in that class even though we are using the same name. We will understand this with an example.

In SV, to override a function present in parent class, we need to use virtual keyword in the function declaration present in parent class.

It is not necessary for the child class to have the virtualkeyword, but if we intend to create a child class from this class then we must use virtual keyword to enable function overriding.


virtual function void test();


Let us consider an example, where we have a parent class UsbBasePacket and a child class Usb2Packet. In UsbBasePacket we have 2 methods printf and send where printf is normal method and send uses virtual keyword for function overriding.

In out test module we are creating an object of Usb2Packet and assigning the parent class handle to point to the object of Usb2Packet.

class UsbBasePacket;
    bit [31:0] data;
    bit [9:0] addr;
    int usb_version;

    function new();
       usb_version = 1;
    endfunction //new()

    function void printf();
        $display("Base version packet");
        $display("usb_version: %0d", usb_version);
        $display("addr: 0x%0h", addr);
        $display("data: %0d", data);

    virtual function int send(int data);
        this.data = data;
        $display("Sending usb base packet");
        return data;
endclass: UsbBasePacket

class Usb2Packet extends UsbBasePacket;
    function new();
        data = 0;
        usb_version = 2;
    endfunction //new()

    function void printf();
        $display("USB version 2 packet");
        $display("addr: 0x%0h", addr);
        $display("data: %0d", data);

    function int send(int data);
        this.data = data;
        $display("Sending usb2 packet");
        return this.data;
endclass: Usb2Packet

module test;
    UsbBasePacket packet;
    Usb2Packet usb2_pkt;

    initial begin
        usb2_pkt = new();

        packet = usb2_pkt;

# Base version packet
# usb_version: 2
# addr: 0x0
# data: 0
# Sending usb2 packet
Try this code in EDA Playground

In output, observe that parent object handle is using the parent class definition for printf function but using child class definition for send function. So, we can see that even if printf function name is same in child and parent, but this function is not overridden.

Also observe that all variable of parent class is by default overridden and thus, we see the usb_version as 2 getting printed in printf call from parent class.


In this article we saw how to override a function using virtual keyword. Function overriding plays a major role in test bench designs as it facilitates changing the functionalities for a newer update of the design without affecting the legacy behaviour. In next article we will see parameterized class and how it can be helpful in RTL verification.