Skip to content

SystemVerilog · Module 16

import — Explicit & Wildcard

SystemVerilog import — the shorthand for using package members without the pkg_name:: prefix. The two forms behave very differently: explicit (import pkg::item) verifies the name at the import line and errors on conflict with locals; wildcard (import pkg::*) registers a deferred search path and silently shadows under local declarations. Module-header vs body placement, nested-import non-export, name-resolution priority, and the UVM-style three-tier import pattern.

Module 16 · Page 16.2

import is a shorthand that lets a consumer use package members without writing pkg_name:: every time. There are two forms — explicit (import pkg::item) and wildcard (import pkg::*) — and they behave very differently under name conflicts, typos, and ambiguity. Equally important: an import statement must appear in the module header (not the body) if any package type appears in the port or parameter list, because port declarations are parsed before the body. Get the form right and import is a clean readability win; get it wrong and you create silent shadowing bugs, latent ambiguity errors, or unnecessary scope pollution that breaks elaboration in unrelated files.

1. Engineering Problem — Why import Exists

In Package Declaration & Usage (16.1) every package member was accessed with the :: operator — apb_pkg::apb_txn_t, apb_pkg::APB_DATA_W, and so on. That always works, but it becomes verbose when a module uses many items from the same package. A driver that uses ten items from axi4_pkg would have axi4_pkg:: written ten times — noise that obscures the actual logic.

import is the shorthand for that exact case. It tells the compiler: "for this scope, when you see a bare name you can't resolve locally, look inside this package." After the import, apb_txn_t resolves to the same thing as apb_pkg::apb_txn_t — the compiler does the lookup for you.

Critically, import does not copy code. It is purely a compile-time name-resolution hint. The package remains the single source of truth — import only adjusts what the compiler considers "in scope" for that consumer. This is a different model from `include (which physically pastes file contents) and from `define (which substitutes text before the compiler sees the code). import is a language-level construct that the compiler tracks symbolically.

The two forms then differ in what they bring into scope and how aggressively they bring it. Knowing which to use, and where to place the import statement, is what separates clean from fragile code.

2. Mental Model — Explicit = Name Copy; Wildcard = Search Path

The picture every engineer carries:

An explicit import (import pkg::item) copies exactly one name into the current scope, checked at the import line. If the name doesn't exist in the package, the error fires at the import statement — not deep inside the module. The compiler verifies eagerly. A wildcard import (import pkg::*) does not eagerly inject every name from the package. It registers the package as a fallback search path. When the compiler sees an unresolved bare name, it searches the wildcard-imported packages on demand. If exactly one provides the name, resolution succeeds. If two provide it, you get an ambiguity error — but only at the point of use, not at the import statements.

Four invariants this picture preserves:

  • import adds a search path; it does not duplicate code. A million consumers can import pkg::* and there is still exactly one definition of pkg::axi_ar_t in the elaboration.
  • Imports are scope-specific. An import inside module A is visible only inside module A. It does not leak to module B even if they live in the same file. (The single exception is $unit-scope import, which leaks across the whole file — and is why nobody does it.)
  • Explicit catches typos at the import line; wildcard catches conflicts at point of use. This asymmetry is the entire engineering distinction between the two forms. Pick explicit when you want eager checking; pick wildcard when you want the convenience of "let me reach the whole package."
  • Local declarations always win over imports, silently for wildcards and as a hard error for explicit. A localparam WIDTH = 64 quietly hides a wildcard-imported WIDTH = 32 with no warning — a real, silent bug. The same local conflicting with an import pkg::WIDTH is a compile error. This asymmetry is the single most surprising rule for newcomers.

3. Visual Explanation — Name Resolution Order

The figure below shows the four-level priority order the compiler uses when resolving an unqualified name.

