Using clocking blocks and modports inside Interfaces - system-verilog

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.

Related

Why use ports in interfaces?

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

Two Modports of same Interface have 1 clock cycle difference

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.

Systemverilog modport access to interface clk without being declared as input

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."

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 Clocking block

I am trying to perform a simple test with demo code of Clocking block, but encountered the error.
The code could be find at "EDA playground" http://www.edaplayground.com/x/3Ga
And the error says:
** Error: testbench.sv(38): A default clocking block must be specified to use the ##n timing statement.
** Error: testbench.sv(40): A default clocking block must be specified to use the ##n timing statement.
I think the clocking block has already been specified in the code.
Any Help?
As the error message says, you have to define the clocking block as default:
default clocking cb_counter #(posedge Clock);
Full code here: http://www.edaplayground.com/x/37_
The SV 2012 standard specifies that the ##n operator can only be used if there is a default clocking block defined for the module/program/interface, otherwise it wouldn't be able to know what clock event to use for the delay.
##N delays are not a very useful feature unless you can put them in the same module or interface that the clocking block is defined in. That is typically not the case because you usually put your driver code inside a class inside a package.
repeat (N) #cb_counter;
This works uniformly, even if referencing the cb through a virtual interface.
To resolve the error add default clocking cb_counter; after your clocking block.
SystemVerilog IEEE1800-2012 Section 14 Covers Clocking Blocks.
// Test program
program test_counter;
// SystemVerilog "clocking block"
// Clocking outputs are DUT inputs and vice versa
clocking cb_counter #(posedge Clock);
default input #1step output #4;
output negedge Reset;
output Enable, Load, UpDn, Data;
input Q;
endclocking
default clocking cb_counter; //<-- Set default clocking
// Apply the test stimulus
initial begin
//..
Below I have included a my method of creating a testbench clock, with the initial it is easy to work out when it will be triggered, compared to the original always the time for the first trigger my vary depending on how when the Clk is initialised.
initial begin
Clk = 0;
forever begin
#5ns Clk = ~Clk;
end
end
If you simulator allows system-verilog, I would use #5ns so that it does not rely on the timestep, I find this to more readable and reliable for code reuse.
The version from the question, used an always block.
timeunit 1ns;
// Clock generator
always
begin
#5 Clock = 1;
#5 Clock = 0;
end