This part covers SystemVerilog OOP concepts including classes, inheritance, and polymorphism. These form the backbone of modern UVM-based verification.
logic and wire/reg in SystemVerilog?logic is a 4-state type that replaces both wire and reg in most situations. It can be driven by continuous assignment, procedural blocks, or module outputs. Unlike wire, logic cannot have multiple drivers (for that, use wire or tri). Unlike reg, logic can be used on the left-hand side of assign statements and as a port direction type. This simplification removes the confusion between wire and reg that existed in Verilog.
A class is defined using the class keyword, followed by the class name and optional extends clause. Classes encapsulate data members and methods. Objects are created using new().
class Packet;
rand bit [7:0] addr;
rand bit [7:0] data;
function new(bit [7:0] addr = 0);
this.addr = addr;
endfunction
virtual function void display();
$display("addr=%0h data=%0h", addr, data);
endfunction
endclass
new() constructor in SystemVerilog.new() is a special function that allocates memory for the object and initializes its members. It is called when an object is created: Packet p = new();. The constructor can take arguments to initialize the object with specific values. Classes can also have a new() that calls super.new() to invoke the parent class constructor. If no constructor is defined, a default new() with no arguments is implicitly created.
Inheritance allows a new class (derived/child) to extend an existing class (base/parent) using the extends keyword. The derived class inherits all members and methods of the base class and can add new members or override virtual methods. Inheritance enables code reuse and polymorphism.
class BadPacket extends Packet;
constraint bad { addr inside {[8'hFF:8'hFF]}; }
function void display();
super.display();
$display("bad packet");
endfunction
endclass
Polymorphism is achieved through virtual methods and base-class handles pointing to derived-class objects. When a virtual method is called via a base-class handle, the actual method executed is determined at runtime based on the object type (dynamic dispatch). This allows writing generic code that works with any subclass.
Packet p;
BadPacket bp = new();
p = bp;
p.display(); // calls BadPacket's display() because it is virtual
Virtual methods (declared with the virtual keyword) enable dynamic dispatch: the method implementation called depends on the object's actual type, not the handle type. This is essential for polymorphism in OOP. If a method is not virtual, the base class version is always called even if the object is of a derived type. In UVM, virtually every method (like build_phase, connect_phase) is virtual to allow overriding.
Shallow copy copies the object handle but not the object itself. If you assign p2 = p1, both handles point to the same object. A deep copy creates a new object with copies of all member variables. SystemVerilog provides new() copy constructors and copy() methods for deep copies. For classes containing handles to other objects, deep copy must recursively copy those objects.
Packet p1 = new();
Packet p2 = new p1; // deep copy via copy constructor
Parameterized classes allow the class to be defined with type or value parameters, similar to parameterized modules. This enables generic, reusable components.
class #(type T = int) Stack;
T items[100];
int top;
function void push(T item);
items[top++] = item;
endfunction
function T pop();
return items[--top];
endfunction
endclass
Stack#(bit [7:0]) byteStack;
this keyword refer to?this is a built-in handle that refers to the current object instance. It is used to disambiguate between class members and local variables or function arguments that share the same name. For example, this.addr = addr; assigns the argument addr to the class member addr. It is also used to call methods on the current instance.
Static members (static keyword) belong to the class itself rather than to any specific object instance. All objects share the same static variable. Static methods can only access static members. Non-static members are instance-specific: each object has its own copy. Static members are useful for shared counters, configuration, or factory registration. They can be accessed via the class name (Packet::count) or via an object handle.
Mastering OOP concepts is essential for SystemVerilog verification. Continue to Part 2 for randomization and coverage topics.