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.
- 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:1apb_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 🙂
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:
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:
2
3
4
5
6
7
8
9
10
11
12
13
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
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
Thank you for the post, very helpfull
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 ?
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 ?