Why use ports in interfaces? - system-verilog

The SystemVerilog LRM (IEEE 1800-2017) describes ports in interfaces as follows:
One limitation of simple interfaces is that the nets and variables declared within the interface are only used to connect to a port with the same nets and variables. To share an external net or variable, one that makes a connection from outside the interface as well as forming a common connection to all module ports that instantiate the interface, an interface port declaration is required. The difference between nets or variables in the interface port list and other nets or variables within the interface is that only those in the port list can be connected externally by name or position when the interface is instantiated. Interface port declaration syntax and semantics are the same as those of modules (see 23.2.2).
What is the first sentence saying exactly? I don't see the limitation.
In the second sentence, what is an example of an external signal? How do you decided whether a signal should be declared inside the interface or as a port to the interface? The text used in the LRM just doesn't click for me.

The problem is shown with the simple_bus example that follows the section of the IEEE 1800-2017 SystemVerilog LRM you quoted.
There are two instances of the interface sb_intf1 and sb_intf2 each creating a unique set of internal signals (req, int, ...). If clk had also been declared as internal signal, there would also be two clock signals. What's not shown in the example is the code generating the clock signal. That could have been in the top module or another module. They would have needed to add continuous assignments to get the generated clock signal to each the internal clk in each interface instance.
By putting the shared signals in the interface in their port declarations, it makes it much easier to join the common signals.
interface simple_bus (input logic clk); // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
endinterface: simple_bus
module memMod(simple_bus a); // Uses just the interface
logic avail;
always #(posedge a.clk) // the clk signal from the interface
a.gnt <= a.req & avail; // a.req is in the 'simple_bus' interface
endmodule
module cpuMod(simple_bus b);
...
endmodule
module top;
logic clk = 0;
simple_bus sb_intf1(clk); // Instantiate the interface
simple_bus sb_intf2(clk); // Instantiate the interface
memMod mem1(.a(sb_intf1)); // Reference simple_bus 1 to memory 1
cpuMod cpu1(.b(sb_intf1));
memMod mem2(.a(sb_intf2)); // Reference simple_bus 2 to memory 2
cpuMod cpu2(.b(sb_intf2));
endmodule

Related

How to use system Verilog hierarchical interfaces

Can the top module in system verilog have interface ports? I am trying to define a hierarchical interface and then trying to use that interface for the top module.
Please check below example code and let me know if I am missing something
interface bank_inf (
inout logic [20:0] data_io,
inout logic qsb_io,
inout logic ctrl_io);
endinterface
interface channel_inf #(parameter numbanks = 7)
( bank_inf banks[numbanks-1:0] );
endinterface
interface ss1_inf #(parameter numchannels = 8, parameter numbanks = 7)
( channel_inf channels[numchannels-1:0]);
endinterface
module die1 (ss1_inf ss);
endmodule
module die2 (ss1_inf ss);
endmodule
module top (
ss1_inf ss1,
ss1_inf ss2
);
die1 inst1(ss1);
die2 inst2(ss1);
endmodule
I am getting below error:
Error-[SV-UIP] Unconnected interface port
../test.v, 22
"ss1"
The port 'ss1' of top-level module 'top' whose type is interface 'ss1_inf'
is left unconnected. It is illegal to leave the interface ports unconnected.
Please make sure that all the interface ports are connected.
From a language and simulation perspective, the top level module cannot have interface ports. Interface ports represent references to hierarchical interface instances. Those instances are where allocation of variables and other processes exist. Also, interfaces may be parameterized and the only way to specify that parameterization is through an actual instance. There is no syntax that allows parameterization in the port declaration.
But from the synthesis perspective, the highest level you provide to a synthesis tool may allow interface ports. But you should check with the particular synthesis tool vendor you are using.
No, top level module cannot have interface ports.
The reason is that interface ports require connection to an interface instance. Instantiation of an interface can only happen inside a module. So, only modules instantiated in hierarchy can be connected to an instance of an interface.
No interface instance can exist at the $root/$unit level, therefore there is no interface instance top-level module ports can be connected to.

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.

Systemverilog interface - nested interface vs modport

