Skip to content

Macros

A macro is a name defined with `define that the preprocessor, the first step of a compiler or simulator, replaces with text before the code is compiled (`define LANES 2 turns each `LANES into 2). Macros are hard to follow by eye: the definition is often in another file, and a call may expand to many lines you never see, such as the code behind a `uvm_component_utils in UVM (a SystemVerilog verification library; see UVM). SystemVerilog Studio treats a macro like any other declaration. You can jump from a call to its definition, list every call, rename the macro in all files, complete its name, and see the code a call expands to. (UVM macros expand too once the UVM sources are in your project; the sample project has only a few UVM names, so the steps below use its own macros.)

Each step uses the sample project: unzip it and open the folder with File | Open…, and wait until the progress bar at the bottom right of the IDE (indexing) has finished. Click in the editor so the text cursor, called the caret, sits on the word a step names. F-keys may need Fn on a laptop keyboard. Steps 4, 5 and 8 change a file; each says how to undo, and the steps after it expect the original text, so undo before you go on. To start over, unzip the project again.

  1. Jump to a definition. Open rtl/top.sv and ⌘-click (macOS) / Ctrl-click (Windows/Linux) `LANES in the line for (i = 0; i < `LANES; i++) begin : g_lane. You should see rtl/defines.svh open with the caret on `define LANES 2. (With the caret on a macro call, ⌘B / Ctrl+B does the same.)

  2. Jump from a directive. In rtl/defines.svh, ⌘-click / Ctrl-click DEFINES_SVH in `ifndef DEFINES_SVH. You should land on the `define DEFINES_SVH line just below it.

  3. Find every use. In rtl/defines.svh, put the caret on LANES in `define LANES 2 and press ⌥F7 (macOS) / Alt+F7 (Windows/Linux). The Find tool window (a panel at the bottom of the IDE) lists one result, under rtl/top.sv: the line for (i = 0; i < `LANES; i++) begin : g_lane. The `define itself is not listed.

  4. Rename it everywhere. With the caret still on LANES, press ⇧F6 / Shift+F6, type LANE_COUNT and press Enter (if a dialog titled Rename asks, click Refactor). You should see both the `define and the use in rtl/top.sv change to LANE_COUNT. Undo this before step 5: press ⌘Z / Ctrl+Z in the editor, then check that rtl/defines.svh reads `define LANES 2 and rtl/top.sv reads `LANES in the for loop. If either still says LANE_COUNT, undo in that file too, or unzip the project again.

  5. Complete a name. In rtl/top.sv, on a new line after logic done;, type localparam int N = `LA and press ⌃Space / Ctrl+Space. LANES is the only match, so it is inserted without a list: the line now reads localparam int N = `LANES (if a list opens, pick LANES and press Enter). Remove the line again: press ⌘Z / Ctrl+Z until the localparam line is gone and logic done;, a blank line and the counter #(.MAX(20)) line follow each other again.

  6. A name passed to a macro is a use. Open uvm/my_pkg.sv, put the caret on my_driver in class my_driver extends and press ⌥F7 / Alt+F7. Among the usages listed (the Find window groups them by file; look under my_pkg.sv) is `uvm_component_utils(my_driver) on the next line: the macro’s argument counts as a use of the class. (The UVM library is not part of the sample project, so you cannot jump to uvm_component_utils itself, but the class name inside it is still found.)

  7. See what a call expands to. In rtl/top.sv, put the caret on `LANES and choose Code | Expand Macro (⇧⌥⌘9 / Ctrl+Shift+Alt+9). A popup titled `LANES expanded opens under the call. It shows the macro’s `define, then Expands to with 2, then the file the macro is defined in. If the shortcut does nothing, use the menu.

  8. Expand a call with arguments. In rtl/top.sv, add the line `COUNTER(u_counter2, clk, rst_n) on its own line above genvar i;, inside the module. Red or grey marks on the new line are expected (it is a throwaway line) and do not affect the popup; you delete it at the end of the step. COUNTER is defined in rtl/defines.svh as counter __name (.clk(__clk), .rst_n(__rst_n), .count(), .done()) (on two lines there, joined by a \). Put the caret on `COUNTER and choose Code | Expand Macro. The popup shows the instance the call stands for, with your arguments in place: counter u_counter2 (.clk(clk), .rst_n(rst_n), .count(), .done()). Delete the line you added (press ⌘Z / Ctrl+Z until it is gone) so the later steps start from the original file.

    The Expand Macro popup under the call: the define of COUNTER, Expands to the counter instance with the arguments filled in, and the file the macro is defined in

  9. Read it in the documentation popup. With the caret on `LANES, press F1 (macOS) / Ctrl+Q (Windows/Linux), or choose View | Quick Documentation. The popup shows the `define and, under Expands to, 2.

  10. Use the gutter icon. The gutter is the strip left of the line numbers. On the line of `LANES in rtl/top.sv a small icon appears in the gutter, just right of the line number (every macro call has one); hover it to see the tooltip Show `LANES and its expansion. (No icon? See If it doesn’t work.) Click it: the Expand Macro popup opens, with `define LANES above Expands to. You can turn the icons off in Settings | Editor | General | Gutter Icons (Macro expansion).

    The generate loop in rtl/top.sv: the show-code icon in the gutter, just right of the line number, on the line with `LANES

These count as uses of a macro, and navigation, Find Usages and Rename treat them alike:

Use Example
A macro call, with or without arguments, in any expression or as a statement `WIDTH, `MAX(a, b), `LOG("hi")
Conditional compilation `ifdef WIDTH, `ifndef WIDTH, `elsif WIDTH
Undefining it `undef WIDTH
Inside the body of another macro `define BUS_W (`WIDTH * 2)

