How Virtual Interface can be pass using uvm_config_db in the UVM Environment?

How to connect the DUT to the UVM Testbench??

In our traditional directed Testbench environments, all the components are β€œstatic” in nature & information (data/control) is also exchanged in the form of signals/wire/nets at all levels in the DUT as well as in TestBench.

But this is not the case in the latest β€œConstrained Random Verification Methodology” like UVM where DUT is static (module-based) in nature yet Testbench is class (SystemVerilog OOPs) based. Hence the communication between the DUT and TestBench can not be like the one in traditional Testbenches i.e. port-based connection of the DUT ports to the TestBench ports.

A standard classification of a UVM environment is shown in the figure below:

A Standard Classification of an UVM Environment

In UVM, for this, we utilize the newly introduced SystemVerilog feature called β€œVirtual Interface”. An β€œInterface” is a collection of common signals between two entities & the signal direction is governed by the β€œmodports”. 

We can see the virtual interface as a handle pointing to the interface instance. The virtual interface acts as a medium to connect the DUT (SystemVerilog/Verilog module-based) and the Testbench (SystemVerilog OOPs class-based). Testbench access the DUT signals via virtual interface and vice versa.

How it all happens, let’s understand using a diagram given below:

As we can see in the diagram that the virtual interface information is provided to the Testbench (Test Class) from the DUT (light pink color coding) which acts as a pointer to the interface instance being used by the DUT. In an actual UVM environment, the test class receives this virtual interface information from the DUT and later the test class propagates this virtual interface (interface instance handle) information to the physical component e.g. Driver, Monitor, Scoreboard, etc, via a configuration object (dark pink color coding), which in the real sense needs this information to communicate with the DUT. The whole of this process happens by utilizing the β€œuvm_config_db” API.

In particular, passing the virtual interface from the test class to the hierarchy below utilizes one more UVM concept i.e. β€œConfiguration Object”. I will try to write in more detail about it in one of the next upcoming posts.

Placing a Virtual Interface into Configuration Database using uvm_config_db API:

The limitation of OVM was that only integral values, strings & objects (driven from ovm_objects) are allowed to kept inside Configuration Database. Due to these limitations, the virtual interface needs to be wrapped into an object before using the config_db APIs.

But this limitation has been removed in UVM and now users can place Virtual Interface, User Defined Types, etc directly into the Configuration Database. We can utilize this feature directly and use β€œset()” and β€œget()” call to place and retrieve the Virtual Interface to/from Configuration Database.

module top;

 import uvm_pkg::*;

 `include "uvm_macros.svh"

 //// Interface Declaration

 interface dut_if();

 logic data, clock, reset;

 endinterface: dut_if

 //// Interface Instantiation

 dut_if dut_if_inst;

 //// DUT Instantiation

 DUT DUT_inst (.....

 .....

 .....

 );

 //// Initial Block

 initial

  begin     

    uvm_config_db #(virtual dut_if)::set(null,"*", "dut_vi", dut_if_inst);

  run_test();

  end

endmodule: top

Making the Virtual Interface Available in the Testbench:

A configuration object is created inside the test class & this configuration object contains a virtual interface property. Inside the test class, another uvm_config_db method i.e. uvm_config_db::get() is used to fetch the value of the virtual interface and assign it to the configuration object property. This process is shown in the code below:

class my_test extends uvm_test;

 `uvm_component_utils(my_test)

 //// Configuration Handle 

 my_config my_config_h;

 ......

 ......

 ......

 //// Build Function

 function void build_phase (uvm_phase phase);

  super.build_phase(phase);

  my_config_h = new();   

        if (!uvm_config_db #(virtual dut_if)::get(this, "", "dut_vi", my_config_h.v_dut_if))            

        `uvm_fatal("FATAL MSG", "Virtual Interface Not Set Properly");

  //// Propagating my_config object down to the hierarchy

         uvm_config_db #(my_config)::set(this, "*", "config", my_config_h);

 endfunction: build_phase

endclass: my_test

Assigning Virtual Interface Property into Transactor:

Now in this stage, the physical component i.e. the transactor e.g. Driver, Monitor & Scoreboard, etc., which in real sense access the DUT assigns the virtual interface property from the received configuration object from the top i.e. uvm_test (as described above). Let’s see the code provided below to make it clear:

class my_driver extends uvm_driver #(my_txn);
  `uvm_component_utils(my_driver)

 ...

 //// Virtual Interface Declaration
   virtual dut_if dut_vi;

 //// Configuration Handle
  my_config my_config_h;

 //// Build Function
  function void build_phase (uvm_phase phase);
   super.build_phase(phase);
    if(!uvm_config_db #(my_config)::get(this, "", "config", my_config_h))
              `uvm_fatal("FATAL MSG", "Configuration Object Not Received Properly");

   ....
  endfunction: build_phase

 //// Connect Function
  function void connect_phase (uvm_phase phase);
   super.connect_phase(phase);
   dut_vi = my_config_h.v_dut_if;
  endfunction: connect_phase

 //// Run Task
  task run_phase (uvm_phase phase);

  ...
  ...
  ...
  endtask: run_phase

endclass: my_driver

So this way, we can use Virtual Interface and uvm_config_db APIs to set up an effective communication between DUT and UVM Testbench which provides modularity, high re-usability & better control from the top (uvm_test_top).

I hope you enjoyed the learning. Please, feel free to put your comments and/or suggestions for future topics. This blog post is amazingly explained by one of the senior-most engineer from Mentor Graphics Manish. Till then see ya take care and Keep on learning Keep on Growing πŸ™‚

Similar Posts

7 Comments

  1. Hi,
    I think that instead of:
    1. passing the virtual interface to the config object
    2. passing the config object to config_db
    one could do it in a single step: pass the virtual interface using config_db
    Isn’t it better this way?

    1. Hi Zeev,

      Yeah, I do agree with you that we can do with a single step as well but in that example or for reusability purposes we usually keep all the configurations in a separate class or file so whenever we need it we can reuse it anywhere in the UVM environment. I hope you got the point.

      Thank you

Comments are closed.