SystemVerilog OOP – Part 2

Hii!  Welcome to SystemVerilog OOP – Part 2. I hope you’ve already gone through SystemVerilog OOP – Part 1, where we touched base with different key concepts in OOP like Encapsulation, Inheritance, Data Hiding, Parametrizaton & Polymorphism.

We discussed about SystemVerilog Class Data type, Class Variable, Handle, Constructors, Object Construction or Instantiation, Null, Class Properties, Class Methods, this keyword. If you’ve still not gone through the SystemVerilog OOP – Part 1, I would strongly recommend you to visit that first.

Now moving to Part 2, we’ll further discuss few more concepts of SystemVerilog OOP. As you know, we got to know about Class Handles and Class Objects in Part 1. Now lets understand the differentiation between copying Class Handles and copying Class Objects.

SystemVerilog Copying Handles:

We learned that we create Class Objects dynamically and the only way to refer an Object is by its Handle. When an Object is constructed, its Handle is assigned to the Class variable.

To understand How copying Handles works, lets analyze the following SystemVerilog codes:


typedef enum {IDLE, RESET, P1} cmd_t;

class Packet;

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

   /// Methods
   function int GetStatus();
      return(status);
   endfunction: GetStatus

   task SetCommand (input cmd_t X);
      command = X;
   endtask: SetCommand

endclass: Packet

module top;
  Packet pkt1, pkt2;

  initial begin
    pkt1 = new();
    pkt2 = pkt1;
    pkt1.status = 8;
    $display("Status of pkt1 is %0d", pkt1.status);
    $display("Status of pkt2 is %0d", pkt2.status);
  end

endmodule: top

In the above example, two Class variables are declared named pkt1 & pkt2. First Object is constructed and its Handle is assigned to the variable pkt1. In other words, we can say that pkt1 variable is pointing to the first Object of “Packet” Class type. Next, pkt1 Handle is copied into the second variable i.e pkt2. So, at this stage both the variable hold the same Handle and it means both the variables are pointing to the same Object in the memory. To cross check  that we assigned a value i.e. 8 to the Property called “status” inside the Object. In the last statement $display, we’re accessing the same Property “status” using the other variable pkt2. If return value is 8, it clearly shows that both the variables are pointing to the same Class Object.

SystemVerilog Copying Objects:

Now, lets see what happens if we copy the Class Objects instead of Class Handles.


class Packet;

   /// Properties
   cmd_t command;
   int status;
   logic [7:0] data [0:255];
   /// Constructor
   function new (input int N);
     status = N;
   endfunction: new
   /// Methods
   function int GetStatus();
      return(status);
   endfunction: GetStatus

   task SetCommand (input cmd_t X);
      command = X;
   endtask: SetCommand

endclass: Packet

module top;
  Packet pkt1, pkt2;

  initial begin
    pkt1 = new(5);
    pkt2 = new pkt1;
    pkt2.status = 3;
    $display("Status of pkt1 is %0d", pkt1.status);
    $display("Status of pkt2 is %0d", pkt2.status);
  end

endmodule: top

Here in the above example, again we created two variable of Class Packet i.e. pkt1 & pkt2. With-in the initial block, first an Object is constructed and the Handle of the created Object is assigned to the variable pkt1. Notice that we passed integer value i.e. 5 as argument to the constructor. Next, we construct the second Object by copying the already created Object. It means all the Properties of the previously constructed Object are copied for the second Object. But be very clear that this second Object consumes separate space in the memory. The Handle of this second Object is assigned to the variable pkt2. As a result, both variables holds the Handles of two different Objects of Packet Class type. We assigned different status integer value to the pkt1.status & pkt2.status to ensure they return value accordingly in $display statements. You may play around with this code to assigned different values and to prove the behavior.

SystemVerilog Shallow Copy:

Lets understand separately about Shallow copy. As we learned that Classes contains Properties and Methods. A Class may also contain other Class Instantiation as are part of it. All these variables inside a Class got initialized once the Class is constructed. How does that happen, lets see using following code:


class Lion;
 int a; 
endclass: Lion

class Animal;
 int b = 5;
 Lion Li = new;
endclass: Animal

module top;
 Animal A1, A2;
 initial begin
 A1 = new();
 A2 = new A1;
 A2.b = 9;
 A2.Li.a = 100;
 $display ("A1.b = %0d", A1.b);
 $display ("A2.b = %0d", A2.b);
 $display ("A1.Li.a = %0d", A1.Li.a);
 $display ("A2.Li.a = %0d", A2.Li.a);
 end
endmodule: top

Here we have two Classes named “Animal” & “Lion“. Class Animal instantiates another Class i.e. Lion. It means, Class Animal is having two variables inside i.e. integer variable b and Class variable Li of type Class “Lion”. Now we created two variables of Class Animal i.e. A1 & A2. First one Object is constructed and its Handle is assigned to A1. Another Object is created by doing the “Shallow” copy using “new” construct. In this case, two Object occupy different space in the memory. An important thing to note is that only first level of Properties are copied using the Shallow copy not the Object inside the Parent class. In our example given above, Object of Class type Lion is constructed automatically when we construct the Object of Animal Class type. During Shallow copy Object of type Lion is not copied just like integer variable “b” is copied. This behavior can be observed in the given example. If we will not assign any value to “A2.b“, same value i.e 5 will be shown by the $display statements. Notice we’ve only assigned “A2.Li.a” the value 100. But if we access using the Handle “A1.Li.a“, we got the same value i.e. 100. It shows that Object Li is not copied separately but commonly referenced by both the variables.

