How to use modport and What is the benefit to instanciate between interface and DUT in systemverilog? - system-verilog

I'm verilog user and unfamiliar with systemverilog.
I have found what to used modport and instanciate between DUT and interface in systemverilog.
But I don't no why use the modport and how to use and interconnect between interface and DUT in systemverilog?

Modport is short for module port. They allow for the definition of different views of the signals within the interface. In many cases, just two modports, or views, are needed - One for the source-side of the interface, and one for the sink-side. A simple example is below:
interface simple_if ();
wire we;
wire wdata;
wire full;
// source-side view
modport src (
output we,
output wdata,
input full
);
// sink-side view
modport snk (
input we,
input wdata,
output full
);
endinterface
The interface can be used to connect two module instances together, and which view, or modport to use can be specified at each module instance using the dot notation. Example below using the interface definition above:
module top();
// first, instantiate the interface
simple_if simple_if ();
// source-side module instantiation
src_side_module u_src_side_module (
.clk (clk),
.rstl (rstl),
.if(simple_if.src) // .src specifies the modport
);
// sink-side module instantiation
snk_side_module u_snk_side_module (
.clk (clk),
.rstl (rstl),
.if(simple_if.snk) // .snk specifies the modport
);
endmodule
Couple other notes:
The clocks and resets can also be passed around inside the interface.
Alternatively, the modport can be specified down in the module where you specify the IO, like this:
module src_side_module (
input wire clk,
input wire rstl,
simple_if.src if
);
....
Hope this helps.

Related

passing generated modports to instances of the same module

I'm pretty sure there is no way to do what I am trying, but just in case there is an interesting clever solution, I thought I'd ask around. I have a parameterized SystemVerilog interface, inside of which I am generating modports
interface some_interface #(parameter NUM_READERS=3);
logic [`kBitsPerProgramCounter-1:0] read_addr[NUM_READERS];
logic [`kBitsPerProgramCounter-1:0] write_addr;
genvar i;
generate
// generates Reader[n].Modport
for (i = 0; i < NUM_READERS; ++i) begin : Reader
modport Modport
(
output .read_addr(read_addr[i])
);
end
endgenerate
endinterface
Using this is easy if I have different module types taking different modports. However, what I wanted to try doing is to have multiple instances of the same module. I tried this by parameterizing on type
module some_block#(parameter type SOMETYPE) (
input logic CLK200MHZ,
SOMETYPE aarrgghh);
But getting it to work syntactically has been challenging. Giving SOMETYPE a default value doesn't work because Vivado complains about not allowing hierarchical types, so right off the bat I don't think this will work. When instantiating, I tried using the full modport name, the full modport name with the instantiated interface, and a few others, but nothing seems to work.
So I am curious if there is a clever way I can have multiple instantiations of some_block, each taking a different generated modport. And if not, is there some fun clever trick I can do to use the generated modports? The idea in the first place was that I have a thing that has one writer and multiple readers. I wanted to generate a modport for each reader so that it could only touch it's own read address. I guess I could always just parameterize some_block on an integer, pass some_block the whole interface, and then rely on some_block to only touch the read address corresponding to the passed in integer, but that might be error prone.
Assuming that 'generate' works, there is nothing to be concerned about modules. There is no need to pass a type parameter. The module port is just supposed to be of the type of your interface.
module top();
some_interface ifc;
for (genvar gi = 0; gi < NUM_REASDERS; gi++) begin: inst
some_block sb(ifc.Reader[gi].Modport);
end
endmodule
module some_block (some_interface ifc);
always_comb myvar = ifc.read_addr;
some_block just always references the 'read_addr' which is the modport var. You can use a generate block in the 'top' module.

Parameterized interface in systemverilog

