Virtual Interfaces: Connecting Classes to the DUT
A class-based testbench is dynamic — objects come and go at run time — but the DUT it drives is a static module connected by an interface. The bridge between the two worlds is the virtual interface: a handle, stored in a class, that points at a real interface instance so the class can drive and sample DUT pins.
How it connects
You declare an interface, instantiate it in the top module and connect it to the DUT, then pass it (via the config database in UVM, or directly) to a driver class that holds a virtual handle to it. The canonical shape (shown for reference — the open-source browser simulator focuses on RTL and class code rather than full virtual-interface elaboration):
interface bus_if(input logic clk);
logic valid;
logic [7:0] data;
modport drv (output valid, data, input clk);
endinterface
class Driver;
virtual bus_if vif; // handle to a REAL interface
function new(virtual bus_if vif); this.vif = vif; endfunction
task run();
@(posedge vif.clk);
vif.valid <= 1; vif.data <= 8'hA5; // drive DUT pins through the vif
@(posedge vif.clk);
vif.valid <= 0;
endtask
endclass
module top;
logic clk = 0; always #5 clk = ~clk;
bus_if bif(clk); // the real interface instance
// dut u_dut(.bus(bif)); // connect to DUT
initial begin
Driver d = new(bif); // pass the vif into the class
d.run();
#30 $finish;
end
endmoduleThe value is reuse and decoupling: the Driver class knows nothing about which interface instance it drives, so the same class drives any instance handed to it — exactly how UVM drivers get their pins via a virtual interface stored in the uvm_config_db.
The idea, runnable
The essence — a class holding a handle to shared signal state and driving it — is provable in pure class code the browser lab runs:
module tb;
// stand-in for interface signal state the class drives through a handle
class BusState; logic valid; logic [7:0] data; endclass
class Driver;
BusState vif; // 'virtual interface' stand-in
function new(BusState b); vif = b; endfunction
task send(logic [7:0] d); vif.valid = 1; vif.data = d; endtask
endclass
initial begin
BusState bus = new();
Driver drv = new(bus);
drv.send(8'hA5);
$display("driver drove the shared bus: valid=%b data=%h", bus.valid, bus.data);
$finish;
end
endmoduledriver drove the shared bus: valid=1 data=a5 - /var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmph8lj3urc/tb.sv:14: Verilog $finish
Check yourself
Why does a class-based testbench need a virtual interface instead of referencing the interface directly?
Classes are dynamic objects with no static connection to hardware; an interface is a static instance. A virtual interface is a handle a class stores at run time pointing to a real interface instance, letting dynamic class code drive and sample the DUT’s static pins — and letting one class work with any instance passed to it.
In UVM, how does a driver typically obtain its virtual interface?
The top/test sets the interface handle into the uvm_config_db, and the driver gets it in its build phase. This keeps the driver decoupled from which physical interface instance it drives.
Share your question in comments or talk to our mentor team for batch guidance.
Compile and simulate this in the browser — no install. Edit the code and re-run instantly.
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