Reputation: 1262
I'm using SystemVerilog for synthesis. I fought with the fact that arrays of interfaces are not really arrays in SystemVerilog and the index has to be a constant value, but got over it using at lot of boilerplate generate for
and assign
statements to overcome what is really a language limitation (if I can emulate the effect using more code, the language could just do The Right Thing(tm) itself).
For the following pseudo-code, I leave out much of what's there in the real code (modports, tasks, etc) for clarity. I have an interface:
interface i_triplex();
logic a; // input wire
logic b; // output wire
logic [127:0] data; // output wires
endinterface
And I am passing an array of these interfaces to a module which looks like
module roundrobin_triplex#(
parameter int NUNITS = 8
)
(
input wire clk,
input wire rst,
i_triplex t[NUNITS]
);
always_ff @(posedge clk) begin
if (rst) begin
// insert code to initialize the "data" member
// in all interface instances in the array.
end
else begin
// ...
end
end
endmodule
What would your preferred way to use all interface instance in the array uniformly -- regardless of the value of NUNITS
? I have some suggestions, but I am eager to learn what other engineers can come up with.
Suggestion 1: Use VHDL.
Suggestion 2: Scrap the interface and do it oldschool Verilog-style, as in
module roundrobin_triplex#(
parameter int NUNITS = 8
)
(
input wire clk,
input wire rst,
// This was once a proud i_triplex array
input wire i_triplex_a[NUNITS],
input wire i_triplex_b[NUNITS],
input wire [127:0] i_triplex_data[NUNITS],
);
always_ff @(posedge clk) begin
if (rst) begin
for (int i = 0; i < NUNITS; i++)
i_triplex_data[i] <= '1;
end
else begin
// ...
end
end
endmodule
Suggestion 3:
Use a struct
for input wires and a struct
for output wires instead of the interface.
Suggestion 4:
Use a preprocessor-like system that unrolls generate for
loops inside processes (what the language should do anyway!), so the resulting code looks like (preprocessed with NUNITS=4):
module roundrobin_triplex#(
parameter int NUNITS = 8
)
(
input wire clk,
input wire rst,
i_triplex t[NUNITS]
);
always_ff @(posedge clk) begin
if (rst) begin
i_triplex.data[0] <= '1;
i_triplex.data[1] <= '1;
i_triplex.data[2] <= '1;
i_triplex.data[3] <= '1;
end
else begin
// ...
end
end
endmodule
Suggestion 5:
Use the generate for
/ assign
solution:
module roundrobin_triplex#(
parameter int NUNITS = 8
)
(
input wire clk,
input wire rst,
i_triplex t[NUNITS]
);
wire i_triplex_a[NUNITS];
wire i_triplex_b[NUNITS];
wire [127:0] i_triplex_data[NUNITS];
generate
genvar i;
// The wires of the interface which are to be driven
// from this module are assigned here.
for (i = 0; i < NUNITS; i++) begin
assign t[i].b = i_triplex_b[i];
assign t[i].data = i_triplex_data[i];
end
endgenerate
always_ff @(posedge clk) begin
if (rst) begin
for (int i = 0; i < NUNITS; i++)
i_triplex_data[i] <= '1;
end
else begin
// ...
end
end
endmodule
Upvotes: 7
Views: 8360
Reputation: 11
Maybe I'm missing something by why not putting all yours code from roundrobin_triplex into generate (you do not need extra assigns)
interface i_triplex();
logic a; // input wire
logic b; // output wire
logic [127:0] data; // output wires
initial $fmonitor(1,data);
endinterface
module roundrobin_triplex#(parameter int NUNITS = 8)
(
input wire clk,
input wire rst,
i_triplex t[NUNITS]
);
genvar i;
for( i=0; i<NUNITS; i++)begin
always_ff @(posedge clk) begin
if (rst) begin
t[i].data <=0;
end
else begin
t[i].data <=t[i].data+1;
end
end
end
endmodule
module top;
parameter P=4;
bit clk,rst;
i_triplex ii[P]();
roundrobin_triplex #(P)uut(clk,rst,ii);
initial begin
rst=1;
repeat(2) @(posedge clk);
rst=0;
repeat(10) @(posedge clk);
$finish;
end
always #5 clk =~clk;
endmodule
Upvotes: 0
Reputation: 12384
how about suggestion #6, use parameterized interface:
interface I #(NPORTS = 8);
logic clk;
logic a[NPORTS];
logic b[NPORTS];
logic [127:0] data [NPORTS];
endinterface //
module roundrobin#(NUMPORTS = 8) (I t);
logic [127:0] data[NUMPORTS];
always_ff @(posedge t.clk) begin
data <= t.data;
end
endmodule // roundrobin
Note, that you do not need the loop in system verilog. you can use array assignments:
data <= t.data;
and for the sake of convenience you can add functions or statements to the interface itself, i.e.
interface I #(NPORTS = 8);
logic clk;
logic a[NPORTS];
logic b[NPORTS];
logic [127:0] data [NPORTS];
function logic [127:0] getData(int n);
return data[n];
endfunction // getData
endinterface // I
and use
data[i] <= t.getData(i);
Sorry, the above example is probably not very useful, but it might give you an idea.
Upvotes: 2
Reputation: 42788
Arrays of module or interface instances cannot be treated as regular arrays because parameterization, generate blocks, and defparam statements can make elements of the array instance non-unique. That cannot happen with arrays of variables/wires.
My suggestion would be a modification of your suggestion 2; put arrays of variables/wires inside a single interface instance.
Upvotes: 2
Reputation: 626
Suggestion 1: VHDL may be a practical. However, it seems to become marginal in industry.
Suggestion 2: In my opinion, Interfaces are relevant if you intent to reuse it widely and implement verification/protocols into it. If you can unpack your interface like this, while keeping your sanity, then I do not see a justification for an interface in the first place.
Suggestion 3: I never tried to synthesise struct
, it may be a good idea.
Suggestion 4: Simple solution,although quite verbose .
Suggestion 5: I used something similar for one of my project. However, I wrote a sort of adapter module to hide the assigns.
Actually, when I need something like that, I try to write an atomic module which operates on a fixed number of interfaces. Then, I use for generate
structures to generalize it on an interface array.
Upvotes: 0