SystemVerilog Inheritance

Hello!, Welcome back again to a new topic with in SystemVerilog OOP. This time we’ll cover about SystemVerilog Inheritance. I decided to keep the post title name as “SystemVerilog Inheritance” so that it should not feel monotonous seeing SystemVerilog OOP from outside everytime and find it hard to figure out what topics are inside it. Its easy with the clearly mentioned title since we’ll discuss here about Inheritance & its different flavors.

I believe you’ve already gone through the previous two related posts on SystemVerilog OOP. If not, you may access them here:

Fine, Lets start now…

Introduction:

Inheritance is the most commonly used principles of Object Oriented Programming (OOP) that facilitates re-use. Its called Inheritance because it carry forwards all the Properties and Methods from the Original Class aka Base Class. The new Class is called Extended Class or Derived Class. The Extended Class contains everything declared in the Base Class. In addition, we may choose to add additional Properties and/or Methods. We can also override the existing Methods. This way we can take the Original Base Classes and modify them as we like without disturbing the existing functionalities.

Extending the Class Properties:

Lets see the beauty of Inheritance in terms of Extending Class Properties using the same SystemVerilog code which we used to explain the Sub-title called Class Properties in SystemVerilog OOP – Part 1.


typedef enum [IDLE, RESET, P1, ...] cmd_t;

class Packet;
   /// Properties
   cmd_t command;
   int status;
   logic [7:0] data [0:255];
endclass: Packet

class myPacket extends Packet;
   bit errBit;
endclass: myPacket

class myPacket;
   /// Properties
   cmd_t command;
   int status;
   logic [7:0] data [0:255];
   bit errBit;
endclass: myPacket

In shown code above, Initially we defined a Class called “Packet“. Three Properties are part of this Packet Class. Later, we extend the Packet Class to declare another Class called “myPacket“. So as per definition, we can say that Class myPacket is Extended/Derived/Child Class of Original/Base/Parent Class i.e. Packet. Notice that we added a new Property i.e. errBit inside the Derived Class myPacket.

myPacket Class shown in the lower section is the effective Class definition of the Extended Class myPacket. It means all the Properties from the Base Class are available in the Extended Class along with the newly added Properties. A big advantage of Inheritance is that any change made inside the Base Class will be automatically reflected in all the Derived Classes and we need not to make those changes in all those Classes.

Extending the Class Methods:

We can add Methods to an Extended Class in the same way as we did with Properties. Please refer to the code shown below, the code is explained in the section below the it:


typedef enum [IDLE, RESET, P1, ...] cmd_t;

class Packet;
   /// Properties
   cmd_t command;
   int status;
   logic [7:0] data [0:255];
   /// Methods
   function void SetStatus (input int Y);
      status = Y;
   endfunction: SetStatus
endclass: Packet

class myPacket extends Packet;
   bit errBit;
   /// Overriding SetStatus Function
   function void SetStatus (input int Y);
      status = Y + 3;
   endfunction: SetStatus
   /// Adding a New Method
   function int errStatus();
      return(errBit);
   endfunction: errStatus

endclass: myPacket

In our example code above, we have Original Class named “Packet” containing 3 Properties and 1 Method. There is another Class name “myPacket” which is Derived from Base Class Packet. myPacket contains the Method “SetStatus” with updated functionality. Overriding a Method means it hides the Base Class Method from the Extended Class but it does not write over it or replace it. There is a new Method added i.e. “errStatus” to the myPacket Class.

super:

SystemVerilog provides a “super” keyword to access what would have been accessed if we had not overridden it. It means, we can access the Properties and Methods which are present in the Base Classes using super keyword. This aids reuse because we can make small modifications to the Method by adding the code around the overridden Method. The Method being overridden does not have to be immediate Base Class. Remember there can be many layers of extended Classes and each extension inherits everything what Base Class inherited. super reaches down as many level are needed to find the overridden Method.


typedef enum [IDLE, RESET, P1, ...] cmd_t;

class Packet;

   /// Properties
   int status;
   /// Methods
   function void SetStatus (input int Y);
      status = Y;
   endfunction: SetStatus

endclass: Packet

class myPacket extends Packet;

 bit errBit;
 /// Overriding SetStatus Function
 function void SetStatus (input int Y);
   super.SetStatus(Y + 3);
 endfunction: SetStatus

endclass: myPacket

In the above code, the Method inside the Extended Class i.e. myPacket calls the SetStatus function from the Base Class i.e Packet. We’ll see important applications of “super” feature in upcoming topic to construct the Objects hierarchy. Tune in for that..

Constructing Extended Classes:

We discussed in SystemVerilog OOP – Part 2 that all the Classes needed a “constructor” i.e. new() to build an Object of that Class type. SystemVerilog implicitly declare it for us if we do not define it. Extended Classes also need a constructor as well as needing a call to their Base Class constructor as the first statement in their constructor. Again, SystemVerilog provide “super.new()” for us if we do not call up by our-self.

