Skip to content

Conditional compilation

A design often keeps several versions of the same code behind `ifdef switches (RTL or gate-level, simulation or synthesis). Without the right defines an editor shows code that the tools never see: wrong errors, wrong find-usages. What you see should be what is built, so 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 any block: put the caret in it and press ⌘ and - (macOS) / Ctrl and - (Windows/Linux), or click the arrow in the left margin;
  • 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.

Open the sample project and wait until indexing has finished (the progress bar at the bottom right disappears). rtl/top.sv ends with an `ifdef GATE_LEVEL block. Nothing defines GATE_LEVEL, so the simulator would run the `else branch, and so does the IDE.

  1. Open rtl/top.sv. Near the end of the module, the line inside `ifdef GATE_LEVEL (initial $sdf_annotate(...)) is greyed out. The line in the `else branch (initial $display("RTL simulation");) is ordinary code.
  2. Click at the very start of line 1 (above `timescale), type `define GATE_LEVEL and press Enter. There is no need to save; the greying changes within a moment. If a suggestion popup is open when you press Enter, press Esc first; if the line is still there after one undo, undo again until `timescale is the first line. The $sdf_annotate line becomes code and the $display("RTL simulation") line is greyed out instead. Undo your change with ⌘Z (macOS) / Ctrl+Z (Windows/Linux).

Real projects set such macros in a compile file (.f, the list of sources and defines you give your simulator). The sample project has one, sim.f:

  1. Open Settings (⌘, on macOS, Ctrl+Alt+S on Windows/Linux), then 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 below the fields says sim.f (selected automatically): 0 defines, 3 source files. Close the settings with Cancel.
  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 and save.
  3. In the settings, type GATE_LEVEL into Extra defines and click OK: the same happens, for every file of the project. Wait for the progress bar at the bottom right to finish before you look at rtl/top.sv. To undo, clear the field and click OK 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 and save.

Finished: rtl/top.sv shows the $sdf_annotate line greyed out and the $display("RTL simulation") line as code again, and sim.f is back to its three source lines and its one +incdir+ line.

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.
You see Likely cause What to do
Nothing is greyed out anywhere Evaluate `ifdef / `ifndef is off Turn it on in Settings | Languages & Frameworks | SystemVerilog
A branch is greyed out that should be code The macro is not defined where the IDE looks: not in the file above the `ifdef, not in the compile file or Extra defines Define it there; see Defines from a compile file
The compile file’s defines have no effect The file is not covered by the compile file, or a change was not saved Check the page under the settings fields; save the .f; see Limitations
The settings page does not say sim.f (selected automatically) Another .f file is in the project next to sim.f, or the checkbox is off Type sim.f into Compile file (.f), or turn the checkbox on
Steps 2 to 4 of the second Try it do nothing The file was not saved, or the background parse is still running Save (⌘S / Ctrl+S), then wait until the progress bar at the bottom right is gone
After saving a header the greying is stale The files are parsed again in the background Wait for the progress bar at the bottom right
Find Usages or Rename skips a name It is in inactive code, which declares and uses nothing Make the branch active