passing generated modports to instances of the same module - system-verilog

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.

Related

Is a struct packed allowed to be used in ports?

Most of the tools I use allow it but one doesn't. I've read the specs, IEEE1800-2017 I couldn't find it.
module mymod (
input logic clk,
input logic reset,
input struct packed {
logic [1:0] var0;
logic [1:0] var1;
logic [8:0] var2;
} addr,
...
I saw some examples here and there, using even typedef structures in ports.
Is it allowed by the specs? Where?
Cf. 7.2 Structures and 7.2.1 Packed structures
A port can be any data type. (section 23.2.2) There are some restrictions on whether that datatype can be represented by a variable or net signal that interact with the port direction.
But I would strongly discourage the use of an anonymous type (struct in your example) and instead declare a user defined type with a typedef in a common package and use that typedef when declaring that port. That eliminates type compatibility issues when trying to connect unpacked struct and enums.

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.

verilog_mode autoreginput behavior when using assignment

I wonder if the following case is possible.
I have :
module a(
input [2:0] a_i
);
endmodule
module b ();
/*AUTOREGINPUTS*/
a u_a(/*AUTOINST*/)
endmodule
It expands to:
module b ();
/*AUTOREGINPUTS*/
reg [2:0] a_i;
a u_a(/*AUTOINST*/
.a_i(a_i))
endmodule
But if I modify adding the line assign a_i = '0;, then it does not expands AUTOREGINPUTS anymore. Is there a way to expand it even if I'm doing an assignment ?
The short answer is because when running verilog-auto to fill in /*AUTOREGINPUT*/ will exclude any signal that is already declared and by adding assign a_i = '0;, you are declaring a_i.
In Verilog, explicit variable declarations are not required and will take on the default nettype if left undeclared under certain circumstances. So, if I had the following:
module x;
assign myVar = '0;
endmodule
myVar will be implicitly declared to be a net with the default nettype (which by default is wire). You can read more in the System-Verilog LRM (IEEE1800-2009 Section 6.10). One recommendation to avoid typos generating implicitly declared variables is to change the default nettype with the `default_nettype macro to none (ie `default_nettype none on the top of every file); doing this forces all variables to be explicitly declared or the compiler/synthesizer will throw an error.
verilog-mode mode in emacs is aware of implicit declaration and, as such, will not autogenerate anything declared. Thus, when you add the assign statement, you are declaring a_i and so the autogenerator will not "redefine" a_i.
To avoid this, I can only recommend running the generator before you assign any of the variables to be autogenerated. Im not sure if it handles `default_nettype none correctly, but I would assume not.
Also note, it should be /*AUTOREGINPUT*/, not /*AUTOREGINPUTS*/, no 's' at the end.

any way how to define msb:lsb range as parameter?

having tons of registers defined in my hardware, containing bit fields, I wanted to 'name' those registers and access in SystemVerilog the bit fields using their names instead of msb:lsb format. So, I made a new package, and declared constant parameters inside, and as well tried those which describe range. Something like this:
package VmeAddressMap;
parameter SYS_INTCONFIG = 32'h00000044;
parameter RSYS_INTCONFIGRORA = 31:16;
parameter RSYS_INTCONFIGENABLE = 15:0;
endpackage // VmeAddressMap
quite evidently, this does not work. So I came with a 'hybrid' solution, i.e. simple constants stay in package, and for ranges I made another file, which contains macros:
package file:
package VmeAddressMap;
parameter SYS_INTCONFIG = 32'h00000044;
endpackage // VmeAddressMap
macro file:
`define RSYS_INTCONFIGRORA 31:16
`define RSYS_INTCONFIGENABLE 15:0
this solution permits me to do things as follow (Read is a task reading data through VME bus):
Read(SYS_INTCONFIG);
`CHECK_EQUAL(LastVmeReadData_b32[`RSYS_INTCONFIGRORA], 15,
"IRQ setup invalid");
This works, and does what I want. However I don't like it. In particular mixing macros with SystemVerilog style of description.
Is there a way how to accomplish the same task directly in the package?
This is exactly what the UVM register abstraction layer does for you. You define fields giving them a name, bit width, and other attributes. Those fields are grouped into a registers, and registers are grouped into blocks with addresses and offsets.
Now that I've told you that, here's a simple solution that does what you are looking for using the let construct.
package VmeAddressMap;
parameter SYS_INTCONFIG = 32'h00000044;
let RSYS_INTCONFIGRORA(field) = field[31:16];
let RSYS_INTCONFIGENABLE(field) = field[15:0];
endpackage // VmeAddressMap
But now you have to put the range in front of the variable.
`CHECK_EQUAL(RSYS_INTCONFIGRORA(LastVmeReadData_b32), 15,
"IRQ setup invalid");
You can use one parameter for the MSB and another for the LSB.
parameter RSYS_INTCONFIGRORA_MSB = 31;
parameter RSYS_INTCONFIGRORA_LSB = 16;
LastVmeReadData_b32[RSYS_INTCONFIGRORA_MSB:RSYS_INTCONFIGRORA_LSB]
That is a bit unwieldy, so if everything is 16 bits wide you can just define the LSB:
parameter RSYS_INTCONFIGRORA = 16;
LastVmeReadData_b32[RSYS_INTCONFIGRORA +: 16]
Or, you can use a struct:
typedef struct packed {
logic [15:0] RSYS_INTCONFIGRORA;
logic [15:0] RSYS_INTCONFIGENABLE;
} some_register_t;
You can further make a union with that struct if some parts of the design need to interact with the whole register object and others with just the bit fields.
These register structs can be built up into a much larger register map struct.
If you are using UVM then you should be building up a RAL module of your registers

How to pass a class between two modules?

I have two modules and a class and I would like to move that class from one module to the other. Something like this:
class foo;
int x;
int y;
endclass
module mod_A(output foo foo_inst, output event trig);
initial begin
foo my_foo = new;
my_foo.x = 1;
my_foo.y = 2;
foo_inst = my_foo;
->trig;
end
endmodule
module mod_B(input foo foo_inst, input event trig);
always #(trig) begin
$display("%d%d, is a funky number.", foo_inst.x, foo_inst.y);
end
endmodule
module top();
event trig;
foo foo_inst;
mod_A mod_A(.trig, .foo_inst);
mod_B mod_B(.trig, .foo_inst);
endmodule
Of course there're also some functions defined in the class which are used in each module.
The issue with this setup is that I'm seeing errors for each ports of mod_B:
Error-[RIPLIP] Register in low conn of input port
Non-net variable 'foo_inst' cannot be an input or inout port.
Non-net variable 'trig' cannot be an input or inout port.
EDAplayground does not support class objects as module ports and 1800-2012 only mentions interface declarations, events, arrays structures or unions in Port declarations (23.2.2) so my questions are:
Is it even legal to pass classes through ports? If not, what is an elegant
method of accomplishing this?
What does "Register in low conn of
input port" mean? I'm aware that this might be a compiler specific
error and nothing indicative but if I knew what it was trying to tell me I might be a step closer to fixing this.
A variable of any type can be an input or output port. You might have to write for your compiler
input var foo foo_inst,
But it would be better to use a ref when a port is really a handle.
module mod_A(ref foo foo_inst, ref event trig);
Note that you have a typo with foo_o or foo_inst and a race condition between a trigger ->trig and an event control #(trig).