Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations35
  • Object-Oriented SystemVerilog for Verification35
  • Constraint Randomization — Interview-Critical Deep Dive66
  • SystemVerilog Assertions — Interview-Critical Deep Dive63
  • Functional Coverage in SystemVerilog28
  • Testbench Architecture in SystemVerilog38
  • Advanced Language & Integration30
    • DPI-C: Calling C from SystemVerilog
    • bind: Attaching Verification to RTL
    • Macros, Defines & Compilation
      • `define & Text Macros
      • `ifdef & Conditional Compilation
      • `include & Guard Discipline
      • Debugging Macro Problems
      • Compilation, Elaboration & Optimization Flow
    • Plusargs, Files & Runtime Configuration
    • Simulation Performance & TB Memory
    • RTL/TB Boundaries, Races & Timing
  • Senior & Interview Preparation34
  • Principal-Level Verification28
Loading lesson

‹Macros, Defines & Compilation
  1. Topics
  2. SystemVerilog
  3. Advanced Language & Integration
  4. Macros, Defines & Compilation
  5. `define & Text Macros

Part 7 · Advanced & Integration · Intermediate

`define & Text Macros

Object-like and function-like text macros, safe argument habits, and when parameters win.

On this page

    Substitution, not variables

    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.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    `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.

    Multiline bodies, strings and pasting

    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.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 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.
    Diagram
    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 macro

    Macros versus parameters

    Parameters 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 ` ifdef`` tests

    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.

    design.sv
    1
    2
    3
    4
    5
    6
    // 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.

    Testbench

    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.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    `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
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    `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

    Common pitfall

    A space between the macro name and the parenthesis in the definition turns a function-like macro into an object-like one whose body starts with (. Unparenthesized arguments miscompare, and a trailing semicolon inside the body doubles up with the one at the call site.

    Interview

    Likely question: macro versus parameter.

    • Macro is untyped text substitution before compilation, visible from its definition point within the compilation unit.

    • Parameter is a typed elaboration-time constant, scoped and overridable per instance.

    • UVM uses macros for factory registration because parameters cannot generate code.

    Check yourself ` 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.

    Check yourself ` 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.

    Related topics

    • Conditional compilation

    • Include guards

    Previous

    282 / 363

    Next: `ifdef & Conditional Compilation

    Up next

    Up next`ifdef & Conditional Compilation

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • `include & Guard Discipline
    • Debugging Macro Problems
    • Compilation, Elaboration & Optimization Flow