Skip to content

Verilog · Chapter 7.2 · Compiler Directives

`define in Verilog — Text-Substitution Macros

The define directive is Verilog's mechanism for text-substitution macros, which are name-to-expression mappings the preprocessor expands inline at every use site. Every included header carries them, every project-wide constant rides on top of them, and reusable RTL depends on them. The mechanism is borrowed from C, with the same affordances such as multi-line continuation, argument-style macros, and nested definitions, and the same hazards such as multiple evaluation of arguments with side effects, scope pollution, and name collisions. This page is the deep drill: the declaration syntax, argument-form macros, the multi-line backslash trick, nested expansion, undef semantics, the decision rule that separates define from parameter and localparam, and the failure modes every macro author hits at least once.

Foundation18 min readVerilogdefineMacrosPreprocessor

Chapter 7 · Section 7.2 · Compiler Directives

1. The Engineering Problem

A junior engineer is asked to drop a one-cycle pipeline stage between two modules. The change is mechanical — wrap the inter-module signal in a flop, add clk to the receiving module's sensitivity list. The engineer writes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module wrapper (input wire clk, ...);
    reg [31:0] data_stage1;
    always @(posedge clk) data_stage1 <= data_in;
    // ...
endmodule

The team's senior engineer reviews and asks for a cleaner version. The data path is 32-bit across the project, and a future-proof design shouldn't hard-code the width in three files. The senior writes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// In common_defines.vh
`define DATA_WIDTH 32
 
// In wrapper.v
`include "common_defines.vh"
 
module wrapper (input wire clk, ...);
    reg [`DATA_WIDTH-1:0] data_stage1;
    always @(posedge clk) data_stage1 <= data_in;
endmodule

The macro `DATA_WIDTH is text-substituted at preprocessing — every `DATA_WIDTH in any file that `include-s common_defines.vh becomes the literal text 32. The synthesis tool sees reg [32-1:0], exactly as if the engineer had written reg [31:0] directly. Future migration to 64-bit is a one-line change in the header; every consuming file picks it up.

This is the canonical role of `define: share constants across files. The 6.1 parameter page covered module-scoped configuration knobs; the 7.1 `include page covered file inclusion; this page covers the substitution primitive that ties them together. The mechanism has narrow but real edge cases — argument forms, multi-line definitions, nested expansion, multiple-evaluation hazards — that every working engineer needs to know.

2. Anatomy of `define

The full syntax has two forms.

2.1 Simple form — name → text

define-simple-syntax.v — name-only macros
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// `define MACRO_NAME <substitution-text>
 
`define DATA_WIDTH    32
`define ADDR_WIDTH    16
`define CLK_HZ        100_000_000
`define PROJECT_NAME  "my_soc"
 
`define ENABLE_LOW    1'b0
`define DEFAULT_MASK  8'hFF
`define ALL_ZEROS     32'h0000_0000

Each declaration names a macro and provides the substitution text. The text can be any sequence of characters — numbers, strings, bit patterns, complex expressions. The preprocessor literally substitutes the text wherever the macro is referenced (with a backtick prefix).

2.2 Argument form — name(args) → expression

define-args-syntax.v — function-like macros
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// `define MACRO_NAME(arg1, arg2, ...) <substitution-expression>
 
