Trying to access an interface inside interface from the env (SystemVerilog, OVM) - system-verilog

Inside the build function of my env class trying to connect interfaces:
virtual my_if my_vif;
for (int i = 0; i<32; i++) begin
_agent[i]._vif = my_vif._if[i];
end
Inside my_if:
interface my_if();
if _if[32]();
endinterface :my_if
When running simulation I get this error:
Error-[MFNF] Member not found
my_env.sv, 229 "this.my_vif."
Could not find member '_if' in interface 'my_if', at "my_if.sv", 1.
_if is also an interface with the next signals:
interface if();
logic clk;
logic rstn;
logic [101:0] requests;
logic [63:0] dataOut;
endinterface :if
The thing is that everything worked fine when the interface _if was not wrapped under the my_if interface.

It looks like you are trying to instantiate some design element of type _if 32 times in the interface, using array instances.
array instances are related to generate blocks. As a result the only way to index them is to do it from another generate blocks. You cannot use a simple for loop with indexing to do it.
Generate blocks are a part of rtl design and in general have very limited applicability to the test bench, in particular with dynamic structs as an agent.
You need to re-think the way you want to implement it.

Related

System verilog simulation performance for uvm_hdl_read vs assign statement

I need to generate around 10,000 connectivity assertions to check that values driven at DUT interface at the beginning of simulation has reached (and is retained) at around 10,000 points inside the DUT throughout the simulation.
The 10k destination points inside DUT are available through xls, hence, if I can take the paths to an array, I could write a single assertion property and instantiate it multiple times while iterating through the array.
What is the better approach here for simulation performance?
Using assign :
logic[7:0] dest_vals[10000];
assign dest_vals[0] = dut_path0.sig0; //This assignment code can be script-generated
assign dest_vals[1] = dut_path1.sig1;
....
assign dest_vals[9999] = dut_path9999.sig9999;
property check_val(expected_val, dut_val);
#(posedge assert_clk) expected_val == dut_val;
endproperty
genvar i;
generate
for(i=0;i<9999;i++)
assert property check_val(exp_val[i], dest_vals[i]);
endgenerate
Using uvm_hdl_read :
string dest_val_path[10000] = '{
"dut_path0.sig0",
"dut_path1.sig1",
....
"dut_path9999.sig9999"
};
property check_val(expected_val, dut_val);
#(posedge assert_clk) expected_val == dut_val;
endproperty
genvar i;
generate
for(i=0;i<9999;i++) begin
initial forever begin
#(posedge assert_clk);
uvm_hdl_read(dest_val_path[i],dest_vals[i]);
end
assert property check_val(exp_val[i], dest_vals[i]);
end
endgenerate
Follow up question: Is there a way to change static array of size 10k to dynamic array/associative array/queue, so that if 10k destination points changes to 11k in future, array need not be re-sized manually?
Using uvm_hdl_read to access a signal by a string name is significantly slower than a direct hierarchical reference to a signal in an assign statement. A lot of code gets executed to lookup up that signal name in a string database and retrieve it from the simulator's internal database using the SystemVerilog's C based "VPI" routines. Plus, you can lose some optimizations because the value of the signal needs to be readily available at any point in time. In the case of the continuous assign statement, the simulator can work backwards and figure out it only needs the signal value on the assert_clk trigger.
How noticeable this performance difference is depends on a number of other factors
The overall size of the entire design's activity relative to additional code executed by uvm_hdl_read
How often the assert_clk triggers relative to the other activity in the design
How good a job you were able to limit VPI access to just the signals involved. Every signal you give VPI access to increases the size of the string database and prevents optimizations around that signal.
There is another option that does not involve assign statements or generate loops. You can use the bind construct to insert a module with your assertion. This works particularly well in your situation since you already have a script that produces the code for you.
module #(type T=logic [7:0]) mcheck(input logic clk, T expected_val, dut_val);
property check_val;
#(posedge clk) expected_val == dut_val;
endproperty
assert property(check_val);
endmodule
bind dut_path0 mcheck m(tb_path.assert_clk,tb_path.expected_val[0], sig0);
bind dut_path1 mcheck m(tb_path.assert_clk,tb_path.expected_val[1], sig1);
...
Note the port connections in the bind module are relative to the dut_path where mcheck is bound into.

