Generate blocks are a mechanism by which we can generate lots of Verilog. To quote the Sutherland HDL guide, "generate blocks provide control over the creation of many types of module items. A generate block must be defined within a module, and is used to generate code within that module."
Within these generate blocks, you can do things like declare variables, instantiate modules, wire modules together, etc. And furthermore, you can do these things conditionally. Usually, the conditions within a generate block will depend on the value of one or a number of genvars
.
A genvar
is "an integer variable which must be a positive value. They may only be used within a generate block. Genvar
variables only have a value during elaboration, and do not exist during simulation. Genvar
variables must be declared within the module where the genvar
is used. They may be declared either inside or outside of a generate block." (Evans and Sutherland HDL guide).
Because generate blocks are only evaluated at elaboration, any conditionals within a generate block must have arguments which are constant expressions. Recall that the generate blocks are being used to build hardware. It therefore must be the case that all conditionals are evaluatable at compile time. If not, that would suggest we were building hardware at runtime, which doesn't make sense.
Suppose that we wanted to construct a shift register, like the one shown below.
We'll consider two ways of doing this. One using a generate block, and the other without using a generate block. Hopefully, by doing so, the utility of the generate block will be clear.
Let us first construct this circuit without using a generate block.
Please note that I have implemented this shift register such that we can replace a section with the generate block. In practice, you may implement this a bit more concisely. This is optimized instead for clarity. The output of this module will be 8 0's, then 8 1's, then 8 0's, etc.
module shift (input wire clock, // clock input
output wire q) ; // output of shift register
wire [7:0] p ; // the wire through which we will shift
reg [3:0] count = 4'd_0 ; // a register which will contain an incrementing value
always @ (posedge clock) begin // the always block which increments that value
count <= (count + 4'd_1) ;
end
assign p[0] = count[3] ; // assign the first bit of p the MSB of count
reg [7:0] preg ; // the shift register
always @ (posedge clock) begin // repetitive logic which implements the shift register
preg[1] <= p[0] ;
preg[2] <= p[1] ;
preg[3] <= p[2] ;
preg[4] <= p[3] ;
preg[5] <= p[4] ;
preg[6] <= p[5] ;
preg[7] <= p[6] ;
end
assign p[7:1] = preg[7:1]; // assign the p wire the value of the p register
assign q = p[7]; // output the MSB of the shift register
endmodule
Instead of building all of the repetitive logic above, we could instead use a generate block. We would do so by creating a separate flip-flop module. This is the logic which we will use the generate block to create many copies of.
module dflop(input wire clock, // clock input
input wire d, // data input (1 bit wide)
output wire q) ; // data output (1 bit wide)
reg out ; // register that will contain the data for output
always @ (posedge clock) begin // at each rising clock edge, out gets the value of the input data
out <= d ;
end
assign q = out ; // the output is assigned the value of out
endmodule
We can then use a generate block to instantiate and connect a bunch of copies of this module.
module shift (input wire clock, // clock input
output wire q) ; // output of shift register
wire [7:0] wire p ; // the wire through which we will shift
reg [3:0] count = 4'd_0 ; // a register which will contain an incrementing value
always @ (posedge clock) begin // the always block which increments that value
count <= (count + 4'd_1) ;
end
assign p[0] = count[3] ; // assign the first bit of p the MSB of count
// The generate block below replaces the repetitive logic from above
generate // indicate start of generate block
genvar n; // instantiate a genvar called n
for (n=0; n<=6; n=n+1) begin: flopGen // a for loop which increments n from 0 to 6 (named)
dflop u(.clock(clock), // each time, instantiate a dflop module, connect to p
.d(p[n]),
.q(p[n+1]));
end
endgenerate
assign q = p[7] ; // output the MSB of the shift register
endmodule