Part 7 · Advanced & Integration · Intermediate

`include & Guard Discipline

Textual include semantics, the double-include failure, ifndef guard pattern, +incdir+ search paths, and include-based class libraries.

`include is textual paste

`include "file.svh" replaces the directive with the entire contents of the file , verbatim, at that exact spot — as if you had typed it there. It is not an import, not a module reference, and creates no namespace. The included text is then preprocessed and compiled in whatever context surrounds the include — inside a package, inside a module, or at file scope.

systemverilog
// my_pkg.sv — the canonical include-based class library layout
package my_pkg;
  import uvm_pkg::*;
  `include "uvm_macros.svh"

  // Order matters: base classes before derived classes,
  // because this is literal text pasted top to bottom.
  `include "my_txn.svh"        // class my_txn extends uvm_sequence_item
  `include "my_driver.svh"     // uses my_txn — must come after it
  `include "my_monitor.svh"
  `include "my_agent.svh"      // uses driver + monitor
endpackage

// Compile the PACKAGE file; the .svh files are never compiled
// on their own — they only exist as text pasted into the package.

The double-include failure and the guard

If two files both include the same header, the class inside it gets pasted — and therefore declared — twice, producing a duplicate-definition compile error. The fix is the ifndef include guard : the first paste defines a marker macro; every later paste sees the marker already defined and skips the body.

systemverilog
// my_txn.svh — guarded header, safe to include any number of times
`ifndef MY_TXN_SVH        // first include: not yet defined → enter
`define MY_TXN_SVH        // define the marker immediately

class my_txn extends uvm_sequence_item;
  rand bit [31:0] addr;
  rand bit [31:0] data;
  `uvm_object_utils_begin(my_txn)
    `uvm_field_int(addr, UVM_ALL_ON)
    `uvm_field_int(data, UVM_ALL_ON)
  `uvm_object_utils_end
  function new(string name = "my_txn");
    super.new(name);
  endfunction
endclass

`endif // MY_TXN_SVH       // comment the endif — helps when guards nest
diagram
DOUBLE-INCLUDE: FAILURE vs GUARD

  WITHOUT GUARD                        WITH GUARD
  ─────────────                        ──────────
  driver.svh:  `include "my_txn.svh"   driver.svh:  `include "my_txn.svh"
  monitor.svh: `include "my_txn.svh"   monitor.svh: `include "my_txn.svh"
       │                                    │
       ▼ both pasted into package           ▼
  class my_txn ...  ← 1st copy         1st paste: MY_TXN_SVH undefined
  class my_txn ...  ← 2nd copy            define it, paste class body
       │                               2nd paste: MY_TXN_SVH defined
       ▼                                  body skipped entirely
  ERROR: 'my_txn' already declared     COMPILES CLEAN

Include paths and library organization

The preprocessor finds included files by searching directories given with +incdir+ (plus the including file's own directory, tool-dependent). Source code stays portable — it names only the file, and the build flow supplies the search path.

bash
# Each agent directory added to the include search path
vcs +incdir+tb/agents/bus_agent \
    +incdir+tb/agents/mem_agent \
    +incdir+tb/env \
    tb/my_pkg.sv tb/tb_top.sv

# Source says:           `include "bus_driver.svh"
# Preprocessor searches: tb/agents/bus_agent/  → found

Library conventions that scale

  • .sv files are compilation units (packages, modules); .svh files are include-only fragments that never compile standalone.

  • Every .svh file gets a guard whose macro name mirrors the file path — MY_PROJ_BUS_DRIVER_SVH — to avoid collisions between same-named files in different agents.

  • One package file per agent or env includes its .svh files in dependency order; everything else imports the package.

  • Never `include a file that declares a module — modules belong in separately compiled .sv files; includes are for classes, macros, and shared declarations.

Key takeaways

  • `include is literal text paste — included files compile in the surrounding context, and paste order is dependency order.

  • Guard every .svh header with `ifndef/`define/`endif so multiple includes are harmless.

  • Use +incdir+ for search paths; keep file names (not paths) in source for portability.

  • Organize class libraries as one compiled package that includes guarded .svh fragments in dependency order.

Common pitfalls

  • Guard macro name copy-pasted between two headers — the second include silently vanishes because the marker is already defined.

  • Compiling a .svh file directly — classes land in the file's compilation scope instead of the intended package, causing baffling type-mismatch errors.

  • Includes in the wrong order inside a package — derived class pasted before its base class fails to compile.

  • Missing +incdir+ for a nested directory — 'cannot find include file' even though the file plainly exists on disk.

Interview angle

Common questions: what `include actually does (text paste, not import); why a class declared twice errors and how guards prevent it; difference between `include and package import (text insertion vs compiled-symbol visibility). A favorite trap: two headers accidentally sharing one guard macro name — explain why one class silently disappears.