From any of these, Navigate | Declaration or Usages (⌘B / Ctrl+B) opens the `define. If the macro is defined in several places, a popup lists them and you pick one. A `define in an inactive `ifdef branch is not a definition. Inactive code is greyed out; your compile file (the .f list of sources and defines your simulator uses; sim.f in the sample project) or the settings decide which branches are inactive (see Conditional compilation). Find Usages (⌥F7 / Alt+F7) on a `define lists every use above, and Rename (⇧F6 / Shift+F6) renames the definition and all its uses. Only the macro’s name is renamed; its formal arguments (`define MAX(a, b)) are not part of the name.

A call without a trailing ; is still a complete statement, so the next line is read normally: after `uvm_info("ID", "msg", UVM_LOW), the line x = 1; is an assignment to x. More cases are under Parsing rules for calls below.

A name passed on its own as a macro argument is a use of that name. Go to Declaration works on it, and Find Usages and Rename of the declaration include it:

class my_item extends uvm_sequence_item;
rand bit [7:0] addr;
`uvm_object_utils_begin(my_item) // the class my_item
`uvm_field_int(addr, UVM_ALL_ON) // the property addr
`uvm_object_utils_end
endclass

The macro’s body decides how an argument is used, so the name refers to whatever declaration of it is visible at that point: a class or type, a variable or parameter, a function or task, a module, or a package. The usual lookup applies: a local declaration hides a class of the same name elsewhere in the project, pkg::name names a package member, and a name from a package that is not imported gets the same warning and quick fix as anywhere else. This holds for macro calls inside expressions (x = `CREATE(my_item);) and for nested calls (`A(`B(my_item))). A name that is only part of an argument (`MAX(a + 1, b), `SHOW(h.f), `M(f(1, a))) is resolved like that expression outside a macro.

After a backtick, Code | Code Completion (⌃Space / Ctrl+Space) offers the project’s macros, marked macro and shown with their formal arguments (`WAIT_CYCLES(n)), and the compiler directives (`define, `ifdef, `include, …), marked directive. Ordinary names and keywords are not offered there.

Names match from their start, ignoring case (`la offers LANES). Completion is not offered inside strings or comments, or right after `` (token pasting).