assign statement using virtual interface variable

I want to connect the module variable to the port in virtual interface using assign statement.
I created one interface and set it as virtual in config_db in my top module. I got the virtual interface via config_db in another module and trying to connect the port in another module to the port in the virtual interface
//Below is the sample code
interface intf(); //Interface
int values;
endinterface
module another_module(); //Another module
virtual intf u_intf;
int val;
assign val = u_intf.values; //I am getting ERROR here
initial begin
uvm_config_db#(virtual intf)::get(null,"","assoc_array",u_intf);
end
endmodule
module tb(); // Top TB
intf u_intf();
another_module u_another_module();
initial begin
uvm_config_db#(virtual intf)::set(uvm_root::get(),"","assoc_array",u_intf);
end
endmodule
Standard does not allow using virtual interfaces in assign statements.
assign is used in verilog to connect different RTL blocks.
virtual interface is a system verilog test bench concept.
So, they cannot be mixed together.
You have to clarify, why you really need a virtual interface in this contents. They are not synthesizable. Are you writing a test bench module? In general case, you should use regular interfaces to connect modules.
However, in your example you can use always #* to do the assignment:
always #* val = u_intf.values;

What counts as an illegal hierarchical reference for a virtual interface?

The IEEE 1800-2017 LRM states in section 25.9 Virtual interfaces that:
Although an interface may contain hierarchical references to objects outside its body or ports that reference
other interfaces, it shall be illegal to use an interface containing those references in the declaration of a
virtual interface.
Is the following an example of such a disallowed hierarchical reference?
interface some_other_intf();
bit some_signal;
endinterface
interface some_intf();
some_other_intf intf();
task foo();
intf.some_signal <= 0;
endtask
endinterface
virtual some_intf some_vif;
I have a tool that complains about the line containing intf.some_signal <= 0. While intf.some_signal is a hierarchical reference, it's a relative reference, so I don't see why this would be disallowed.
intf is part of the interface body. I'm not sure how to interpret the ports that reference other interfaces part.
Here's an example of a port that references another interface
interface some_other_intf();
bit some_signal;
parameter T = int;
endinterface
interface some_intf(some_other_interface intf);
task foo();
intf.some_signal <= 0;
endtask
typefef intf.T myT;
myT another_signal;
endinterface
virtual some_intf some_vif;
The problem comes in with a reference to some_vif.another_signal Its type could change depending on what parametrization of T got connected to intf.
For most use cases, this is not a problem, but the SystemVerilog committee never spent the time on clarifying specific cases that could be allowed; the just made a wide sweeping prohibition.

Virtual interface between monitor/driver and their BFM ??? What they are actually, can someone explain?

I was reading UVM cookbook and I got confused about virtual interface connection in between monitor, driver and their BFM. Does it mean there could be multiple driver or monitor, or this is independent of interfacing that does not know either its monitor or driver. Can anybody help?
The keyword virtual is re-used a number of times in SystemVerilog. The interface is virtual in the sense that its hierarchical path is set at runtime by passing it through a variable. All other connections in Verilog/SystemVerilog are fixed paths.
This does indeed allow you to have multiple instances of the same driver code connect to multiple interface instances. It also helps in block-to-system reuse so you can change the hierarchical path as the interface gets deeper into your system level.
Verilog was not created as a programming langue, more over, it was not suitable for object oriented programming. On the other hand, System verilog test bench language was created as an object oriented programming language.
One of the issues is to semantically connect HDL verilog with TB. All verilog HDL/RTL objects are statically compiled and cannot be manipulated dynamically (which is needed at TB). You cannot get pointers to modules, variables, ... (well except through some back-door PLI mechanism).
So, System verilog came up with the interface construct, which was intended as a connectivity object in RTL world. It is similar to a module in a sense, that it is a compile-time static object. But, SV also added a trick, which allows you to have a reference to an interface. The trick is called virtual interface.
From the point of view of a programmer, you can think of it as a reference, or a pointer to the static interface object. This gives you an ability to pass this reference to different TB class, or create multiple references tot he same interface.
Here is a schematic example:
class Transaction;
virtual dut_if trans; // <<< virtual interface
function new(virtual dut_if t);
trans = t; // <<<< assign it to trans
endfunction // new
endclass // Transaction
// definition of the interface
interface dut_if import trans_pkg::*;
(input trans_t trans);
endinterface
// instantiate the interface in module 'rtl'
bind rtl dut_if dut_if(data);
program tb;
// pass it to the constructor as a virtual interface pointer.
Transaction trans1 = new (rtl.dut_if);
Transaction trans2 = new (rtl.dut_if);
endprogram // tb