I have a full AXI interface and a lite AXI interface as shown below:
interface axi_full ();
logic [63:0] wdata;
logic . wid;
logic wcredit;
endinterface
interface axi_lite();
logic [63:0] wdata;
logic wcredit;
endinterface
Is there a way I can use only one interface with a parameter lite something like below instead of two separate interfaces? The only difference is there is no wid in the case of AXI lite.
interface axi #(bit Lite = 1'b0) ();
...
...
endinterface
Unfortunately not. You should be able to use the axi_full and leave the wid signal unused.
There is nothing in the standard that would prevent from using generate blocks inside the interfaces. So, the following is supposed to work:
interface axi #(FULL=1)();
logic [63:0] wdata;
logic wid;
if (FULL != 0) begin: fff
logic wcredit;
end
endinterface
module top;
axi#(1) full();
axi#(0) lite();
imod fm(full);
//imod lm(lite);
endmodule
module imod(axi i);
initial begin
$display(i.wdata, i.wid, i.fff.wcredit);
end
endmodule
The problem as always is in vendor implementation. The above seems to work nicely in the cadence's nc. Uncommenting the 'lite' string will cause it to generate an error.
However, synopsys vcs, at least the one in eda playground refuses to compile it complaining about unimplemented feature of xmrs into interfaces.
Synthesis might have their own thoughts about this. So, the best way is to use separate definitions.
Also it depends on how you use them, modports can be handy. They are implemented better and the following example should work everywhere:
interface axi_if;
logic [63:0] wdata;
logic wid;
logic wcredit;
modport full (input wdata, wid, wcredit);
modport lite(input wdata, wid);
endinterface
module top;
axi_if intf();
fmod fm(intf);
lmod lm(intf);
endmodule
module fmod(axi_if.full i);
initial begin
$display(i.wdata, i.wid, i.wcredit);
end
endmodule
module lmod(axi_if.lite i);
initial begin
$display(i.wdata, i.wid); //, i.wcredit);
end
endmodule
What you are saying is not possible, but you can place wid signal inside `ifdef compile directive like following
interface axi();
logic [63:0] wdata;
`ifdef AXI_FULL
logic wid;
`endif
logic wcredit;
endinterface
define AXI_FULL when you are not using AXI-Lite
also take this thing into consideration while connecting axi interface

How to use a parameter to add or remove a signal from a system verilog interface and modport

Here is a snippet of some interface code that has some parameterized sizes to it. The fourth parameter, HAS_BURST is something I have experimented with, but it has only resulted in compilation errors.
Effectively I am looking for a way to ADD/REMOVE a signal from a interface based on parameter. Is there a way to have a generic interface with removable signals?
interface axi_if
#(parameter ID_WIDTH = 4,
ADDR_WIDTH = 40,
DATA_WIDTH = 64,
HAS_BURST = 0)
();
logic aw_ready;
logic aw_valid;
logic [ID_WIDTH-1:0] aw_bits_id;
logic [ADDR_WIDTH-1:0] aw_bits_addr;
logic [7:0] aw_bits_len;
logic [2:0] aw_bits_size;
generate
if (HAS_BURST)
logic [1:0] aw_bits_burst;
endgenerate
logic [2:0] aw_bits_size;
modport slave (
output aw_ready,
input aw_valid,
input aw_bits_id,
input aw_bits_addr,
input aw_bits_len,
generate
if (HAS_BURST)
input aw_bits_burst,
endgenerate
input aw_bits_size
);
modport master (
input aw_ready,
output aw_valid,
output aw_bits_id,
output aw_bits_addr,
output aw_bits_len,
generate
if (HAS_BURST)
output aw_bits_burst,
endgenerate
output aw_bits_size
);
endinterface
`endif
No, there isn't. Ports aren't valid in generate blocks. Parameters can be used to asjust the width of a port but not remove it entirely. You could use an `ifdef to compile it conditionally but that's an all-or-none solution. There can't be some instances with the signal and others without it.
Having the signal unconditionally present is fine in many situations and it's the easiest way to handle this problem. Tie any unused inputs to logic 0 and unused outputs can remain unconnected.
If neither of these options work there's no other way than to define two different interfaces. Doing this by hand quickly becomes unmaintainable. If there are two variations now you can be sure a third one will be needed soon, then a fourth, a fifth... Many chip design companies have SystemVerilog code generators which create customized modules for each instance.

SystemVerilog Interface in a vertical hierarchy

I'm implementing a system that uses an interface to connect two blocks that implements AXI4 lite functions.
Problem is the master and the slave blocks are inside other blocks.
The code above shows an example of what I'm trying to do:
interface bus
logic [ADDR_WIDTH-1:0] wr_addr; // Write address
logic [DATA_WIDTH-1:0] wr_data; // Write data
logic [ADDR_WIDTH-1:0] rd_addr; // Read address
logic [DATA_WIDTH-1:0] rd_data; // Read data
modport master (
output wr_addr,
output wr_data,
input rd_addr,
input rd_data
);
modport slave (
input wr_addr,
input wr_data,
output rd_addr,
output rd_data
);
endinterface
// innermost master block
module m0(bus.master bus_inst_m0);
// write/ read data from/to master modport
bus_inst_m0.wr_addr <= ...
if(bus_inst_m0.rd_addr == ...
endmodule
// instance of m0 block connected
// to interface
module m1(bus.master bus_inst_m1);
m0 m0_inst(.bus_inst_m0 (bus_inst_m1))
endmodule
// innermost slave block
module s0(bus.slave bus_inst_s0);
// write/ read data from/to slave modport
bus_inst_s0.rd_addr <= ...
if(bus_inst_s0.wd_addr == ...
endmodule
// instance of ms block connected
// to interface
module s1(bus.slave bus_inst_s1);
s0 s0_inst(.bus_inst_s0 (bus_inst_s1))
endmodule
// top module
module top();
bus local_bus;
m1 m1_inst (.bus_inst_m1 (local_bus));
s1 s1_inst (.bus_inst_s1 (local_bus));
endmodule
In Cadence Incisive simulator, the code compiles, but it not works. Looking in the schematic I see that the top interface is connected, but the inner interfaces are just open.
I made a small test connecting directly the master and slave blocks and it worked fine.
I have tried other combinations of interface/modport too, like using modport just in the innermost blocks, but it didn't work.
I read the LRM and did not found any clue about what I'm doing wrong.
Can anyone help?
Thanks
Rods
First of all, you have a bunch of options to use.
You can use generic interfaces as module parameters to avoid type errors. In this case, as long as the connected interface has all the signals that the module use, you will be okay. (And if the tool finds the proper interface...) Eg.:
module mod_gen_inf(interface gen_if);
endmodule
...
module top();
m_bus_if m_bus;
m_1 slave_actor(.gen_if(m_bus.master));
endmodule: top
Secondly, you can, but you are not required to specify the modport (type) at the module declaration. You can use the interface's name, and connect the specific modport at the module instantiation. Eg.:
module slave_actor(m_bus m_bus_if);
endmodule
...
module top();
m_bus_if m_bus;
m_1 slave_actor(.m_bus_if(m_bus.slave));
endmodule: top
Think of this as the modport is a compatible type of the given interface.
Thus a corrected/shortened version of your code:
interface m_bus;
logic [1:0] a;
modport master(input a);
modport slave (output a);
endinterface
module m1(m_bus.master bus_inst_m1);
m2 m2_inst(.bus_inst_m2(bus_inst_m1));
endmodule
module m2 (m_bus.master bus_inst_m2);
endmodule
module s1(m_bus.slave bus_inst_s1);
endmodule
module top();
m_bus local_bus();
m1 m1_inst (.bus_inst_m1 (local_bus.master));
s1 s1_inst (.bus_inst_s1 (local_bus.slave));
endmodule
This worked me fine in QuestaSim 10.5c.
If your code compiled but does not work, make sure that your innermost block sees the interface as well. (include the library that your IF is compiled into.)

Synchronous Counter

I'm trying to create a 32-bit synchronous counter using J-K flip-flops. I have a functional module for individual J-K flip-flops...
jkff(J, K, CLK, Q) where the first three are wire inputs and the last is a reg output.
I then have another functional module for the counter...
thirty_two(J, K, CLK, OUT[31:0]) where the first three are inputs and the last is output
In the thirty_two module, I instantiate many jkff modules, but I seem to be restricted to using wires as my output. Thus, OUT[31:0] is a wire instead of the desired reg I want.
Any suggestions?
A common mistake when starting out with verilog is thinking that wire & reg types have to match across hierarchy, they do not. A modules inputs are always wires and outputs can be regs or wires. Connectivity between modules are wires. The difference between usage of the two is purely down to how values are assigned or driven.
For example module thirty_two can use reg type to drive its output:
module thirty_two(
output reg [31:0] OUT
);
always #* begin
OUT = 32'bx;
end
endmodule
When instantiating thirty_two, outputs must drive wires. This make sense as the level that instantiates it can not directly change a sub modules output.
module top_level();
wire [31:0] thirty_two_out;
thirty_two thirty_two_i0 (
.OUT( thirty_two_out )
);
endmodule