Quick Documentation (F1 / Ctrl+Q, or View | Quick Documentation) on a macro call shows:

  • the `define with its body;
  • Expands to: what this call expands to, laid out as in the Expand Macro popup;
  • the file the macro is defined in.

For a macro defined in several places, Expands to is under the definition the call uses, and there is one entry per definition. Limits: the body shows its first 40 lines, Expands to the first 25 followed by how many more (Expand Macro shows them all), and an error comes after what was expanded.

Macros are also listed by Go to Symbol (⌥⌘O / Ctrl+Alt+Shift+N).

Click the gutter icon on a macro call’s line, or put the caret on a macro call and choose Code | Expand Macro (⇧⌥⌘9 / Ctrl+Shift+Alt+9, also in the editor’s context menu). A popup titled `<name> expanded appears under the call and shows:

  • the macro’s `define (up to 12 lines, then it scrolls);
  • Expands to: the code the call stands for, as a SystemVerilog preprocessor expands it;
  • the file the macro is defined in.

Opening it for another call replaces the popup. The expansion is laid out for reading: lines are re-indented by the blocks around them (begin/end, class/endclass…) with your SystemVerilog indent size, and only indentation changes. The popup scrolls when long, and can be moved and resized. A call that expands to nothing but an error shows the error as a hint instead, and one that expands to nothing says so in a hint.

If the call cannot be expanded, the popup shows what was expanded up to there and the reason as a comment. The reasons are:

  • the macro is not defined;
  • an argument is missing;
  • the macro expands to itself;
  • the expansion is larger than a million characters, or takes too much work.

The expansion runs in the background. Editing the file before the popup is shown drops it: choose the action again.

The expansion rules in full
  • Arguments replace the formal arguments as written (not inside strings or system names such as $bits), and are expanded with the body and the text after the call, as the preprocessors do: an argument the body drops has no effect, and a call of a macro inside its own argument is no recursion. A missing argument takes its default (`define ADD(a, b=1)), an empty one is empty. Commas inside parentheses, brackets, braces and strings do not separate arguments.
  • `` (token pasting, which glues two pieces of text into one name) joins text (x``_q gives data_q for x = data), `"…`" makes a string with the arguments in it (a quote in an argument is escaped), and macros inside the expansion or its arguments are expanded too. A macro name before `` that is not defined is joined with everything pasted to it: `m_``T``_resize calls `m_da_resize for T = da (UVM’s array and queue field macros build their names this way); a defined one expands first. Pasting also completes a name from arguments: `CAT(`FO,O) with `define CAT(a,b) a``b calls `FOO. An argument right after a backtick names a macro (`a with a = foo calls `foo).
  • Macros inside macros: calls in a body and in arguments at any depth, a macro’s name passed as an argument and called (`m(a)), a body that ends in a macro whose arguments follow the call (`define TICKC `C then `TICKC(3) is `C(3)), and defaults that are macros. A `define or `undef in expanded text takes effect for the rest of the expansion; a `define’s text runs to the end of the source line, as in the preprocessors. An `include in expanded text ("file", <file> or a macro giving the name) brings in the header’s text (found as for the file’s own `includes), its `defines taking effect; a header that is not found or that includes itself is an error.
  • `ifdef in an expanded body sees the macros defined at the call: those the compile file (the .f file listing your sources and defines) or the settings define for the file, with the `defines and `undefs before the call (in the file and the files it includes). A macro those do not reach counts as defined if a `define in active code in the project defines it. `__FILE__ gives the file name (a header’s, as it was included, in a header); `__LINE__ the line it is written on (in an argument or a header), else the line of the call.
  • The definition is the one in effect at the call, as the preprocessor gets there: the file’s own directives before the call and the files it `includes (in active branches; found through the compile file’s include directories, else next to the including file, else the one file in the project with that path ending), the last `define it reaches winning and an `undef undoing it. A header’s `ifdef branches are taken with the macros defined where it is included, so a header included twice can define a macro differently each time. A macro the walk does not reach (defined by an earlier file of the compile, or in a file that includes this one) takes the first definition in active code, preferring a header that some file includes in an active branch (as that file’s own `ifdefs decide) or that no file includes, then by file path. UVM defines many macros twice in opposite `ifdef branches and in two files: the one the `includes reach is used, and where they do not reach, not the deprecated copy that uvm_macros.svh includes only under `ifdef UVM_ENABLE_DEPRECATED_API.
  • Comments in a macro’s body are left out; its lines are kept.

The expansion is checked against the open-source tools Verilator and slang, including UVM’s `uvm_component_utils, the object utility macros and the field macros for values, arrays and queues.