Assertion module in an UVM testbench

I have written an UVM testbench that has 3 agents and am now in the process of writing a scoreboard/checker. I need to have a checker module for my SystemVerilog Assertions, but this checker module needs to be aware of register configuration that is done from the test (and can be random, decided during run_phase of the test).
I am unable to figure out how this would work? If I were to create a checker module for my assertions, and bind it at the top level (tb_top) to the dut, how does this checker module know my register configuration?
After reading some papers, I figured I could write my checker module as an interface, set it in tb_top. But this would give access to the variables in my interface to the UVCs. How does the interface access variables in the UVCs?
Any help is appreciated. I feel I am missing something key here as this has probably been done plenty of times before.
EDIT: Please don't tell me I have to implement some kind of API to set each individual register setting from my UVCs? I want to just get a handle to my reg_block (or any other config variable in my agents)
It seems that you want to pass information from tb_top to your UVC or vice versa. This information will be used by your assertion in tb_top, and shared by your UVC. My suggestion, you can either use uvm_resource_db or uvm_config_db.
I can think of two ways of achieving this communication.
First method is set the configuration in your tb_top, then your UVC grab this handle. From here on, you can communicate your register or whatever info you need for your assertion.
class my_tb_config extends uvm_object;
// ...
endclass
module tb_top;
my_tb_config tcfg;
initial begin
tcfg = new("tcfg");
uvm_config_db#(my_tb_config)::set(uvm_root::get(), "*", "my_tb_config", tcfg);
end
endmodule
// somewhere in your UVC
class my_uvc extends uvm_component;
my_tb_config tcfg;
function void build_phase(uvm_phase phase);
// now both tb_top and your UVC point to the same config object
void'(uvm_config_db#(my_tb_config)::get(this,"","my_tb_config", tcfg));
endfunction
endclass
Another method is the other way around. Pass your UVC configuration to your tb_top.
class my_other_uvc extends uvm_component;
my_tb_config tcfg;
function void build_phase(uvm_phase);
tcfg = new("tcfg");
uvm_resource_db#(my_tb_config)::set("*", "my_tb_config", tcfg);
endfunction
endclass
// somewhere in your tb_top
module tb_top;
my_tb_config tcfg;
initial begin
#1ps; // small delay, making sure resource is submitted
void'(uvm_resource_db#(my_tb_config)::read_by_name("*","my_tb_config",tcfg);
// Now both your tb_top and UVC share same object, so you can freely define your whatever communication between them
end
endmodule
I figured out a way to do this. Firstly, I realized that I had asked two separate questions:
1) My checker module needs to be aware of register configuration that is done from the test
I use cross module reference into my design to access my registers, and this provides me with up-to date register configuration as set by the test during the run-phase.
tb.sv
module tb;
dut my_dut( ... )
interface my_checker (
.input_registerA (tb.my_dut.my_sub_module.regA),
.input_registerB (tb.my_dut.my_sub_module.regB),
.input_registerC (tb.my_dut.my_other_sub_module.regC),
....
)
endmodule
my_checker.sv
interface my_checker (
input input_registerA,
input input_registerB,
input input_registerC,
....
);
// Here I can write properties/assertions that are register-aware
endinterface
2) How does the interface access variables in the UVCs?
This is a little trickier. I want to dynamically update my checker variables from the uvm_sequence or uvm_monitor etc.
I read this paper by Verilab that clearly describes the method to do this:
http://www.verilab.com/files/litterick_sva_encapsulation.pdf
In my checker module, I create a uvm_component. From this component, I now have access to the uvm_resource_db, through which I can exchange info with my UVM-testbench.
One thing to remember is that the uvm_component instantiated in the checker module is located at the top-level (uvm_root).