Virtual Interfaces in SystemVerilog

Virtual interfaces are the bridge between class-based testbenches (like UVM) and the RTL world. Classes can't directly connect to interfaces, but they can hold a "handle" to an interface through virtual interfaces.

The Problem: Classes Can't Connect to Interfaces

Classes are dynamic - they're created at runtime with new(). Interfaces are static - they're instantiated at compile time like modules. Classes can't have interface ports because interfaces aren't available when classes are compiled.

Think of it like sending letters. The post office (interface) exists at a physical location. You (class) need the address (virtual interface) to reach the post office. The address is a reference, not the building itself.
This Won't Work!
// ERROR - Classes can't have interface ports!
class Driver;
    bus_if bus;  // ERROR! Can't instantiate interface in class
    
    function new(bus_if b);  // ERROR! Can't pass interface
        bus = b;
    endfunction
endclass

Virtual Interface: The Solution

A virtual interface is a variable that holds a handle to an actual interface instance. It's like a pointer to the interface.

Virtual Interface Declaration
// This works!
class Driver;
    virtual bus_if vif;  // Virtual interface handle
    
    function new(virtual bus_if vif);
        this.vif = vif;  // Store the handle
    endfunction
    
    task drive(Transaction tr);
        @(posedge vif.clk);
        vif.addr  <= tr.addr;
        vif.data  <= tr.data;
        vif.valid <= 1;
        @(posedge vif.clk);
        while (!vif.ready) @(posedge vif.clk);
        vif.valid <= 0;
    endtask
endclass

Key Insight

The keyword virtual before the interface name creates a reference type. You can then assign actual interface instances to it.

Connecting Virtual Interfaces

The connection between the actual interface and the virtual interface happens in the top-level testbench, usually in an initial block.

Complete Example
// Interface definition
interface bus_if (input logic clk);
    logic [31:0] addr;
    logic [31:0] data;
    logic        valid;
    logic        ready;
endinterface

// Driver class with virtual interface
class Driver;
    virtual bus_if vif;
    
    function new(virtual bus_if vif);
        this.vif = vif;
    endfunction
    
    task run();
        forever begin
            @(posedge vif.clk);
            // Drive signals through virtual interface
            vif.addr  <= $urandom;
            vif.valid <= 1;
            @(posedge vif.clk);
            vif.valid <= 0;
        end
    endtask
endclass

// Top-level testbench
module tb_top;
    logic clk = 0;
    always #5 clk = ~clk;
    
    // Instantiate actual interface
    bus_if bus(clk);
    
    // Connect to DUT
    dut u_dut (
        .clk   (bus.clk),
        .addr  (bus.addr),
        .data  (bus.data),
        .valid (bus.valid),
        .ready (bus.ready)
    );
    
    // Create driver and pass virtual interface
    initial begin
        Driver drv;
        drv = new(bus);  // Pass interface to class
        fork
            drv.run();   // Driver now controls the interface
        join_none
    end
endmodule

The Magic Line

drv = new(bus); - This is where the actual interface bus is passed to the class. The driver can now access the real signals through vif.

Virtual Interfaces in UVM

In UVM, virtual interfaces are usually passed through the configuration database. This allows components to get the interface without direct constructor parameters.

UVM Virtual Interface Pattern
// In top-level test
class my_test extends uvm_test;
    // ...
    
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        // Get interface from top and store in config db
        if (!uvm_config_db#(virtual bus_if)::get(null, "", "vif", vif))
            `uvm_fatal("NO_VIF", "Virtual interface not found")
        
        // Make it available to all components
        uvm_config_db#(virtual bus_if)::set(this, "*", "vif", vif);
    endfunction
endclass

// In driver
class my_driver extends uvm_driver#(my_transaction);
    virtual bus_if vif;
    
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        // Get virtual interface from config db
        if (!uvm_config_db#(virtual bus_if)::get(this, "", "vif", vif))
            `uvm_fatal("NO_VIF", "Virtual interface not found")
    endfunction
    
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            // Drive through virtual interface
            @(posedge vif.clk);
            vif.addr <= req.addr;
            vif.data <= req.data;
            seq_item_port.item_done();
        end
    endtask
endclass

// In top module
module tb_top;
    bus_if bus(clk);
    
    initial begin
        // Store interface in config db
        uvm_config_db#(virtual bus_if)::set(null, "*", "vif", bus);
        run_test("my_test");
    end
endmodule

Common Mistakes

1. Forgetting to Assign the Virtual Interface

Null Virtual Interface
class Driver;
    virtual bus_if vif;  // Declared but not assigned!
    
    task run();
        @(posedge vif.clk);  // ERROR at runtime! vif is null
    endtask
endclass

2. Using @ with Expressions

Clocking Block Recommended
// Works, but consider clocking blocks for cleaner code
@(posedge vif.clk);
while (!vif.ready) @(posedge vif.clk);

// Better - use clocking block
@(vif.driver_cb);
while (!vif.driver_cb.ready) @(vif.driver_cb);

Always Check for Null!

In important code, check if the virtual interface was assigned before using it. A null access will cause simulation to crash.

Quick Summary

  • Virtual interface = handle to an actual interface
  • Use virtual keyword before interface type
  • Required for class-based (UVM) testbenches
  • Passed from top-level to classes via constructor or config_db
  • Access signals using vif.signal_name
  • Always ensure virtual interface is assigned before use

You've Completed SystemVerilog Basics!

Congratulations! You now understand the key SystemVerilog concepts for verification. Continue your learning journey: