A ` define`` creates a text macro. Every later use of the macro name is replaced by the macro body, character for character, before compilation.
An object-like macro has no arguments. A function-like macro takes arguments that are pasted into the body, with no space allowed between the macro name and the opening parenthesis.
`define DATA_WIDTH 32
`define RESET_VECTOR 32'h0000_1000
logic [`DATA_WIDTH-1:0] data_bus; // becomes: logic [32-1:0] data_bus;
// Function-like macro: each argument and the whole body are parenthesized.
`define MIN(a, b) (((a) < (b)) ? (a) : (b))
int smaller = `MIN(x + 1, y);
// expands to: (((x + 1) < (y)) ? (x + 1) : (y))The parentheses are the whole game. Without them, ` MIN(a | b, c)`` would expand the bitwise-or unparenthesized into the comparison, and precedence would silently change the meaning.
Default macro arguments are standard SystemVerilog, not an extension. They are left out of the runnable code here because support varies across open tools.
A macro body ends at the newline unless the line ends with a backslash continuation. Stringification (` "``) turns macro text into a string literal with argument substitution still applied.
Token pasting (``` ` ```) joins macro text into one identifier. It is how one macro call can declare a family of related names.
// snippet
`define REPORT_FIELD(obj, field) \
$display("%s = %0d at %0t", \
`"field`", \
obj.field, $time);
// `REPORT_FIELD(req, addr) prints: addr = 256 at 100
// The `" stringifies the argument: the literal text "addr" appears,
// which a plain quoted string could not do.MACRO EXPANSION PIPELINE
source text `REPORT_FIELD(req, addr)
│
▼ preprocessor finds macro, binds args: obj=req, field=addr
body lookup $display("%s = %0d at %0t",
`"field`", obj.field, $time);
│
▼ substitute args, apply `" stringification
expanded text $display("%s = %0d at %0t",
"addr", req.addr, $time);
│
▼
COMPILER sees only the expanded text — never the macroParameters are typed, scoped and overridable per instance. Macros are untyped text with effect from their definition point onward within the compilation unit.
Decision | Use | Reason |
|---|---|---|
Sizing ports and arrays | Parameters | Typed, scoped, per-instance override, visible after elaboration. |
Generating code | Macros | Parameters cannot declare functions or paste identifiers. |
Reaching strings or ` | Macros | Parameters cannot stringify or guard compilation. |
One width for a whole build | Neither alone | A macro forces every instance to one width silently; prefer a package constant. |
// bad-example
`define CHECK_FATAL(cond, msg) \
if (!(cond)) $fatal(1, "CHECK failed: %s", msg);
// Bare-if body plus the caller's semicolon breaks if/else chains.
// Shown as the mistake: lesson 4 replaces it with a safe form.Two runnable blocks. The first checks ` MIN`` against a reference model over biased arguments. The second checks pasted names, stringified labels and a generated getter.
`define MIN(a, b) (((a) < (b)) ? (a) : (b))
module tb_min;
int errors = 0;
int aa;
int bb;
int got;
int want;
initial begin
for (int i = 0; i < 8; i++) begin
aa = (i * 13 + 5) & 8'hFF;
bb = (i * 29 + 3) & 8'hFF;
got = `MIN(aa | 8'hA0, bb);
want = ((aa | 8'hA0) < bb) ? (aa | 8'hA0) : bb;
if (got !== want) errors++;
got = `MIN(aa & 8'h0F, bb);
want = ((aa & 8'h0F) < bb) ? (aa & 8'h0F) : bb;
if (got !== want) errors++;
got = `MIN(aa + 1, bb - 1);
want = ((aa + 1) < (bb - 1)) ? (aa + 1) : (bb - 1);
if (got !== want) errors++;
end
if (errors == 0) $display("PASS: MIN matches the reference model over all 24 biased argument sets");
else $fatal(1, "FAIL: MIN disagrees with the reference model");
$finish;
end
endmodule`define SHOW_VAL(name, val) $display("NOTE: %s = %0d", `"name`", val)
`define PASTE_SUM(tag) (sum_``tag)
`define MAKE_GETTER(name, backing) \
function int get_``name(); \
return backing; \
endfunction
module tb_paste;
int errors = 0;
int sum_a = 0;
int sum_b = 0;
int count_store = 0;
`MAKE_GETTER(count, count_store)
initial begin
for (int i = 1; i <= 5; i++) begin
sum_a = sum_a + i;
sum_b = sum_b + 2 * i;
end
count_store = sum_a;
if (`PASTE_SUM(a) !== 15) errors++;
if (`PASTE_SUM(b) !== 30) errors++;
if (get_count() !== 15) errors++;
`SHOW_VAL(sum_a, sum_a);
if (errors == 0) $display("PASS: pasted names, stringified labels and the generated getter agree");
else $fatal(1, "FAIL: macro-generated names disagree with direct code");
$finish;
end
endmodule MIN(a & 8'h0F, b)` expands without parens around a. With a = 8'hA4 and b` = 8'h10, what does the broken form compute?< binds tighter than &, so the broken condition reads a & (8'h0F < b), which is 8'hA4 & 1 = 0. The ternary picks b = 16. The parenthesized form computes min(4, 16) = 4.
SHOW_VAL(sum_a, sum_a)` prints NOTE: sum_a = 15. What does the "`` contribute that a plain string could not?Argument substitution inside the string. A plain quoted label would print the same text for every call; the stringified argument names the actual variable.