Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations35
    • SystemVerilog Data Types
    • Arrays: Packed, Dynamic, Queues & Associative
    • Procedural Code & Scheduling
    • Operators, Expressions & Width Rules
    • Packages, Typedefs & Compilation
      • Packages & Imports
      • Compilation Units & Compile Order
      • typedef, parameter & localparam
      • `include vs import
      • Namespace Hygiene at Scale
    • Interfaces, Modports & Clocking Blocks
  • Object-Oriented SystemVerilog for Verification35
  • Constraint Randomization — Interview-Critical Deep Dive66
  • SystemVerilog Assertions — Interview-Critical Deep Dive63
  • Functional Coverage in SystemVerilog28
  • Testbench Architecture in SystemVerilog38
  • Advanced Language & Integration30
  • Senior & Interview Preparation34
  • Principal-Level Verification28
Loading lesson

‹Packages, Typedefs & Compilation
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Language Foundations
  4. Packages, Typedefs & Compilation
  5. `include vs import

Part 1 · Language Foundations · Intermediate

`include vs import

Textual include against compiled import, guarded headers, and which mechanism shares what.

On this page

    Why this matters

    One header included into two modules gave two struct types that would not connect. include pastes text; it never shares a definition.

    After this lesson you can: choose include or import for a sharing problem, write a guarded header, and explain why macros still need headers.

    Paste against reference

    An include is a preprocessor directive: before compilation, the file text is pasted verbatim at the directive site. An import is a compiler construct: it makes names from an already-compiled package visible in the current scope.

    The consequences diverge completely. Included text is re-parsed at every site and belongs to the surrounding scope. Imported names are compiled once and keep one identity: import a struct from a package into two hundred modules and there is still exactly one type.

    An include pastes copies; an import refers to one compiled definition.

    Shared thing

    Mechanism

    Why

    Macros and conditional-compile blocks

    `include

    The preprocessor has no packages

    Types, parameters, functions, classes

    Packages plus import

    Textual sharing breaks type identity

    One large package split for readability

    `include inside the package body

    File-level readability with package-level identity

    Tool and legacy headers

    `include

    Same macro rule as any header

    Guards make headers repeatable

    Textual inclusion pastes at every site, so including one header twice re-declares everything in it. The guard idiom wraps the body in ifndef, define and endif so later inclusions expand to nothing.

    Every header file gets a guard with no exceptions. Diamond-shaped include graphs appear within weeks, and the guard macro lives in the global preprocessor namespace, so name it after the full file path. Macros are preprocessor objects and cannot live in a package, which is why headers exist at all.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    module tb_guarded_header;
      timeunit 1ns; timeprecision 1ps;
      // ---- emulated first `include "bus_hdr.svh" ----
      `ifndef BUS_HDR_SVH
      `define BUS_HDR_SVH
      typedef struct packed {
        logic [7:0] lo;
        logic [7:0] hi;
      } bus_hdr_t;
      function automatic logic [7:0] hdr_sum(bus_hdr_t h);
        return h.lo + h.hi;
      endfunction
      `endif
      // ---- emulated second `include "bus_hdr.svh": same text, expands to nothing ----
      `ifndef BUS_HDR_SVH
      `define BUS_HDR_SVH
      typedef struct packed {
        logic [7:0] lo;
        logic [7:0] hi;
      } bus_hdr_t;
      function automatic logic [7:0] hdr_sum(bus_hdr_t h);
        return h.lo + h.hi;
      endfunction
      `endif
    
      bus_hdr_t h;
      int errors = 0;
      initial begin
        h.lo = 8'h11; h.hi = 8'h22; #1;
        if (hdr_sum(h) !== 8'h33) errors++;
        if (errors != 0) $fatal(1, "FAIL: guarded header names did not survive double inclusion");
        $display("PASS: guarded header included twice declares one set of names");
        $finish;
      end
    endmodule
    Terminal
    PASS: guarded header included twice declares one set of names

    The two pasted copies above stand in for two include lines naming one guarded header. The second copy vanishes in preprocessing, so the scope sees exactly one declaration set.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad-example: the same header pasted twice with no guard
    module tb_unguarded_header;
      typedef struct packed {
        logic [7:0] lo;
        logic [7:0] hi;
      } bus_hdr_t;
      typedef struct packed {
        logic [7:0] lo;
        logic [7:0] hi;
      } bus_hdr_t;   // re-declaration: no guard removed the duplicate
    endmodule
    Check yourself Two headers each include a third, and a testbench includes both. Which inclusion breaks, and what prevents it?

    The third header would declare twice without protection. A guard named after its path makes the second expansion empty, so the diamond is harmless.

    Later reading: the VIP package layout

    Note

    This section uses classes, which the OOP part teaches. Skip it on a first pass and come back once classes are familiar.

    Production verification IP combines both mechanisms in one fixed idiom. Macros stay outside in a guarded header, and class files are included one by one inside the package body in dependency order.

    The outside world then sees a clean namespace through one import. The inside stays navigable as one file per class, and every class compiles into the package single scope with identity preserved. The sketch below spans several files, so it is shown as structure rather than a checked listing.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // snippet: one package per VIP, class files included inside in dependency order
    `include "my_vip_macros.svh"   // macros: preprocessor layer, outside any package
    
    package my_vip_pkg;
    `include "my_vip_types.svh"    // typedefs and enums
    `include "my_vip_txn.svh"      // class my_vip_txn
    `include "my_vip_driver.svh"   // class my_vip_driver, uses the transaction
    `include "my_vip_monitor.svh"
    `include "my_vip_agent.svh"    // include order is dependency order
    endpackage
    
    // user side:
    // `include "my_vip_macros.svh"
    // import my_vip_pkg::*;
    Check yourself Why do class files sit inside the package body instead of being imported separately?

    There is no per-file import for classes. Inclusion inside the body compiles every class into the one package scope, which keeps a single type identity across the VIP.

    Common mistakes

    Common pitfall

    Sharing a struct by including its file into several modules and expecting one type. Each inclusion makes a new, distinct type.

    • Classes, enums and unpacked structs from two inclusions cannot be assigned to each other.

    • Packed copies can be assigned, but type comparisons and type parameters still tell them apart.

    • Only an import from one package shares one type.

    Interview

    Likely questions: “include or import for this sharing problem”, “why does UVM ship both a package and a macro header”, and “how is a VIP package structured”.

    • Include or import: preprocessor paste against compiled-namespace reference, and identity follows the choice.

    • Package plus header: macros cannot live in packages, so the header carries them.

    • VIP structure: one package whose body includes one class file per component, macros outside.

    Check yourself A header guard is named DEFS_SVH. Two unrelated headers use the same name. What happens?

    The first inclusion defines the macro and empties the second header entirely. Guard names must carry the file path to stay unique.

    Remember

    • include pastes text before compiling; import refers to a compiled package.

    • Each inclusion of a type makes a new, distinct type.

    • Guard every header, and name the guard after the file's path.

    Rule of thumb: macros travel by include; everything else by import.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 (-g2012) runs them too, with the same output.

    The double-inclusion bench writes the two expanded copies inline, because a lesson listing is one file; the unguarded listing is a deliberately broken example.

    Related topics

    • Next topic: Interfaces, Modports & Clocking Blocks

    • Later reading: Namespace Hygiene at Scale

    Previous

    33 / 363

    Next: Namespace Hygiene at Scale

    Up next

    Up nextNamespace Hygiene at Scale

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Packages & Imports
    • Compilation Units & Compile Order