Can I use a nested interface in place of a modport?
The purpose of this is large-scale interconnect of many different modules while taking advantage of interfaces to simplify connectivity. I prefer not to use generic interfaces ever - they are hard to follow in source code.
If I have a generic module that is used by many modules (eg, reset_synchronizer), then I could define a modport in each interface to define the signals for that module, eg:
interface xyz(input clk, input arst);
logic srst;
modport reset_synchronizer_mp (input clk, input arst, output srst);
endinterface
I can define this in each interface where I have the need to synchronize the reset. But when I need to change the reset_synchronizer module, perhaps to pass in an enable, I'd need to update the interface ports (add enable signal), and update each modport in all interfaces that create a modport for the reset_synchronizer.
interface xyz(input clk, input enable, input arst);
logic srst;
modport reset_synchronizer_mp (input clk, input enable, input arst, output srst);
endinterface
If instead of modport, I used a nested interface (reset_synchronizer_intf), couldn't I just do this inside the main interfaces:
interface xyz(input clk, input arst);
logic srst;
reset_synchronizer_intf reset_synchronizer_mp (clk, arst, srst);
endinterface
And to simplify this (assuming the signal names match):
interface xyz(input clk, input arst);
logic srst
reset_synchronizer_intf reset_synchronizer_mp (.*);
endinterface
And to add enable, I'd just be updating the main reset_synchronizer.sv (where the module and interface are defined) and update all interfaces but only at the port list or in the signal declaration:
interface xyz(input clk, input enable, input arst);
reset_synchronizer_intf reset_synchronizer_mp (.*);
endinterface
This becomes even more attractive when the signals I'm looking for exist in the main interface, but I now want to bring them into some generic module. Eg, a power-gating module that normally only got srst, but now I want to pass in rst as well. I could change this at the interface level of the power-gating module instead of inside each interface.
Although this works for defining structured bundles of signals, you lose the ability to specify directions with modports as they cannot be composed hierarchically. This also means you need to know the nesting when connecting to interface xyz.
module top;
xyx xyz_if(.*)
mymodule inst(xyx_if);
endmodule
module mymodule (xyz xyz_p);
...
xyz.reset_synchronizer_mp.enable = 1;
endmodule
This most likely will not be synthesizable.

system verilog interface for multiple clocks

My DUT is a memory controller. I have to write a system verilog interface for the DUT.
Memory Controller DUT supports 32 AXI Masters.
When I am writing an AXI interface, it will consist of ACLK which is generated and passed on through the top(verification). When I am connecting this interface to the DUT, will there be 32(AXI ACLK) + 1(clk on which DUT is working) , inall 33 clks to the DUT..
I am quite confused in these.
logically there should be only one clk in the DUT..
Thanks in advance for the answers
Shared interface signals should be declared as input ports to your interface. That way you can tie them all together to make one logical signal.
interface myintf(input wire sig_shared);
wire sig_internal;
endinterface
module top;
wire s1,s2;
myintf i1(s1);
myintf i2(s1);
myintf i[31:0](s2);
endmodule
Now signals i1.sig_internal and i2.sig_internal will be independent, but i1.sig_shared and i2.sig_shared are logically equivalent. Same thing for i[0].sig_shared thru i[31].sig_shared.

system verilog bind used together with interface

I defined an interface in system verilog and use bind statement to bind to internal RTL signals. I want to be able to force internal RTL signals through the interface. However, this is causing the RTL signal to go to 'x' if I don't force these signals explicitly, it seems bind to interface is having driving capability. I don't want RTL signal to change to 'x' when nothing is forcing it in this case, not sure what I am doing wrong here?
my code looks like this with DUT being the design:
interface myInf(
inout RTL_a,
inout RTL_b
);
bind DUT myInf myInf_inst(
.RTL_a(DUT.a),
.RTL_b(DUT.b)
);
bind DUT myDrv(myInf_inst);
where myDrv is a module which drives the ports on myInf.
In this case, DUT.a and DUT.b are internal RTL signals, they have their driver from design, but I want to be able to force them if needed. however, these signals becoming 'x' when I am just binding them to myInf without actually driving them.
The inout signals might be a non-net type. It is better to be explicit in the the declaration and define them as inout wire. Inside the interface, assign the nets to a logic and initialize the logics to z. A non-z value will apply a driver while a z will allow signals to drive. Example:
interface myInf(
inout wire RTL_a,
inout wire RTL_b
);
logic drv_a, drv_b;
initial {drv_a,drv_b} = 'z; // z means not driving
assign RTL_a = drv_a;
assign RTL_b = drv_b;
endinterface
There might be conflicting drivers, such as the normal drivers from the design. In this case you will need to override the driver. Assuming the signal being overrode is a net type, this is done by changing the assign statements to assign (supply1,suppl0) RTL_a = drv_a;. This is utilizing the Verilog concept of drive strength. Assigning to z will still all other drivers. Most nets are driven with a strength of strong1,strong0 which is weaker then supply1,supply0. Drive strength will not work for non-net types (e.g. logic & reg). These register/variable-types use a last-assignment-wins approach. Fore more on drive strength read IEEE Std 1800-2012 sections 28.11 through 28.15
Your sample code has some bugs. The pin connections for myInf_inst should use hierarchical references relative to its target scope. Unless there is an instance called DUT inside module DUT, then the DUT. should be omitted (See IEEE Std 1800-2012 ยง 23.11 Binding auxiliary code to scopes or instances). The bind statement for myDrv is missing an instance name. The code should be:
bind DUT myInf myInf_inst(
.RTL_a(a), // no DUT.
.RTL_b(b) // no DUT.
);
bind DUT myDrv myDrv_inst(myInf_inst);
sample code: http://www.edaplayground.com/x/2NG