`define MAX(a, b)       ((a) > (b) ? (a) : (b))
`define MIN(a, b)       ((a) < (b) ? (a) : (b))
`define ABS(x)          ((x) >= 0 ? (x) : -(x))
`define BIT(name, idx)  name[idx]
`define MASK(width)     ((1 << (width)) - 1)

The macro takes one or more arguments; the substitution text references the arguments by name. At every use site, the preprocessor substitutes the arguments verbatim into the substitution expression. Two important rules emerge from this:

  1. Always parenthesise arguments. Without parens, the substituted text picks up operator precedence from its context. `define DBL(x) x * 2 plus `DBL(a + b) expands to a + b * 2 (= a + 2b), not the intended (a + b) * 2.
  2. Always parenthesise the whole expression. Without outer parens, the macro's result picks up its surrounding context's precedence the same way.

The canonical safe form is ((arg1) <op> (arg2)) — every argument parenthesised AND the whole expression wrapped.

3. Mental Model

4. Multi-Line Definitions

Long macros span multiple lines using a backslash-newline continuation:

multi-line-macro.v — backslash continuation
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define ASSERT_RESET(rst_n) \
    if (!rst_n) begin \
        $display("RESET ASSERTED at %0t", $time); \
        // ... reset assertion logic ... \
    end
 
// Use:
always @(posedge clk or negedge rst_n) begin
    `ASSERT_RESET(rst_n)
    // ... rest of logic ...
end

The backslash at end of each line tells the preprocessor "the macro continues on the next line." The newlines DON'T appear in the substituted text — the entire multi-line definition becomes one continuous expression at expansion time.

Three rules for multi-line macros:

  • No trailing whitespace after the backslash. A space between \ and the newline is a syntax error in some tools.
  • No comments at end of continuation lines. The // ... syntax may swallow the backslash. If you need to comment, put the comment on its own line above the macro or use /* ... */ block-style.
  • Closing brace / end on the last line should NOT have a trailing backslash. The last line of the macro doesn't continue.

5. Nested Macros

A macro's substitution text can reference other macros:

nested-macros.v — macros referencing macros
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define DATA_WIDTH 32
`define DATA_MASK  ((1 << `DATA_WIDTH) - 1)
`define HALF_WIDTH (`DATA_WIDTH / 2)
 
