Part 1 · Language Foundations · Intermediate
`include vs import
Textual inclusion vs namespace import, include guards, when each mechanism is correct, and the hybrid pattern in VIP code.
Two mechanisms, two layers of the language
`include is a preprocessor directive: before the compiler proper ever runs, the file's text is pasted verbatim at the directive's location, exactly as if you had typed it there. 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 inclusion site and its declarations belong to whatever scope surrounds the directive; imported names are compiled once and keep a single identity. Include the same struct definition into two modules and you have two incompatible types; import it from a package into two hundred modules and you have one.
`include (preprocessor) import (compiler)
──────────────────────── ─────────────────────
text pasted at each site names referenced from
│ one compiled package
▼ │
file.svh ──paste──► module A ▼
file.svh ──paste──► module B bus_pkg ◄── import ── module A
bus_pkg ◄── import ── module B
two copies, two scopes,
two DISTINCT types one definition, one type,
re-parsed every time compiled exactly onceInclude guards — mandatory armor for .svh files
Because inclusion is textual, including the same file twice into one compilation unit re-declares everything in it — an instant compile error. The guard idiom wraps the file body in `ifndef / `define / `endif so the second and later inclusions expand to nothing. Every header file (.svh) you ever write gets a guard, no exceptions — diamond-shaped include graphs (A includes B and C, both of which include D) appear in any project within weeks. Remember the guard macro lives in the global preprocessor namespace, so name it after the full file path to avoid two files claiming the same guard.
// bus_defines.svh
`ifndef BUS_DEFINES_SVH
`define BUS_DEFINES_SVH
`define BUS_MAX_BURST 16
`define bus_assert(cond, msg) \
if (!(cond)) $error("[BUS] %s", msg)
`endif // BUS_DEFINES_SVH
// consumer file — safe to include from many places:
`include "bus_defines.svh"
`include "bus_defines.svh" // second expansion is empty: no errorNote what lives in this header: macros . Macros are preprocessor objects and cannot live in a package — there is no other way to share them than inclusion. This is the first half of the rule of thumb: `include for things the preprocessor owns, import for things the compiler owns.
When each is correct, and the hybrid VIP pattern
Decision rules
Macros (`define) and conditional-compile blocks — must be `include; the preprocessor has no packages.
Types, parameters, functions, classes — must be packages + import; textual sharing breaks type identity.
Splitting one large package across files for readability — `include the fragments INSIDE the package body.
Tool/legacy headers (uvm_macros.svh is the canonical example) — `include, by the same macro rule.
Production VIP combines both in a fixed idiom: one package per VIP whose body includes its class files one by one, in dependency order. The outside world sees a clean namespace (import my_vip_pkg::*;), the inside stays navigable as one-class-per-file, and because the includes are expanded inside the package body, every class compiles into the package's single scope — type identity preserved. UVM itself ships exactly this shape: uvm_pkg plus the separately included uvm_macros.svh.
// my_vip_pkg.sv — the hybrid pattern
`include "my_vip_macros.svh" // macros: preprocessor layer, outside pkg
package my_vip_pkg;
import uvm_pkg::*;
// class-per-file readability, single-package identity:
`include "my_vip_types.svh" // typedefs, enums
`include "my_vip_txn.svh" // class my_vip_txn
`include "my_vip_driver.svh" // class my_vip_driver (uses txn)
`include "my_vip_monitor.svh"
`include "my_vip_agent.svh" // include order = dependency order
endpackage
// user code:
`include "my_vip_macros.svh"
import my_vip_pkg::*;Interview angle
"Difference between `include and import?" — preprocessor paste vs compiled-namespace reference; type identity follows.
"Why does UVM need both uvm_pkg and uvm_macros.svh?" — macros cannot live in packages.
"How is a VIP package typically structured?" — package body that `includes one class file per component.
Key takeaways
`include is textual paste at the preprocessor layer; import references one compiled package definition.
Every .svh header gets an `ifndef guard named after its path — diamond includes are inevitable.
Macros can only be shared by `include; types and classes should only be shared by package import.
The VIP idiom: `include class files inside the package body — file-level readability with package-level identity.
Common pitfalls
Sharing a struct via `include into multiple modules — structurally identical but incompatible types.
Shipping a .svh without an include guard — re-declaration errors the first time two paths include it.
Guard macro named SVH or DEFINES_SVH generically — collides with another file's guard and silently empties it.
`include-ing class files both inside a package and directly into $unit — two class definitions, baffling errors.