SystemVerilog Virtual Methods and Polymorphism
Polymorphism is what makes a UVM testbench reusable: a base transaction or driver handle can point to any derived object, and the virtual method dispatched is the one belonging to the actual object, not the handle type. This chapter shows the mechanism with runnable SystemVerilog.
Without virtual: the handle type wins
If a method is not virtual, the call resolves to the handle’s class. Mark it virtual and the call resolves to the object’s class — the behaviour you almost always want.
module tb;
class Packet;
virtual function string kind(); return "base"; endfunction // virtual
function string tag(); return "PKT"; endfunction // non-virtual
endclass
class EthPacket extends Packet;
function string kind(); return "ethernet"; endfunction // override
function string tag(); return "ETH"; endfunction
endclass
initial begin
Packet p; // base handle
EthPacket e = new();
p = e; // base handle points to derived object
$display("virtual kind() -> %s (dispatched by OBJECT)", p.kind());
$display("non-virtual tag() -> %s (dispatched by HANDLE)", p.tag());
end
endmodulevirtual kind() -> ethernet (dispatched by OBJECT) non-virtual tag() -> PKT (dispatched by HANDLE)
Read the log: kind() is virtual, so the base handle calls EthPacket‘s version (ethernet); tag() is not virtual, so it calls the base version (PKT) even though the object is an EthPacket. This is exactly why UVM marks its hooks (do_copy, do_compare, the phase methods) virtual.
Abstract base classes
Declare a class virtual class and a method pure virtual to force every subclass to implement it — a compile-time contract. This is how UVM’s uvm_sequence requires you to write a body().
▶ Try it in the ChipXpert Lab
Build a base Transaction with virtual compare() and two subclasses in the cocotb/SV verification lab, then drive a scoreboard through base-class handles to see polymorphism carry the right compare.
Check yourself
Why must UVM callback and phase methods be declared <code>virtual</code>?
Because the framework holds your components and transactions through base-class handles (uvm_component, uvm_sequence_item). Only a virtual method dispatches to your derived override; a non-virtual one would call the empty base version, so your custom behaviour would never run.
What does <code>pure virtual</code> inside a <code>virtual class</code> enforce?
It declares a method with no body that every concrete subclass must implement, enforced at compile time. The abstract base cannot be instantiated directly — it defines an interface the subclasses must fulfil.
Share your question in comments or talk to our mentor team for batch guidance.
Learn Verilog with mentors, labs and placement support
This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.
Explore VLSI Courses