Skip to content

Verilog · Chapter 7 · Compiler Directives

Compiler Directives in Verilog

Compiler directives are backtick-prefixed commands that run before compilation in the preprocessor phase. Where a parameter configures a single module's behaviour, compiler directives configure how the entire source file is processed: which other files to pull in with include, what macros to substitute with define, which simulation timescale to use, whether to skip blocks of code with ifdef, and what the implicit net type should be. The directives do not generate hardware themselves; they shape the source text that the compiler sees. This chapter overview frames the directive family, the preprocessor execution model that runs them, and the scoping and lifetime rules that determine where each directive's effect applies, then points to the deep drills on the most-used directives.

Foundation18 min readVerilogCompilerPreprocessorincludedefinetimescaleifdef

Chapter 7 · Compiler Directives

1. The Engineering Problem

A small team starts a chip project. Three modules each have a synthesis-pragma block to disable simulation-only code for synthesis:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// In uart.v
// synthesis translate_off
initial $display("UART instantiated");
// synthesis translate_on
 
// In spi.v
// synthesis translate_off
initial $display("SPI instantiated");
// synthesis translate_on
 
// In top.v
// synthesis translate_off
initial $display("Top instantiated");
// synthesis translate_on

Each module also hard-codes a 32-bit data path width:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reg [31:0] data_q;       // hard-coded 32-bit

Three months later, the team needs to migrate to a 64-bit data path. The change is mechanical but requires touching dozens of reg [31:0] declarations across the codebase — every one has to become reg [63:0], and any constant like 32'h00000000 becomes 64'h0000_0000_0000_0000. The migration takes a week and ships with three subtle width-mismatch bugs that survive code review.