Four stacked priority bands for resolving an unqualified name: Priority 1 local scope declarations, Priority 2 explicit imports, Priority 3 wildcard imports, Priority 4 $unit scope.Name Resolution Priority (highest → lowest)Name Resolution Priority (highest → lowest)Priority 1 — Local scope declarationsA local WIDTH always wins — even over a wildcard pkg. Silent shadow: no error, no warning.A local WIDTH always wins — even over a wildcard pkg. Silent shadow: no error, no warning.Priority 2 — Explicit imports (import pkg::item)Beats wildcard for the same name. Conflict with a local name → COMPILE ERROR (not silent).Beats wildcard for the same name. Conflict with a local name → COMPILE ERROR (not silent).Priority 3 — Wildcard imports (import pkg::*)Searched at the point of use. Two wildcards providing the same name → ambiguity COMPILE ERROR.Searched at the point of use. Two wildcards providing the same name → ambiguity COMPILE ERROR.Priority 4 — $unit scopeFile-level items, outside any module/package. Lowest priority — avoid putting anything here.File-level items, outside any module/package. Lowest priority — avoid putting anything here.
Figure 1 — name-resolution priority, highest band to lowest. The compiler resolves an unqualified name top-down: local declarations first, then explicit imports, then wildcard imports, then $unit scope.

The key asymmetry every engineer must internalise: local vs wildcard → silent shadow (no error); local vs explicit → compile error; explicit vs wildcard → explicit wins silently; wildcard vs wildcard → compile error, but only at the point of use.

The wildcard search itself is deferred — it does not happen at the import pkg::* statement. The compiler records "treat pkg as a search candidate" and moves on. When a bare name is encountered later, the compiler walks the recorded candidates in declaration order and applies the rules above. This is why two wildcard imports of packages that share a name do not error at the import statements; they only error at the line that actually uses the ambiguous bare name.

4. Syntax — Two Forms

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Explicit — exactly one identifier into scope ───────────────────
import pkg_name::item_name;     // brings in one name; verified now
 
// ── Wildcard — register package as fallback search path ────────────
import pkg_name::*;             // names resolved on demand

Both forms are statements (not declarations) and can appear in several places:

