Parameters and Generate
Write reusable, scalable hardware
Good RTL is reusable. Instead of hand-writing an 8-bit version and a 16-bit version of the same block, you write it once with a parameter and pick the size when you use it. Parameters and generate are how real designs stay scalable.
Parameters
A parameter is a constant you can set per instance. localparam is a constant you cannot override from outside, used for internal values you want named.
// A register whose width is a parameter
module reg_n #(parameter WIDTH = 8) (
input wire clk,
input wire [WIDTH-1:0] d,
output reg [WIDTH-1:0] q
);
always @(posedge clk) q <= d;
endmodule
// Choose the width when you instantiate it
reg_n #(.WIDTH(16)) u_reg16 (.clk(clk), .d(d16), .q(q16));Generate: replicate hardware
A generate block builds repeated or conditional hardware at elaboration time. With a genvar loop you can instantiate N copies of something, like the bit-slices of an adder, without writing them out by hand.
// Build a WIDTH-bit ripple-carry adder from full adders
genvar i;
generate
for (i = 0; i < WIDTH; i = i + 1) begin : adders
full_adder fa (.a(a[i]), .b(b[i]), .cin(carry[i]),
.sum(sum[i]), .cout(carry[i+1]));
end
endgenerateGenerate also has an if form, so you can include or leave out hardware based on a parameter, for example adding an optional pipeline stage only when a parameter asks for it.
generate builds hardware, it is not a software loop. The for runs once at build time to lay down that many real instances. Keep that straight and generate stops feeling strange: it is a hardware factory, not a runtime loop.
give every generate for-loop a label (the begin : adders above). Tools use that label to name the generated instances, and an unlabeled generate loop is harder to debug and, in some tools, an error.