Virtual Methods in System Verilog

The virtual keyword in SystemVerilog is used to declare methods and interfaces that support polymorphism. It allows a derived class to override methods from a base class, enabling dynamic binding at runtime. Without the virtual keyword, method binding is static, determined at compile time.

Where is the virtual Keyword Used?

The virtual keyword is primarily used in two contexts:

  1. Virtual Methods: To declare methods in a base class that can be overridden by derived classes.
  2. Virtual Interfaces: To define interface variables that can be dynamically connected.

Let’s explore these applications in detail.

1. Virtual Methods

A method marked with virtual allows derived classes to override its behavior, enabling runtime polymorphism. This is particularly useful in verification environments, where you might want different behaviors depending on the object type being used.

Example: Virtual Methods

class BaseClass;
  virtual function void display();
    $display("This is BaseClass.");
  endfunction
endclass

class DerivedClass extends BaseClass;
  function void display();
    $display("This is DerivedClass.");
  endfunction
endclass

module test;
  initial begin
    BaseClass base;
    DerivedClass derived = new();
    
    // Polymorphism
    base = derived; // Assign derived object to base class reference
    base.display(); // Calls DerivedClass::display due to virtual
  end
endmodule

Output:

This is DerivedClass.

Explanation:

  • The display() method in BaseClass is declared as virtual.
  • When the base reference points to a DerivedClass object, the overridden display() method in DerivedClass is invoked, demonstrating polymorphism.

In the above code, if we remove the virtual keyword then the output will be:

This is BaseClass.

2. Virtual Interfaces

Virtual interfaces are used to connect modules or environments dynamically. They enable passing interface handles to different modules, avoiding hard-coded connections and improving flexibility.

Example: Virtual Interfaces

interface my_interface;
  logic signal;
endinterface

class Driver;
  virtual my_interface vif; // Virtual interface declaration

  function new(virtual my_interface vif);
    this.vif = vif;
  endfunction

  task drive();
    vif.signal = 1'b1; // Drive the interface signal
    $display("Signal driven: %b", vif.signal);
  endtask
endclass

module test;
  my_interface intf(); // Instantiate the interface
  Driver drv;

  initial begin
    drv = new(intf); // Pass the interface to the driver
    drv.drive();
  end
endmodule

Output:

Signal driven: 1

Explanation:

  • The my_interface is declared as a virtual interface in the Driver class.
  • The intf instance is dynamically passed to the Driver during its construction, allowing the driver to manipulate the interface signals.

Common Use Cases

  • UVM (Universal Verification Methodology): The virtual keyword is integral in UVM, where virtual methods are used for factory overrides and callbacks, and virtual interfaces connect testbenches to DUTs.
  • Reusable Test Environments: Virtual interfaces allow creation of generic drivers and monitors that can work with different designs by connecting dynamically at runtime.

Key Points to Remember

  • Declare a method as virtual in the base class to allow it to be overridden in derived classes.
  • Use virtual interfaces to enable dynamic binding of interface connections.
  • Without the virtual keyword, methods and interfaces are statically bound, limiting flexibility.