LocationAllowed?Purpose
Module / interface / program header (before #( or ()YesRequired when package types appear in port or parameter declarations
Module / interface / program / class bodyYesStandard placement when port types are plain logic and the package is only used inside
Inside another package (nested import)YesLets one package depend on another's types — but the import does not re-export
At $unit scope (top of file, outside any module)Legal but discouragedPollutes every compilation unit in the file — unintended coupling

The placement choice is rarely cosmetic — it determines whether the package types are visible to the port-list parser, and whether the import leaks beyond the intended module.

5. Code — Explicit, Wildcard, and Placement

5.1 Explicit Import — Verified at the Import Line

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Package ────────────────────────────────────────────────────────
package uart_pkg;
    parameter int BAUD_DIV  = 868;     // 100 MHz / 115200
    parameter int DATA_BITS = 8;
    typedef enum logic [1:0] { NONE=0, ODD=1, EVEN=2 } parity_e;
    typedef struct packed {
        logic [DATA_BITS-1:0] data;
        parity_e              parity;
    } uart_frame_t;
endpackage : uart_pkg
 
// ── Explicit import — only the named items enter scope ─────────────
module uart_tx;
    import uart_pkg::parity_e;       // ONLY this type is imported
    import uart_pkg::uart_frame_t;   // and this struct
    // uart_pkg::BAUD_DIV still needs pkg:: prefix — it was NOT imported
 
    parity_e     par_mode;           // OK — explicitly imported
    uart_frame_t tx_frame;           // OK — explicitly imported
 
    localparam int DIV = uart_pkg::BAUD_DIV;  // still needs :: (not imported)
 
    initial begin
        par_mode = EVEN;             // OK — parity_e literals also enter scope
        tx_frame.data   = 8'hA5;
        tx_frame.parity = par_mode;
    end
endmodule
 
// ── Compile-time safety: explicit import verifies the name exists ──
module bad_module;
    import uart_pkg::baud_div;       // COMPILE ERROR: 'baud_div' does not exist
                                     // (correct name is BAUD_DIV — case-sensitive)
endmodule

The typo on the last import line fires immediately. That early catch is the key advantage of explicit import — a misspelled identifier never makes it past the import statement to corrupt downstream resolution.

Note also that importing an enum-typed name (uart_pkg::parity_e) brings in the type and its literals (NONE, ODD, EVEN) — the literals enter scope as part of the enum definition.

5.2 Wildcard Import — Deferred Search, Latent Ambiguity

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Two packages that share a name ─────────────────────────────────
package bus_a_pkg;
    typedef enum { RD, WR }   cmd_e;     // 'cmd_e' defined here
    parameter int             WIDTH = 32;
endpackage
 
package bus_b_pkg;
    typedef enum { GET, PUT } cmd_e;     // 'cmd_e' ALSO defined here
    parameter int             DEPTH = 16;
endpackage
 
// ── Wildcard import — unambiguous names resolve cleanly ────────────
module consumer_ok;
    import bus_a_pkg::*;
    import bus_b_pkg::*;
 
    logic [WIDTH-1:0] data_a;            // OK: WIDTH only in bus_a_pkg
    logic [DEPTH-1:0] addr_b;            // OK: DEPTH only in bus_b_pkg
endmodule
 
// ── Ambiguous wildcard: using the shared name causes an error ──────
module consumer_bad;
    import bus_a_pkg::*;
    import bus_b_pkg::*;
 
    bus_a_pkg::cmd_e my_cmd;             // OK — explicit :: resolves ambiguity
    cmd_e            bad_cmd;            // COMPILE ERROR — ambiguous: found in both
endmodule
 
// ── Local declaration silently shadows wildcard ────────────────────
module consumer_shadow;
    import bus_a_pkg::*;                 // WIDTH = 32 available via wildcard
 
    localparam int WIDTH = 64;           // LOCAL declaration hides the wildcard
 
    logic [WIDTH-1:0] data;              // resolves to 64 (local) — NOT 32 (pkg)
endmodule
 
// ── Explicit import wins over wildcard for same name ───────────────
module consumer_explicit_wins;
    import bus_a_pkg::*;
    import bus_b_pkg::cmd_e;             // explicit import of bus_b_pkg::cmd_e
 
    cmd_e my_cmd;                        // resolved to bus_b_pkg::cmd_e
                                         // explicit wins over wildcard, silently
endmodule

The third example is the silent-shadow trap: the local WIDTH = 64 overrides the package's WIDTH = 32 without any diagnostic. If the intent was to use the package value, the bug compiles cleanly and produces wrong hardware. The mitigation is discipline: never name a local identical to a wildcard-imported one, or use pkg::WIDTH explicitly when you mean the package value.

5.3 Module Header Import — Required for Typed Ports

Port and parameter declarations are parsed before the module body. If you need a package type in a port declaration, the import must appear in the module header, between the module name and the #( or (.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Package ────────────────────────────────────────────────────────
package axi_pkg;
    parameter int DATA_W = 64;
    typedef struct packed {
        logic [DATA_W-1:0] wdata;
        logic [7:0]        wstrb;
        logic              wlast;
    } axi_w_t;
endpackage : axi_pkg
 
// ── WRONG: import inside body — too late for port list ─────────────
module bad_dut (
    input  axi_w_t wch,        // COMPILE ERROR: axi_w_t not yet visible
    output logic   wready
);
    import axi_pkg::*;         // too late — port list was already parsed
endmodule
 
// ── CORRECT: import in module header — before the port list ────────
module good_dut
    import axi_pkg::*;         // ← header import, before #() or ()
(
    input  axi_w_t wch,        // OK — axi_w_t is now visible
    input  logic   clk,
    output logic   wready
);
    axi_w_t captured;          // also visible in the body
    always_ff @(posedge clk)
        if (wch.wlast) captured <= wch;
endmodule
 
// ── Header import + parameter defaults using pkg type ──────────────
module parameterised_dut
    import axi_pkg::*;
#(
    parameter int W = DATA_W   // DATA_W from axi_pkg — visible via header import
) (
    input  axi_w_t      wch,
    output logic [W-1:0] rdata
);
endmodule
 