How the editor reads macro calls
  • A macro call on its own line is a statement or item whether or not it ends with ;.
  • A macro used as a type declares the name after it only on the same line (`REG_T(8) r;).
  • Arguments may be empty (`M(, x)) or be code other than an expression, such as a constraint block in `uvm_do_with(req, { addr == 5; }); names inside such an argument are not navigable.
  • A name passed inside a nested call is also a use: `A(`B(my_item)).
You see Likely cause What to do
Find Usages shows nothing, or the wrong result (steps 3 and 6) The caret is not on the name itself, or indexing is not finished Put the caret on LANES or my_driver and wait for the progress bar to finish
Rename does nothing, or shows a preview The caret is not on LANES in `define LANES 2, or indexing is not finished Click on LANES there and try again
Nothing works right after opening the project Indexing is still running Wait for the progress bar at the bottom right to finish, then try again
The call is greyed out It is in an inactive `ifdef branch See Conditional compilation
Code | Expand Macro is greyed out The caret is not on a macro call Click on the call’s name (or inside its arguments) first
⌘B / Ctrl+B or Expand Macro does nothing on a call The macro is defined in a file that is not in the project or not reachable by `include or the compile file, or it is only defined with +define+ on the simulator command line Add the file, or put the define in your compile file or in the settings
A UVM macro such as `uvm_info has no definition to go to The UVM sources are not in the project Add them to the project or attach them as a library
The popup shows an error comment after the expansion One of the failures listed under Expand Macro Read the comment: it names the reason
A shortcut does nothing (on a laptop, F-keys may need Fn) The keymap differs, or the key is taken by the system Use the menu path in the step, such as Code | Expand Macro
Step 8 shows a different expansion The call differs from the step (macro name, commas, the three arguments) Retype the line exactly as written, with the caret on `COUNTER
Nothing is offered after a backtick You are inside a string, a comment, or right after `` Move out of it
The gutter icon is missing Icons are turned off, or the line has no macro call Settings | Editor | General | Gutter Icons, tick Macro expansion
A name a macro declares does not navigate The macro is called outside a class body: names a macro declares among a class’s items (type_id from uvm_component_utils) do navigate, to the macro call, when the macro’s definition is in the project See UVM testbenches and Known limitations
  • Predefined macros (`__FILE__, `__LINE__) and macros defined on the simulator’s command line (+define+) have no `define to go to.
  • Formal arguments inside a macro body are not navigable.
  • A `define whose formal arguments go on to the next line ( `define A(a, then b) a+b) is read by the editor as ending on its first line, so a call shows an error; keep the formal list on one line (a \ there makes the second formal \ b, as Verilator reads it). A /* … */ comment in a `define that goes on to the next line without a \ ends the definition there in the editor.
  • With the caret inside the arguments of a call, the call is the nearest one before the caret whose (…) holds it: in `E(`N (x), y) with an object-like macro (one without arguments) `N, the caret in (x) gives `N; and with 64 or more macro calls between a call’s ( and the caret, put the caret on the call’s name instead.
  • The preview expands the call alone: a macro whose expansion opens an `ifdef that the code after the call closes is shown with “`ifdef without `endif” after what was expanded.
  • A `define that a macro call makes (a macro whose body is `define …) is seen within that call’s expansion only: a later call of the macro it defines, elsewhere in the file, does not find it. Headers a macro `includes are looked for from the call’s file, also for the `includes inside them.
  • Names declared by a macro called anywhere other than among a class’s items are not navigable. Names a macro declares among a class’s items, such as type_id from uvm_component_utils, are members of the class and navigate to the macro call (see UVM testbenches and Known limitations).