UVM: How to Pass a Virtual Interface from Testbentch to Environment

UVM: How to Pass a Virtual Interface from Testbentch to Environment

Posted by

Connecting a verification environment with a DUT is not a straight forward task for someone new to UVM and the purpose of this post is to give a quick tutorial on how to do it fast and correctly.

Let’s assume that we have a verification environment with two APB agents like in the picture below.


Verification environment with two APB agents
I’ll describe two methods:

  • a quick way which can be used in most of the cases
  • an advanced way suitable for scenarios in which you have two or more interfaces of the same type



The Quick Way

Step #1: declare the interfaces in the testbench

An interface is instantiated just as any other verilog module

1
2
3
4
5
//instance of the APB interface used by APB agent agent0
cfs_apb_if intf0(.pclk(clk));

//instance of the APB interface used by APB agent agent1
cfs_apb_if intf1(.pclk(clk));

Step #2: put the interfaces in the database

UVM comes with a database which you can use to save some information for future use. In our case, we can use it from the testbench to save the virtual interfaces and use them when the two APB agents are created.

1
2
3
4
5
6
7
initial begin
   //put in the database the interface used by APB agent agent0
   uvm_config_db#(virtual cfs_apb_if)::set(null, "uvm_test_top.env.agent0*", "VIRTUAL_INTERFACE", intf0);

   //put in the database the interface used by APB agent agent1
   uvm_config_db#(virtual cfs_apb_if)::set(null, "uvm_test_top.env.agent1*", "VIRTUAL_INTERFACE", intf1);
end

There are a few things you need to pay attention to here:

  • the parameter of uvm_config_db is a virtual interface (virtual cfs_apb_if), NOT the interface (cfs_apb_if)
  • the second argument of the set() function is the full name of the agent which must get the interface.
    This full name is build by concatenating the names of components not the names of the “physical” SystemVerilog variables:

    1
    apb_agent_0 = cfs_apb_agent::type_id::create("agent0", this);

    In this example “apb_agent0” is the name of the “physical” SystemVerilog variable while “agent0” is the name of the component.

  • The third argument of the set() function is like a “key” which the agents will use to get from the database the virtual interface. The same name must be used on Step#3

Step #3: get the interfaces from the database