The right approach uses compiler directives:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// In a shared header — common_defines.vh
`define DATA_WIDTH 32
`define DATA_RST   {`DATA_WIDTH{1'b0}}
 
// In uart.v, spi.v, top.v — each file includes the header
`include "common_defines.vh"
 
module uart (...);
    reg [`DATA_WIDTH-1:0] data_q;
    always @(posedge clk or negedge rst_n)
        if (!rst_n) data_q <= `DATA_RST;
endmodule

Migration to 64-bit data path is now a one-line change in common_defines.vh:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define DATA_WIDTH 64       // was 32 — change once, propagates everywhere

The preprocessor substitutes `DATA_WIDTH with 64 at every use site across every file. The synthesis tool sees the substituted text and produces 64-bit hardware. No manual edits in uart.v, spi.v, top.v — and zero opportunity for the width-mismatch bugs.

This is the role of compiler directives: configure the source text before the compiler sees it. The directives are the language's preprocessing layer; the constants and macros they define are not module-scoped (like parameter) but file-scoped or project-scoped, depending on where the directive lives. This chapter overview is the framing; the four sub-pages drill into each directive's mechanics.

2. Why Verilog Has a Preprocessor

Verilog inherits its preprocessor model from C — and for the same reasons. The compiler operates on individual files, but real-world designs have cross-file concerns:

  • Shared constants (data widths, address constants, version numbers) that should match across files.
  • Conditional compilation to enable/disable code paths for different build configurations.
  • File inclusion — pulling header files with declarations into multiple sources.
  • Project-wide configuration (timescale, default net type) that should apply uniformly.

The preprocessor handles these by running before the compiler sees the source. It:

  1. Walks the source file line by line.
  2. Expands `define-d macros into their substitution text.
  3. Pulls in `include-d files inline at the directive's location.
  4. Skips code inside `ifdef/`endif blocks when the condition is false.
  5. Strips comments.
  6. Passes the resulting "preprocessed" source to the compiler.

By the time the compiler runs, the directives are gone — replaced by their effects on the source text. The compiler sees a flat, expanded source file with constants substituted, conditional blocks resolved, and included files inlined.

This is fundamentally different from parameters and localparams — those are part of the language's module system, scoped to a single module instance, and resolved at elaboration time (after compilation). Directives operate at a layer below the language, and their effect is on the text the compiler reads.

3. Mental Model

The mental-model has three practical consequences:

  • Use directives for project-wide configuration. `define DATA_WIDTH 32, `include "common_macros.vh", `timescale 1ns/1ps. Use parameters for per-instance configuration.
  • Keep directives at file or header-file scope. A `define inside a module body still affects every subsequent line — it's not module-scoped. The discipline is to put directives in dedicated header files, not scattered through modules.
  • Watch for directive collisions. Two `defines with the same name in different files can produce inconsistent macro values depending on include order. The convention is to wrap header files in `ifndef MY_GUARD / `define MY_GUARD / `endif (include guards, exactly like C).

4. The Six Directive Families

IEEE 1364-2005 §19 defines a dozen compiler directives. They group into six families by purpose:

FamilyDirectivesPurpose
Macro definition`define, `undefDefine / undefine text-substitution macros
File inclusion`includePull in a header or shared file
Conditional compilation`ifdef, `ifndef, `else, `elsif, `endifSkip blocks based on macro existence
Simulation time`timescaleSet the simulation time unit and precision
Net type defaults`default_nettypeChange the default net type for undeclared identifiers
Library / lifecycle`celldefine / `endcelldefine, `resetall, `line, `nounconnected_drive / `unconnected_driveLibrary-cell markers, project reset, source mapping, port handling

The first four families account for ~95% of all directive usage in production codebases. The fifth (`default_nettype) is universal but typically set once in a project-wide style file. The sixth family is rare — most of those directives appear only in foundry libraries.

This chapter's four sub-pages cover the four most-used families: 7.1 `include, 7.2 `define, 7.3 `timescale, 7.4 Conditional Compilation.

5. The Four Core Directives at a Glance

5.1 `define and `undef — Macro Definition

define-syntax.v — macro definition
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Simple text-substitution macro
`define DATA_WIDTH 32
`define BIT_MASK   8'hFF
 
// Use the macro
reg [`DATA_WIDTH-1:0] data_q;
assign masked = data_q[7:0] & `BIT_MASK;
 
// Macros can take arguments (function-like)
`define MAX(a, b)  ((a) > (b) ? (a) : (b))
 
assign max_val = `MAX(input_a, input_b);
 
// Macros can span multiple lines using backslash-newline
`define LONG_DEF \
    one_line_of_text \
    another_line
 
// Undefine a macro (rarely needed)
`undef DATA_WIDTH

The preprocessor literally substitutes the macro's text. `DATA_WIDTH becomes 32 everywhere it appears. The substitution is textual — the preprocessor doesn't understand Verilog syntax; it just replaces the macro reference with its definition.

The 7.2 sub-page covers `define in depth — argument-style macros, multi-line definitions, common pitfalls (operator precedence, multiple evaluations of arguments).

5.2 `include — File Inclusion

include-syntax.v — file inclusion
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Pull in a header file
`include "common_defines.vh"
`include "macros.svh"
`include "../shared/version.vh"
 
// After `include, all macros from the included file are available
reg [`DATA_WIDTH-1:0] q;       // DATA_WIDTH defined in common_defines.vh

The preprocessor reads the named file and inlines its contents at the directive's location. The included file is processed identically to a regular source file — its own directives are honoured.

Convention: header files use .vh extension (Verilog header) or .svh (SystemVerilog header). The convention is purely organisational; the compiler treats .vh files exactly like .v files.

The 7.1 sub-page covers `include — path resolution, include guards, the difference between <> and "" syntax in some tools, and the canonical "common_defines.vh" pattern.

5.3 `timescale — Simulation Time

timescale-syntax.v — set simulation time unit
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Format: `timescale <time_unit> / <time_precision>
 
`timescale 1ns/1ps          // common — nanosecond unit, picosecond precision
`timescale 1us/100ns         // for slower designs
`timescale 10ps/1ps          // for high-frequency / fast-clock designs
 
module my_dut;
    // Now `#10` means "10 nanoseconds" because the unit is 1ns
    reg clk = 0;
    always #5 clk = ~clk;    // 5 ns half-period → 100 MHz clock
endmodule

The directive sets two things:

  • Time unit — the default unit for #delay expressions and $realtime results.
  • Time precision — the simulator's internal time-step granularity.

Setting it consistently is critical — every file in a simulation must agree on the timescale, or #10 in one file means 10 ns and in another means 10 ps. The convention is to set it once in a project-wide header file (`include "timescale.vh") and let every other file inherit.

The 7.3 sub-page covers `timescale in depth — the unit/precision distinction, the most-common settings per design speed, the time-scaling rules across module boundaries.

5.4 Conditional Compilation — `ifdef / `ifndef / `endif

conditional-syntax.v — conditional compilation
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define USE_FPGA              // enable FPGA-specific code
 
// Compile only if USE_FPGA is defined
`ifdef USE_FPGA
    altera_pll u_pll (.refclk(refclk), .clk(clk));
`else
    asic_pll u_pll (.refclk(refclk), .clk(clk));
`endif
 
// Compile only if USE_FPGA is NOT defined
`ifndef SYNTHESIS
    initial $display("This is a simulation-only block");
`endif
 
// elsif for multi-way selection
`ifdef TARGET_XILINX_VIVADO
    // Xilinx-specific code
`elsif TARGET_INTEL_QUARTUS
    // Intel-specific code
`else
    // Generic / ASIC code
`endif

The preprocessor checks each `ifdef / `ifndef condition and includes or skips the bracketed block accordingly. The bracketed code that's skipped is invisible to the compiler — syntax errors inside it don't matter.

Common patterns:

  • FPGA vs ASIC builds. A single source supports both targets via `ifdef USE_FPGA.
  • Synthesis vs simulation. Most synthesis tools predefine `SYNTHESIS; testbench code wraps in `ifndef SYNTHESIS.
  • Debug vs release. `ifdef DEBUG blocks for $display statements, assertions, or extra checks.

The 7.4 sub-page covers conditional compilation — the canonical patterns, the interaction with synthesis tools, the include-guard idiom.

6. Visual Explanation

Three figures cover the directive execution model.

6.1 Visual A — the two-stage compile pipeline

The mental model: preprocessor first, compiler second.

Verilog compile pipeline — preprocessor then compiler

data flow
Verilog compile pipeline — preprocessor then compilersource files.v + .vh filespreprocessorruns every backtick directivepreprocessed textmacros expanded, includes inlinedcompilerparses Verilog, elaboratessim / synthoutput
Two stages. Preprocessor handles directives; compiler handles language. By the time the compiler runs, the directives are gone — they've done their work on the source text.

6.2 Visual B — directive families

The six directive families and their roles.

Six compiler directive families`define / `undefmacro substitution`includefile inclusion`ifdef familyconditional compilation`timescalesimulation time`default_nettypeimplicit type`celldefine,`resetall...library / lifecycle12
Six directive families. Macro definition (define/undef), file inclusion (include), conditional compilation (ifdef/ifndef/else/elsif/endif), simulation time (timescale), net type default (default_nettype), and library/lifecycle (celldefine, resetall, line, unconnected_drive). The four families covered in this chapter's sub-pages account for ~95% of usage.

6.3 Visual C — preprocessing in action

A concrete example. A `define in a header file becomes a literal value at every use site after preprocessing.

Preprocessor text substitutionBefore`define DATA_WIDTH 32 / reg[`DATA_WIDTH-1:0] q;After(macro applied) reg[32-1:0] q;Compiled32-bit reg in the module'ssignal table12
Before preprocessing: source contains `define DATA_WIDTH 32 in the header and reg [`DATA_WIDTH-1:0] q; in the module. After preprocessing: the macro reference is replaced literally with 32, producing reg [32-1:0] q; — which the compiler then parses as a 32-bit reg declaration. The transformation is textual and happens before the compiler sees the code.

7. The `default_nettype Directive

A directive that deserves a section of its own — even though it's not in this chapter's deep-drill set. `default_nettype controls Verilog's implicit-net behaviour.

default-nettype.v — control implicit declarations
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module example (input wire a, input wire b);
    // BUG (caught by `default_nettype none): typo in signal name
    wire result;
    assign reslut = a & b;       // typo - "reslut" not "result"
endmodule

Without `default_nettype none, the compiler would silently create an implicit 1-bit wire called reslut and the typo would survive — producing wrong behaviour at simulation time. With `default_nettype none at the top of the file, the compiler errors out: "Identifier reslut used without declaration."

Every working RTL house puts `default_nettype none at the top of every source file. It catches typos at compile time instead of debug time. The convention is universal; lint suites that enforce it as a hard rule are standard.

Other legal values:

  • `default_nettype wire — implicit nets are wire (the language default; same as omitting the directive entirely).
  • `default_nettype none — strongly recommended.
  • `default_nettype tri, `default_nettype wand, etc. — legal for any net type; almost never used in practice.

The directive's effect lasts until the end of the file (or the next `default_nettype directive). Don't change it mid-file.

8. The Include-Guard Pattern

The canonical pattern for shared header files — exactly like C/C++.

shared_defines.vh — header with include guards
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// shared_defines.vh — common project-wide macros
 
`ifndef SHARED_DEFINES_VH       // if not already included
`define SHARED_DEFINES_VH        // mark as included
 
`define DATA_WIDTH    32
`define ADDR_WIDTH    16
`define VERSION       "1.0"
 
`define LO_BYTE(x)    ((x) & 8'hFF)
`define HI_BYTE(x)    (((x) >> 8) & 8'hFF)
 
`endif                            // SHARED_DEFINES_VH

The pattern protects against multiple-inclusion bugs. Without the guard:

  • File A includes shared_defines.vh.
  • File B includes shared_defines.vh AND File A.
  • The preprocessor processes the defines twice. Some tools warn ("macro redefinition"); some accept silently; some produce different behaviour depending on which definition is "current."

With the guard, the second include sees SHARED_DEFINES_VH already defined and skips the body. The macros are defined exactly once.

The guard name is typically the filename in UPPER_CASE with _VH suffix. The convention is universal; any header file without include guards is a code smell.

9. Simulation and Synthesis Behavior

Compiler directives run before the compile. By the time the compiler runs:

  • All `define macros have been substituted into their use sites as literal text.
  • All `include-d files have been inlined.
  • All conditional-compilation blocks have been resolved — included or stripped.
  • `timescale has set the time-unit metadata on every module that follows.
  • `default_nettype has set the implicit-net policy.

The compiler then processes the resulting text identically to any other Verilog source. It has no notion of "this came from a macro" or "this was inside an ifdef" — the directives are erased.

The synthesis tool sees the same preprocessed source. `define DATA_WIDTH 32 produces 32-bit gates exactly like writing reg [31:0] directly. The synthesis tool doesn't know the difference.

10. The Synthesis-Translate-Off Pragma

A historically-important pattern that uses comment-syntax directives rather than backtick directives. Synthesis tools recognize pragmas embedded in comments:

synthesis-pragmas.v — synthesis directives in comments
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module example;
    // synthesis translate_off
    initial $display("DUT instantiated");      // simulation-only — skipped by synthesis
    // synthesis translate_on
 
    // synopsys translate_off
    initial $monitor("clk = %b", clk);          // alternative spelling — same effect
    // synopsys translate_on
endmodule

The synthesis translate_off / translate_on pair tells the synthesis tool: "skip the lines between these comments." The simulator ignores the pragmas (they're regular comments to the simulator) and runs the bracketed code normally. The synthesis tool skips the bracketed code and synthesises only what's outside.

This is functionally equivalent to `ifndef SYNTHESIS / `endif (assuming the synthesis tool predefines `SYNTHESIS). The pragma form is historically older and persists in legacy codebases; new code typically uses the cleaner `ifdef-based approach.

11. Common Mistakes

Five pitfalls that catch engineers using compiler directives.

11.1 Defining a macro in module body, expecting module scope

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module a;
    `define LOCAL_MACRO 8       // ⚠ this is FILE-scoped, not module-scoped
