Virtual Interface declaration

Hi all,
I am confused about how to properly instantiate a virtual interface within a module. I have a simple interface as follows:

`ifndef ADDSUB_INTERFACE__SV
`define ADDSUB_INTERFACE__SV
interface addsub_if();
	logic clk;
	logic addsub, cin, cout;
	logic [7:0] a, b, sum;
endinterface: addsub_if
`endif

Then I need to be able to access this interface from both the top-level test class as well as the _env class. Note that I am following the UVM basics series of videos in this attempt to implement my own. If this thread is better located in the UVM thread just let me know and I’ll head over there, but I believe my confusion to be a matter of SV convention.

In the videos, the virtual interface is instantiated in the my_env class, and it is not referenced at all within the my_test class, even though it is required to add it to the uvm database via:

uvm_config_db #(virtual addsub_if)::set…etc.

When I attempted to compile this way (listing addsub_env prior to addsub_test in my package), I get the following errors:

Error-[IND] Identifier not declared
8bitaddsub_tb.sv, 18
Identifier ‘addsub_vi’ has not been declared yet. If this error is not
expected, please check if you have set `default_nettype to none.

Error-[IND] Identifier not declared
8bitaddsub_tb.sv, 22
Identifier ‘addsub_vi’ has not been declared yet. If this error is not
expected, please check if you have set `default_nettype to none.

I can get my code to compile by adding another declaration within the test class, but this can’t be the correct way to go about solving the issue.

Now after that long (hopefully not confusing) explanation, my question boils down to: how do I properly instantiate a virtual interface and share it between all classes? I was hoping to avoid global variables here as well. If there is not enough information here to answer my question, please let me know and I can dig deeper. Below are my env and test classes for reference:

class my_test extends uvm_test;
	`uvm_component_utils(my_test)
	addsub_env addsub_env_h;
	virtual addsub_if addsub_vi;
	function new(string name, uvm_component parent);
		super.new(name, parent);
	endfunction: new
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		if(!uvm_resource_db#(virtual addsub_if)::read_by_name("addsub_ifs", "addsub_vi", addsub_vi))
		begin
			//print("\nERROR: VIRTUAL DB INTERFACE ERROR\n\n");
		end
		uvm_config_db #(virtual addsub_if)::set(this, "*", "addsub_vi", addsub_vi);
		addsub_env_h = addsub_env::type_id::create("addsub_env_h", this);
	endfunction: build_phase
endclass: my_test

class addsub_env extends uvm_env;
	`uvm_component_utils(addsub_env)
	virtual addsub_if addsub_vi;
	function new (string name, uvm_component parent);
		super.new(name, parent);
	endfunction: new
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		assert(uvm_config_db #(virtual addsub_if)::get(this, "*", "addsub_vi", addsub_vi));
	endfunction: build_phase
	task run_phase(uvm_phase phase);
		phase.raise_objection(this);
		addsub_vi.a = 1;
		addsub_vi.b = 1;
		#10;
		phase.drop_objection(this);
	endtask: run_phase
endclass: addsub_env

Any guidance would be greatly appreciated. I hope that all makes sense.

Somewhere you have to instantiate a “real” interface, usually in the top level verilog module from where you start the UVM testbench. You then have to put a virtual interface handle into the uvm_config_db so that your classes can access the interface.

module my_tb;

add_sub_if ADD_SUB();

initial begin
uvm_config_db #(virtual add_sub_if)::set(null, “*”, “ADD_SUB”, ADD_SUB);
run_test()
end

endmodule: my_tb

The * means that you can access it from any uvm_component in the UVM hierarchy using the following command:

if(!uvm_config_db #(virtual add_sub_if)::get(this, “”, “ADD_SUB”, add_sub_vi)) begin
// error message

However, in practice, I’d recommend putting the virtual interface handle inside a configuration object for an agent and passing the config object to the agent.

In reply to mperyer:

Thanks for your response. Let me see if I understand correctly:

I must first add an instance of my interface to the uvm_config_db (or uvm_resource_db) in the top-level module of my test. From there, say I wanted access to the interface in the class addsub_env. I would need to instantiate a virtual interface in that class, then call uvm_config_db…::get… as follows:

class addsub_env extends uvm_env;
	`uvm_component_utils(addsub_env)
	virtual addsub_if addsub_vi;
	function new (string name, uvm_component parent);
		super.new(name, parent);
	endfunction: new
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		assert(uvm_config_db #(virtual addsub_if)::get(this, "", "my_if", addsub_vi));
	endfunction: build_phase
	task run_phase(uvm_phase phase);
		phase.raise_objection(this);
		addsub_vi.a = 1;
		addsub_vi.b = 1;
		#10;
		phase.drop_objection(this);
	endtask: run_phase
endclass: addsub_env

In which case, as a result of my call to “assert(uvm_config_db…)::get…”, addsub_vi becomes a reference to my interface, “my_if”. Then, for every class that I want to reference that interface in, I need to re-instantiate another virtual interface, and use “uvm_config_db::set” to point the virtual interface to interface in the database. Is this correct, or am I still missing something?

You can only have one virtual interface entry in the uvm_config_db, and then reference it from as many classes as you like using uvm_config_db #()::get(). You will need to declare a virtual interface handle in each of the classes.

However, this is not a common use model. Usually there is a virtual interface associated with each agent, and access to the virtual interface is restricted to the agent.