The hardest part was already done by the set() function. All we have to do in the agent class is to get the virtual interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class cfs_apb_agent extends uvm_component;
   
   //pointer to the interface
   virtual cfs_apb_if vif;
   
   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
     
      if(uvm_config_db::#(virtual cfs_apb_if)::get(this, "", "VIRTUAL_INTERFACE", vif) == 0) begin
         `uvm_fatal("ALGORITHM_ISSUE", "Could not get from the database the virtual interface for the APB agent")
      end
   endfunction

endclass

The most important thing here is the third argument of the get() function – it must be the same key used by the set() function in Step#2.

That’s it!

The “Be Prepared For Future Changes” Way

The steps above work fine in most of the cases. But what if our design will change from two APB interfaces to four, or ten, or one hundred? Copy-Paste won’t do.
Let’s see how we can change the code to work with a generic number of interfaces.

Step #1: put in the database the number of APB interfaces

Ideally we should change only in one place the number of interfaces used by the DUT. One option is to have a define in the testbench which we can pass to the environment via the database.

1
2
3
4
5
6
//define the number of APB interfaces
`define NUM_OF_APB_INTF 4

initial begin
   uvm_config_db#(integer)::set(null, "uvm_test_top.env*", "NUM_OF_APB_INTF", `NUM_OF_APB_INTF);
end

Step #2: use “generate” block to declare interfaces and put them in the database

1
2
3
4
5
6
7
8
9
10
11
12
generate
   for(genvar idx = 0; idx < `NUM_OF_APB_INTF; idx++) begin
      //instance of the APB interface used by APB agent with index idx
      cfs_apb_if intf(.pclk(clk));

      initial begin
         //put in the database the interface used by APB agent with index idx
         uvm_config_db#(virtual cfs_apb_if)::set(null,
            $sformatf("uvm_test_top.env.agent[%d]*", idx), "VIRTUAL_INTERFACE", intf);
      end
   end
endgenerate

You will probably need to do some extra work here for connecting each interface with the DUT, but in most of the cases you will probably get away with some assings based on idx.

Step #3: instantiate the APB agents based on the number of interfaces

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class cfs_env extends uvm_component;

   cfs_apb_agent agents[$];

   virtual function void build_phase(uvm_phase phase);
      integer num_of_agents;
     
      if(uvm_config_db::#(integer)::get(this, "", "NUM_OF_APB_INTF", num_of_agents) == 0) begin
         `uvm_fatal("ALGORITHM_ISSUE", "Could not get from the database the number of APB interfaces")
      end
      else begin
         for(int i = 0; i < num_of_agents; i++) begin
            agents.push_back(cfs_apb_agent::type_id::create($sformatf("agent[%d]", i), this));
         end
      end
   endfunction

The are some things to pay attention to here:

  • The key used to get the number of agents (e.g. “NUM_OF_APB_INTF”) must be the same as used at Step#1
  • The name of the agent used in create() function must be the same as used in Step#2

Step #4: get the interfaces from the database

This step is identical with Step#3 from “The Quick Way”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class cfs_apb_agent extends uvm_component;
   
   //pointer to the interface
   virtual cfs_apb_if vif;
   
   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
     
      if(uvm_config_db::#(virtual cfs_apb_if)::get(this, "", "VIRTUAL_INTERFACE", vif) == 0) begin
         `uvm_fatal("ALGORITHM_ISSUE", "Could not get from the database the virtual interface for the APB agent")
      end
   endfunction

endclass

Hope you found this information useful 🙂

Cristian Slav

7 Comments

  • mseyunni@gmail.com' Madhu Eyunni says:

    Good article. I am not sure I understood what you meant by “You will probably need to do some extra work here for connecting each interface with the DUT, but in most of the cases you will probably get away with some assings based on idx.”

    I would be happy to know more details about the second approach. Do you have working example?

    • Hi Madhu,

      At “Step 2”, in a “generate” block there is a declaration of an interface which is put in the database to be used by some agent in the verification environment.

      However, this would be useless if that interface is not connected to the DUT that you want to verify. This is why I said:

      You will probably need to do some extra work here for connecting each interface with the DUT

      Now, let’s say that our DUT has two inputs: PADDR0 and PADDR1.
      One way of connecting the PADDR signals from the interfaces to the two signals from the DUT would be like this:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      generate
         for(genvar idx = 0; idx < `NUM_OF_APB_INTF; idx++) begin
            //instance of the APB interface used by APB agent with index idx
            cfs_apb_if intf(.pclk(clk));
           
            if(idx == 0) begin
                assign dut.PADDR0 = intf.PADDR;
            end
            else if(idx == 1) begin
               assign dut.PADDR1 = intf.PADDR;
            end
         end
      endgenerate
  • mseyunni@gmail.com' Madhu says:

    Hi,
    Another question. In the quickest way, you declared the instances of two interfaces as intf0, intf1. While in genvar block you have declared without the idx. How does this work. If you don’t intend to do something like (“intf%0d”,i), why putting inside the genvar block. Alternatively, in the quickest way, you could have instanced the interface once.

    Thanks,

    • Hi Madhu,

      in the “generate” block there is a “for” loop. For each iteration of the loop I am creating a different instance of the interface. It is very important to understand here that each interface instance is unique.
      I am passing each of these interfaces to the correct agent using uvm_config_db::set() – notice that there I am making use of the loop index.

      Hope this clarify things for you!
      Cristi

  • ramesh1madatha@gmail.com' Ramesh says:

    Thank you for the post, very helpfull

  • abdelaali.khardazi@gmail.com' Abdelaali khardazi says:

    Fruitful article,
    I have build an agent for a protocol, and now i’m construncting an uvm tb to verify a dut that has more than 3 interfaces for the same protocol. I want to create this agent more than twice without changing in agent code and only by parametrizing agent with viftrual interface.
    Is there a way to do so ?

  • abdelaali.khardazi@gmail.com' Abdelaali khardazi says:

    Really helpful article, I did as you mentioned up above, but in my agents classes ( driver, sequencer,..) i get the run phase error ; unintialsed virtual iterface. Does this mean that i must get virtual interface also in these classes cuz you got it only in the agnet level ?

Leave a Reply

Your email address will not be published.