Part 11 · Senior Prep · Intermediate

Virtual Interface Interview Q&A

Model answers on virtual interface propagation from HDL top through config_db to driver, timing, and multi-interface scenarios.

Virtual interface propagation

Virtual interface questions test the HDL-to-UVM bridge — every senior candidate should draw top module → config_db → driver get without hesitation.

Q: Why do we need virtual interfaces in UVM?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Why virtual interfaces?

A:
  MECHANISM:  SystemVerilog classes cannot contain static module interfaces; virtual
              interface is a handle to hierarchical interface instance in module world.
  MOTIVATION:  UVM components are classes — they need a bridge to pin-level signals
              defined in HDL modules without direct hierarchical reference.
  WHEN:       Every pin-wiggling driver/monitor needs virtual interface handle from
              config_db set in HDL top or test wrapper module.
  PITFALL:    Passing interface directly as port to uvm_component — illegal in SV;
              must use virtual interface indirection.
  EXAMPLE:    top module instantiates axi_if; run_test sets config_db in initial block; axi_driver
              gets virtual axi_if handle and drives vif.awvalid.

Q: Where should virtual interface be set?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Where to set vif?

A:
  MECHANISM:  HDL top module (or uvm_initiator module) calls uvm_config_db#(virtual if_type)::set
              before run_test() — typically in initial block or module constructor context.
  MOTIVATION:  Interface instance lives in module hierarchy — only module scope has
              direct handle; must push into config_db before UVM build_phase consumes it.
  WHEN:       Set in top initial block before run_test. Alternative: test build_phase
              if test module wraps interface (less common).
  PITFALL:    Set in agent build_phase — circular: agent needs vif to build driver but
              agent is the one setting it; driver get in same phase fails.
  EXAMPLE:    tb_top: initial begin uvm_config_db#(virtual axi_if)::set(null,"*","vif", axi_if_inst);
              run_test("smoke_test"); end

Q: Where should driver get virtual interface?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Where driver gets vif?

A:
  MECHANISM:  uvm_config_db#(virtual if_type)::get(this, "", "vif", vif) in driver
              or agent build_phase — before run_phase pin activity.
  MOTIVATION:  Driver must hold vif before run_phase loop — get in build guarantees
              handle available when run_phase starts.
  WHEN:       Agent build gets vif and assigns to driver.vif, or driver build gets
              directly. Fatal if get fails — silent null causes obscure hang.
  PITFALL:    Get in run_phase first iteration — works but delays error if vif missing,
              wastes debug time in phase trace before obvious null check.
  EXAMPLE:    agent.build: get vif, assign drv.vif = vif. driver.run_phase: drive on
              vif.awvalid — vif guaranteed non-null from build.

Q: Multiple interfaces — how do you avoid vif collision?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Multiple interfaces without collision?

A:
  MECHANISM:  Use distinct field names ("tx_vif", "rx_vif") or scoped inst_path
              ("env.agt_tx", "env.agt_rx") instead of shared "vif" wildcard.
  MOTIVATION:  Wildcard set of single "vif" gives all agents same handle — TX agent
              drives RX bus in multi-interface TB.
  WHEN:       Explicit per-agent set: set(null, "env.agt_tx", "vif", tx_if) and
              set(null, "env.agt_rx", "vif", rx_if).
  PITFALL:    set(null,"*","vif", tx_if) — both agents get tx_if, rx monitor samples
              wrong bus, scoreboard mismatch looks like DUT bug.
  EXAMPLE:    Dual AXI port DUT: agt_master and agt_slave each get distinct vif via
              scoped inst_path — scoreboard matches correct bus pair.

Key takeaways

  • Virtual interface bridges module interfaces to class components.

  • Set vif from HDL top before run_test; get in build_phase.

  • Distinct field names or scoped paths for multi-interface TBs.

Common pitfalls

  • Wildcard vif set with multiple interfaces — silent bus swap.

  • Get in run_phase instead of build — delayed fatal on missing vif.


Virtual interface edge cases

Q: typedef wrapper on virtual interface — why does it matter?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: typedef and virtual interface type match?

A:
  MECHANISM:  config_db parameterized type must match exactly between set and get —
              typedef axi_vif_t = virtual axi_if differs from virtual axi_if if
              different typedef names used inconsistently.
  MOTIVATION:  SV type equivalence is nominal for typedefs in some contexts — mismatch
              causes get failure even when underlying interface identical.
  WHEN:       Define one typedef in shared package; use same typedef in set, get,
              and driver member declaration.
  PITFALL:    Top sets virtual axi_if; driver gets using typedef alias from VIP package
              — compile OK if wrong cast, get fails at runtime.
  EXAMPLE:    Common package: typedef virtual axi_if axi_vif_t; all set/get use
              uvm_config_db#(axi_vif_t) — consistent match guaranteed.

Q: How do you pass clock/reset handles alongside data interface?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: Clock/reset with data vif?

A:
  MECHANISM:  Separate config_db entries: "vif" for bus, "clk_vif" or clocking block
              handle for timing. Or embed clocking modport in same interface.
  MOTIVATION:  Driver needs both signal access and clocking source — single vif with
              clocking modport is cleanest pattern.
  WHEN:       Prefer interface with clocking block for driver clock sync. Separate
              config only when clock is shared IP module interface.
  PITFALL:    Driver uses `posedge vif.clk without checking clk in same interface as
              data — race if clk from different hierarchy path.
  EXAMPLE:    axi_if contains clocking cb @(posedge aclk); driver uses vif.cb.awvalid
              for synchronous drive — one vif handle covers data + clock.
systemverilog
// vif propagation — interview must-know
module tb_top;
  axi_if axi_if_inst(.*);
  initial begin
    uvm_config_db#(virtual axi_if)::set(null, "env.axi_agt*", "vif", axi_if_inst);
    run_test();
  end
endmodule

// agent build
if (!uvm_config_db#(virtual axi_if)::get(this, "", "vif", vif))
  `uvm_fatal("VIF", "no vif")

Q: Virtual interface in block vs chip testbench?

diagram
[INT][SENIOR][UVM] MODEL ANSWER

Q: vif at block vs chip?

A:
  MECHANISM:  Block TB: one top sets vif for one DUT instance. Chip TB: wrapper module
              sets array of vif or per-agent scoped set from generate loop.
  MOTIVATION:  Chip integrates block VIPs unchanged — only top wiring and config_db
              scope change, not agent internals.
  WHEN:       Block: set(null, "env.agt*", "vif", vif). Chip: generate N interfaces,
              set(null, $sformatf("env.agt[%0d]", i), "vif", vif_arr[i]).
  PITFALL:    Chip top uses block-level wildcard set — all agents get interface [0].
  EXAMPLE:    PCIe chip: 4 lanes, 4 vif handles, 4 scoped config_db sets — block VIP
              agent code unchanged from single-lane block TB.

Key takeaways

  • Typedef consistency between set/get/driver member is mandatory.

  • Clocking block in interface reduces separate clk config entries.

  • Chip TB changes only top wiring scope — not agent get pattern.

Common pitfalls

  • Typedef mismatch between set and get — runtime get failure.

  • Chip-level wildcard vif giving all agents same interface index.