[Day-6] Abstract
It's been quite some time now since the last article was published. Let's start with something new today. A very useful topic to discuss in object-oriented programming. The Virtual class and method.
What do we do with it?
First, let's go back a bit and think about the overriding of class properties. If you can recall from Day-3, you can see that as long as we have different class handles announced and instantiated(or assigned) for both child and parent classes, then even if we have similar named properties in different classes, all will work differently, none of them will override each other.
But!
What if we want to override the methods? What if we want to announce the name at first and later on, in any of the inherited child classes, we want to describe the methods?
This is something we are going to discuss today. To start with it, let's first understand what's the significance of the word virtual in SystemVerilog.
Virtual means, unreal. Something that does not have real existence. It means we use virtual when we want to replace a property with a new, real one.
Virtual Class
Now, there are two different uses of virtual keywords. First, when we want to replace the class itself, including all of the values of it's properties, we will put the virtual keyword before class. Let's see the example below:
virtual class Packet;
int address ;
function new ();
address = 20;
endfunction
function display ();
$display ("[Base] addr=0x%0h", address);
endfunction
endclass
class ExtPacket extends Packet;
// This is a new variable only available in child class
int data_ext;
function new (int addr, data);
super.new (); // Calls 'new' method of parent class
address = addr;
data_ext = data;
endfunction
function display ();
$display ("[Ext Child] addr=0x%0h data=0x%0h", address, data_ext);
endfunction
endclass
module tb;
Packet bc;
ExtPacket sc;
initial begin
sc = new (32'hfeed_beef, 32'h1234_5678);
bc = sc;
bc.display();
end
endmodule
Output:
# [Base] addr=0xfeedbeef
Even though we have announced the value of the address as 20 in the virtual parent class named Packet, the printed value in the display method of Packet class got the value from child class.
The reason behind this is that the memory location for the values of the virtual class Packet does not exist. It actually takes values from the similar properties of its child class. Which we have provided as 32'hfeed_beef which can be seen in the output window at the end.
Now, what if we instantiated the virtual class? As we know from the previous discussion(day-1), instantiation gives the handle a memory location. So, what if we do something like this?
virtual class Packet;
int address ;
function new (int addr);
address = addr;
endfunction
function display ();
$display ("[Base] addr=0x%0h", address);
endfunction
endclass
module tb;
Packet bc;
initial begin
bc = new(20);
bc.display();
end
endmodule
Output:
# ** Fatal: testbench.sv (17): Class allocator method 'new' called on abstract class 'Packet'.
Meaning, we can't instantiate or provide memory location to virtual class. However, works that we desire to do upon it need to be done by first creating a derived class(inherit a child off) of the virtual class at-first, assign the class handle of the child class to parent class and then calling the methods of the virtual parent class.
Seems like a process huh? The process is actually a very good tool to automate things and make things reusable. We are going to see the usefulness of this process pretty soon.
So, virtual class is something that we have discussed so far. But what is virtual methods?
Virtual Method
Like virtual classes, virtual method is not so rigorous! Even if we don't have a method definition in the child class, the method can be called by the class handle like any other, general method. For example:
class Packet;
int address;
function new (int addr);
address = addr;
endfunction
virtual function display ();
$display ("[Base] addr=0x%0h", address);
endfunction
endclass
class ExtPacket extends Packet;
// This is a new variable only available in child class
int data_ext;
function new (int addr, data);
super.new (addr); // Calls 'new' method of parent class
data_ext = data;
endfunction
endclass
module tb;
Packet bc;
ExtPacket sc;
initial begin
bc = new(32'hfeed_feed);
sc = new (32'hfeed_beef, 32'h1234_5678);
bc = sc;
bc.display();
end
endmodule
Output:
领英推荐
# [Base] addr=0xfeedbeef
As you can see, the display function in the base class Packet is being displayed.
How are virtual methods different then?
The difference emerges when we define a method in the derived class that is similar to the method name in the parent class. Kind of like this:
class Packet;
int address;
function new (int addr);
address = addr;
endfunction
virtual function display ();
$display ("[Base] addr=0x%0h", address);
endfunction
endclass
class ExtPacket extends Packet;
// This is a new variable only available in child class
int data_ext;
function new (int addr, data);
super.new (addr); // Calls 'new' method of parent class
address = addr;
data_ext = data;
endfunction
function display ();
$display ("[Ext Child] addr=0x%0h data=0x%0h", address, data_ext);
endfunction
endclass
module tb;
Packet bc;
ExtPacket sc;
initial begin
bc = new(32'hfeed_feed);
sc = new (32'hfeed_beef, 32'h1234_5678);
bc = sc;
bc.display();
end
endmodule
Output:
# [Ext Child] addr=0xfeedbeef data=0x12345678
As you can see, even though we instantiated the Packet class separately, the display method in the child class replaced the display method in Packet class.
This means we use virtual methods when we want to replace certain parent class property values with the one in the child class.
Pure Virtual
Now, this can sometimes cause confusion or can be honestly ignored or missed in child class. To make sure the methods in the parents class are redefined in the derived class, we can use pure virtual.
What pure virtual keyword does to the method is that,
If any of the mentioned three is not maintained, there will be an error. So, a useful way of using pure virtual keywords is:
virtual class Packet;
int address;
function new (int addr);
address = addr;
endfunction
pure virtual function display ();
endclass
class ExtPacket extends Packet;
// This is a new variable only available in child class
int data_ext;
int address;
function new (int addr, data);
super.new (addr); // Calls 'new' method of parent class
address = addr;
data_ext = data;
endfunction
function display ();
$display ("[Ext Child] addr=0x%0h data=0x%0h", address, data_ext);
endfunction
endclass
module tb;
Packet bc;
ExtPacket sc;
initial begin
sc = new (32'hfeed_beef, 32'h1234_5678);
bc = sc;
bc.display();
end
endmodule
Output:
# [Ext Child] addr=0xfeedbeef data=0x12345678
This is it! That was everything that is in my knowledge about virtual keywords in SystemVerilog.
The most useful thing about it is reusability as mentioned earlier. How?
Say I have a virtual class that only has the names of the properties in it with virtual keyword (or better, pure virtual keyword). We need it to make sure we have a fixed architecture of our testbench(like UVM).
If so, then we won't have to worry about the base class at all! We can just derive child classes from it and redefine all the methods and properties as of our need!
I am hoping you are going to share your thoughts on it as well.
See you next day!
Disclaimer: The knowledge above is true and rigid by the confidence of all the experiments and resources I went through. But it's not the holy grail of course! I cordially request by connections to add anything that I might have missed while defining class or maybe have mistaken or misinterpreted something. Your cooperation will help me learn and grow more!
If you are new to this article series and kind of lost, I started a 10-day discussion series about Object Oriented Programming using SystemVerilog. Follow the series to learn about my understanding of Object Oriented Programming and add your knowledges as well to make my understanding better in the comments.