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
 Simulation Log
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
 Simulation Log
      $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
 Simulation Log
      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.

 Simulation Log
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
 Simulation Log
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 !");