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 Foundations28
    • 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 Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Packages, Typedefs & Compilation
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Language Foundations
  4. Packages, Typedefs & Compilation
  5. Namespace Hygiene at Scale

Part 1 · Language Foundations · Intermediate

Namespace Hygiene at Scale

Collision failure modes, leaf-only wildcards, prefixed names and vendor IP patterns.

On this page

    Why this matters

    A shared environment package stopped compiling for every team after a vendor release added one name. Wildcard imports turned someone else's change into everyone's error.

    After this lesson you can: recognise the three ways imported names collide, apply the leaf-only wildcard rule, and keep package names prefixed.

    Note

    Later reading. This lesson matters once a bench imports packages from several teams or vendors, and parts of it use classes from the OOP part. On a first pass, the three rules in Packages & Imports are enough.

    Three ways sharing breaks

    Two-package benches never collide. Forty-package SoC benches with three vendors fail in three escalating ways: ambiguity errors the compiler reports, silent shadowing it does not, and latent breakage that arrives with a vendor next release.

    Enum members make all three worse. Every member name lands in the importing namespace beside the type, and every protocol package on earth defines an IDLE. Unprefixed members are a guaranteed future clash.

    Three ways wildcard imports collide, from a loud error to a silent wrong answer.
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // bad-example: two wildcards, one shared name
    package axi_pkg;
      typedef enum logic [1:0] { AXI_IDLE, AXI_MOVE, AXI_DONE, AXI_ERR } status_e;
    endpackage
    package pcie_pkg;
      typedef enum logic [1:0] { PCIE_IDLE, PCIE_MOVE, PCIE_DONE, PCIE_ERR } status_e;
    endpackage
    module tb_clash;
      import axi_pkg::*;
      import pcie_pkg::*;
      status_e s;   // ambiguous: both packages define status_e
    endmodule

    The leaf-only wildcard rule

    Mature codebases converge on one import discipline. Package headers and shared files never wildcard-import: they use explicit imports or fully qualified references, because anything a package imports shapes resolution for everything compiled inside it.

    Leaf scopes may wildcard-import: one test class, one testbench top, the inside of one module. The blast radius ends at the scope closing keyword. Where two packages both provide a needed name, qualify at the use site instead of importing either.

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package axi_pkg;
      typedef enum logic [1:0] { AXI_IDLE, AXI_MOVE, AXI_DONE, AXI_ERR } axi_status_e;
      typedef struct packed {
        logic [31:0]   addr;
        axi_status_e   st;
      } axi_txn_t;
      function automatic bit axi_ok(axi_status_e s);
        return (s == AXI_DONE);
      endfunction
    endpackage
    
    package pcie_pkg;
      typedef enum logic [1:0] { PCIE_IDLE, PCIE_MOVE, PCIE_DONE, PCIE_ERR } pcie_status_e;
      typedef struct packed {
        logic [31:0]    addr;
        pcie_status_e   st;
      } pcie_txn_t;
      function automatic bit pcie_ok(pcie_status_e s);
        return (s == PCIE_DONE);
      endfunction
    endpackage
    
    // Shared code: explicit imports only, qualified rare references.
    package soc_env_pkg;
      import axi_pkg::axi_txn_t;
      import pcie_pkg::pcie_txn_t;
      function automatic bit pair_ok(axi_pkg::axi_status_e a, pcie_pkg::pcie_status_e p);
        return (axi_pkg::axi_ok(a) && pcie_pkg::pcie_ok(p));
      endfunction
    endpackage
    
    module tb_hygiene;
    
      timeunit 1ns; timeprecision 1ps;
      import soc_env_pkg::*;   // leaf scope: the blast radius ends here
      axi_pkg::axi_txn_t at;
      pcie_pkg::pcie_txn_t pt;
      int errors = 0;
      initial begin
        at.addr = 32'h1000; at.st = axi_pkg::AXI_DONE;
        pt.addr = 32'h2000; pt.st = pcie_pkg::PCIE_DONE; #1;
        if (pair_ok(at.st, pt.st) !== 1'b1) errors++;
        pt.st = pcie_pkg::PCIE_MOVE; #1;
        if (pair_ok(at.st, pt.st) !== 1'b0) errors++;
        if (at.addr !== 32'h1000) errors++;
        if (pt.addr !== 32'h2000) errors++;
        if (errors != 0) $fatal(1, "FAIL: protocol names collided or crossed in the shared environment");
        $display("PASS: explicit imports and prefixed names keep two protocols side by side");
        $finish;
      end
    endmodule
    Terminal
    PASS: explicit imports and prefixed names keep two protocols side by side
    Check yourself A shared environment package wildcard-imports two vendor packages today and compiles. Why might it fail next quarter with no local edits?

    A vendor release can add a name that collides with the other package or with local code. The wildcard turns their addition into your ambiguity error. Explicit imports are immune.

    Prefixes and constant containers

    Every name a package exports earns a short package tag: axi_txn_t, AXI_IDLE, pcie_cfg_t. Collisions turn from likely into improbable, and readers can place each name instantly.

    Related constants can also hide inside a class scope as static members (classes come in the OOP part), referenced as axi_consts::IDLE. Class members never leak into importer namespaces, which makes the class an airtight constant container. Either habit works; mixing bare global names does not.

    Check yourself Two packages each define IDLE as an enum member. One prefixes, one does not. Which import breaks first under wildcards?

    The unprefixed one collides with every other bare IDLE in the bench. Prefixing moves the name out of the shared pool of short words.

    Vendor IP package patterns

    Commercial VIP shows the pattern to copy. Each VIP ships exactly one public package with prefixed names, one guarded macro header, and one filelist stub that solves compile order for the integrator.

    The contract runs both ways. The vendor adds names but never repurposes published ones, since it cannot fix the importing code it would break. Your in-house VIP deserves the same courtesy the moment a second team imports it: public names are an API.

    Who

    Ships or writes

    Promises

    Vendor

    One public package with prefixed names (vip_axi_pkg), one guarded macro header, one filelist stub with the compile order solved

    Never rename or repurpose a published name; only add

    Integrator

    include of the macro header; explicit imports in shared code, or a wildcard inside one leaf test

    Never import vendor internals or rely on unprefixed names

    Common mistakes

    Common pitfall

    Wildcard-importing two packages inside a shared environment package. A new name in either source makes references inside the package ambiguous, so the shared package stops compiling for everyone, far from the import that caused it.

    Interview

    Likely questions: “why are wildcards discouraged in packages”, “how do enum members cause collisions”, and “how does vendor VIP avoid clashes”.

    • Wildcards in packages: a new name in either source makes references inside the package ambiguous, so the shared package stops compiling for everyone. Imports are not re-exported unless you use export.

    • Enum members: each member name enters the namespace with the type, so prefix members or contain them.

    • Vendor VIP: one prefixed public package, one guarded macro header, additive-only releases.

    Check yourself A leaf test class wildcard-imports the environment package. Is that acceptable under the rule?

    Yes. The blast radius ends at the class closing keyword, which is exactly what the leaf-only rule permits.

    Check yourself Your package must use a name that exists in two imported packages. What is the cheapest correct fix?

    Qualify at the use site: axi_pkg::txn_t costs one line and no aliasing machinery. Import neither bare name.

    Remember

    • Ambiguity is loud; a future collision is latent; shadowing by a local is silent.

    • Wildcard-import only in leaf scopes; shared packages use explicit imports.

    • Prefix exported names, enum members included.

    Rule of thumb: public names are an API: prefix them and never repurpose them.

    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 two-wildcard listing is a deliberately broken example and is not run.

    Related topics

    • Next topic: Interfaces, Modports & Clocking Blocks

    Previous

    30 / 259

    Next: Interfaces, Modports & Clocking Blocks

    Up next

    Up nextInterfaces, Modports & Clocking Blocks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Packages & Imports
    • Compilation Units & Compile Order
    • typedef, parameter & localparam