Whenever we have a chain of Extended Classes, we also have a chain of constructors from the Base Class to the outer most Class. If none of our Classes have explicit constructors thats perfectly OK since SystemVerilog automatically inserts the constructors for us as well as calls the super.new() for us. But the challenge arises when one of the constructors in the chain have arguments. In that case, we need to call the super.new() with expected type of arguments else Simulator will generate compilation error.


class Packet;

   int pkt_id;
   static int sid;
   string CMP_Name;
  /// Constructor with Argument
   function new (string name);
      CMP_Name = name;
      pkt_id = sid++;
   endfunction: new

endclass: Packet

class argPacket extends Packet;

   function new;
      super.new("newpkt");
   endfunction: new

endclass: argPacket

In the above given example code, we can see that the Base Class i.e. Packet contains a constructor which requires an argument of string type to pass to construct the Object. Hence the Extended Class i.e. argPacket needs to call super.new() with a string argument, if not simulator will pop up the compile error.

Inheritance & Class Variables:

Now lets talk about how SystemVerilog Inheritance works with Class Variables and Handles. Remember when constructing an Extended Class, the Extended Class variable gets the Handle of a single Object which contains all the Properties and Methods of Base Class as well as Extended Class. Base Class variable can hold the Handles of Extended Class Objects but reverse will generate compilation error in normal scenarios. We need to perform Down Casting ($cast) if we want to assign Base Class Handle to the Extended Class variable. We’ll see this feature later in this article.

Lets undergo following example code:


typedef enum {IDLE, RUN, P0, P1} cmd_t;

///// Base Class Declaration
class Packet;
 
 /// Properties
 cmd_t cmd;
 int status;
 bit [7:0] data [0:255];
 
 /// Method
 function void SetStatus (input int y);
 status = y;
 endfunction: SetStatus
 
endclass: Packet

///// Extended Class Declaration
class extndPacket extends Packet;
 
 /// Added Properties
 bit errBit;
 
 /// Newly Added Method
 function bit ShowError();
   return(errBit);
 endfunction: ShowError
 
 /// Overriding Method
 function void SetStatus (input int y);
   status = y + 2;
 endfunction: SetStatus
 
endclass: extndPacket

module top;
 
 /// Array of Type Extended Packet
 extndPacket epkt [3:0]; 
 initial begin
   Packet pkt;
   foreach (epkt[i]) begin
     epkt[i] = new;
     epkt[i].cmd = IDLE;
     epkt[i].SetStatus(1);
     $display("#######################################");
     $display("Extended Packet epkt[%0d] Status Value is: %0d & Command value is:%s", 
     i, epkt[i].status, epkt[i].cmd);
   end
  pkt = epkt[0];
  pkt.cmd = RUN;
  pkt.SetStatus(1);
  $display("Base Packet variable holding Extended Handle pkt.status=%0d, pkt.cmd=%s",
  pkt.status, pkt.cmd);
  $display("#######################################\n");
  epkt[0] = pkt; // This line will generate compilation error. Comment it to run.
 end

endmodule: top

In the above code, there are familiar Classes i.e. Packet and extndPacket. An array of Class type extndPacket is created which contains four elements. Using foreach() loop, Objects are being constructed for the Extended Class. An Object is constructed for Base Class Packet. In the blue highlighted code line, Handle of one of the Extended Object i.e. epkt[0] is assigned to the Base Class variable. Then values are assigned to different Properties and Methods. Using $display we can see that pkt.status is 1, it means it is accessing the Method inside the Base Class Object. The reason for this is absence of Virtual Methods, which will be discussed in the Polymorphism in upcoming post.

With-in foreach loop we can see that Extended Object Handle access the SetStatus() Method from the Extended Class. But even we assigned the Extended Object Handle to Base Class variable, it still use to access the Base Class SetStatus() Method.

We can not assign the Base Class Handle to the Extended Class variable as shown in the above code with a line of comment. If this line of code is part of code, it will generate compilation error. How to resolve this assignment – it will be covered in the next section.

Up Casting & Down Casting of Class Handles:

Lets dive deeper into Inheritance. It is useful to draw a graph of our SystemVerilog Class Inheritance Tree an example is shown in the Figure 1 below:

Inheritance

                               Figure 1: SystemVerilog Class Inheritance Tree

Here we’ve five Classes with “Four_Wheelers” at the root of the hierarchy. “Truck” & “Cars” Classes are derived from Four_Wheelers Class. Similarly “Sedan” and “Sports_Car” are derived from Class “Cars“. One Property is given to each Class. So as per this example in Figure 1, following Properties are available to mentioned Classes:

  • Class “Four_Wheelers” => Variable ‘a
  • Class “Truck” => Variable ‘a‘, ‘b
  • Class “Cars” => Variables ‘a‘, ‘c
  • Class ” Sedan” => Variables ‘a‘, ‘c’, ‘d’
  • Class “Sports_Car” => Variables ‘a‘, ‘c‘, ‘e