SystemVerilog Deep Copy:

If we want to build a Class containing other Class Object we need to understand that there is no ownership of an Object in SystemVerilog. It only gives us Class variables that refer to other Objects by Handle. We can freely pass that Handle around to other Class variable. It is only by a coding agreement that we can say  a Class B Object owns or has a Class A Object instead of a just saying Class B refers to a Class A Object. The problem is when we want to copy an entire Object the language does not know if an embedded Class variable has a relationship or refers to a relationship. It means we need to have a copy() function to show our intent. Lets see the code here:


class Lion;
 int a = 200; 
endclass: Lion

class Animal;
 int b = 5;
 Lion Li = new;
 
 function void copy (Animal Ani);
 this.b = Ani.b;
 this.Li = new Ani.Li;
 endfunction: copy
 
endclass: Animal

module top;
 Animal L1, L2;
 initial begin
 L1 = new();
 L2 = new();
 L2.copy(L1);
 L2.b = 9;
 L1.Li.a = 100;
 $display ("L1.b = %0d", L1.b);
 $display ("L2.b = %0d", L2.b);
 $display ("L1.Li.a = %0d", L1.Li.a);
 $display ("L2.Li.a = %0d", L2.Li.a);
 end
endmodule: top

Taking the Class Animal and Class Lion we just defined, we’ve inserted a copy() function in Class Animal which copies the Class Properties “b” & “Li” as we wanted them to be copied. We used a Shallow copy for “Li” because Class Lion has no Class variable inside it, so it will work in the present example. But normally we have to write copy() methods for all our sub-Class & call its copy() method. Since copy() method is a custom method so it gives better control in terms of choosing what to copy and what Properties to exclude from copy.

Here from the code, we can see that there is a copy of all the Properties including the instantiated Class Lion as well. As L1.Li.a is assigned a value of 100. L2.Li.a will show the default value 200 while running the above code. It shows that two different Objects are constructed for Class Lion yet in Shallow copy only one Object was constructed for Class Lion.

SystemVerilog Static Properties:

As we know that Class Properties do not get created until the Object gets constructed. But there is exception to that. When we add a “static” modifier to a Property that Property becomes a Static variable and it is Global to the Class type. It is created as soon as we create the type and its available during the entire simulation. We access Static Class Properties not by Handle but using the Class Scope operator i.e. “::”. One more important thing to note about Static Properties is that they’re visible simultaneously to all the instances of the Class. Lets see below an example related to Static Properties:


class Packet;
  int id;
  static int pkt_id;
endclass: Packet

module top;
  Packet dynapkt [];
 
 initial begin
 dynapkt = new[4];
 foreach (dynapkt[i]) begin
 dynapkt[i] = new();
 dynapkt[i].id = Packet::pkt_id++;
 $display("dynapkt[%0d].id = %0d", i, dynapkt[i].id);
 end
 for (int i = 0; i<dynapkt.size(); i++)
 $display("dynapkt[%0d].pkt_id = %0d", i, dynapkt[i].pkt_id);
 end
 
endmodule: top

Here is Class “Packet” having two Properties. One of them i.e. “id” is normal integer in nature yet another one i.e. “pkt_id” is a Static Property. A dynamic array of type Packet Class is declared and later four Objects are constructed. After that using the Class Scope operator (::) id value is assigned to each Object. In the end, “for” loop shows that the Static Property pkt_id is reflected in all the instances of the Packet Class.

SystemVerilog Static Methods:

As we begin to use more & more Static variables, the code to manipulate them may grow into a full fledged routines. In SystemVerilog, we can create Static Method inside a Class that can only read & write Static variables, even before any Object of that Class is constructed. Non-Static variables are not allowed inside the Static Methods.


program top;

class Packet;
  int id;
  static int pkt_id;
  static function int PKT_ID;
     return PKT_ID++;
  endfunction: PKT_ID
endclass: Packet

  Packet dynapkt [];
 
 initial begin
 dynapkt = new[4];
 foreach (dynapkt[i]) begin
 dynapkt[i] = new();
 dynapkt[i].id = Packet::PKT_ID;
 $display("dynapkt[%0d].id = %0d", i, dynapkt[i].id);
end

endprogram: top

In the above code, we defined a Static function i.e. PKT_ID which returns the incremented value of a Static variable called pkt_id. Later in the code, PKT_ID is used with the Class Scope operator to assigned the incremented values to the non-static integer variable i.e. id of the created Objects.  Constructed Objects id value can be seen using $display statement.


I’ll stop here with the SystemVerilog OOP – Part 2. I’ll cover rest of the topics in upcoming Parts. As a quick recap, we covered Copying Handles, Copying Objects, Shallow Copy, Deep Copy, Static Variables and in the end Static Methods. Stay tuned for the upcoming Part of SystemVerilog OOP. Even I would recommend to subscribe (at homepage) to receive auto updates once a new post is released.

See you soon again with a new post..till then, take care bye!