Guest
ABC,
The generated instantiation allows recursion in Verilog.
I was told that's officially allowed in 2005 LRM - haven't seen copy
yet.
For example in the biton_merge module the generate loop instantiates
another two instances biton_merge module (both half the size of the
original one)
Of course you have to terminate recursion by using the if statement.
That's for synthesizable code. You can also use recursion for function
calls and tasks (they should be declared "automatic" to preserve local
signals)
Thanks,
Przemek
Code snipet:
ABC wrote:
The generated instantiation allows recursion in Verilog.
I was told that's officially allowed in 2005 LRM - haven't seen copy
yet.
For example in the biton_merge module the generate loop instantiates
another two instances biton_merge module (both half the size of the
original one)
Of course you have to terminate recursion by using the if statement.
That's for synthesizable code. You can also use recursion for function
calls and tasks (they should be declared "automatic" to preserve local
signals)
Thanks,
Przemek
Code snipet:
..module biton_merge (
data_in,
data_out
);
..
generate
if(DATA_ARRAY_SIZE_EXP>1) begin: nest
wire [DATA_UPPER_HALF_OFFSET-1:0] data_merge_1;
wire [DATA_UPPER_HALF_OFFSET-1:0] data_merge_0;
biton_merge
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_merge_0
(
.data_in(data_div[ DATA_UPPER_HALF_OFFSET-1:0]),
.data_out(data_merge_0)
);
ABC wrote:
Hi Przemek,
Which statement of verilog allow recursive instantiation?
Best regards,
ABC
bazarnik@hotmail.com wrote:
Dear All,
FYI this issue was confirmed to be a bug in Design Compiler
(hopefully fixed in a next service pack)
I recoded the bitionic sort in an iterative form.
I would not recommend it unless one feels a need for a some of
the "human compiler" type of brain workout.
Actually doing the b.sort with recursion in Verilog is not really
that complex (see below) One just needs to use modules to avoid
too many nested loops and to keep things neat.
I did try Confluence quite some time earlier. It is a nice tool.
I have found the syntax to be quite "peculiar" but still better
than HDCaml bracket flood.
We do not have that much datapath code which would warrant
adding Confluence to our ASIC flow. This may change though.
Ultimately Confluence type languages are thay way HW design must go.
SystemVerilog interfaces and other stuff are another way to introduce
high level hardware design features.
Thanks,
Przemek
PS For you viewing pleasure:
the bitionic sort implemented in Verilog 2001 with recursive
instantiation.
Module list:
system (top level)
biton_sort
biton_merge
biton_bn
biton_reverse
I leave the iterative version as an excercise for the reader
module biton_bn (
data_in,
data_out
);
// Global parameters
parameter DATA_WIDTH = 4;
parameter DATA_ARRAY_SIZE_EXP = 2; // array is 2^DATA_ARRAY_SIZE_POW
elements this is equal to N for B bitonic sort operator
// Local parameters
// data organized in following way:
// single long vector: { data3[3:0], data2[3:0], data1[3:0],
data0[3:0]}
parameter DATA_ARRAY_SIZE = (1<<DATA_ARRAY_SIZE_EXP); // array is
2^DATA_ARRAY_SIZE_POW elements
parameter DATA_ARRAY_MSB = (1<<DATA_ARRAY_SIZE_EXP) * DATA_WIDTH -1;
parameter DATA_UPPER_HALF_OFFSET = DATA_WIDTH * (DATA_ARRAY_SIZE>>1);
input [DATA_ARRAY_MSB:0] data_in;
output reg [DATA_ARRAY_MSB:0] data_out;
// ===============
genvar i;
generate for(i=0;i<(DATA_ARRAY_SIZE>>1);i=i+1) begin: comp
always @*
if( data_in[ (i+1)*DATA_WIDTH-1 : i*DATA_WIDTH] > data_in[
(i+1)*DATA_WIDTH-1+DATA_UPPER_HALF_OFFSET :
i*DATA_WIDTH+DATA_UPPER_HALF_OFFSET] ) begin
// swap
data_out[ (i+1)*DATA_WIDTH-1 : i*DATA_WIDTH] = data_in [
(i+1)*DATA_WIDTH-1+DATA_UPPER_HALF_OFFSET :
i*DATA_WIDTH+DATA_UPPER_HALF_OFFSET];
data_out[ (i+1)*DATA_WIDTH-1+DATA_UPPER_HALF_OFFSET :
i*DATA_WIDTH+DATA_UPPER_HALF_OFFSET] = data_in [ (i+1)*DATA_WIDTH-1 :
i*DATA_WIDTH];
end
else begin
// keep the same
data_out[ (i+1)*DATA_WIDTH-1 : i*DATA_WIDTH] = data_in [
(i+1)*DATA_WIDTH-1 : i*DATA_WIDTH];
data_out[ (i+1)*DATA_WIDTH-1+DATA_UPPER_HALF_OFFSET :
i*DATA_WIDTH+DATA_UPPER_HALF_OFFSET] = data_in [
(i+1)*DATA_WIDTH-1+DATA_UPPER_HALF_OFFSET :
i*DATA_WIDTH+DATA_UPPER_HALF_OFFSET];
end
end
endgenerate
endmodule
module biton_merge (
data_in,
data_out
);
// Global parameters
parameter DATA_WIDTH = 4;
parameter DATA_ARRAY_SIZE_EXP = 2; // array is 2^DATA_ARRAY_SIZE_POW
elements this is equal to N for B bitonic sort operator
// Local parameters
// data organized in following way:
// single long vector: { data3[3:0], data2[3:0], data1[3:0],
data0[3:0]}
parameter DATA_ARRAY_SIZE = (1<<DATA_ARRAY_SIZE_EXP); // array is
2^DATA_ARRAY_SIZE_POW elements
parameter DATA_ARRAY_MSB = (DATA_ARRAY_SIZE * DATA_WIDTH) -1;
parameter DATA_ARRAY_SIZE_EXP_M1 = DATA_ARRAY_SIZE_EXP-1;
parameter DATA_UPPER_HALF_OFFSET = DATA_WIDTH * (DATA_ARRAY_SIZE>>1);
input [DATA_ARRAY_MSB:0] data_in;
output [DATA_ARRAY_MSB:0] data_out;
// ===============
wire [DATA_ARRAY_MSB:0] data_div;
biton_bn
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP)
)
biton_bn_1
(
.data_in(data_in),
.data_out(data_div)
);
generate
if(DATA_ARRAY_SIZE_EXP>1) begin: nest
wire [DATA_UPPER_HALF_OFFSET-1:0] data_merge_1;
wire [DATA_UPPER_HALF_OFFSET-1:0] data_merge_0;
biton_merge
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_merge_0
(
.data_in(data_div[ DATA_UPPER_HALF_OFFSET-1:0]),
.data_out(data_merge_0)
);
biton_merge
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_merge_1
(
.data_in(data_div[ DATA_ARRAY_MSBATA_UPPER_HALF_OFFSET]),
.data_out(data_merge_1)
);
assign data_out = {data_merge_1, data_merge_0};
end
else begin: last
assign data_out = data_div;
end
endgenerate
endmodule
module biton_reverse (
data_in,
data_out
);
// Global parameters
parameter DATA_WIDTH = 4;
parameter DATA_ARRAY_SIZE_EXP = 2; // array is 2^DATA_ARRAY_SIZE_POW
elements this is equal to N for B bitonic sort operator
// Local parameters
// data organized in following way:
// single long vector: { data3[3:0], data2[3:0], data1[3:0],
data0[3:0]}
parameter DATA_ARRAY_SIZE = (1<<DATA_ARRAY_SIZE_EXP); // array is
2^DATA_ARRAY_SIZE_POW elements
parameter DATA_ARRAY_MSB = (DATA_ARRAY_SIZE * DATA_WIDTH) -1;
input [DATA_ARRAY_MSB:0] data_in;
output [DATA_ARRAY_MSB:0] data_out;
// ===============
genvar i;
generate for(i=0;i<DATA_ARRAY_SIZE;i=i+1) begin: rev
assign data_out[ (i+1)*DATA_WIDTH-1 : i*DATA_WIDTH] = data_in[
(DATA_ARRAY_SIZE-i)*DATA_WIDTH-1 : (DATA_ARRAY_SIZE-i-1)*DATA_WIDTH];
end
endgenerate
endmodule
module biton_sort (
data_in,
data_out
);
// Global parameters
parameter DATA_WIDTH = 4;
parameter DATA_ARRAY_SIZE_EXP = 2; // array is 2^DATA_ARRAY_SIZE_POW
elements this is equal to N for B bitonic sort operator
// Local parameters
// data organized in following way:
// single long vector: { data3[3:0], data2[3:0], data1[3:0],
data0[3:0]}
parameter DATA_ARRAY_SIZE = (1<<DATA_ARRAY_SIZE_EXP); // array is
2^DATA_ARRAY_SIZE_POW elements
parameter DATA_ARRAY_MSB = (DATA_ARRAY_SIZE * DATA_WIDTH) -1;
parameter DATA_ARRAY_SIZE_EXP_M1 = DATA_ARRAY_SIZE_EXP-1;
parameter DATA_UPPER_HALF_OFFSET = DATA_WIDTH * (DATA_ARRAY_SIZE>>1);
input [DATA_ARRAY_MSB:0] data_in;
output [DATA_ARRAY_MSB:0] data_out;
// ===============
wire [DATA_ARRAY_MSB:0] data_conq;
generate
if(DATA_ARRAY_SIZE_EXP>1) begin: nest
wire [DATA_UPPER_HALF_OFFSET-1:0] data_sort_1;
wire [DATA_UPPER_HALF_OFFSET-1:0] data_sort_0;
wire [DATA_UPPER_HALF_OFFSET-1:0] data_rev_0;
wire [DATA_UPPER_HALF_OFFSET-1:0] data_rev_1;
biton_sort
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_sort_0
(
.data_in(data_in[ DATA_UPPER_HALF_OFFSET-1:0]),
.data_out(data_sort_0)
);
biton_reverse
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_reverse_1_0
(
.data_in(data_in[ DATA_ARRAY_MSBATA_UPPER_HALF_OFFSET]),
.data_out(data_rev_0)
);
biton_sort
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_sort_1
(
.data_in(data_rev_0),
.data_out(data_rev_1)
);
biton_reverse
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP_M1)
)
biton_reverse_1_1
(
.data_in(data_rev_1),
.data_out(data_sort_1)
);
assign data_conq = {data_sort_1, data_sort_0};
end
else begin: last
assign data_conq = data_in;
end
endgenerate
biton_merge
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP)
)
biton_merge_1
(
.data_in(data_conq),
.data_out(data_out)
);
endmodule
module system;
parameter DATA_WIDTH = 4;
parameter DATA_ARRAY_SIZE_EXP = 3; // array is 2^DATA_ARRAY_SIZE_POW
elements
// ==================
// data organized in following way:
// single long vector: { data3[3:0], data2[3:0], data1[3:0],
data0[3:0]}
parameter DATA_ARRAY_MSB = (1<<DATA_ARRAY_SIZE_EXP) * DATA_WIDTH -1;
wire [DATA_ARRAY_MSB:0] data_in = {4'h1, 4'h3, 4'h5, 4'h2, 4'hf, 4'hb,
4'h0, 4'ha };
wire [DATA_ARRAY_MSB:0] data_out;
biton_sort
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP)
)
biton_sort_1
(
.data_in(data_in),
.data_out(data_out)
);
/*
biton_merge
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP)
)
biton_merge_1
(
.data_in(data_in),
.data_out(data_out)
);
biton_bn
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_ARRAY_SIZE_EXP(DATA_ARRAY_SIZE_EXP)
)
biton_bn_1
(
.data_in(data_in),
.data_out(data_out)
);
*/
endmodule