Verilog generate statement is a powerful construct for writing configurable, synthesizable RTL. It can be used to create multiple instantiations of modules and code, or conditionally instantiate blocks of code. However, many Verilog programmers often have questions about how to use Verilog generate effectively. In this article, I will review the usage of three forms of Verilog generate—generate loop, if-generate, and case-generate.
Types of Verilog Generate Constructs
There are two kinds of Verilog generate constructs. Generate loop constructs allow a block of code to be instantiated multiple times, controlled by a variable index. Conditional generate constructs select at most one block of code between multiple blocks. Conditional generate constructs include if-generate and case-generate forms.
Verilog generate constructs are evaluated at elaboration, which occurs after parsing the HDL (and preprocessor), but before simulation begins. Therefore all expressions within generate constructs must be constant expressions, deterministic at elaboration time. For example, generate constructs can be affected by values from parameters, but not by dynamic variables.
A Verilog generate block creates a new scope and a new level of hierarchy, almost like instantiating a module. This sometimes causes confusion when trying to write a hierarchical reference to signals or modules within a generate block, so it is something to keep in mind.
Use of the keywords generate and endgenerate (and begin/end) is actually optional. If they are used, then they define a generate region. Generate regions can only occur directly within a module, and they cannot nest. For readability, I like to use the generate and endgenerate keywords.
Verilog Generate Loop
The syntax for a generate loop is similar to that of a for loop statement. The loop index variable must first be declared in a genvar declaration before it can be used. The genvar is used as an integer to evaluate the generate loop during elaboration. The genvar declaration can be inside or outside the generate region, and the same loop index variable can be used in multiple generate loops, as long as the loops don’t nest.
Within each instance of the “unrolled” generate loop, an implicit localparam is created with the same name and type as the loop index variable. Its value is the “index” of the particular instance of the “unrolled” loop. This localparam can be referenced from RTL to control the generated code, and even referenced by a hierarchical reference.
Generate block in a Verilog generate loop can be named or unnamed. If it is named, then an array of generate block instances is created. Some tools warn you about unnamed generate loops, so it is good practice to always name them.
The following example shows a gray to binary code converter written using a Verilog generate loop.
Example of parameterized gray to binary code converter module gray2bin #(parameter SIZE = 8) ( input [SIZE-1:0] gray, output [SIZE-1:0] bin ) Genvar gi; // generate and endgenerate is optional // generate (optional) for (gi=0; gi<SIZE; gi=gi+1) begin : genbit assign bin[gi] = ^gray[SIZE-1:gi]; // Thanks Dhruvkumar! end // endgenerate (optional) endmodule
Another example from the Verilog-2005 LRM illustrates how each iteration of the Verilog generate loop creates a new scope. Notice wire t1, t2, t3 are declared within the generate loop. Each loop iteration creates a new t1, t2, t3 that do not conflict, and they are used to wire one generated instance of the adder to the next. Also note the naming of the hierarchical reference to reference an instance within the generate loop.
module addergen1 #(parameter SIZE = 4) ( input logic [SIZE-1:0] a, b, input logic ci, output logic co, output logic [SIZE-1:0] sum ); wire [SIZE :0] c; genvar i; assign c[0] = ci; // Hierarchical gate instance names are: // xor gates: bitnum[0].g1 bitnum[1].g1 bitnum[2].g1 bitnum[3].g1 // bitnum[0].g2 bitnum[1].g2 bitnum[2].g2 bitnum[3].g2 // and gates: bitnum[0].g3 bitnum[1].g3 bitnum[2].g3 bitnum[3].g3 // bitnum[0].g4 bitnum[1].g4 bitnum[2].g4 bitnum[3].g4 // or gates: bitnum[0].g5 bitnum[1].g5 bitnum[2].g5 bitnum[3].g5 // Gate instances are connected with nets named: // bitnum[0].t1 bitnum[1].t1 bitnum[2].t1 bitnum[3].t1 // bitnum[0].t2 bitnum[1].t2 bitnum[2].t2 bitnum[3].t2 // bitnum[0].t3 bitnum[1].t3 bitnum[2].t3 bitnum[3].t3 for(i=0; i<SIZE; i=i+1) begin:bitnum wire t1, t2, t3; xor g1 ( t1, a[i], b[i]); xor g2 ( sum[i], t1, c[i]); and g3 ( t2, a[i], b[i]); and g4 ( t3, t1, c[i]); or g5 ( c[i+1], t2, t3); end assign co = c[SIZE]; endmodule
Generate loops can also nest. Only a single generate/endgenerate is needed (or none, since it’s optional) to encompass the nested generate loops. Remember each generate loop creates a new scope. Therefore the hierarchical reference to the inner loop needs to include the label of the outer loop.
Conditional If-Generate
Conditional if-generate selects at most one generate block from a set of alternative generate blocks. Note I say at most, because it may also select none of the blocks. The condition must again be a constant expression during elaboration.
Conditional if-generate may be named or unnamed, and may or may not have begin/end. Either way, it can contain only one item. It also creates a separate scope and level of hierarchy, like a generate loop. Since conditional generate selects at most one block of code, it is legal to name the alternative blocks of code within the single if-generate with the same name. That helps to keep hierarchical reference to the code common regardless of which block of code is selected. Different generate constructs, however, must have different names.
Conditional Case-Generate
Similar to if-generate, case-generate can also be used to conditionally select one block of code from several blocks. Its usage is similar to the basic case statement, and all rules from if-generate also apply to case-generate.
Direct Nesting of Conditional Generate
There is a special case where nested conditional generate blocks that are not surrounded by begin/end can consolidate into a single scope/hierarchy. This avoids creating unnecessary scope/hierarchy within the module to complicate the hierarchical reference. This special case does not apply at all to loop generate.
The example below shows how this special rule can be used to construct complex if-else if conditional generate statements that belong to the same hierarchy.
module test; parameter p = 0, q = 0; wire a, b, c; //--------------------------------------------------------- // Code to either generate a u1.g1 instance or no instance. // The u1.g1 instance of one of the following gates: // (and, or, xor, xnor) is generated if // {p,q} == {1,0}, {1,2}, {2,0}, {2,1}, {2,2}, {2, default} //--------------------------------------------------------- if (p == 1) if (q == 0) begin : u1 // If p==1 and q==0, then instantiate and g1(a, b, c); // AND with hierarchical name test.u1.g1 end else if (q == 2) begin : u1 // If p==1 and q==2, then instantiate or g1(a, b, c); // OR with hierarchical name test.u1.g1 end // "else" added to end "if (q == 2)" statement else ; // If p==1 and q!=0 or 2, then no instantiation else if (p == 2) case (q) 0, 1, 2: begin : u1 // If p==2 and q==0,1, or 2, then instantiate xor g1(a, b, c); // XOR with hierarchical name test.u1.g1 end default: begin : u1 // If p==2 and q!=0,1, or 2, then instantiate xnor g1(a, b, c); // XNOR with hierarchical name test.u1.g1 end endcase endmodule
This generate construct will select at most one of the generate blocks named u1. The hierarchical name of the gate instantiation in that block would be test.u1.g1. When nesting if-generate constructs, the else always belongs to the nearest if construct. Note the careful placement of begin/end within the code Any additional begin/end will violate the direct nesting requirements, and cause an additional hierarchy to be created.
Named vs Unnamed Generate Blocks
It is recommended to always name generate blocks to simplify hierarchical reference. Moreover, various tools often complain about anonymous generate blocks. However, if a generate block is unnamed, the LRM does describe a fixed rule for how tools shall name an anonymous generate block based on the text of the RTL code.
First, each generate construct in a scope is assigned a number, starting from 1 for the generate construct that appears first in the RTL code within that scope, and increases by 1 for each subsequent generate construct in that scope. The number is assigned to both named and unnamed generate constructs. All unnamed generate blocks will then be given the name genblk[n] where [n] is the number assigned to its enclosing generate construct.
It is apparent from the rule that RTL code changes will cause the unnamed generate construct name to change. That in turn makes it difficult to maintain hierarchical references in RTL and scripts. Therefore, it is recommended to always name generate blocks.
Conclusion
Verilog generate constructs are powerful ways to create configurable RTL that can have different behaviours depending on parameterization. Generate loop allows code to be instantiated multiple times, controlled by an index. Conditional generate, if-generate and case-generate, can conditionally instantiate code. The most important recommendation regarding generate constructs is to always name them, which helps simplify hierarchical references and code maintenance.
Great article! I often use for-generate loops but I didn’t know about existing of if- and case-generate conditions 🙂
Thanks for the Posting. I recently saw a “conditional generate” code that I wasn’t aware of. Good to see this detailed explanation.
This is superb. Thanks for this great resource Jason!
Hi Jason,
Is there a way to use generate blocks for signal declarations?
When I check with the systemverilog LRM I see this sentence, “A generate block may not contain port declarations, specify blocks, or specparam declarations.”.
What about internal signal declarations?
For instance, if I have two different structs with different fields inside the structs, and I want to choose between the two types for an internal signal declaration type (note not a port) something like the below gives a compile error saying the signal is not declared.
parameter integer unsigned some_parameter = 1;
generate
if (some_parameter == 1) begin
type_a x;
end else if (some_parameter = 0) begin
type_b x;
end
endgenerate
Then at some point below X is referenced.
Thanks in advance!
Hi Sami. That’s an interesting question. I tried something similar to your code and also encountered an error during compilation that the type is unknown. I suspect it’s because generate statement is only resolved during elaboration, but the type needs to already exist during compilation. How I usually handle something like that is if the parameter only affects a field within the struct, I parameterize that field to use the parameter, rather than use generate to select between two structs. Also, you can pass a TYPE as a parameter into a module, and use that TYPE in the struct. That may also achieve what you’re looking for.
Thanks for the quick reply!
Yes, what you suggested is the first approached I tried. I have parameters for the width of the fields in the struct. My hope with that approach is if the width parameter is set to 0 then that field should not exist. I wasn’t sure if that would be reality of what would happen. I noticed that in simulations that field shows up with negative index. For example:
parameter FIELD_WIDTH = 0;
…
logic [FIELD_WIDTH-1:0] x;
This will show up as x[-1:0] in waveforms. What I don’t know is if this is a tool quirk or this will actually synthesize to two bits.
Hi Jason,
Great article. Is it possible to change the loop index inside the generate block itself? For example, in a generate block, I have an if statement which generates two instantiations and in the else statement it instantiates one. So I’d like to increment the loop index in the if statement itself. Is it possible?
Regards,
Kunal
Nice post. Seeing that you are using instances of logic gates. Guess I can instantiate any other module, but what about processes such as “always” or “assign”?
Yes you can also wrap always and assign statements in a generate.
Beautifully written, terse and clear!
Great article but there are several errors,
module for_loop_synthesis (i_Clock);
input i_Clock;
integer ii=0;
reg [3:0] r_Shift_With_For = 4’h1;
reg [3:0] r_Shift_Regular = 4’h1;
// Performs a shift left using a for loop
always @(posedge i_Clock)
begin
for(ii=0; ii<3; ii=ii+1)
r_Shift_With_For[ii+1] <= r_Shift_With_For[ii];
end
// Performs a shift left using regular statements
always @(posedge i_Clock)
begin
r_Shift_Regular[1] <= r_Shift_Regular[0];
r_Shift_Regular[2] <= r_Shift_Regular[1];
r_Shift_Regular[3] <= r_Shift_Regular[2];
end
endmodule
module for_loop_synthesis_tb (); // Testbench
reg r_Clock = 1'b0;
// Instantiate the Unit Under Test (UUT)
for_loop_synthesis UUT (.i_Clock(r_Clock));
always
#10 r_Clock = !r_Clock;
endmodule
module gray2bin
#(parameter SIZE = 8)
(
input [SIZE-1:0] gray,
output [SIZE-1:0] bin
);
generate
genvar gi;
// generate and endgenerate is optional
// generate (optional)
for (gi=0; gi<SIZE; gi=gi+1) begin : genbit
assign bin[gi] = ^gray[SIZE-1:gi];
end
// endgenerate (optional)
endgenerate
endmodule
module gray2bin_tb(); //Testbench
reg [15:0] in,out;
gray2bin #(16) DUT ( .gray(in), .bin(out) );
integer jj=0;
initial begin
for(jj=0;jj<16;jj=jj+1) begin
in=jj;
#5;
end
end
endmodule
i have corrected all of them for you
thank you for making this cheers
Thanks for spotting the typo!
Nice work… Keep going…. Nice page…
Sir gray2bin code is synthesizable yes not and can I implement on the FPGA board
Yes it is certainly synthesizable and can be implemented in FPGA.
Nice article Jason, very helpful for beginners.
in the for loop,what is the meaning of the” gi=0; gi<SIZE; gi=gi+1″ .should gi < size