Understanding virtual interfaces with classes - system-verilog

I am studying SystemVerilog for verification purposes and stumbled upon virtual interfaces and classes. I am trying to set up an example where I define a virtual interface attribute in a class to be initialized through a method. It looks like this:
class myclass;
virtual interface my_intf val;
...
function void configure(virtual interface my_intf input_intf);
val = input_intf;
endfunction
...
endclass
An object of this class is instantiated in a testbench that has the interface I intend to pass on to the object as an input.
module mytb(my_intf bus);
...
myclass c1;
initial
begin
c1 = new;
c1.configure(bus);
...
endmodule
What I am encountering is a warning at elaboration time that says [related to c1.configure(bus)] formal and actual do not have assignment compatible data types (expecting datatype compatible with 'virtual interface my_intf' but found 'my_intf' instance instead. Nevertheless, the simulation ends with success, and I made sure to follow the code presented in the course I am attending. Is there anything I am still missing or can I neglect this warning?
Thanks!

I have looked more into my code and found out that it had to do with the fact that the testbench uses a specific configuration of the interface defined through a modport and I had to make sure that I extended that to the class too. In short, the testbench receives a my_intf.intf1 bus interface and this caused the tool to complain as it was expecting my_intf but got my_intf.intf1 instead.
Thanks everyone

You missing a lot of code, so it it difficult to figure out what is going one.
However, for references the following code works:
class myclass;
virtual interface my_intf val;
function void configure(virtual interface my_intf input_intf);
val = input_intf;
endfunction
endclass
module mytb(my_intf bus);
myclass c1;
initial
begin
c1 = new;
c1.configure(bus);
end
endmodule
interface my_intf;
endinterface
module top;
my_intf intf();
mytb mytb(intf);
endmodule
Few rules of thumb:
an interface must be defined as intercface..endinterface and it must be visible in all parts of compilation (can be declared after it is used, similarly to moduels). See code example.
the interface must be instantiated in a module, e.g. in module top of the example.
Note, similar issues as you described can happen if you incorrectly use compilation units.

I'm not familiar with SystemVerilog, but I've just RFTM around and the right syntax is:
virtual interface_name instance_name;
So maybe you should try something like this:
virtual my_intf val;
function void configure(virtual my_intf val);
this.val = val;
endfunction
...
endclass
See https://verificationguide.com/systemverilog/systemverilog-virtual-interface/#What_is_the_need_for_a_virtual_interface_in_SystemVerilog

Related

What causes 'interface resolution' compilation error when working with classes and virtual interfaces

I have declared in a design file following interfaces:
interface t_clocks;
ckrs_t ClkRs125MHz_ix;
ckrs_t ClkRs160MHz_ix;
ckrs_t [3:0] ClkRsLink_ixb;
ckrs_t ClkRsLinkx2_ixb;
logic tdclk_sampling;
modport producer(output ClkRs125MHz_ix,
output ClkRs160MHz_ix,
output ClkRsLink_ixb,
output tdclk_sampling);
modport consumer(input ClkRs125MHz_ix,
input ClkRs160MHz_ix,
input ClkRsLink_ixb,
input tdclk_sampling);
endinterface // clocks
interface t_bst(input ckrs_t ClkRs_ix);
tb_t mark;
modport consumer(input ClkRs_ix, input mark);
modport producer(output ClkRs_ix, output mark);
endinterface // t_bst
and in a package classes working with those interfaces:
package clspkg;
import DefinitionsPkg::*;
class bst_generator;
virtual t_bst.producer bst_master;
....
endclass // bst_generator
class clock_generator;
virtual t_clocks.producer clk_tree;
....
endclass // clock_generator
endpackage // clspkg
I have glued all together in the testbench, using only clock_generator class from the package:
module tb_turn_emulator;
import clspkg::*;
t_clocks clk_tree();
clock_generator cgen;
// ???????????????
//t_bst blast_radius(clk_tree.ClkRs160MHz_ix);
default clocking cb #(posedge clk_tree.ClkRs160MHz_ix.clk); endclocking
`TEST_SUITE begin
`TEST_SUITE_SETUP begin
cgen = new;
cgen.clk_tree = clk_tree;
cgen.run();
##10;
end
....
endmodule
Now, when I try to compile such example, it FAILS with Virtual interface resolution cannot find a matching instance for 'virtual t_bst.producer'
It took me quite some time to find out, that if I instantiate as well the t_bst interface in the testbench module (uncommenting the t_bst line in the code above), everything goes smoothly, no compilation error and the testbench passes as usual.
I don't understand why the t_bst interface has to be instantiated as it is not at all used in code. It is true that I am importing the entire clspkg package, but nothing changes when I cherry pick by importing only the clock_generator by import clspkg::clock_generator.
What is wrong? I'm using Mentor Modelsim
First about using packages:
Once you import one identifier or every identifier from a package in a design, the entire package exists in your design. That means all the static variables get allocated and initialized, as well as all the code for tasks and functions get generated. If static variable initializations call functions like class constructors, that implies executing procedural code without ever referencing it directly. (For those of you familiar with the factory design pattern and UVM, this is exactly how factory registration works.)
Next about using interfaces:
Like a module, an interface is a design element containing definitions of lots of different things. They can define variables, routines, import other packages, and have port connections. But nothing inside an interface exists or generates any code in a design until other design unit instantiates it, or in the case of a module, gets established as a top-level instance.
Now about using virtual interfaces:
Virtual interface are the primary method used to bridge the two kingdoms of the dynamic class based testbench world with the static instance based design world. Virtual interfaces behave much like a class type, except that there is no rule to have seen a definition of the interface before using it in your code. Hierarchical reference also have this capability of referencing something that does not exist at the point of compilation, but must be resolved later in the elaboration phase of compilation.
To summarize, the compiler generates code for a virtual interface once it sees a reference to it whether you actually think you are using it or not. I would try to align packages with specific interfaces they are associated with.
From SV LRM (25.9 Virtual interfaces):
A virtual interface is a variable that represents an interface instance.
A virtual interface shall be initialized before referencing a component of the virtual interface; it has the value null before it is initialized. Attempting to use a null virtual interface shall result in a fatal run-time error.
I hope it answers to your question.

Is there a way to fix the warning related to the support of string-based lookup for the factory?

I often get this warning message:
UVM_WARNING # 0: reporter [TPRGED] Type name 'packet2mem_comp_Str' already registered with factory. No string-based lookup support for multiple types with the same type name.
I did not register any class with the same name, unless the parent one which, I suppose, does not present any problem.
My class is an inherited parametrized class declared as follows:
class packet2mem_comp #(string S = "MEM") extends mem_comp;
typedef packet2mem_comp #(S) packet2mem_comp_Str;
`uvm_object_utils(packet2mem_comp_Str)
function new (string name = "packet2mem_comp");
super.new(name);
endfunction : new
... //rest of my code
endclass: packet2mem_comp
Does anyone know how to fix this warning?
There are special versions of the macros for parameterised classes. Instead of
`uvm_object_utils(packet2mem_comp_Str)
try
`uvm_object_param_utils(packet2mem_comp_Str)
or perhaps
`uvm_object_param_utils(packet2mem_comp #(S))
You have not posted an MCVE, so I have not tested this.

How to handle the interface with package in systemverilog

I am creating a simple uvm tb these days and I meet an issue with interface usage. Here is my code.
in the /my_proj/tb_uvm/intf/my_if.svh file: (interface definition)
interface my_if (
input iCLK,
input iRSTb,
inout data
);
clocking monitor_cb #(posedge iCLK);
input iRSTb;
input data;
endclocking
modport monitor_mp(
clocking monitor_cb
);
endinterface : my_if
and I need to instance this interface in the /my_proj/tb_uvm/agent/my_driver.svh file :
class my_driver extends uvm_driver;
`uvm_component_utils(my_driver)
virtual my_if m_vif;
...
endclass
I tried to define a package file(named my_agt_pkg.sv) under /my_proj/tb_uvm/agent/ because there are several driver/monitor files in this directory including the my_driver.svh I mentioned above.
package my_agt_pkg;
import uvm_pkg::*;
`include "my_driver.svh"
`include "../intf/my_if.svh"
....
endpackage
but I failed to compile because of below error. Could anyone kindly give me a help on this issue ?
Found 'interface' inside package before 'endpackage'. 'interface' inside 'package' is not allowed.
The error message means what is says: you are not allowed to declare an interface inside a package.
A virtual interface is a peculiar concept. It behaves like a class variable, but an interface gets defined and instantiated like a module.
Just move your interface declaration outside the package

Virtual Interface in Config class

How do I point out an interface in TB_top from my configuration class using virtual interface without using uvm_config_db?
This question tests your knowledge of uvm_config_db, which is just a database of global variables inside the uvm_pkg. All your have to do is create a virtual interface variable inside the package where you define your configuration class, then set it before calling run_test()
package my_config_pkg;
virtual my_interface vif;
class my_configuration;
...
endclass
endpackage
module TB_top;
my_interface ifinst();
initial begin
my_config_pkg::vif = ifinst;
run_test("my_test");
end
endmodule

Inheritance-like feature for interfaces

A great feature of SystemVerilog is inheritance, which AFAIK is limited to classes.
I was wondering if there is a way to mimic inheritance and overloading for interfaces.
For example, suppose interface2 has all signals defined in interface1 pluse sig1001 and modport2. What is the best way of defining interface2 without rewriting everything?
interface interface1;
logic sig1;
...
logic sig1000;
modport modport1(....);
task task1;
...
endtask
endinterface
interface interface2;
logic sig1; //similar to interface1
...
logic sig1000; //similar to interface1
logic sig1001;
modport modport1(....); //similar to interface1
modport modport2(....);
task task1; //similar to interface1
...
endtask
endinterface
I need it to be synthesizable. My goal is not to maintain several interfaces as my code evolves
I was thinking of defining a parameter and use if/generate. Any ideas are welcome.
There is no way to compose a SystemVerilog interface from other interfaces either by using inheritance or encapsulating them hierarchically. The only way to achieve something similar is to put sections of the interface in separate files and `include them as needed.
If you need to do this for a testbench, then you are better off using classes instead.
Even if you do parameters and generate statements inside, this still isn't inheritance. Every specialization of such a parameterized interface creates an own type which is not compatible with another type, i.e.:
interface some_interface #(int some_parameter = 1);
//...
endinterface
some_interface #(2) intf1;
virtual some_interface vif; // per default some_parameter is 1
vif = intf1; // this is illegal because some_interface #(1) and some_interface #(2) are not type compatible
If your idea is to use parameterized ifs in classes, then you're only bet is to define the classes themselves as parameterized.