Polymorphism in SystemVerilog is a powerful feature that allows you to use a variable of a base class type to hold objects from subclass types. It also enables you to reference methods from those subclass objects directly through the base class variable. Polymorphism allows a child class to redefine a parent class method if the method is declared as virtual in the parent class.

What Is a Class Handle in SystemVerilog?

A class handle is like a container that can hold objects from either the parent class or any of its subclasses. It’s crucial to understand how these handles work when assigning objects from one class type to another in SystemVerilog.

Example of Polymorphism: Assigning a Child Class to a Base Class Handle

Let’s consider a simple example to illustrate polymorphism. In this case, we’ll assign a child class instance to a base class handle.

module tb;
    Packet      bc;  // bc represents BaseClass
    ExtPacket   sc;  // sc represents SubClass

    initial begin
        sc = new (32'hfeed_feed, 32'h1234_5678);

        // Assign child class instance to base class handle
        bc = sc;

        // Call display function through base class handle
        bc.display();
        sc.display();
    end
endmodule

Simulation Log:

[Base] addr=0xfeedfeed
[Child] addr=0xfeedfeed data=0x12345678
Simulation is complete.

Even though bc points to the child class instance, when you call the display() function from bc, it still calls the method from the base class. This is because the method is called based on the type of the handle (bc) and not the type of the object the handle points to.

Trying to Reference a Subclass Member via a Base Class Handle

Now, let’s try to access a member of the child class using a base class handle. This will result in a compilation error, as shown below.

module tb;
    Packet      bc;  // bc represents BaseClass
    ExtPacket   sc;  // sc represents SubClass

    initial begin
        sc = new (32'hfeed_feed, 32'h1234_5678);
        bc = sc;

        // Attempt to access child class member through base class handle
        $display ("data=0x%0h", bc.data);
    end
endmodule

Simulation Log:

ncvlog: *E,NOTCLM: data is not a class item.

This error occurs because you cannot directly access a subclass’s member variable using a base class handle. The base class handle does not have visibility of subclass members, resulting in a compilation error.

Assigning a Base Class Object to a Subclass Handle

Next, let’s assign a base class object to a subclass handle, which will also cause a compilation error.

module tb;
    ExtPacket   sc;  // Subclass handle
    Packet      bc;  // Base class handle

    initial begin
        bc = new (32'hface_cafe);

        // Assign base class object to subclass handle
        sc = bc;

        bc.display();
    end
endmodule

Simulation Log:

ncvlog: *E,TYCMPAT: Type mismatch, assignment failed.

In this case, you cannot assign a base class object to a subclass handle. This results in a type-checking error because the base class (Packet) is not compatible with the subclass (ExtPacket).

Using $cast to Assign a Base Class Object to a Subclass Handle

You can use $cast to dynamically assign a base class handle to a subclass handle, but this will only work if the object the base class handle points to is compatible with the subclass.

module tb;
    Packet      bc;  // Base class handle
    ExtPacket   sc;  // Subclass handle

    initial begin
        bc = new (32'hface_cafe);

        // Dynamically cast base class object to subclass type
        $cast(sc, bc);

        bc.display();
    end
endmodule

Although this code compiles without errors, it may result in a runtime error if $cast fails. Let’s look at the simulation log:

Simulation Log:

ncsim: *E,BCLCST: Invalid cast: a value with the class datatype '$unit_0x06d772f8::Packet' cannot be assigned to a class variable with the datatype '$unit_0x06d772f8::ExtPacket'.

In this case, $cast fails because bc is not pointing to an object that is compatible with sc.

Example with Successful Dynamic Casting

Now, let’s see how dynamic casting works successfully when the base class handle points to a compatible subclass object.

module tb;
    ExtPacket sc2;  // New subclass handle
    Packet    bc;   // Base class handle
    ExtPacket sc;   // Subclass handle

    initial begin
        bc = new (32'hface_cafe);
        sc = new (32'hfeed_feed, 32'h1234_5678);
        bc = sc;

        // Dynamically cast subclass object in base class handle to subclass type
        $cast(sc2, bc);

        sc2.display();
        $display ("data=0x%0h", sc2.data);
    end
endmodule

Simulation Log:

[Child] addr=0xfeedfeed data=0x12345678
data=0x12345678
Simulation is complete.

In this example, $cast successfully casts bc (the base class handle) to sc2 (the subclass handle). You can then access the subclass’s members and call its methods.

Virtual Methods in SystemVerilog

In SystemVerilog, you can declare a method in the parent class as virtual. This allows child classes to override the method with a different definition. The method’s prototype (return type and arguments) must remain the same, but the implementation can differ.

class Base;
    rand bit [7:0] addr;
    rand bit [7:0] data;

    // Parent class method defined as virtual
    virtual function void display(string tag="Thread1");
        $display ("[Base] %s: addr=0x%0h data=0x%0h", tag, addr, data);
    endfunction
endclass

class Child extends Base;
    rand bit en;

    // Child class overrides display method
    function void display(string tag="Thread1");
        $display ("[Child] %s: addr=0x%0h data=0x%0h en=%0d", tag, addr, data, en);
    endfunction
endclass

Important Notes on Handle Assignments

You can assign a derived class handle to a base class handle, but the reverse is not allowed. If you attempt to assign a base class handle to a derived class handle, it will result in a compilation error.

base b = new;
child c = new;

b = c;  // Allowed
c = b;  // Compilation Error

The $cast function returns 0 if the cast fails. To handle this, you can use an if statement or an assert to check if the cast is successful.

base b = new;
child c = new;

if (!$cast(c, b))
    $error("Cast failed!");

assert($cast(c, b)) else
    $error("Cast failed!");

Conclusion

SystemVerilog polymorphism offers flexibility in object-oriented programming by allowing you to work with objects of different classes through a single handle. With the help of class handles and $cast, you can manipulate objects in a way that is both efficient and flexible. By understanding and applying these concepts, you can write more robust and reusable code in your SystemVerilog projects.

Scroll to Top