The short answer: A SystemVerilog class bundles data (properties) and behaviour (methods). You create objects with new(), which returns a handle; copying a handle shares the object rather than copying it. Inheritance (extends) lets a child class reuse and change a parent, and virtual methods give polymorphism: a call through a base-class handle runs the child’s version. Encapsulation (local, protected), static members and parameterised classes complete the toolkit that UVM is built on.
Classes are the biggest jump from Verilog to SystemVerilog, and many hardware engineers find them strange at first. You don’t need deep software engineering to use them well; you need the six ideas below and a clear picture of what a handle is.
This post replaces my older posts on classes and objects and on parameterised classes and modules.
- The six ideas
- Classes, objects and handles
- Copying objects
- Encapsulation
- Inheritance and polymorphism
- Abstract classes and pure virtual methods
- Parameterised classes
Table of Contents
Part 1: The six ideas

Part 2: Classes, objects and handles
class packet;
rand bit [7:0] addr;
rand bit [7:0] data[];
static int count = 0; // shared by all packets
function new(bit [7:0] a = 0);
addr = a;
count++;
endfunction
function void print();
$display("addr=%h len=%0d", addr, data.size());
endfunction
endclass
packet p1, p2; // handles, both null
p1 = new(8'h10); // object created
p2 = p1; // same object, two handles
p2.addr = 8'h20; // p1.addr is now 8'h20 too
A handle is like a pointer. Objects are freed automatically when no handle refers to them.
Part 3: Copying objects
p2 = new p1; // shallow copy: new object, same nested handles
function packet copy(); // deep copy you write yourself
copy = new();
copy.addr = addr;
copy.data = data; // arrays are copied by value
endfunction
Shallow copies share any objects that the original points to, which is a common source of scoreboard bugs. UVM gives you a standard copy() and clone() for this reason.
Part 4: Encapsulation
local members are visible only inside the class; protected members are also visible in child classes. Hiding internals behind methods lets you change the implementation without breaking every test that uses the class.
Part 5: Inheritance and polymorphism
class err_packet extends packet;
bit bad_crc;
function new(bit [7:0] a = 0);
super.new(a); // call the parent constructor
endfunction
virtual function void print();
super.print();
$display(" bad_crc=%0b", bad_crc);
endfunction
endclass
packet p;
err_packet e = new(8'h30);
p = e; // base handle, child object
p.print(); // runs err_packet::print because print is virtual
Mark methods virtual in the base class when children should be able to change them. Without it, the call follows the handle type and runs the base version. This is how UVM lets you swap in a different sequence item or driver without editing the environment.
Part 6: Abstract classes and pure virtual methods
virtual class base_checker;
pure virtual function bit check(packet p);
endclass
You can’t create an object of a virtual class; it defines a contract that children must implement.
Part 7: Parameterised classes
class fifo_model #(type T = int, int DEPTH = 16);
T items[$];
function bit push(T x);
if (items.size() >= DEPTH) return 0;
items.push_back(x);
return 1;
endfunction
endclass
fifo_model #(packet, 32) pkt_fifo = new();
Type parameters let one class work for any element type; UVM’s TLM ports and many utilities are built this way. Modules can take type parameters too: module fifo #(type T = logic [7:0]).
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Previous in my SystemVerilog series: Interfaces, modports and clocking blocks. Next: Constrained randomisation.
FAQ
What is the difference between a class and a module?
A module is static hardware created at elaboration. A class is a dynamic object created at run time with new(); classes are for testbenches.
What is a handle in SystemVerilog?
A reference to an object. Copying a handle doesn’t copy the object; both handles point to the same one.
Why use virtual methods?
So that a call through a base-class handle runs the derived class’s implementation. That’s polymorphism, and UVM depends on it.
What is the difference between shallow and deep copy?
A shallow copy duplicates an object’s properties but shares nested objects; a deep copy duplicates nested objects too.
