Part 7 · Advanced & Integration · Intermediate
`ifdef & Conditional Compilation
ifdef/ifndef/elsif/else/endif, +define+ command-line defines, simulation-vs-synthesis guards, and configuration discipline.
Compile-time code selection
`ifdef includes a region of source text only if a macro is defined — the macro's value is irrelevant, only its existence matters. Code in the false branch is never compiled: it can reference signals that do not exist, call undefined functions, or be syntactically valid only in one configuration. This is fundamentally different from a run-time if, which compiles both branches and selects at simulation time.
`define FPGA_TARGET // value-less define: existence is the flag
`ifdef FPGA_TARGET
bufg clk_buf (.I(clk_in), .O(clk)); // FPGA clock buffer primitive
`elsif ASIC_TARGET
CLKBUF_X4 clk_buf (.A(clk_in), .Z(clk)); // ASIC std-cell buffer
`else
assign clk = clk_in; // plain sim model
`endif
`ifndef SYNTHESIS // ifNdef: include when NOT defined
initial $display("Simulation-only banner: build %s", `BUILD_TAG);
`endif
// `undef removes a definition from this point onward
`undef FPGA_TARGETDefines from the command line
Most defines in real projects come not from source code but from the build script via +define+. This is how one codebase compiles into RTL sim, gate-level sim, FPGA prototype, and synthesis configurations without editing a single file.
# Define existence flags and valued macros at compile time
vcs +define+ASIC_TARGET +define+BUILD_TAG=\"nightly_2317\" design.sv
# Multiple defines in one switch (tool-dependent separator)
vlog +define+GATE_SIM+SDF_ANNOTATE tb_top.sv
# Typical Makefile pattern: configuration lives in the flow, not the source
SIM_DEFINES = +define+RTL_SIM +define+ASSERT_ONStandard guard patterns
// 1. Simulation vs synthesis guard — synthesis tools predefine SYNTHESIS
`ifndef SYNTHESIS
always @(posedge clk)
if (fifo_full && wr_en)
$error("Write to full FIFO at %0t", $time);
`endif
// 2. RTL vs gate-level configuration
`ifdef GATE_SIM
initial $sdf_annotate("netlist.sdf", tb_top.dut);
`define CLK_PERIOD 12 // slower clock for annotated timing
`else
`define CLK_PERIOD 10
`endif
// 3. Assertion enable knob, defaulted ON for sim
`ifdef ASSERT_ON
assert_no_overflow: assert property (@(posedge clk) !(full && wr_en));
`endifONE SOURCE TREE, MANY BUILDS
design.sv + tb_top.sv
│
┌──────────────────────┼──────────────────────┐
│ │ │
+define+RTL_SIM +define+GATE_SIM (synthesis tool
+define+ASSERT_ON +SDF_ANNOTATE defines SYNTHESIS)
│ │ │
▼ ▼ ▼
RTL simulation gate-level sim netlist
assertions ON SDF timing ON sim-only code
fast clock slow clock stripped outThe ifdef-explosion smell
Conditional compilation does not compose. Two independent flags create four configurations; five flags create thirty-two — most of which are never compiled, never tested, and quietly broken. When a file reads as more `ifdef than logic, that is the ifdef explosion smell, and it is a maintenance debt that compounds.
Configuration discipline
Keep one central defines file (or build-script block) listing every legal flag with a comment — never scatter `define statements across random files.
Prefer run-time plusargs for anything that does not change which code compiles — verbosity, test selection, and iteration counts do not need recompilation.
Prefer parameters and generate blocks for structural variants inside a module — they are type-checked in all branches and visible in elaboration.
Limit `ifdef to true compile-time concerns: tool/target differences, synthesis guards, and licensing-restricted code.
If two flags interact, document the legal combinations and add a preprocessor error for illegal ones.
Key takeaways
`ifdef tests existence, not value — +define+FLAG from the command line is the normal way to set it.
Code in a false `ifdef branch is never compiled, so it can rot silently — every legal configuration needs a regression build.
Use SYNTHESIS guards for sim-only constructs; use `ifndef for default-on behavior.
Reach for plusargs or parameters before `ifdef — conditional compilation is the most expensive configuration mechanism to maintain.
Common pitfalls
Misspelled macro name in `ifdef — silently false, the guarded code vanishes with no warning from any tool.
Forgetting `endif — the error often reports at end-of-file, far from the real omission.
Testing a valued define with `ifdef expecting value semantics — `define MODE 0 still makes `ifdef MODE true.
Untested ifdef branches rotting for months — they fail exactly when someone finally needs that configuration.
Interview angle
Expect: difference between `ifdef and a run-time if (compile-time text exclusion vs simulated branch); how to compile one TB for RTL and gate-level (command-line defines selecting SDF annotation and clock period); and the design-judgment question — when would you refuse to add another `ifdef and use a plusarg or parameter instead.