The short answer: Use parameter for module constants that each instance may override (widths, depths), localparam for constants that must not be overridden (state encodings, derived values), and `define only for truly global settings, because macros are plain text substitution that leaks across files in compile order. Conditional compilation (`ifdef), `include and `timescale are the other compiler directives you’ll use regularly.
Parameters are what turn a one-off module into a reusable one. Macros are useful too, but they cause some of the most confusing bugs in large projects. This post combines my older posts on parameters and defines, with the rules I follow for each.
- At a glance
- parameter
- localparam
- `define and its traps
- Conditional compilation and include
- Other directives
Table of Contents
Part 1: At a glance

Part 2: parameter
module fifo #(
parameter DW = 32, // data width
parameter DEPTH = 16
) (
input wire clk,
input wire [DW-1:0] wdata,
...
);
localparam AW = $clog2(DEPTH); // derived: not overridable
reg [DW-1:0] mem [0:DEPTH-1];
reg [AW-1:0] wptr, rptr;
...
endmodule
// override per instance
fifo #(.DW(64), .DEPTH(32)) u_rx_fifo ( ... );
- Parameters are resolved at elaboration, so they can size ports, arrays and generate loops.
- Override by name (
#(.DW(64))), never by position. defparamcan also override parameters from elsewhere in the hierarchy, but it’s deprecated and hard to follow. Don’t use it.- Check parameter values that must be legal (DEPTH a power of two, say) with a generate-time check or a simulation assertion.
Part 3: localparam
localparam [1:0] IDLE = 2'd0,
RUN = 2'd1,
DONE = 2'd2;
localparam CNT_MAX = CLK_HZ / BAUD - 1; // derived from parameters
Anything derived from other parameters, and anything that would break the design if someone changed it, should be a localparam.
Part 4: `define and its traps
`define DATA_W 32
reg [`DATA_W-1:0] r; // note the backtick when using it
`define MAX(a,b) ((a) > (b) ? (a) : (b)) // macros can take arguments
- Global scope. A `define stays defined for every file compiled after it, so compile order changes behaviour.
- Silent redefinition. Two IPs that both define WIDTH differently will clash, often with just a warning.
- Plain text. Always wrap macro arguments and the whole body in parentheses.
- Use `undef at the end of a file for macros that should stay local.
My rule: parameters for anything per-module, and `define only for project-wide switches like simulation-only features.
Part 5: Conditional compilation and include
`ifndef FIFO_DEFS_VH // include guard
`define FIFO_DEFS_VH
`define FIFO_DW 32
`endif
`ifdef SIMULATION
initial $display("fifo depth = %0d", DEPTH);
`endif
Pass macros on the command line (for example +define+SIMULATION in many simulators) instead of editing files.
Part 6: Other directives
| Directive | Use |
|---|---|
| `timescale 1ns/1ps | Time unit and precision for delays |
| `default_nettype none | Turn undeclared nets into errors |
| `resetall | Reset directives to defaults |
| `celldefine / `endcelldefine | Mark library cell modules |
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
RTL Design problems · Daily challenge
Previous in my Verilog series: Tasks and functions. Next: System tasks.
FAQ
What is the difference between parameter and localparam?
Both are module constants, but a parameter can be overridden when the module is instantiated and a localparam can’t.
What is the difference between parameter and `define?
A parameter belongs to one module and can differ per instance. A `define is a global text macro that applies to everything compiled after it.
Should I use defparam?
No. It’s deprecated in SystemVerilog and makes designs hard to follow. Override parameters at instantiation.
How do I calculate an address width from a depth?
Use $clog2: localparam AW = $clog2(DEPTH);
