Part 3 · Factory & Configuration · Intermediate

Static-to-UVM Bridge: Why Virtual Interfaces Exist

The HDL/UVM boundary problem, virtual interface semantics, interface vs virtual interface, and why config_db is the transport layer.

The boundary problem

SystemVerilog interface constructs exist in the static elaboration hierarchy — modules, interfaces, and wires. UVM class objects are created dynamically at simulation time inside build_phase. A class cannot contain an interface instance as a member — the language forbids it.

diagram
[HDL] elaboration-time                [UVM] run-time
  ───────────────────────                ────────────────
  interface apb_if { signals }           class driver {
  module top;                               // ILLEGAL:
    apb_if bus();                          apb_if vif;  
  endmodule                                virtual apb_if vif;  
                                           }

Virtual interface semantics

A virtual interface is a handle — a pointer to an interface instance that already exists in the HDL hierarchy. It carries no signals itself; dereferencing it accesses the real interface instance's signals.

systemverilog
interface apb_if(input logic pclk);
  logic [31:0] paddr, pwdata, prdata;
  logic        pwrite, psel, penable, pready;
  modport drv_mp (clocking cb @(posedge pclk));
  modport mon_mp (clocking cb @(posedge pclk));
endinterface

class apb_driver extends uvm_driver #(apb_item);
  virtual apb_if vif;  // handle, not instance

  task run_phase(uvm_phase phase);
    @(vif.pclk);
    vif.psel <= 1'b1;
    vif.paddr <= req.addr;
  endtask
endclass
diagram
[HDL][CONFIG][UVM] handle lifecycle

  elaboration:  apb_if apb_if_i exists in top module
  top initial:  config_db stores virtual handle to apb_if_i
  build_phase:  driver.vif = handle from config_db
  run_phase:    driver drives vif.psel (same signals as apb_if_i)

Why config_db is the bridge

There is no direct path from a UVM class constructor to an HDL interface instance. The top module's initial block is the only place that can see both the interface instance and the UVM startup. config_db stores the handle in a globally accessible typed pool keyed by path.

  • top.sv initial block has access to interface instances.

  • config_db.set(null, ...) works without a UVM component context.

  • UVM components get() the handle in build_phase — after UVM hierarchy exists.

  • Field name "vif" is convention — any string works if set/get agree.

systemverilog
module top;
  logic clk;
  apb_if apb_if_i(.pclk(clk));

  initial begin
    // [HDL] bridge point — only place with both if instance and UVM access
    uvm_config_db#(virtual apb_if)::set(
      null, "uvm_test_top.env.apb.*", "vif", apb_if_i);
    run_test();
  end
endmodule

Key takeaways

  • Virtual interface = handle to static interface instance, not a copy.

  • Classes use virtual if; modules instantiate real interface.

  • config_db transports the handle from top initial into UVM build_phase.

Common pitfalls

  • Trying to new() an interface inside a class — compile error.

  • Passing interface through module ports into UVM — wrong layer.

  • Assuming virtual if copies signals — it aliases the same instance.