Skip to content

Conditional compilation

SystemVerilog Studio evaluates `ifdef, `ifndef, `elsif, `else and `endif the way a compiler does, and treats only the active branch as code:

  • the code of an inactive branch is greyed out;
  • it declares nothing: Go to Declaration, Find Usages, completion, the Structure View and Go to Symbol see only the active branch, and a `define in it defines no macro;
  • it folds like a block (⌘- / Ctrl+-);
  • it is not checked for syntax errors or typos, and Reformat Code leaves it as written (the directive lines take the indent of the items around them); Enter in inactive code keeps the indent of the line you are on.

A macro is defined when a `define for it comes earlier in the file, in active code, and no `undef (or `undefineall) removed it — also in a header it includes, or, for the files a compile file lists, in a file before it (see below) — or when the compile file or the settings define it. Directives inside an inactive branch are part of it and have no effect. A comment on the directive line (`ifdef SIM // gate-level only) stays a comment. An `ifdef without a macro name is false; a branch without `endif runs to the end of the file.

GATE_LEVEL is not defined
`ifdef GATE_LEVEL
initial $sdf_annotate("top.sdf", u_counter); // greyed out
`else
initial $display("RTL simulation"); // code
`endif

Because only one branch is code, branches that make sense only one at a time — two alternative module or class headers, for example — parse without errors.

The color of inactive code is Inactive code in Settings | Editor | Color Scheme | SystemVerilog.

Defines from a compile file and the settings

Section titled “Defines from a compile file and the settings”

Simulators get their macros from the command line or from a compile file (.f). Set yours in Settings | Languages & Frameworks | SystemVerilog:

Setting Meaning
Evaluate `ifdef / `ifndef On by default. Off: every branch is code, nothing is greyed out.
Compile file (.f) Its defines (+define+NAME[=value], -D NAME, -DNAME, -define NAME, …, minus +undefine+/-U) apply to the files it covers: the files it lists (also in nested -f/-F files and as -v library files) and the files under its +incdir+/-I and -y/+libdir+ directories. A local path; a relative one starts at the project directory. If you rename or move the file, it stays selected.
When empty, use the project’s only .f file On by default: with the field empty, a project with exactly one .f file uses it (the choice is not written to the settings). Off: no compile file.
Working directory Where the relative paths in the .f start (as when you run the simulator). Empty: the directory of the .f.
Extra defines One per line, NAME or NAME=value. They apply to every file.

Below the fields, the page shows what the compile file says: its number of defines and source files, and any problem (a nested file that cannot be read, an undefined environment variable, a malformed extra define, a missing working directory, …). Environment variables ($VAR, ${VAR}) in paths are expanded.

Files the compile file does not cover use the macros they define themselves (and the extra defines). The settings are stored in .idea/systemverilog.xml, so they can be shared through version control. When you apply a change, or save a change to a compile file, the files are parsed and indexed again with the new defines, in the background; until that is done, the old defines stay in effect everywhere. If the settings, a compile file or an environment variable it uses changed while the IDE was closed, the files are indexed again when the project opens; until then, the defines of the last session stay in effect.

As in a simulator, a macro stays defined across files until it is undefined:

  • Compile order. Each file the compile file lists starts with the macros defined by the files listed before it (and by the headers they include). A `define in defs.sv is seen in the files after defs.sv, not in those before it.
  • Includes. An `include "file.svh" applies the macros the header defines (or undefines) at that point, following its own `ifdefs — include guards work. The header is looked for next to the including file, then in the working directory, then in the compile file’s +incdir+ directories.
  • Headers. A header you open starts with the macros defined where it is first included.

When you save a file, the files after it are parsed again only if a macro they test (in their own `ifdefs or in the headers they include) changes — a new `define that no later file tests, or a change that adds or removes no directive, parses nothing again. A change of a header’s directives parses every file again.

  • Only headers that the compile file’s sources include (directly or through other headers) are known: an `include of another header defines nothing. An `include of a macro (`include `FILE) or in angle brackets (`include <file>) is not followed.
  • Macros from other files take effect when those files are saved. An `ifdef you type (not yet saved) on a macro the saved file did not test sees the compile file’s and the settings’ defines until you save.
  • Changes to the compile file take effect when it is saved.
  • Paths in the compile file are matched as written: a different letter case (RTL/a.sv for rtl/a.sv) or a path through a symbolic link does not cover the file. +incdir+. covers every file under the working directory.
  • Any file with the extension .f counts as a compile file for the automatic choice.
  • The first time a project is opened (for example a fresh clone), files that get macros from other files (or, with the automatic choice, from the compile file) are indexed once without them and then once more with them.
  • Rename changes the active branch and the usages; a name in inactive code is text and is not renamed.
  • The SystemVerilog 2023 condition expressions (`ifdef (A && !B)) are not supported: the condition counts as missing, so it is false.
  1. Open rtl/top.sv from the sample project.
  2. Near the end of the module, the line inside `ifdef GATE_LEVEL is greyed out: GATE_LEVEL is not defined. The line in the `else branch is ordinary code.
  3. Add the line `define GATE_LEVEL at the top of the file. The $sdf_annotate line becomes code and the $display("RTL simulation") line is greyed out instead. Undo your change afterwards.

With a compile file:

  1. Open Settings | Languages & Frameworks | SystemVerilog. Compile file (.f) is empty and When empty, use the project’s only .f file is on, so sim.f is used: the page says sim.f (selected automatically): 0 defines, 3 source files.
  2. Open sim.f, add the line +define+GATE_LEVEL and save (⌘S / Ctrl+S). In rtl/top.sv, the $sdf_annotate line becomes code and the $display("RTL simulation") line is greyed out. Remove the line again.
  3. In the settings, type GATE_LEVEL into Extra defines and click OK: the same happens, for every file of the project. Clear the field again.
  4. Open rtl/defines.svh (which top.sv includes), add the line `define GATE_LEVEL after `define LANES 2 and save: in rtl/top.sv the $sdf_annotate line becomes code again. Remove the line.