Here notice that all the Classes shared the root Class Four_Wheelers Property ‘a‘, but Class Truck only has a Property ‘b’. We can create an Object of any Class type and store its Handle in Class variable of the same type OR a type up the Class tree in the hierarchy. That is called up-casting.

If I construct a Sports_Car Object, we can assign it Handle to either of Class variable of type Sports_Car, Cars or Four_Wheelers. But we can not assign its Handle to Class variable of type Truck or Sedan. A Sports_Car Class does not have a ‘b‘ Property which a Truck Class expect to be able to access.

But now lets assume we’ve a Four_Wheelers Class variable that we know holds the Handle of a Class Sports_Car Object. At this stage, how can we access the Sports_Car‘s ‘e‘ Property? To achieve this we need to downcast the Handle presently hold by Four_Wheelers Class back to the Sports_Car Class variable. Since this is not allowed to be done directly, SystemVerilog provides a dynamic $cast function that checks to see if the assignment is compatible & if yes, its does assign the Handle back.

Lets see extended Packet example again to understand this:


typedef enum {IDLE, RUN, P0, P1} cmd_t;

///// Base Class Declaration
class Packet;
 
 /// Properties
 cmd_t cmd;
 int status;
 bit [7:0] data [0:255];
 
 /// Method
 function void SetStatus (input int y);
 status = y;
 endfunction: SetStatus
 
endclass: Packet

///// Extended Class Declaration
class extndPacket extends Packet;
 
 /// Added Properties
 bit errBit;
 
 /// Newly Added Method
 function bit ShowError();
 return(errBit);
 endfunction: ShowError
 
 /// Overriding Method
 function void SetStatus (input int y);
 status = y + 2;
 endfunction: SetStatus
 
endclass: extndPacket

module top;
 
 /// Array of Type Extended Packet
 extndPacket epkt [3:0]; 
 /// Initial block Initialize the extended array of Packets. 
 initial begin
 Packet pkt;
 foreach (epkt[i]) begin
 epkt[i] = new;
 epkt[i].cmd = IDLE;
 epkt[i].SetStatus(1);
 $display("#######################################");
 $display("Extended Packet epkt[%0d] Status Value is: %0d & Command value is:%s 
 Error Bit = %b", i, epkt[i].status, epkt[i].cmd, epkt[i].errBit);
 end
 /// epkt[0] handle is copied to the pkt Base Class variable.
 pkt = epkt[0];
 pkt.cmd = RUN;
 pkt.SetStatus(1);
 $display("Base Packet variable holding Extended Handle pkt.status=%0d, pkt.cmd=%s", 
 pkt.status, pkt.cmd);
 $display("#######################################\n");
 /// Now since we want to access errBit which not part of pkt Object.Performing 
 /// Downcasting to allocate the Handle back to epkt[0] variable.
 if ($cast(epkt[0],pkt))
 $display("Extended Class Variable holding the Handle epkt[0].status=%0d, 
 epkt[0].cmd=%s Error Bit = %b", epkt[0].status, epkt[0].cmd, epkt[0].errBit);
end

endmodule: top

In the above code, we’ve a Base Class i.e. Packet and extended Class i.e. extndPacket. Packet contains 3 Properties and 1 Method. Inside extndPacket, there is 1 new Property and 1 new Method i.e. errBit & ShowError. One Method is overridden i.e. SetStatus(). Inside top module’s initial block, 4 array elements are constructed. Values for their Properties are set and displayed using $display. Next we assigned the Handle of epkt[0] to the Base Class variable pkt. Here pkt.SetStatus() with some argument value is called and pkt.cmd is assigned with RUN value. $display is again used to show the generated value & observe which of the SetStatus() Method is executed.

In the end, its shown that if we want to access the errBit Property it can not be done in the current state since the Handle is in pkt variable which does not contain the Property errBit. To achieve this goal SystemVerilog checks the compatibility & re-allocate the Handle to the Sports_Car variable. Since this can not be done directly, Dynamic $cast function (aka Down Casting) is used with-in ‘if‘ construct and if successful $display shows the resultant values.


With this, we reached to the end of topic “SystemVerilog Inheritance” & related sub-topics with Inheritance. I hope it will help you to provide a fair picture of the concepts. I believe by playing around with the shared example codes, many dimensions of the subject can be explored. Keep sharing your inputs/suggestions/comments as always. We’ll cover the final topic in our series of SystemVerilog OOP i.e. Polymorphism in the upcoming post.

Till then..I wish for you a happy & healthy life.

See you..Bye! 🙂