Generate Blocks in Verilog
The generate statement in Verilog is a very useful construct that generates synthesizable code during elaboration time dynamically. The simulator provides an elaborated code of the ‘generate’ block. It provides the below facilities:
- To generate multiple module instances or code repetition.
- Conditionally instantiate a block of code based on the Verilog parameter, however, the parameter is not permitted in the generate statement.
It basically provides control on variables, functions, tasks, and instantiation declarations. A generate block has been written within generate and endgenerate keywords.
Types of generate instantiation
- Modules
- Verilog gate primitives
- Continuous assignments
- Initial and always blocks
- User-defined primitives
Let’s see what is allowed within the scope of a generate block.
A. Data types
- integer, real
- net, reg
- time, realtime
- event
B. Function and task
Note: Function and task are not allowed within a generate loop, but they are allowed in generate block.
Below module items/declarations are not allowed within the scope of a generate block
- Port declarations like input, output, and inout
- specify blocks
- parameters and local parameters
Methods to write generate statements
- Generate loop
- Generate conditional (includes generate if-else and generate case)
Generate loop
The generate loop is similar to the for loop statement, but it uses genvar keyword as a loop variable.
- The genvar keyword is only used during the evaluation of generate block and does not exist during the simulation of the design. It needs to be used by a generate loop.
- Generate loop provides flexibility to reduce code lines by replacing repetitive statements to a single statement like for loop.
- Similar to a for loop, generate loops also can be nested with different genvar as an index variable.
Example: Using always block inside generate block
genvar k;
generate
for (k = 0; k < 4; k++) begin
always@(posedge clk) begin
val[k] = a[k] & b[k];
end
end
endgenerate
This code expands to
always@(posedge clk) begin
val[0] = a[0] & b[0];
val[1] = a[1] & b[1];
val[2] = a[2] & b[2];
val[3] = a[3] & b[3];
end
Example: Ripple Carry Adder
module full_adder(
input a, b, cin,
output sum, cout
);
assign {sum, cout} = {a^b^cin, ((a & b) | (b & cin) | (a & cin))};
//or
//assign sum = a^b^cin;
//assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
module ripple_carry_adder #(parameter SIZE = 4) (
input [SIZE-1:0] A, B,
input Cin,
output [SIZE-1:0] S, Cout);
genvar g;
full_adder fa0(A[0], B[0], Cin, S[0], Cout[0]);
generate // This will instantial full_adder SIZE-1 times
for(g = 1; g<SIZE; g++) begin
full_adder fa(A[g], B[g], Cout[g-1], S[g], Cout[g]);
end
endgenerate
endmodule
Output:
A = 0001: B = 0000, Cin = 0 --> S = 0001, Cout[3] = 0, Addition = 1
A = 0010: B = 0100, Cin = 1 --> S = 0111, Cout[3] = 0, Addition = 7
A = 1011: B = 0110, Cin = 0 --> S = 0001, Cout[3] = 1, Addition = 17
A = 0101: B = 0011, Cin = 1 --> S = 1001, Cout[3] = 0, Addition = 9
Simulation complete via $finish(1) at time 12 NS + 0
Generate conditional
A generate block allows conditionally instantiated using if-else-if construct and case keyword.
Example: generate If-else
In the below example, based on parameter sel full adder or half-adder design is instantiated. By default, parameter sel = 0
means half adder will be instantiated. But from the testbench code, parameter sel = 1
is passed to instantiate full adder. $display can not be used within generate block without initial block, otherwise, it throws an error '$display' is an invalid generate scope construct.
module half_adder(
input a, b,
output sum, cout
);
assign {sum, cout} = {a^b, (a & b)};
//or
//assign sum = a^b;
//assign cout = a & b;
endmodule
module full_adder(
input a, b, cin,
output sum, cout
);
assign {sum, cout} = {a^b^cin, ((a & b) | (b & cin) | (a & cin))};
//or
//assign sum = a^b^cin;
//assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
module gen_if_ex #(parameter sel = 0)(
input A, B, Cin,
output S, Cout);
generate
if(sel) begin
initial $display("Full Adder is selected");
full_adder fa(A, B, Cin, S, Cout);
end
else begin
initial $display("Half Adder is selected");
half_adder ha(A, B, S, Cout);
end
endgenerate
endmodule
Output:
Full Adder is selected
A = 0: B = 1, Cin = 1 --> S = 0, Cout = 1
A = 1: B = 1, Cin = 1 --> S = 1, Cout = 1
A = 1: B = 0, Cin = 1 --> S = 0, Cout = 1
xmsim: *W,RNQUIE: Simulation is complete.
Example: generate case
Similarly, the above example if-else generate block can alternatively use case statement as specified in the below example.
module half_adder(
input a, b,
output sum, cout
);
assign {sum, cout} = {a^b, (a & b)};
//or
//assign sum = a^b;
//assign cout = a & b;
endmodule
module full_adder(
input a, b, cin,
output sum, cout
);
assign {sum, cout} = {a^b^cin, ((a & b) | (b & cin) | (a & cin))};
//or
//assign sum = a^b^cin;
//assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
module gen_if_ex #(parameter sel = 0)(
input A, B, Cin,
output S, Cout);
generate
case(sel)
0: begin
initial $display("Full Adder is selected");
half_adder ha(A, B, S, Cout);
end
1: begin
initial $display("Full Adder is selected");
full_adder fa(A, B, Cin, S, Cout);
end
endcase
endgenerate
endmodule
Output:
Full Adder is selected
A = 0: B = 1, Cin = 1 --> S = 0, Cout = 1
A = 1: B = 1, Cin = 1 --> S = 1, Cout = 1
A = 1: B = 0, Cin = 1 --> S = 0, Cout = 1
xmsim: *W,RNQUIE: Simulation is complete.
Verilog Tutorials