Part 3 · Factory & Configuration · Intermediate

Single-Instance vif Pattern: Top → Env → Agent → Driver

The canonical four-step pattern for one interface instance — define, instantiate, publish from top, forward through agent, consume in driver/monitor.

Four-step canonical flow

  1. Define interface with signals and modports.

  2. Instantiate interface in top, connect to DUT.

  3. Publish vif from top initial before run_test().

  4. Agent gets vif, forwards to children; driver/monitor get in build_phase.

diagram
[HDL][CONFIG][UVM] single-instance flow

  Step 1-2 [HDL]:
    apb_if apb_if_i in top  dut.apb(apb_if_i)

  Step 3 [HDLCONFIG]:
    set(null, "uvm_test_top.env.apb.*", "vif", apb_if_i)

  Step 4 [UVM]:
    agent: get("", "vif")  set("*", "vif", vif)
    drv:   get("", "vif")  use in run_phase

Step 1 — interface and top module

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

module top;
  logic clk, rst_n;
  apb_if apb_if_i(.pclk(clk));
  dut u_dut(.apb(apb_if_i));

  initial begin
    clk = 0; forever #5 clk = ~clk;
  end

  initial begin
    uvm_config_db#(virtual apb_if)::set(
      null, "uvm_test_top.env.*", "vif", apb_if_i);
    run_test();
  end
endmodule

Step 2 — agent forwards, driver consumes

systemverilog
class apb_agent extends uvm_agent;
  virtual apb_if vif;
  apb_driver drv;
  apb_monitor mon;

  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", "apb_if not set for agent")
    uvm_config_db#(virtual apb_if)::set(this, "*", "vif", vif);
    drv = apb_driver::type_id::create("drv", this);
    mon = apb_monitor::type_id::create("mon", this);
  endfunction
endclass

class apb_driver extends uvm_driver #(apb_item);
  virtual apb_if vif;
  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", "virtual apb_if not set for driver")
  endfunction
  task run_phase(uvm_phase phase);
    @(vif.pclk);
    vif.psel <= 1'b1;
  endtask
endclass
diagram
[CONFIG] agent fan-out pattern

  top sets on env.* or env.apb.*
  agent gets once:  get(this, "", "vif", vif)
  agent forwards:   set(this, "*", "vif", vif)
  children get:     get(this, "", "vif", vif)

  agent is the vif distribution point for its subtree

Key takeaways

  • Top publishes once before run_test(); agent fans out to children.

  • Driver and monitor get vif in build_phase — fatal if missing.

  • Use consistent field name "vif" across the agent subtree.

Common pitfalls

  • Creating driver before forwarding vif — child get fails.

  • Top sets on env.drv directly — bypasses agent, breaks reuse.

  • Using run_phase for first vif access — no early failure.