reg [`DATA_WIDTH-1:0]  data;
reg [`HALF_WIDTH-1:0]  half_data;
assign masked = data & `DATA_MASK;

When the preprocessor encounters `DATA_MASK, it substitutes ((1 << \`DATA_WIDTH) - 1), then recursively expands the inner `DATA_WIDTH to 32, producing ((1 << 32) - 1). Each level of nesting expands once.

The order of declaration matters: the inner macro must be defined before any reference (even via another macro). Forward-referencing an undefined macro produces a compile error or — worse — emits the literal backtick-name as text and produces silent garbage.

6. The Multiple-Evaluation Pitfall

The single biggest pitfall in argument-form macros. Because the preprocessor substitutes the literal argument text into every appearance of the parameter in the macro body, an argument that has side effects (or is expensive to compute) executes multiple times.

multiple-eval-bug.v — the canonical pitfall
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define MAX(a, b)  ((a) > (b) ? (a) : (b))
 
reg [31:0] counter;
reg [31:0] result;
 
// Buggy use — `f()` is called twice
result = `MAX(f(counter), 10);
 
// After preprocessing:
result = ((f(counter)) > (10) ? (f(counter)) : (10));
//        ^^^^^^^^^^^^                ^^^^^^^^^^^^
//        first call                  second call (when first > 10)

If f() has side effects (e.g., f increments counter, writes to a log, allocates memory in a testbench), the macro evaluates them twice. If f() is expensive (a deep arithmetic chain), the work runs twice.

The same pitfall appears with simpler expressions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define DBL(x)  ((x) * (x))
 
// Looks safe…
result = `DBL(counter++);
 
// After preprocessing:
result = ((counter++) * (counter++));
// counter is incremented TWICE before the multiply

Verilog's ++ is technically not standard Verilog (it's SystemVerilog), but the underlying issue — arguments expanded literally — applies to any expression with side effects.

The fix: avoid side effects in macro arguments. Either compute the value once outside the macro and pass a simple identifier in, or use a function (Chapter 15) instead of a macro for non-trivial logic.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Right way: compute once, pass the result
reg [31:0] tmp = f(counter);
result = `MAX(tmp, 10);

7. `undef — Removing a Macro

undef-syntax.v — undefining a macro
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define DATA_WIDTH 32
 
reg [`DATA_WIDTH-1:0] x;        // x is reg [31:0]
 
`undef DATA_WIDTH
 
// Subsequent references to `DATA_WIDTH produce an undefined-macro error.
// reg [`DATA_WIDTH-1:0] y;     // ERROR: macro DATA_WIDTH not defined

`undef removes a macro from the preprocessor's symbol table. After the directive, the macro name is undefined; subsequent references error out (or in some tools silently emit the literal backtick-name as text).

Use cases for `undef:

  • Localised macro re-definition. A header may temporarily redefine a macro for a specific section, then undef it to restore the prior definition (rare in practice).
  • Clean-up at end of file. A "library" file may undef all its macros at the bottom to avoid polluting the consuming file's namespace (rare; usually solved with macro-name prefixing instead).

`undef is rarely used in modern code. Include guards plus disciplined macro naming (project-wide prefixes like MYPROJ_) handle the namespace concerns more cleanly.

8. Visual Explanation

Three figures cover the substitution model.

8.1 Visual A — substitution flow

The conceptual picture: `define registers a name → text mapping; `-prefixed references trigger expansion at preprocessing time.

`define and expansion at preprocessing time

data flow
`define and expansion at preprocessing time`define MACRO 32register name → text mappingreg [`MACRO-1:0]q;reference site (with backtick)reg [32-1:0] q;preprocessor substitutes literallycompilerparses substituted text
Two-input mapping: the `define line registers the name; every `MACRO reference triggers substitution. By the time the compiler runs, all macros are gone — only the substituted text remains.

8.2 Visual B — argument-form expansion

A function-like macro and how its arguments expand.

Argument-form macro expansion`MAX(x+1, y)use site`define MAX(a,b) ((a)> (b) ? (a) : (b))macro definition((x+1) > (y) ? (x+1): (y))expanded text12
Argument-form expansion. `MAX(x+1, y) substitutes `x+1` for `a` and `y` for `b` in the macro body. Without inner parens, the expansion `(x+1) > (y) ? (x+1) : (y)` may pick up unexpected precedence — the canonical safe form parenthesises every argument inside the macro body.

8.3 Visual C — `define vs parameter

Same value, two mechanisms. The right choice depends on scope: project-wide → `define; module-scoped → parameter.

`define vs parameter scope comparisonshared constantDATA_WIDTH = 32via `definefile-scoped, no overridevia parametermodule-scoped,instantiation overrideable12
Two paths for cross-cutting constants. `define lives at the preprocessor layer — file-scoped, applies everywhere via `include. parameter lives at the language layer — module-scoped, per-instance overridable. Project-wide constants belong in `define; per-instance knobs belong in parameter.

9. The `define vs parameter Decision Rule

When you need a constant, the question is: who needs to change this value, and how often?

Aspect`defineparameter
ScopeFile / project (via `include)Module-scoped
Override at instantiationNoYes (via #(.PARAM(value)))
LayerPreprocessor (text substitution)Language (compile-time elaboration)
Type informationNone — pure textTyped (width / signedness)
Side effects on consumersAffects every subsequent fileAffects only this instance
Typical useProject-wide widths, version numbers, pathsPer-module width, depth, mode
Failure modeName collision across librariesNone

The working rule:

  • `define when the value needs to be shared across multiple files and should change in lockstep across all of them. Examples: `DATA_WIDTH, `CLK_HZ, `PROJECT_VERSION, `REG_BASE_ADDR.
  • parameter when the value is a per-instance configuration knob. Examples: a FIFO's DEPTH, an arbiter's NUM_REQUESTERS, a counter's WIDTH.
  • localparam when the value is module-internal and should not be overridden. Examples: state-encoding values (localparam IDLE = 2'b00;), derived values (localparam ADDR_W = $clog2(DEPTH);).

A common mistake: using `define for what should be a parameter. Symptom — the FIFO can't be instantiated at two different depths because both reference the same `define value. Fix: convert to parameter.

The opposite mistake: using parameter for what should be a `define. Symptom — every module declares the same parameter DATA_WIDTH = 32; and the team has to update each one in sync. Fix: move to a common_defines.vh header.

10. Industry Use Cases

Three patterns where `define shows up in production RTL.

10.1 Project-wide constants

Every modern Verilog project has a common_defines.vh (or similar) containing version numbers, data widths, address constants, protocol IDs. Every source file `include-s it. Changing a constant updates every file atomically. The 7.1 include-directive page covers this pattern; this page provides the `defines that live inside it.

10.2 Argument-form math macros

Lightweight reusable expressions — `MAX, `MIN, `ABS, `LOG2, `BIT_MASK. Function-style but with zero runtime overhead (they expand at compile time). Use when the equivalent function would be unwieldy and when the macro can stay simple enough to avoid the multiple-evaluation trap.

10.3 Protocol-specific naming

Configurable register names, signal name generation, or bit-field positioning macros — especially common in register-map headers shared between RTL and verification. Example:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define REG_CTRL_OFFSET   12'h000
`define REG_STATUS_OFFSET 12'h004
`define BIT_EN            0
`define BIT_RST           1

Both RTL and testbench `include the header. The same constants drive the decoder logic and the stimulus generator — no risk of divergence between sides.

11. Common Mistakes

Six pitfalls that catch every macro author.

11.1 Forgetting parentheses

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define DBL(x)  x * 2
 
// Buggy use
result = `DBL(a + b);
// Expands to: a + b * 2 = a + 2b (operator precedence picks up *)

Fix: parenthesise everything — `define DBL(x) ((x) * 2).

11.2 Multiple evaluation of side-effecting arguments

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define MAX(a, b)  ((a) > (b) ? (a) : (b))
result = `MAX(f(counter), 10);  // f() called twice if first > 10

Fix: pass simple identifiers. Compute side-effecting expressions once outside the macro.

11.3 Using `define for per-instance configuration

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define FIFO_DEPTH 16  // Whole project sees 16; can't have two depths

Fix: use parameter FIFO_DEPTH = 16; inside the module so each instantiation can override.

11.4 Macro name collision

Two libraries both define `MAX(a, b). Whichever is `include-d last wins; the other is silently overridden. Lint warns about redefinition, but the build proceeds.

Fix: prefix macro names by library (e.g., `MYLIB_MAX) or by project (`MYPROJ_MAX). Universal industry convention for large codebases.

11.5 Forgetting include guards on the host header

A `define outside an include guard fires every time the header is included. With multiple include paths, the same macro is registered twice and lint warns about redefinition.

Fix: every header file uses include guards (the 7.1 include-directive page covers this).

11.6 Mid-token concatenation

Verilog-2001 doesn't support C's ## token-pasting macro operator. SystemVerilog adds \`` (backtick-backtick) for this purpose:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SystemVerilog (not pure Verilog-2001):
`define REG(name)  reg_``name``_q
 
// Pure Verilog-2001: no equivalent. Either spell out the name, or use a
// runtime mechanism (a function returning a string for $display).

Fix: in pure Verilog-2001 codebases, spell out the full identifier in each macro. Migrate to SystemVerilog if you need programmatic signal-name construction.

12. Debugging Lab

Three define-directive debug post-mortems

13. Coding Guidelines

  1. Use ALL_CAPS for macro names. `DATA_WIDTH, `MAX_BURST, `REG_BASE_ADDR. Visually distinguishes macros from variables and parameters.
  2. Always parenthesise arguments and the whole expression. `define MAX(a, b) ((a) > (b) ? (a) : (b)) — every a/b wrapped, outer parens wrap the whole.
  3. Avoid side effects in macro arguments. Compute side-effecting expressions once outside the macro.
  4. Prefix macros by project / library. `MYPROJ_LOG_LEVEL, not `LOG_LEVEL. Prevents collisions across libraries.
  5. Use multi-line backslash continuation for long macros. Keep the macro body readable; one statement per line.
  6. Document non-obvious macros. A /* what it does */ comment above complex macros prevents future engineers from re-deriving the semantics.
  7. Reach for localparam before `define for module-internal constants. Macros are for cross-file sharing; localparam is for in-module use.
  8. Don't use `define for module-instantiable configuration. Width / depth / mode should be parameters so each instance can override.

14. Interview Q&A

15. Exercises

Three exercises that turn macro usage into reflex.

Exercise 1 — Spot the bug

Each of the following macros has at least one bug. Identify and fix.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define SQR(x)     x * x
`define MAX3(a, b, c)  ((a) > (b) ? (a) : (b)) > (c) ? ((a) > (b) ? (a) : (b)) : (c)
`define LOG2_4(x)  x >> 4

Hints. Look for missing parens, missing outer wrap, multiple-evaluation of complex arguments, and operator-precedence pitfalls.

Exercise 2 — Convert to safe forms

The following macros work for simple use cases but break with complex arguments. Rewrite each in the canonical safe pattern (((arg1) <op> (arg2))).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define ABS(x)        x >= 0 ? x : -x
`define MUL(a, b)     a * b
`define EVEN(x)       x % 2 == 0
`define CLIP(x, max)  x > max ? max : x

What to produce. For each, write the canonical safe form with full parenthesisation.

Exercise 3 — Macro vs parameter

For each scenario, recommend `define, parameter, or localparam.

#Scenario
1A 32-bit data path shared across 12 source files
2A FIFO's depth (different instances can have different depths)
3A counter's state-encoding values (IDLE=0, BUSY=1, DONE=2)
4The system clock frequency (100 MHz) referenced in baud-rate calculations
5An arbiter's number of requesters
6The project version string

Hints. Cross-file shared → `define. Per-instance configurable → parameter. Module-internal fixed → localparam.

16. Summary

`define declares a text-substitution macro — a name-to-text mapping the preprocessor expands inline at every reference. The mechanism is borrowed from C and works the same way.

The two forms:

  • Simple: `define MACRO_NAME substitution-text — bare name → text mapping.
  • Argument: `define MACRO_NAME(arg1, arg2) expression — function-like macro with parameters.

The canonical safe pattern:

  • ALL_CAPS naming to visually distinguish macros from variables.
  • Parenthesise every argument in the macro body: ((a) > (b) ? (a) : (b)).
  • Wrap the whole expression so surrounding context can't pick up unintended precedence.
  • Avoid side-effecting arguments — macro args may evaluate multiple times.
  • Prefix by project / library (`MYPROJ_MAX) to prevent collisions.

The decision rule vs parameter:

  • `define for project-wide constants shared across files.
  • parameter for per-instance module configuration.
  • localparam for module-internal derived / fixed values.

The day-to-day discipline:

  • Every header gets include guards (the 7.1 include-directive page).
  • Macros declared in shared headers, not in module bodies.
  • Multi-line macros use backslash continuation; no trailing whitespace.
  • `undef is rarely needed; rely on naming discipline.
  • For non-trivial logic, use a function, not a macro. Function args evaluate once.

The next two sub-pages drill into the rest of the directive family: 7.3 `timescale sets the simulation time unit and precision, 7.4 Conditional Compilation covers `ifdef / `ifndef / `elsif / `endif for build-target gating.

After Chapter 7 closes, Chapter 8 picks up with system tasks & functions$display, $monitor, $random, $readmemh — the simulation-infrastructure layer.

  • [include](/verilog/include-directive) — Chapter 7.1; the file-inclusion mechanism that ships define-d macros across files.
  • Compiler Directives — Chapter 7; the parent overview of the directive family.
  • parameter — Chapter 6.1; per-instance configuration knob (the language-layer counterpart).
  • localparam — Chapter 6.2; module-internal compile-time constants.
  • Number Representation — Chapter 4.4; literal syntax for macro-defined constants.