Verilog · Chapter 7.1 · Compiler Directives
`include in Verilog — File Inclusion and Header Files
The include directive is Verilog's mechanism for pulling one file into another at preprocessing time. The preprocessor reads the named file, inlines its contents at the directive's location, and then continues. The idea is borrowed directly from C and used for the same reasons: sharing constants, protocol definitions, register maps, and macros across many source files. Every real RTL project has at least one shared header file that most source files include, and larger projects have many for different layers of the design. This lesson covers the syntax, how search paths resolve a filename, the include-guard pattern that prevents multiple-inclusion bugs, sensible conventions for organizing header layers, and the incdir flag that tells the synthesis tool where to find headers.
Foundation16 min readVerilogincludeHeaderPreprocessorMacros
Chapter 7 · Section 7.1 · Compiler Directives
1. The Engineering Problem
A chip project starts with three modules — UART, SPI, and a top-level wrapper. Each module needs to know the system clock frequency to compute baud-rate dividers and SPI clock periods. The first cut hard-codes the constant in each file:
// In uart.v
localparam CLK_HZ = 50_000_000; // 50 MHz system clock
// ... use CLK_HZ for baud-rate dividers ...
// In spi.v
localparam CLK_HZ = 50_000_000; // 50 MHz system clock (must match top.v!)
// ... use CLK_HZ for SPI period ...
// In top.v
localparam CLK_HZ = 50_000_000; // 50 MHz — change here when crystal swaps
// ... PLL configuration ...Two weeks later, the team decides to upgrade to a 100 MHz crystal. The engineer assigned the upgrade greps CLK_HZ = 50 across the codebase, finds three files, and changes them all. The build passes; simulation passes; the chip taps out and fails at first silicon. Why? Because there were four hard-coded clock constants — the engineer missed one in a sub-module they didn't grep into. The four constants disagree by 2×, and every baud-rate calculation is off by exactly that factor.
The right structure uses `include:
// common_defines.vh — single source of truth
`define CLK_HZ 50_000_000
`define BAUD_RATE 115_200
// ... other shared constants ...
// In uart.v
`include "common_defines.vh"
module uart;
// Use `CLK_HZ everywhere — never declare a local copy
localparam DIVISOR = `CLK_HZ / `BAUD_RATE;
// ...
endmodule
// In spi.v
`include "common_defines.vh"
module spi;
// Same `CLK_HZ everywhere
// ...
endmoduleTo upgrade to 100 MHz, change one line in common_defines.vh:
`define CLK_HZ 100_000_000 // was 50 — propagates to every fileEvery source file that `include-s the header sees the new value. The grep-and-replace problem is gone; the multiple-source-of-truth problem is gone; the "I forgot to change one file" failure mode is gone.
This is the role of `include: one file, many readers, single source of truth. The mechanism is identical to C's #include, and the discipline that comes with it — header files, include guards, search paths, layered organisation — transfers directly.
2. Anatomy of `include
The syntax has one form:
`default_nettype none
// `include "<path>"
// Path may be relative or absolute. Quoted strings only.
`include "common_defines.vh" // relative — searches current dir + +incdir+ paths
`include "macros/uart_defines.vh" // relative with sub-directory
`include "../shared/timescale.vh" // relative up-tree
`include "/abs/path/to/header.vh" // absolute (discouraged — not portable)Three syntactic rules:
- The filename is in double quotes. Some tools (notably ModelSim/Questa) accept
<angle brackets>for system headers, but pure Verilog-2005 specifies only double-quoted strings. - No trailing semicolon.
`includeis a preprocessor directive, not a statement. Adding;either errors or produces unexpected behaviour depending on the tool. - One file per directive. To include multiple files, write multiple
`includelines.
2.1 Path resolution
The preprocessor looks for the named file in this order:
- The directory of the file that contains the
`includedirective. - Each directory listed in the synthesis/simulation tool's include-search path (typically
+incdir+<path>flag, sometimes-I <path>on commercial tools). - Tool-specific default paths (rare).
If the file isn't found, compilation errors out:
Error: cannot open include file "common_defines.vh"The fix: either move the header to the search path, add the header's directory via +incdir+, or use a more explicit relative path.
2.2 The .vh convention
Header files conventionally use the .vh extension (Verilog header), or .svh for SystemVerilog headers. The convention is purely organisational — the compiler treats .vh files exactly like .v files (it just reads and processes the text). The convention exists so:
- File listings make headers visually distinct from RTL.
- Build systems can include
.vfiles in the synthesis source list while excluding.vhfiles (which would otherwise be compiled twice — once as`include-d and once standalone).
Some teams use .inc instead of .vh; both are common. The point is to mark the file as "not a standalone module."
3. Mental Model
The mental-model has three practical consequences:
- Headers are for shared text, not standalone code. Put
`define-s,`timescale, project-wide configuration in headers. Don't put module definitions there. - Order matters.
`include "common_defines.vh"at the top of every source file; the module body uses the defines that follow. - Always use include guards. The preprocessor doesn't deduplicate — if
header_a.vhincludesheader_b.vhand so does your source file,header_b.vhis processed twice without guards.
4. The Common-Defines Header Pattern
The canonical pattern: a single project-wide header containing every shared constant. Every source file includes it.
`ifndef COMMON_DEFINES_VH
`define COMMON_DEFINES_VH
// ─── Clock and timing ──────────────────────────────────────────
`define CLK_HZ 100_000_000 // 100 MHz system clock
`define CLK_PERIOD_NS 10 // = 1e9 / CLK_HZ
`define BAUD_RATE 115_200
// ─── Data path widths ──────────────────────────────────────────
`define DATA_WIDTH 32
`define ADDR_WIDTH 16
`define BUS_WIDTH 64
// ─── Memory configuration ──────────────────────────────────────
`define MEM_SIZE_KB 64
`define CACHE_LINE_BYTES 32
// ─── Project metadata ──────────────────────────────────────────
`define VERSION "1.2.0"
`define PROJECT_NAME "my_soc"
`endif // COMMON_DEFINES_VHThe structure:
- Include guard at top + bottom.
`ifndef COMMON_DEFINES_VH/`define COMMON_DEFINES_VH/ ... /`endif— protects against multiple inclusion. - Logical grouping. Section headers (
// ─── Clock and timing ───) make the file scannable. ALL_CAPSmacro names with descriptive prefixes.- End-of-file comment on
`endifidentifies which guard it closes — useful in files with multiple`ifndefblocks.
Every source file in the project includes this:
`default_nettype none
`include "common_defines.vh"
module my_module (
input wire clk,
input wire [`DATA_WIDTH-1:0] data_in,
output reg [`DATA_WIDTH-1:0] data_out
);
// Use `CLK_HZ, `DATA_WIDTH, etc. anywhere
localparam UART_DIV = `CLK_HZ / `BAUD_RATE;
// ...
endmodule5. The Include-Guard Pattern
The single most important convention for header files. Without guards, a header processed twice produces "macro redefined" warnings or — worse — different macro values depending on inclusion order.
// my_header.vh
`ifndef MY_HEADER_VH // if this header hasn't been processed yet
`define MY_HEADER_VH // mark it as processed
// ─── Header contents ──────
`define MY_MACRO 42
// ... other defines, includes, etc. ...
`endif // MY_HEADER_VHHow it works:
- First inclusion.
`ifndef MY_HEADER_VHis true (the macro isn't defined yet). The preprocessor enters the block, definesMY_HEADER_VH(so the guard becomes false on future inclusions), processes the body, and exits at`endif. - Subsequent inclusions.
`ifndef MY_HEADER_VHis now false. The preprocessor skips the entire body — including the`define MY_MACRO 42— preventing redefinition.
The guard macro name is conventionally the filename in UPPER_CASE with _VH suffix. Convention is enough; the only requirement is that the name be unique across all headers in the project. A typo in either occurrence breaks the guard:
`ifndef MY_HEADER_VH // guard reads this
`define MY_HEADER_HV // BUG: typo — guards a DIFFERENT macro
// ... body ...
`endifThe fix: lint suites or a <header-name>_VH = filename-derived convention enforced at code review.
6. Visual Explanation
Three figures cover the include mechanism.
6.1 Visual A — file inclusion at preprocessing
The conceptual picture. `include "header.vh" inlines the header's text at the directive's location.
`include — inlining at preprocessing time
data flow6.2 Visual B — include hierarchy
A typical layered header structure: project-wide defines at the bottom, protocol-specific in the middle, module-specific at the top. Source files include the layer they need; the preprocessor walks the dependency graph.
6.3 Visual C — multiple inclusion without vs with guards
The bug pattern: without guards, the same header is processed multiple times via different paths.
7. Search Paths and +incdir+
When the preprocessor can't find a `include file in the current directory, it consults the include search path — a list of directories the compiler/simulator was told about via command-line flags.
7.1 Setting the search path
Every modern tool supports an +incdir+ flag (or its equivalent):
# ModelSim / Questa / Xcelium — +incdir+<path>
vsim +incdir+./rtl/includes +incdir+./shared/headers my_top
# Synopsys VCS — +incdir+<path>
vcs -sverilog +incdir+rtl/includes +incdir+shared/headers tb.sv my_top.sv
# Synopsys Design Compiler — search_path
dc_shell> set_app_var search_path "rtl/includes shared/headers"
# Cadence Genus — search_path or -incdir
genus -files src.v -incdir rtl/includes -incdir shared/headers
# Verilator — -I<path> (note: no space, C-style)
verilator -I./rtl/includes -I./shared/headers --cc my_top.svEach path the preprocessor consults is added to the include-search-path. When `include "common.vh" is encountered, the preprocessor searches:
- The directory of the file containing the directive.
- Every directory in the include-search path (in the order they were added).
- Tool-specific defaults.
The first match wins. If two paths contain a file with the same name, the first one in the search order is used — fragile, so projects typically ensure unique header names across directories.
7.2 Project structure conventions
A typical project layout:
my_project/
├── rtl/
│ ├── modules/
│ │ ├── uart.v
│ │ ├── spi.v
│ │ └── top.v
│ └── includes/
│ ├── common_defines.vh
│ ├── uart_defines.vh
│ └── spi_defines.vh
├── tb/
│ └── tb_top.sv
├── synth/
│ └── compile.tcl # sets search_path to "rtl/includes tb/includes"
└── sim/
└── run.tcl # sets +incdir+rtl/includesThe build scripts add rtl/includes to the search path; every RTL file can write `include "common_defines.vh" without a relative-path prefix. Portable across reorganisations.
8. The Synthesis Reference — Common Real-World Headers
Three patterns that appear in production codebases.
8.1 Protocol definitions
`ifndef AXI_DEFINES_VH
`define AXI_DEFINES_VH
// AXI4 burst types
`define AXI_BURST_FIXED 2'b00
`define AXI_BURST_INCR 2'b01
`define AXI_BURST_WRAP 2'b10
// AXI4 response codes
`define AXI_RESP_OKAY 2'b00
`define AXI_RESP_EXOKAY 2'b01
`define AXI_RESP_SLVERR 2'b10
`define AXI_RESP_DECERR 2'b11
// AXI4 burst sizes (bytes per beat) — power-of-2 encoding
`define AXI_SIZE_1 3'b000 // 1 byte
`define AXI_SIZE_2 3'b001 // 2 bytes
`define AXI_SIZE_4 3'b010 // 4 bytes
`define AXI_SIZE_8 3'b011 // 8 bytes
`define AXI_SIZE_16 3'b100 // 16 bytes
`define AXI_SIZE_32 3'b101 // 32 bytes
`define AXI_SIZE_64 3'b110 // 64 bytes
`define AXI_SIZE_128 3'b111 // 128 bytes
`endif // AXI_DEFINES_VHEvery AXI4 interconnect module includes this. The constants match the AXI4 spec exactly; changing them would break protocol compliance. The header is the single source of truth for the protocol's vocabulary.
8.2 Register-map header
`ifndef REG_MAP_VH
`define REG_MAP_VH
`define REG_CTRL 12'h000 // Control register (RW)
`define REG_STATUS 12'h004 // Status register (RO)
`define REG_INTR_EN 12'h008 // Interrupt enable (RW)
`define REG_INTR_STAT 12'h00C // Interrupt status (W1C)
`define REG_DATA 12'h010 // Data register (RW)
// Control register bit positions
`define CTRL_EN_BIT 0
`define CTRL_RST_BIT 1
`define CTRL_MODE_LSB 2
`define CTRL_MODE_MSB 3
// ... etc.
`endif // REG_MAP_VHBoth the RTL (register decoder) and the testbench (register-access stimulus) include the same header. The addresses and bit positions match — no risk of "RTL says address 0x10, testbench writes 0x14" divergence.
8.3 Timescale + default_nettype combo
`ifndef TIMESCALE_VH
`define TIMESCALE_VH
`timescale 1ns/1ps
`default_nettype none
`endif // TIMESCALE_VHEvery RTL file `include-s this as the first non-comment line. Ensures consistent timescale and the no-implicit-net policy across the entire codebase. A single change in timescale.vh propagates to every file.
9. Simulation and Synthesis Behavior
`include runs at the preprocessor stage — before the compiler parses Verilog. The behaviour:
- The preprocessor encounters
`include "<file>"in the current source. - It opens the named file (resolving via the search path).
- It substitutes the file's contents at the directive's location.
- It continues processing the merged text.
The compiler that follows sees the merged source. By the time simulation or synthesis runs, the directive is gone — replaced by its effect.
The synthesis tool sees the same merged source. Headers and macros inside them produce identical gates to writing the same constants inline at every use site.
10. Common Mistakes
Six pitfalls that catch engineers using `include.
10.1 Forgetting include guards
The most common mistake. A header without guards processed via multiple paths produces "macro redefined" warnings or — worse — silently inconsistent behaviour. Every header file gets include guards. No exceptions.
10.2 Putting module definitions in headers
`ifndef BAD_HEADER_VH
`define BAD_HEADER_VH
module uart_rx (input wire clk, ...); // BAD: module in a header
// ...
endmodule
`endifIf two source files both `include "bad-header.vh", the uart_rx module is declared twice — compile error ("duplicate module declaration"). Headers should contain `define-s, `parameter declarations of system-wide constants, and `include-s of other headers. Module definitions go in .v files compiled standalone.
10.3 Hard-coded absolute paths
`include "/home/alice/my_project/headers/common.vh" // BADThe build breaks the moment the project moves directories, the moment another engineer pulls the repo, the moment the CI runs in a different filesystem. Use relative paths or +incdir+-relative names:
`include "common.vh" // GOOD: relies on +incdir+
`include "../includes/common.vh" // GOOD: relative10.4 Circular includes
// a.vh
`ifndef A_VH
`define A_VH
`include "b.vh"
`endif
// b.vh
`ifndef B_VH
`define B_VH
`include "a.vh"
`endifBoth guards prevent infinite recursion, but the order of definitions becomes implementation-defined: depending on which file the source includes first, different defines are available at different points. Refactor: extract the common piece into a third header that both a and b include.
10.5 Missing the search path
vsim my_top # no +incdir+!Tool fails with cannot open include file. The fix: add +incdir+ to every tool invocation, or set it in a project-wide configuration file (compile.tcl, Makefile, etc.).
10.6 Putting `timescale in a header without consistency check
If timescale.vh contains `timescale 1ns/1ps but a separate file already has a different `timescale set, the order of declarations becomes important and tool-dependent. The fix: put `timescale in a single header included as the FIRST line of every file, and never set it elsewhere.
11. Industry Use Cases
11.1 Project-wide constants header
Every modern RTL project has at least one common_defines.vh at the project root. It contains version numbers, data widths, address constants, protocol parameters. Every source file includes it as line 1 or 2. Changing a value updates every file atomically.
11.2 Protocol-specific headers
AXI4, AHB, APB, AXI-Stream, ARM CoreSight, RISC-V — each has its own published constants (response codes, burst types, transfer sizes). A protocol header captures these as macros. Modules implementing the protocol include the header and use its constants.
11.3 Register map sharing between RTL and testbench
The DUT's register decoder uses `REG_CTRL for address 0x000. The testbench's stimulus generator writes to `REG_CTRL. Both files `include "reg_map.vh" — same address, no divergence. A register-map change in the header propagates to both sides.
12. Debugging Lab
Three include-directive debug post-mortems
13. Coding Guidelines
- Every header file has include guards.
`ifndef MY_HEADER_VH/`define MY_HEADER_VH/`endif. - Use
.vh(or.svhfor SystemVerilog) extension for headers. Convention; helps build systems exclude headers from standalone compilation. - Headers contain
`define,`include,`timescale,`default_nettype. Nothing else. Modules go in.vfiles. - Use relative paths or
+incdir+-relative names. Never absolute paths. - Set
+incdir+once in build scripts, not per-file. Single source of truth. - Project-wide constants in
common_defines.vh. Protocol constants in<protocol>_defines.vh. Layered organisation. `include "timescale.vh"as line 1 of every RTL file. Forces consistent timescale anddefault_nettype.`endifcomments name the guard.`endif // COMMON_DEFINES_VH— useful in files with multiple`ifndefblocks.
14. Interview Q&A
15. Exercises
Three exercises that turn `include usage into reflex.
Exercise 1 — Add include guards
The following header file is missing include guards. Add them.
// fifo_defines.vh
`define FIFO_DATA_WIDTH 8
`define FIFO_DEPTH 16
`define FIFO_ALMOST_FULL_THRESHOLD 12
`define FIFO_ALMOST_EMPTY_THRESHOLD 4Hints. Use `ifndef FIFO_DEFINES_VH at the top, `define FIFO_DEFINES_VH immediately after, and `endif at the bottom. The trailing `endif should have a comment naming the guard.
Exercise 2 — Build a layered header structure
Design the header hierarchy for a project with these requirements:
- Project-wide data width (32 bits), address width (16 bits), clock frequency (100 MHz).
- An AXI4 interconnect (with AXI burst types and response codes).
- A custom UART peripheral that uses the AXI4 interface (with UART-specific baud rates).
- A custom SPI peripheral that uses the AXI4 interface.
What to produce. Identify three header files. For each, list what it contains and which other headers it includes. Show what a typical UART module file would include and what AXI burst constants it can use.
Exercise 3 — Fix the broken includes
The following project layout has three issues. Identify and fix each.
// project/rtl/modules/my_top.v
`include "common_defines.vh" // expects to find in current dir or +incdir+
// project/rtl/includes/common_defines.vh — NO INCLUDE GUARDS
`define CLK_HZ 100_000_000
`define BAUD 115_200
// Compile command:
// vsim project/rtl/modules/my_top.v (no +incdir+)What to produce. Identify the three problems and show the fix for each: (a) the missing include guards on common_defines.vh, (b) the missing +incdir+ in the compile command, (c) optionally, what else the header should include (timescale, default_nettype).
16. Summary
The `include directive inlines a file's contents at preprocessing time. The preprocessor reads the named file, substitutes its text at the directive's location, and continues processing the merged source. The mechanism is identical to C's #include and serves the same purpose: sharing constants, protocol definitions, register maps, and macros across multiple source files.
The syntax:
`include "filename.vh"— double-quoted, no trailing semicolon, one file per directive.- Path resolution — current directory of the including file, then
+incdir+paths, then tool defaults. - Convention —
.vhextension for headers,.svhfor SystemVerilog headers.
The canonical patterns:
- Include guards — every header wrapped in
`ifndef GUARD_NAME/`define GUARD_NAME/`endifto prevent multiple-inclusion bugs. - Common-defines header — single project-wide header for shared constants, included by every source file.
- Layered structure — project-wide → protocol-specific → module-specific. Each layer includes the layer below.
- Timescale + default_nettype combo — single
timescale.vhas the first include in every RTL file.
The synthesis-flow concerns:
+incdir+(or-Ifor Verilator,search_pathfor Synopsys DC) — set in build scripts to enable simple`include "name.vh"calls without relative paths.- No module definitions in headers — modules go in standalone
.vfiles compiled once. Headers contain only macros, includes, and project-wide directives.
The day-to-day discipline:
- Always use include guards. Every header. No exceptions.
- Relative paths or +incdir+-relative names. Never absolute paths.
- Headers contain macros and includes. Modules go in .v files.
- Set
+incdir+once in the build script. Per-file overrides are fragile. `include "timescale.vh"as line 1. Forces consistent project-wide settings.
The next three sub-pages drill into the other core directives:
- 7.2
`define— macro definition mechanics, argument-style macros, common pitfalls. - 7.3
`timescale— simulation time unit and precision, cross-file consistency. - 7.4 Conditional Compilation —
`ifdef/`ifndef/`else/`elsif/`endifpatterns.
After Chapter 7 closes, Chapter 8 picks up with system tasks & functions ($display, $monitor, $random, etc.) — the simulation-infrastructure side of the language.
Related Tutorials
- Compiler Directives — Chapter 7; the parent overview that frames this sub-topic.
- parameter — Chapter 6.1; module-scoped constants that complement file-scoped headers.
- localparam — Chapter 6.2; the strictly-internal counterpart to parameters.
- Lexical Conventions — Chapter 4; the lexer rules every directive must follow.
- RTL Designing — Chapter 3; the broader RTL framing.