endmodule
 
module b;
    reg [`LOCAL_MACRO-1:0] q;    // still works — LOCAL_MACRO is still defined
endmodule

`define is not module-scoped. It's file-scoped (or project-scoped if the file is included). Defining a macro inside module a makes it visible in every subsequent module in the file. Use parameter or localparam for module-scoped constants.

11.2 Missing include guards

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// shared_macros.vh — NO include guard
`define DATA_WIDTH 32
// ... no `ifndef ... `endif wrapping ...

If two source files both `include "shared_macros.vh", the compiler processes the `define twice. Some tools warn ("macro DATA_WIDTH redefined"); some silently use the second definition. The fix is the include-guard pattern from §8.

11.3 Inconsistent timescale across files

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// file_a.v
`timescale 1ns/1ps
module a; ... endmodule
 
// file_b.v
`timescale 1us/100ns
module b; ... endmodule
 
// In simulation: file_a's `#10` means 10 ns, file_b's `#10` means 10 us — 1000× different

The IEEE 1364 spec requires `timescale to be set for every module in simulation. If two files use different timescales, #10 means different things in different modules. The fix is to put `timescale in a single project-wide header included by every file.

11.4 Macro with side-effecting argument

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`define MAX(a, b)  ((a) > (b) ? (a) : (b))
 
// Buggy use
reg [7:0] count;
assign result = `MAX(count++, 10);    // count is post-incremented TWICE

The preprocessor expands `MAX(count++, 10) to ((count++) > (10) ? (count++) : (10)). The argument count++ appears twice in the expanded text and is evaluated twice — incrementing count twice. The Verilog language doesn't have ++, so this specific example is illegal in pure Verilog (it's SystemVerilog syntax), but the underlying problem — arguments evaluated multiple times — is real for any macro that uses an argument multiple times.

The fix: avoid side effects in macro arguments. Or use parameter/localparam where the value is evaluated once.

11.5 Forgetting `default_nettype none

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// File without `default_nettype none
 
module example;
    wire result;
    assign reslut = a & b;       // typo — silently creates implicit wire `reslut`
                                  // `result` stays at its default (probably X-propagated)
endmodule

Without the directive, the typo reslut becomes an implicit 1-bit wire. The compiler doesn't catch it. The fix is to put `default_nettype none at the top of every source file. Modern projects do this universally.

12. Industry Use Cases

Three patterns where compiler directives are essential.

12.1 Project-wide constants header

Every working RTL project has a common_defines.vh (or similarly named) header at the project root. It contains:

  • Version numbers (`define VERSION "1.0").
  • Data widths (`define DATA_WIDTH 32).
  • Address widths (`define ADDR_WIDTH 16).
  • Magic constants (memory base addresses, status bits, error codes).

Every source file `includes this header. Changing a value updates every file atomically.

12.2 Multi-target builds

The same source supports FPGA, ASIC, and emulation targets via conditional compilation:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`ifdef TARGET_XILINX_FPGA
    xilinx_bram u_bram (...);
`elsif TARGET_INTEL_FPGA
    intel_bram u_bram (...);
`elsif TARGET_ASIC
    asic_sram u_bram (...);
`else
    behavioural_memory u_bram (...);     // simulation-only model
`endif

The build system sets one of the target macros at compile time (-DTARGET_XILINX_FPGA on the command line). The same source produces different netlists for different targets.

12.3 Synthesis-vs-simulation bifurcation

Testbench code, $display statements, and assertions are typically wrapped in `ifndef SYNTHESIS to keep them out of the synthesised netlist:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`ifndef SYNTHESIS
    initial $display("DUT instantiated");
    always @(posedge clk) begin
        assert(state_q != ILLEGAL_STATE)
            else $error("FSM in illegal state");
    end
`endif

The synthesis tool predefines `SYNTHESIS; the simulator does not. The conditional block compiles into the simulation but is stripped from the netlist.

13. Debugging Lab

Three compiler-directive debug post-mortems

14. Coding Guidelines

  1. `default_nettype none at the top of every source file. Catches typos as compile errors.
  2. `timescale in a project-wide header. Ensures consistency across files.
  3. Use include guards in every header file. `ifndef GUARD / `define GUARD / `endif.
  4. Header file extension: .vh (or .svh for SystemVerilog). Convention.
  5. Project-wide constants in a single common_defines.vh. One source of truth.
  6. Use conditional compilation for target-specific code. `ifdef TARGET_FPGA / `else / `endif.
  7. Wrap simulation-only code in `ifndef SYNTHESIS. The synthesis tool predefines the macro.
  8. Use parameter/localparam for module-scoped constants, directives for project-scoped. Don't confuse the two layers.

15. Interview Q&A

16. Exercises

Three exercises that turn directive usage into reflex.

Exercise 1 — Pick the mechanism

For each scenario, recommend a compiler directive, parameter, or localparam.

#Scenario
1Set the default data width for every module in the project to 32
2Make a specific FIFO instance 64-bit wide while others stay 32-bit
3Build an FPGA variant of the design (using Xilinx primitives)
4Disable a synthesis-only block during simulation
5Define a constant for use in arithmetic inside a specific module
6Ensure all source files use the same simulation time unit

Hints. Project-wide → directive. Per-module / per-instance → parameter/localparam. Conditional code paths → `ifdef.

Exercise 2 — Add include guards

Add the canonical include-guard pattern to the following header file.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// fifo_defines.vh — needs include guards
 
`define FIFO_DATA_WIDTH 8
`define FIFO_DEPTH      16
`define FIFO_ALMOST_FULL_THRESHOLD 12

Hints. Use `ifndef FIFO_DEFINES_VH / `define FIFO_DEFINES_VH at the top and `endif at the bottom.

Exercise 3 — Refactor for conditional builds

The following module supports both FPGA and ASIC targets, with the target-specific code commented in/out manually. Refactor to use `ifdef-based conditional compilation.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module memory (
    input  wire clk,
    input  wire wr_en,
    input  wire [9:0] addr,
    input  wire [7:0] wr_data,
    output reg  [7:0] rd_data
);
    // For FPGA, use Xilinx BRAM primitive:
    // xilinx_bram u_mem (.clka(clk), .wea(wr_en), .addra(addr), .dia(wr_data), .doa(rd_data));
 
    // For ASIC, use foundry SRAM macro:
    // asic_sram u_mem (.clk(clk), .we(wr_en), .a(addr), .di(wr_data), .do(rd_data));
 
    // For simulation, use behavioural memory:
    reg [7:0] mem [0:1023];
    always @(posedge clk) if (wr_en) mem[addr] <= wr_data;
    always @(*) rd_data = mem[addr];
endmodule

Hints. Use `ifdef TARGET_XILINX_FPGA, `elsif TARGET_ASIC, `else for the three branches. Set the appropriate macro at compile time (e.g., -DTARGET_XILINX_FPGA).

17. Summary

Compiler directives are preprocessor commands that run before Verilog compilation. They configure the source text the compiler sees but don't generate hardware themselves.

The six directive families:

  • Macro definition`define, `undef
  • File inclusion`include
  • Conditional compilation`ifdef, `ifndef, `else, `elsif, `endif
  • Simulation time`timescale
  • Net type defaults`default_nettype
  • Library / lifecycle`celldefine, `resetall, `line, `unconnected_drive

The four covered in this chapter's sub-pages account for ~95% of usage:

  • 7.1 `include — file inclusion, path resolution, include guards.
  • 7.2 `define — macro definition, argument-style macros, common pitfalls.
  • 7.3 `timescale — simulation time unit and precision, cross-file consistency.
  • 7.4 Conditional Compilation`ifdef/`ifndef/`elsif/`endif patterns.

The execution model:

  • Preprocessor stage runs first. Walks each file, executes every directive, produces a preprocessed source.
  • Compiler stage runs second. Parses the preprocessed text, elaborates modules, produces sim/synth output.

The discipline:

  • `default_nettype none at the top of every source file. Universal convention.
  • `timescale in a project-wide header. Cross-file consistency.
  • Include guards on every header. Protect against multiple inclusion.
  • Project-wide constants in common_defines.vh. Single source of truth.
  • Conditional compilation for target-specific code. `ifdef TARGET_FPGA, etc.
  • Wrap simulation-only code in `ifndef SYNTHESIS. Strip from netlist, keep for sim.
  • parameter/localparam for module-scope, directives for project-scope. Don't confuse the layers.

Chapter 7 closes here. The four sub-pages drill into each major directive. Chapter 8 picks up with system tasks & functions — the $display, $monitor, $random, $readmemh family that drives testbench and simulation infrastructure.

  • localparam — Chapter 6.2; module-scoped constants that complement directives.
  • parameter — Chapter 6.1; user-overridable module parameters.
  • Constant Variables — Chapter 6; the parent overview of module-scoped constants.
  • Lexical Conventions — Chapter 4; the lexer rules every directive must follow.
  • RTL Designing — Chapter 3; the broader RTL framing.