// ── Multiple header imports — wildcard + explicit mixed ────────────
module multi_import_dut
    import axi_pkg::*;
    import apb_pkg::apb_txn_t;
(
    input  axi_w_t   axi_in,
    input  apb_txn_t apb_in
);
endmodule

The pattern module name import pkg::*; #(...) (...); is the canonical header-import form. The same form is legal for interfaces (interface name import pkg::*; (...);) and programs.

5.4 Nested Import — Visible Inside, Not Re-exported

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Base package ───────────────────────────────────────────────────
package base_types_pkg;
    typedef logic [7:0]  byte_t;
    typedef logic [31:0] word_t;
endpackage
 
// ── Higher-level package depends on base_types_pkg ─────────────────
package protocol_pkg;
    import base_types_pkg::*;       // nested import — byte_t/word_t visible HERE
 
    typedef struct packed {
        byte_t opcode;              // resolved via nested import
        word_t payload;
    } msg_t;
 
    parameter int MSG_SIZE = $bits(msg_t);
endpackage
 
// ── Consumer of protocol_pkg — nested import is NOT re-exported ────
module msg_handler
    import protocol_pkg::*;
(
    input  protocol_pkg::msg_t req,
    output logic               ack
);
    // msg_t is visible — imported from protocol_pkg
    // byte_t is NOT automatically visible — nested imports do not propagate
    base_types_pkg::byte_t op = req.opcode;   // :: still works
    // To make byte_t bare-name available here, add:
    //     import base_types_pkg::byte_t;
endmodule

The non-re-export rule is the most-asked LRM clarification in interviews. The nested import base_types_pkg::* inside protocol_pkg makes the base names visible only during the compilation of protocol_pkg itself. A consumer that imports protocol_pkg does not transitively gain access to base_types_pkg — each consumer must import what it needs directly. (Module 16.4 covers the dependency-graph implications.)

5.5 Import in Interface and Class

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Interface header import — same rule as module ──────────────────
interface apb_if
    import apb_pkg::*;          // header import — pkg types usable in signals
(
    input logic pclk, presetn
);
    apb_pkg::apb_txn_t txn;     // struct type from the package
    logic              psel, penable, pready;
 
    modport master (output txn, psel, penable, input pready, pclk, presetn);
    modport slave  (input  txn, psel, penable, output pready);
endinterface
 
// ── Class body import ──────────────────────────────────────────────
class ApbMonitor;
    import apb_pkg::apb_txn_t;  // explicit import inside class
    import apb_pkg::APB_DATA_W;
 
    apb_txn_t observed_q[$];    // OK — apb_txn_t resolved via import
 
    function void capture(apb_txn_t t);
        observed_q.push_back(t);
        $display("[MON] paddr=%0h pwdata=%0h", t.paddr, t.pwdata);
    endfunction
endclass

Interfaces and programs follow the same header-vs-body rule as modules. Classes are simpler: a class has no port list, so any body-position import is sufficient.

6. Waveform — Omitted (Compile-Time Only)

This lesson is architectural, not timing-specific. import is a compile-time name-resolution mechanism — it produces no simulation events, schedules no transitions, and has no waveform behaviour. The timing behaviour of the code that consumes imported types (clocked logic, classes, programs) is covered in the relevant timing pages: program-block, clocking-blocks-deep-dive. This section is intentionally omitted; the topic does not warrant it.

7. Synthesis View — Transparent at Elaboration

import is fully transparent to synthesis. The synthesis tool resolves every reference through the same priority chain (local → explicit import → wildcard import → $unit) and then elaborates the resolved type/parameter/function into the consuming module. The import statement itself produces no gates, no nets, no flip-flops.

The only thing the synthesis tool cares about is whether the referenced item is synthesisable. A wildcard import of a package that contains class definitions and tasks is fine — the synthesis tool simply ignores the non-synthesisable members and only uses the parameters, types, and function automatic declarations that the consumer actually references. The package itself is never "synthesised"; only the resolved consumer-side references are.

