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:
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! 🙂