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.