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.)
Try it
Section titled “Try it”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.
-
Jump to a definition. Open
rtl/top.svand ⌘-click (macOS) / Ctrl-click (Windows/Linux)`LANESin the linefor (i = 0; i < `LANES; i++) begin : g_lane. You should seertl/defines.svhopen with the caret on`define LANES 2. (With the caret on a macro call, ⌘B / Ctrl+B does the same.) -
Jump from a directive. In
rtl/defines.svh, ⌘-click / Ctrl-clickDEFINES_SVHin`ifndef DEFINES_SVH. You should land on the`define DEFINES_SVHline just below it. -
Find every use. In
rtl/defines.svh, put the caret onLANESin`define LANES 2and press ⌥F7 (macOS) / Alt+F7 (Windows/Linux). The Find tool window (a panel at the bottom of the IDE) lists one result, underrtl/top.sv: the linefor (i = 0; i < `LANES; i++) begin : g_lane. The`defineitself is not listed. -
Rename it everywhere. With the caret still on
LANES, press ⇧F6 / Shift+F6, typeLANE_COUNTand press Enter (if a dialog titled Rename asks, click Refactor). You should see both the`defineand the use inrtl/top.svchange toLANE_COUNT. Undo this before step 5: press ⌘Z / Ctrl+Z in the editor, then check thatrtl/defines.svhreads`define LANES 2andrtl/top.svreads`LANESin theforloop. If either still saysLANE_COUNT, undo in that file too, or unzip the project again. -
Complete a name. In
rtl/top.sv, on a new line afterlogic done;, typelocalparam int N = `LAand press ⌃Space / Ctrl+Space.LANESis the only match, so it is inserted without a list: the line now readslocalparam int N = `LANES(if a list opens, pickLANESand press Enter). Remove the line again: press ⌘Z / Ctrl+Z until thelocalparamline is gone andlogic done;, a blank line and thecounter #(.MAX(20))line follow each other again. -
A name passed to a macro is a use. Open
uvm/my_pkg.sv, put the caret onmy_driverinclass my_driver extendsand press ⌥F7 / Alt+F7. Among the usages listed (the Find window groups them by file; look undermy_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 touvm_component_utilsitself, but the class name inside it is still found.) -
See what a call expands to. In
rtl/top.sv, put the caret on`LANESand choose Code | Expand Macro (⇧⌥⌘9 / Ctrl+Shift+Alt+9). A popup titled`LANES expandedopens under the call. It shows the macro’s`define, then Expands to with2, then the file the macro is defined in. If the shortcut does nothing, use the menu. -
Expand a call with arguments. In
rtl/top.sv, add the line`COUNTER(u_counter2, clk, rst_n)on its own line abovegenvar 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.COUNTERis defined inrtl/defines.svhascounter __name (.clk(__clk), .rst_n(__rst_n), .count(), .done())(on two lines there, joined by a\). Put the caret on`COUNTERand 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.
-
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`defineand, under Expands to,2. -
Use the gutter icon. The gutter is the strip left of the line numbers. On the line of
`LANESinrtl/top.sva small icon appears in the gutter, just right of the line number (every macro call has one); hover it to see the tooltipShow `LANES and its expansion. (No icon? See If it doesn’t work.) Click it: the Expand Macro popup opens, with`define LANESabove Expands to. You can turn the icons off in Settings | Editor | General | Gutter Icons (Macro expansion).
Where a macro is used
Section titled “Where a macro is used”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.
Names passed to a macro
Section titled “Names passed to a macro”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_endendclassThe 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.
Completion and documentation
Section titled “Completion and documentation”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
`definewith 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).
Expand Macro
Section titled “Expand Macro”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``_qgivesdata_qforx=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``_resizecalls`m_da_resizeforT=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``bcalls`FOO. An argument right after a backtick names a macro (`awitha=foocalls`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 `Cthen`TICKC(3)is`C(3)), and defaults that are macros. A`defineor`undefin 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`includein 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. `ifdefin an expanded body sees the macros defined at the call: those the compile file (the.ffile 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`definein 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`defineit reaches winning and an`undefundoing it. A header’s`ifdefbranches 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`ifdefbranches and in two files: the one the`includes reach is used, and where they do not reach, not the deprecated copy thatuvm_macros.svhincludes 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.
Parsing rules for calls
Section titled “Parsing rules for calls”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)).
If it doesn’t work
Section titled “If it doesn’t work”| 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 |
Limitations
Section titled “Limitations”- Predefined macros (
`__FILE__,`__LINE__) and macros defined on the simulator’s command line (+define+) have no`defineto go to. - Formal arguments inside a macro body are not navigable.
- A
`definewhose formal arguments go on to the next line (`define A(a,thenb) 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`definethat 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
`ifdefthat the code after the call closes is shown with “`ifdefwithout`endif” after what was expanded. - A
`definethat 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_idfromuvm_component_utils, are members of the class and navigate to the macro call (see UVM testbenches and Known limitations).