A parameter is a constant you can override at each instance. A localparam is a constant derived inside the module that the instantiator cannot change.
Use parameters for the true design choices, such as data width and depth. Derive everything else with localparams.
Quantity | Parameter or derived? | Example |
|---|---|---|
Data width | Parameter |
|
FIFO depth | Parameter |
|
Address width | Derived |
|
Maximum count value | Derived |
|
$clog2(n) is the number of bits needed to index n items: the ceiling of log₂ n. DEPTH must be at least 1: guard the width as localparam int ADDR_W = (DEPTH > 1) ? $clog2(DEPTH) : 1; because $clog2(1) is 0, so [ADDR_W-1:0] would become [-1:0], an unintended two-bit ascending range rather than an empty vector. DEPTH = 1 then gives a 1-bit address that is always 0, and a non-power-of-two depth still needs the rounded-up width.
n | $clog2(n) | Why |
|---|---|---|
1 | 0 | One location needs no address bits, so the |
2 | 1 | |
5 | 3 | Needs 3 bits to address 0…4 |
8 | 3 | |
1000 | 10 | 2¹⁰ = 1024 ≥ 1000 |
Check illegal parameter values at elaboration. In SystemVerilog, an if in a generate block that calls $error stops compilation with a clear message (for example “DEPTH must be at least 1”).
Do not hide magic numbers inside the module. If a width appears twice, derive it from one parameter.
Prefer parameter int (a typed parameter) over an untyped parameter, so the compiler can warn about a wrong-sized override.
Keep parameter lists small. A module with ten parameters is rarely used with more than one configuration.
parameter DEPTH = 100. What address width does it need, and is DEPTH a power of two?$clog2(100) = 7 bits (2⁷ = 128 ≥ 100). It is not a power of two, so pointer-wrap logic cannot simply rely on natural overflow of the address register; it must compare against DEPTH - 1.
The address generator rejects DEPTH below 1 and wraps at DEPTH minus 1. The testbench checks depths 1, 8 and 12 at every edge, including disabled holds and repeated wraps. An invalid DEPTH is a separate expected elaboration diagnostic, not a configuration that should produce PASS.
module addr_gen #(
parameter int DEPTH = 12,
localparam int ADDR_W = (DEPTH > 1) ? $clog2(DEPTH) : 1
) (
input logic clk, rst_n, inc,
output logic [ADDR_W-1:0] addr
);
// Legal range: DEPTH >= 1. DEPTH = 1 gives a 1-bit address that is always 0.
generate
if (DEPTH < 1) begin : gen_bad_depth
$error("DEPTH must be at least 1");
end
endgenerate
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) addr <= '0;
else if (inc)
addr <= (addr == ADDR_W'(DEPTH - 1)) ? '0 : addr + 1'b1; // wraps at DEPTH-1
endmodule