How Virtual Interface can be pass using uvm_config_db in the UVM Environment?
In short: A virtual interface is a handle that lets your class-based UVM code reach a real, static interface connected to the DUT. You pass it in with uvm_config_db#(virtual my_if)::set from the top module, then each component reads it with get in build_phase. This keeps the run-time class world and the static module world connected without hard-coded paths.
UVM components are classes, and classes are created at run time. The DUT and its interface are static, elaborated once at time zero. A class cannot point straight at a static interface, so UVM uses a virtual interface, a handle to the real interface, and passes that handle down using uvm_config_db.
Why a virtual interface is needed
A testbench has two worlds. The class-based world (test, environment, agents, driver, monitor) is created at run time. The structural world (the DUT and the interface wired to it) is static. The driver has to wiggle real pins, so it needs a way to reach into the static interface from inside a class. That bridge is the virtual interface.

A simple way to picture it
Think of a TV remote. The remote (your class-based driver) does not hold the wires inside the TV. It holds a link to the TV so it can change channels. The virtual interface is that link: the driver keeps a handle to the real interface and drives the pins through it, without owning the wires itself.
Step 1: declare the interface and connect it in the top module
First define an interface, instance it in the top module, and connect it to the DUT. Then place the handle in the config database so the testbench can find it.
interface apb_if(input logic pclk);
logic preset_n;
logic [31:0] paddr;
logic pwrite;
logic [31:0] pwdata;
logic [31:0] prdata;
logic psel, penable, pready;
endinterface
module top;
logic pclk = 0;
always #5 pclk = ~pclk;
apb_if apb_vif(pclk); // real interface instance
apb_dut dut(.apb(apb_vif)); // connect to DUT
initial begin
// put the handle in the database for the whole testbench
uvm_config_db#(virtual apb_if)::set(null, "uvm_test_top.env.agt*",
"vif", apb_vif);
run_test("apb_test");
end
endmoduleStep 2: pass it down with uvm_config_db
The interface handle now travels down the hierarchy through the config database. The test does not need to touch it; UVM carries it to the agents, which then hand it to the driver and monitor. The diagram below shows this flow.

Step 3: get the handle in the driver and monitor
Each component that touches pins reads the handle in its build_phase. Always check the return value, and stop the test with a clear message if the handle is missing.
class apb_driver extends uvm_driver #(apb_seq_item);
`uvm_component_utils(apb_driver)
virtual apb_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "No virtual interface set for the driver")
endfunction
task run_phase(uvm_phase phase);
forever begin
apb_seq_item tr;
seq_item_port.get_next_item(tr);
// drive real pins through the virtual interface
@(posedge vif.pclk);
vif.psel <= 1;
vif.paddr <= tr.addr;
vif.pwrite <= tr.write;
vif.pwdata <= tr.data;
seq_item_port.item_done();
end
endtask
endclassThe monitor reads the same handle the same way, then samples vif signals instead of driving them. Both sit inside the agent, so the wildcard path env.agt* from the top reaches them together.
Where to set and where to get
| Action | Where | Why |
|---|---|---|
| set the handle | Top module before run_test | The interface is static and known at time zero |
| get the handle | Driver and monitor build_phase | Children build after parents, so the value is ready |
| check return bit | Every get call | A missing handle should fail early, not mid test |
| use wildcard path | env.agt* on the set | One set reaches every matching agent |
Common mistakes to avoid
- Getting the handle in
run_phaseinstead ofbuild_phase, so early code sees a null handle. - A path mismatch: setting on
agtbut the real instance isagent. The names must match or use a wildcard. - Skipping the return check on
get, then hitting a null virtual interface at the first pin access. - Setting the wrong type, for example
virtual apb_ifon the set and a different interface type on the get. - Connecting the interface to the DUT but forgetting the
setcall, so the handle never enters the database.
Keep learning
This builds directly on the config database. Read more about uvm_config_db and uvm_resource_db, see the typical UVM testbench architecture, and find related topics in the UVM category and the interview questions.
Frequently asked questions
What is a virtual interface in UVM?
A virtual interface is a handle to a real, static SystemVerilog interface. It lets class-based UVM components, which are created at run time, reach and drive the static pins connected to the DUT.
Why do we pass the virtual interface with uvm_config_db?
The config database carries the handle down the hierarchy without hard-coded paths. The top module sets it once, and every driver and monitor that needs it reads it in build_phase.
Where should I set the virtual interface?
Set it in the top module before run_test, because the interface is static and known at time zero. Use a path such as uvm_test_top.env.agt* so it reaches every agent.
Where should I get the virtual interface?
Get it in the build_phase of the driver and monitor. Children build after their parents, so the value set at the top is already available when the component builds.
What happens if the get fails?
get returns 0 when no matching handle is found. Guard it with an if and raise uvm_fatal so the test stops early with a clear message instead of hitting a null handle during the run.
Can one set reach several agents?
Yes. A wildcard path like env.agt* on the set call delivers the same handle to every matching agent, so you do not need a separate set for each one.




Hi Hardik
with the picturization one can get clear idea.. Very nice explanation
Thank you Sattya π
Thank-you very much Hardik. It really helped.
Thank you Bhushan π Please share it with your friends or colleagues so it will help to more engineers π
Hi,
I think that instead of:
1. passing the virtual interface to the config object
2. passing the config object to config_db
one could do it in a single step: pass the virtual interface using config_db
Isn’t it better this way?
Hi Zeev,
Yeah, I do agree with you that we can do with a single step as well but in that example or for reusability purposes we usually keep all the configurations in a separate class or file so whenever we need it we can reuse it anywhere in the UVM environment. I hope you got the point.
Thank you
I need to understand config db. Still not clear please share any video link