8. Explicit vs Wildcard — Side by Side

AspectExplicit import pkg::itemWildcard import pkg::*
What it importsExactly one named identifierRegisters the whole package as a fallback search path
When names resolvedImmediately at the import statementOn demand — only when an unresolved name is used
Typo detectionError at the import lineNo check at import time; errors only at point of use
Conflict with local scopeCompile error if local has same nameLocal silently wins (shadows the package name)
Conflict with another import (same name)Error if same name explicitly imported twiceError only if the ambiguous bare name is actually referenced
ReadabilityReader sees exactly which items come from which packageAny bare name might come from any wildcard pkg — less traceable
Best used whenImporting 1–3 known items; safety-critical code; team standards require traceabilityA module uses many items from one package (UVM env, protocol VIP); the package was designed for wildcard import

The industry convention that follows:

  • Use explicit import when you only need 1–3 items from a package, or when team review must trace every unqualified name back to its source. Explicit is the safer default for new code.
  • Use wildcard import when you use a large portion of a package's contents (common for UVM base-class packages — import uvm_pkg::*; is the canonical form). Keep the number of wildcard-imported packages per module small to reduce ambiguity risk.
  • Fall back to :: for the few items that remain ambiguous after wildcard imports — mixing :: and import in the same module is perfectly legal and often the cleanest solution.

9. Real-World Pattern — UVM-Style Header Import

