Polymorphism allows the use of a variable of the base class type to hold subclass objects and to reference the methods of those subclasses directly from the superclass variable. It also allows a child class method to have a different definition than its parent class if the parent class method is virtual
in nature.
Parent and Child Assignment
A class handle is just a container to hold either parent or child class objects. It is important to understand how parent class handles holding child objects and vice-versa behave in SystemVerilog.
Assign Child Class to Base Class
Taking the same example from Inheritance, we'll assign a sub/child class instance sc to a base class handle bc.
module tb;
Packet bc; // bc stands for BaseClass
ExtPacket sc; // sc stands for SubClass
initial begin
sc = new (32'hfeed_feed, 32'h1234_5678);
// Assign sub-class to base-class handle
bc = sc;
bc.display ();
sc.display ();
end
endmodule
ncsim> run [Base] addr=0xfeedfeed [Child] addr=0xfeedfeed data=0x12345678 ncsim: *W,RNQUIE: Simulation is complete.
Even though bc points to the child class instance, when display()
function is called from bc it still invoked the display()
function within the base class. This is because the function was called based on the type of the handle instead of the type of object the handle is pointing to. Now let's try to reference a subclass member via a base class handle for which you'll get a compilation error.
module tb;
Packet bc; // bc stands for BaseClass
ExtPacket sc; // sc stands for SubClass
initial begin
sc = new (32'hfeed_feed, 32'h1234_5678);
bc = sc;
// Print variable in sub-class that is pointed to by
// base class handle
$display ("data=0x%0h", bc.data);
end
endmodule
$display ("data=0x%0h", bc.data); | ncvlog: *E,NOTCLM (inheritance.sv,49|36): data is not a class item.
Assign Base Class to Child Class
It is illegal to directly assign a variable of a superclass type to a variable of one of its subclass types and hence you'll get a compilation error.
module
initial begin
bc = new (32'hface_cafe);
// Assign base class object to sub-class handle
sc = bc;
bc.display ();
end
endmodule
sc = bc; | ncvlog: *E,TYCMPAT (inheritance.sv,56|12): assignment operator type check failed (expecting datatype compatible with 'class $unit::ExtPacket' but found 'class $unit::Packet' instead).
However, $cast
can be used to assign a superclass handle to a variable of a subclass type provided the superclass handle refers to an object that is assignment compatible with the subclass variable.
module
initial begin
bc = new (32'hface_cafe);
// Dynamic cast base class object to sub-class type
$cast (sc, bc);
bc.display ();
end
endmodule
Although the code will compile well, it will have a run-time simulation error because of the failure of $cast
. This is because bc is not pointing to an object that is compatible with sc.
ncsim> run $cast (sc, bc); | ncsim: *E,BCLCST (./inheritance.sv,57|10): Invalid cast: a value with the class datatype '$unit_0x06d772f8::Packet' cannot be assigned to a class variable with the datatype '$unit_0x06d772f8::ExtPacket'. [Base] addr=0xfacecafe ncsim: *W,RNQUIE: Simulation is complete.
Let's make bc point to another subclass called sc2 and try the same thing. In this case, bc simply acts like a carrier.
initial begin
ExtPacket sc2;
bc = new (32'hface_cafe);
sc = new (32'hfeed_feed, 32'h1234_5678);
bc = sc;
// Dynamic cast sub class object in base class handle to sub-class type
$cast (sc2, bc);
sc2.display ();
$display ("data=0x%0h", sc2.data);
end
ncsim> run [Child] addr=0xfeedfeed data=0x12345678 data=0x12345678 ncsim: *W,RNQUIE: Simulation is complete.
Virtual Methods
A method in the parent class can be declared as virtual
which will enable all child classes to override the method with a different definition, but the prototype containing return type and arguments shall remain the same.
class Base;
rand bit [7:0] addr;
rand bit [7:0] data;
// Parent class has a method called 'display' declared 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 redefines the method to also print 'en' variable
function void display(string tag="Thread1");
$display ("[Child] %s: addr=0x%0h data=0x%0h en=%0d", tag, addr, data, en);
endfunction
endclass
Click here to learn more on Virtual Methods
Rules to follow
- Assignment of derived class handle to base class handle is allowed.
- Assignment of base class handle to derived class handle is NOT allowed and results in compilation error.
base b = new;
child c = new;
b = c; // Allowed
c = b; // Compilation Error
$cast
returns 0 if the cast failed, so use the return type to throw an error. Use if
or assert
to ensure that 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 !");