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.
Related
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
I'm writing some test environment for practise but facing some strange issue. I have interface and made 2 modports for master and slave. But when I check the waveforms I see that when I change something in using master modports it takes additional clock cycle to be seen by slave modport. Same happens on the opposite side too.
Can you please explain why is that happening and what I have done wrong?
Here are portions of my code.
interface axi_if ();
wire wready;
...
clocking m_cb #(posedge aclk);
default input #setup_time output #hold_time ;
...
input wready ;
...
endclocking: m_cb
clocking s_cb #(posedge aclk);
default input #setup_time output #hold_time ;
...
output wready ;
...
endclocking: c_cb
modport axi_master_modport(clocking m_cb, output aresetn);
modport axi_slave_modport( clocking s_cb, input aresetn);
endinterface
Thanks
The modports are used to create different views of the same interface, just like in the example you gave, the same ports viewed from the perspective of the master or the slave.
But I noticed you are using clocking blocks. Clocking blocks are used to view the signals in a specific clock domain. And specially, inputs and outputs sampled at different moments as I explain here.
I see different examples online of using modports for interfaces when there are port inputs on the interface. Should modports include clk and reset on the modport even though they are interface inputs?
Here's an example with clk not included on modport : https://www.doulos.com/knowhow/sysverilog/tutorial/interfaces/ Section: Modports in Interfaces
Putting clk in the modport gives a Spyglass lint error (undriven variable), here's a general idea of the error:
interface MY_IF (input clk);
signal my_sig;
modport master (input clk, my_sig);
endinterface
MY_IF if (.clk(clk));
MY_MOD (.my_if(if.master)); // claims clk is undriven
Whereas Cadence gives issues if the clk is missing and I pass a virtual interface into a class where I use clk.
Which way is correct? Does a modport have to have ALL of the signals listed, or are port interface signals always available?
You can include interface ports in modports. But just that the port direction (input, output, inout) can't be changed in modports.
Here is one example.
interface simple_bus (input logic clk);
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
modport slave (input req, addr, mode, start, clk,
output gnt, rdy,
ref data);
modport master(input gnt, rdy, clk,
output req, addr, mode, start,
ref data);
endinterface: simple_bus
Cadence seems to be right according to https://verificationacademy.com/forums/systemverilog/using-modports-restrict-access-interface-signals :
"That is the whole point of a modport - they define a subset signals that become ports of the module. The signals that are not part of the modport do not get connected and thus you have no access to them. Once defined, modports are only referenced in the port connection."
Can someone give me a hint as to why this interface with modports and clocking blocks might not work?
interface axis (input logic aclk );
logic [15:0] tdata_s;
logic tvalid_s;
logic tready_s;
logic [15:0] tdata_m;
logic tvalid_m;
logic tready_m;
// clocking block for AXI Stream master
clocking cb_axis_mst #(posedge aclk);
default input #1step output #3ns;
output tdata_m;
output tvalid_m;
input tready_m;
endclocking
// clocking block for AXI Stream slave
clocking cb_axis_slv #(posedge aclk);
default input #1step output #1ns;
input tdata_s;
input tvalid_s;
output tready_s;
endclocking
// AXI stream master modport for testbench only
modport tb_axis_mst_mp(clocking cb_axis_mst);
// AXI stream slave modport for testbench only
modport tb_axis_slv_mp(clocking cb_axis_slv);
endinterface
QuestaSIM 10.5c gives me a series of errors like this:
** Error: (vsim-3773) ../../../../rtl/test_driver.sv(37): Interface item 'tvalid_m' is not in modport 'tb_axis_mst_mp'.
The problem goes away if I add the ports to the modport, but my understanding was that it was sufficient to just use the clocking block.
Full code is here: https://www.edaplayground.com/x/5FzC
Your understanding is not correct. Adding a clocking block to a modport only gives you access to the signals created by the clocking block, not the signals it references.
When using clocking block signals you need to reference the clocking block scope, i.e. AXIS_MST.cb_axis_mst.tvalid_m. And instead of #posedge AXIS_MST.aclk, just use #AXIS_MST.cb_axis_mst.
One other comment about your testbench: remove the nested program/endprogram statements; they serve no purpose. Do not use program blocks.
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.