Every UVM environment uses the same import pattern in every component. It is worth memorising as the canonical example.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── UVM-style component package ────────────────────────────────────
package my_agent_pkg;
    `include "uvm_macros.svh"
    import uvm_pkg::*;                  // wildcard: UVM base classes
 
    import my_protocol_pkg::*;          // wildcard: protocol types
    import my_config_pkg::cfg_t;        // explicit: just the one config type
 
    class my_driver extends uvm_driver #(my_txn);
        `uvm_component_utils(my_driver)
 
        cfg_t cfg;
        // ...
    endclass
 
    class my_monitor extends uvm_monitor;
        `uvm_component_utils(my_monitor)
        // ...
    endclass
endpackage

The three-import pattern is intentional:

  1. import uvm_pkg::*; — wildcard because the component uses uvm_driver, uvm_monitor, uvm_sequence_item, uvm_analysis_port, etc. A dozen identifiers per file; wildcard is the only reasonable choice.
  2. import my_protocol_pkg::*; — wildcard because the component uses the protocol's transaction type, opcode enum, parameter constants, and helper functions. Same reasoning.
  3. import my_config_pkg::cfg_t; — explicit because the component only uses the cfg_t type. Importing the entire my_config_pkg would register one more wildcard candidate and increase ambiguity risk for no readability gain.

This three-tier pattern (UVM wildcard + protocol wildcard + targeted explicit) is what every senior UVM engineer writes by reflex.

10. Debug Lab — Six import Bugs

The bugs every code reviewer flags.

import — bugs every engineer makes once

1. Body import when a pkg type is needed in the port list

Symptom: Compile error — "apb_txn_t is not a type" on the port declaration line, even though the package is on the file list and the body import looks correct.

Cause: Port and parameter declarations are parsed before the module body. An import that lives inside the body has not been seen yet when the port-list parser tries to resolve the type name.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ❌ COMPILE ERROR
module bad1 (input apb_pkg::apb_txn_t req);  // port list parsed first
    import apb_pkg::apb_txn_t;               // too late — ports already parsed
endmodule

Fix: Move the import to the module header, before the #( or (:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module good
    import apb_pkg::*;                       // header import
(
    input apb_txn_t req                      // now visible
);
endmodule

Guardrail: any package type in a port or parameter list requires a header import. Body imports are fine only when the package types are used exclusively inside the body.

2. Assuming wildcard imports everything eagerly

Symptom: An engineer adds import some_pkg::*; and expects the whole package surface to be "ready" — but later writes a localparam with the same name as a package constant and is surprised when the local value is silently used.

Cause: Wildcard import does not eagerly inject every name. It registers the package as a deferred search candidate. Local declarations take priority and shadow wildcard-imported names silently — no error, no warning.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
import uart_pkg::*;                          // BAUD_DIV available via wildcard
localparam int BAUD_DIV = 1000;              // silently hides uart_pkg::BAUD_DIV
// All consumers see BAUD_DIV = 1000, not the pkg value

Fix: Either rename the local parameter, or use the package value explicitly when you mean the package value:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
localparam int DIV = uart_pkg::BAUD_DIV;     // explicit — unambiguous

Guardrail: never name a localparam / parameter / localvar identical to a name your wildcard imports might provide. If you can't avoid the collision, use pkg::name explicitly at every use site.

3. Latent ambiguity from two wildcards

Symptom: Two wildcard imports compile fine for months. Then someone adds a new use site of a bare name that happens to exist in both packages, and the build breaks far from the imports.

Cause: Wildcard ambiguity is latent — it only fires at the point where the bare ambiguous name is actually referenced. The two import pkg::*; statements themselves never error.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
import bus_a_pkg::*;
import bus_b_pkg::*;
// No error here — ambiguity is latent
// ... 200 lines later ...
txn_t my_txn;                                // COMPILE ERROR: txn_t in both pkgs

Fix: at the use site, either qualify explicitly (bus_a_pkg::txn_t) or add a targeted explicit import (import bus_a_pkg::txn_t;) to disambiguate.

Guardrail: when reviewing code that has more than one wildcard import, scan for any bare names that might exist in multiple packages — even if today's build passes. The error is one new typedef away.

4. Explicit import of a non-existent name (early catch — a feature)

Symptom: Compile error — "baudRate is not a member of uart_pkg" at the import statement.

Cause: Explicit imports are verified eagerly. The name must exist in the package at the time the import is parsed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
import uart_pkg::baudRate;                   // ERROR: 'baudRate' does not exist
                                             // (correct name: BAUD_DIV — case-sensitive)

Fix: match the exact case of the package member. SystemVerilog identifiers are case-sensitive.

Guardrail: this is actually the desired behaviour — explicit import catches typos at the import line, before they can pollute downstream resolution. If you find this annoying, you are reaching for the wildcard form when explicit's safety is exactly what you wanted.

5. Expecting nested imports to be re-exported

Symptom: A consumer imports protocol_pkg::* and expects to be able to use bare names from base_types_pkg (which protocol_pkg itself imports). The compiler errors on the base-type names.

Cause: Nested imports are visible only inside the package that does the import — they are not re-exported to the package's consumers. Each consumer must import what it needs directly.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
package base_types_pkg;
    typedef logic [7:0] byte_t;
endpackage
 
package protocol_pkg;
    import base_types_pkg::*;                // byte_t visible inside this pkg only
    typedef struct packed { byte_t op; } msg_t;
endpackage
 
module consumer
    import protocol_pkg::*;                  // msg_t visible
(
    input msg_t req
);
    byte_t raw;                              // ERROR: byte_t not visible here
endmodule

Fix: import the base package directly in the consumer:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module consumer
    import protocol_pkg::*;
    import base_types_pkg::byte_t;           // add the base import explicitly
(
    input  msg_t req
);
    byte_t raw;                              // OK
endmodule

Guardrail: think of each package's import set as private. If a consumer needs a transitively-imported name, the consumer must import it itself. Module 16.4 covers the dependency-graph patterns this implies.

6. Import at $unit scope (top of file)

Symptom: An import placed at the top of a file (outside any module) silently affects every module declared later in that file — including modules that were never meant to know about the package. Strange ambiguity errors appear in unrelated modules.

Cause: An import at $unit scope (file level, outside any module) is visible to every compilation unit defined later in the file. This is rarely the intended scope and produces cross-module coupling that breaks the encapsulation packages are meant to provide.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ❌ scope pollution
import apb_pkg::*;                           // $unit scope — affects every module below
 
module foo; /* ... */ endmodule              // foo sees apb_pkg names
module bar; /* ... */ endmodule              // so does bar — unintended

Fix: always place imports inside the module header or body where they are actually needed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module foo
    import apb_pkg::*;
(...);
endmodule
 
module bar;                                  // bar gets no apb_pkg names — clean
    // ...
endmodule

Guardrail: there is essentially no legitimate use for a $unit-scope import in production code. If you find one, treat it as a refactor target.

11. Q & A

The questions that come up in code review and interviews.

12. Cross-References & What's Next

This lesson covered the two import forms, their behaviour under name conflicts, the placement rules (header vs body), and the production conventions for choosing between them.

Related material elsewhere in the curriculum:

  • Class Scope Resolution (::) (Module 9.16) — the same :: operator applied to static class members, super, and parameterised-class specialisations.
  • What UVM Is — and What It Is Not — every UVM component uses the three-tier import pattern from §9; UVM is shipped as uvm_pkg and wildcard-imported into every environment.

13. Quick Reference

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Syntax ─────────────────────────────────────────────────────────
import pkg_name::item_name;     // explicit: exactly one identifier (checked now)
import pkg_name::*;             // wildcard: pkg registered as fallback search path
 
// ── Placement ──────────────────────────────────────────────────────
module my_mod
    import pkg::*;              // ← HEADER import — required when pkg types in ports
#(parameter int W = pkg::DATA_W)
(input pkg::txn_t port_in, ...);
 
    import pkg2::item;          // body import — fine when only used in the body
endmodule
 
// ── Name resolution priority (highest → lowest) ────────────────────
// 1. Local declarations          (always win — silently shadow wildcards)
// 2. Explicit imports            (win over wildcards — no error if same name)
// 3. Wildcard imports            (deferred search; ambiguity error if 2 wildcards match)
// 4. $unit scope                 (lowest — avoid putting imports here)
 
// ── Explicit vs Wildcard summary ───────────────────────────────────
// Explicit: one name, checked immediately, typos caught at import line
// Wildcard: whole pkg as search path, names resolved on demand, ambiguity latent
 
// ── Eight rules to live by ─────────────────────────────────────────
// 1.  Import does NOT copy code — it adds a name-resolution search path only.
// 2.  Wildcard import does NOT eagerly make all names visible.
// 3.  Two wildcards conflict ONLY when an ambiguous bare name is actually used.
// 4.  Local declaration silently shadows wildcard — no error, potential bug.
// 5.  Nested package imports are NOT re-exported to the package's consumers.
// 6.  Always use header import when pkg types appear in port or parameter lists.
// 7.  Never import at $unit scope — pollutes every module in the file.
// 8.  When ambiguity exists, use pkg:: to qualify — mixing :: and import is legal.

14. Summary

import is a compile-time name-resolution shorthand that lets a consumer use package members without writing the pkg_name:: prefix. The two forms are not interchangeable: explicit (import pkg::item) verifies the name eagerly at the import line and errors on conflict with locals; wildcard (import pkg::*) registers the package as a deferred search path, resolves names on demand, and silently shadows under local declarations. The eager-vs-deferred distinction is the entire engineering difference between the two forms.

The disciplines that follow:

  • Place imports in the module header when any package type appears in the port or parameter list — body imports are too late for the port-list parser.
  • Default to explicit imports for small, known item sets; switch to wildcard when the consumer uses a large portion of the package (UVM, protocol VIPs).
  • Never import at $unit scope — file-level imports pollute every module declared later in the file.
  • Nested imports are not re-exported — every consumer must import what it needs directly. This non-transitive rule is what keeps each package's public surface explicit.
  • When ambiguity exists, use :: — mixing explicit qualification with imports is perfectly legal and often the clearest solution.

The UVM-style three-tier pattern (import uvm_pkg::*; import my_protocol_pkg::*; import my_config_pkg::cfg_t;) is the canonical reference every senior verification engineer writes by reflex. The next two pages of Module 16 build on this: the canonical protocol-parameter and shared-type patterns (16.3), and the compilation-order discipline that keeps multi-package builds deterministic (16.4).