In SystemVerilog, Downcasting and Virtual Concepts are important concepts used in object-oriented programming (OOP) within the hardware verification environment. These features help manage and manipulate object types and are particularly useful in writing flexible and reusable verification environments.
1. Downcasting in SystemVerilog
Downcasting is a type of casting where a reference to a superclass object is converted to a reference of a subclass type. It allows you to treat an object of a more general (base) type as a more specific (derived) type.
In other words, downcasting is used when you know the actual type of an object and want to treat it as an object of a more specific class.
2. Virtual Concepts in SystemVerilog
Virtual Concepts are a powerful feature introduced in SystemVerilog to help define abstract types or types that can be used as “placeholders” in assertions or methods where the specific type is not known yet. They are essentially a way to declare a set of constraints without specifying an exact type.
A virtual concept allows you to define a set of attributes or constraints that can be instantiated later as concrete types. This is especially useful in complex verification environments where types or classes might not be fully known until later in the simulation.
class base;
int a = 5;
endclass
class extend extends base;
int b = 1;
endclass
module top;
initial begin
base m_base;
extend m_extend;
m_extend = new();
m_base = new();
$cast(m_extend, m_base);
$display(m_extend.a);
end
endmodule
//Error: (vsim-3971) $cast to type ‘class work.top1_sv_unit::extend’ from ‘class work.top1_sv_unit::base’ failed in file top.sv
Meaning of a successfully casting from super-class(parent class) to sub-class(child class) is that the super-class is actually a sub-class.
class BasePacket;
int A = 1;
int C = 2;
function void printA;
$display(“BasePacket::A is %d”, A);
endfunction : printA
virtual function void printC;
$display(“BasePacket::C is %d”, C);
endfunction : printC
endclass : BasePacket
class My_Packet extends BasePacket;
int A = 3;
int C = 4;
function void printA;
$display(“My_Packet::A is %d”, A);
endfunction: printA
virtual function void printC;
$display(“My_Packet::C is %d”, C);
endfunction : printC
endclass : My_Packet
module top;
initial begin
My_Packet EXT = new;
BasePacket BASE;
BASE = EXT;
BASE.printA;
BASE.printC;
$cast(EXT, BASE);
EXT.printA;
EXT.printC;
end
endmodule : top
//Output:
// BasePacket::A is 1
// My_Packet::C is 4
// My_Packet::A is 3
// My_Packet::C is 4
Here super-class in this example (BASE) is actually the sub-class (EXT), so the $cast is passed.
Here for first call (using BASE handle) of printA and printC, super-class (BASE) is acutally sub-class (EXT) and function printC is declared as virtual in super-class (BASE), so it will execute printC function of sub-class (EXT).
But, function printA is not declared as virtual in super-class (BASE), so it will execute printA function of super-class (BASE), in-spite of it is actually having memory of EXT class, because handle is of super-class.
One usual scenario to use the $cast is to pass some sub-class data into other class, and the developer of the receptor class even don’t know if the incoming class is what kind of sub-class, so usually he will in the receptor class use a super(parent)-class to get the incoming data and then use $cast to check/transfer the data into certain sub-class.
