Skip to main content

SystemVerilog Package – Reusable Types, Constants, and Functions

· loading · loading · ·
Hardware Design Verification SystemVerilog Package Namespace Modular Design RTL UVM
Hardware Design Verification
Axolot Logic
Author
Axolot Logic
Digital Design Engineer
Table of Contents
SystemVerilog Design Series - This article is part of a series.
Part 18: This Article

📦 What is a package?
#

A package in SystemVerilog is a modular container that holds reusable type definitions, constants, functions, and tasks.
It acts like a namespace, similar to namespace in C++ or Java, helping to keep code organized, readable, and reusable across modules and testbenches.


🧱 Basic package Syntax
#

package my_pkg;
  typedef enum logic [1:0] {IDLE, RUN, STOP} state_t;
  int global_counter;
  function int add(int a, b);
    return a + b;
  endfunction
endpackage

🚀 How to Use a package
#

You can use the contents of a package in three ways:

🔹 1. Import Entire Package
#

import my_pkg::*;

Makes all symbols (e.g., state_t, add()) visible in the current scope.

🔹 2. Import Specific Items
#

import my_pkg::state_t;

Safer and cleaner — only brings in what you need.

🔹 3. Use Fully Qualified Names
#

state_t s;
s = my_pkg::IDLE;

Prevents naming collisions by explicitly referencing the namespace.


🧠 Why Use Packages?
#

BenefitDescription
Avoid repetitionCentralizes reusable types, constants, and functions
Reusable testbenchesCommon in UVM for shared testbench utilities
Large-scale designKeeps design modular and organized
Type safetyEach package has its own isolated namespace

🧪 Example: Shared Types in Design & Testbench
#

// my_defs_pkg.sv
package my_defs_pkg;
  typedef logic [31:0] word_t;
  typedef enum {REQ, RESP} tx_type_t;
endpackage
// dut.sv
import my_defs_pkg::*;

module dut(input word_t a, b, output word_t sum);
  assign sum = a + b;
endmodule
// testbench.sv
import my_defs_pkg::*;

initial begin
  word_t a = 32'h10;
  word_t b = 32'h20;
  tx_type_t mode = REQ;
  ...
end

🛠️ What Can You Put Inside a Package?
#

Definition TypeAllowed in package
typedef, enum, struct✅ Yes
parameter, localparam✅ Yes
function, task✅ Yes
interface, module❌ No (must be declared outside)

⚠️ Common Pitfalls
#

  • A package is not a module — it can’t contain always, initial, or procedural logic.
  • import should be placed before usage in the compilation unit.
  • Same names can exist in different packages — namespace prevents collisions.

🧠 package vs include
#

Featurepackageinclude
Modular structure✅ Yes❌ Just text substitution
Namespace support✅ Yes❌ None
Compile-time✅ Recognized by compiler❌ Preprocessor-based
Error handling✅ Scoped and isolated❌ Can introduce hidden bugs

✅ Summary
#

  • Use package to define reusable constants, types, and functions.
  • Use import my_pkg::*; or import my_pkg::item; to control visibility.
  • Use my_pkg::symbol style to avoid naming collisions in large designs.
  • Widely used in UVM, testbenches, and shared RTL infrastructure.

// Best practice
import my_util_pkg::my_function;
import uvm_pkg::*;

Packages help you write cleaner, safer, and more modular SystemVerilog code.


// === File: my_pkg.sv ===
package my_pkg;
  typedef enum logic [1:0] {IDLE, RUN, STOP} state_t;
  int global_counter;

  function int add(int a, b);
    return a + b;
  endfunction
endpackage
// === File: dut.sv ===
import my_pkg::*;

module dut(input logic clk,
           input logic [3:0] a, b,
           output logic [4:0] result,
           output my_pkg::state_t state);

  assign result = add(a, b);
  assign state = RUN;

endmodule
// === File: tb_pkg_demo.sv ===
import my_pkg::*;

module tb_pkg_demo;

  logic clk = 0;
  logic [3:0] a = 4'd7, b = 4'd9;
  logic [4:0] result;
  state_t current_state;

  // Clock generation
  always #5 clk = ~clk;

  // DUT instantiation
  dut u_dut (
    .clk(clk),
    .a(a),
    .b(b),
    .result(result),
    .state(current_state)
  );

  initial begin
    #10;
    $display("Result = %0d", result);
    $display("State  = %s", current_state.name());
    #10 $finish;
  end

endmodule

📘 Scoped Package Import in Modules
#

Starting from SystemVerilog 2012, you can import a package directly inside a module, interface, or function definition. This makes the imported symbols visible only within that specific scope, rather than globally across the file.

This helps avoid name collisions and improves modularity in large projects.


Example: Scoped import Inside a Module
#

module tb_pkg_demo import my_pkg::*;

  logic [3:0] a = 4'd5, b = 4'd3;
  logic [4:0] result;
  state_t s;

  initial begin
    result = add(a, b);
    s = RUN;
    $display("Result = %0d, State = %s", result, s.name());
  end

endmodule

🔎 This imports everything from my_pkg only inside tb_pkg_demo, without affecting other modules or global scope.


SystemVerilog Design Series - This article is part of a series.
Part 18: This Article

Related

SystemVerilog Clocking Block – Timing Control for Testbenches
· loading · loading
Verification Hardware Design SystemVerilog Clocking Block RTL Design Testbench UVM Timing
Verification Hardware Design
SystemVerilog Intoduction
· loading · loading
Hardware Design Verification SystemVerilog Verilog RTL Design UVM Hardware Verification IEEE 1800
Hardware Design Verification
SystemVerilog Data Types
· loading · loading
Hardware Design Verification SystemVerilog Verilog RTL Design Data Types Synthesis Simulation
Hardware Design Verification
SystemVerilog Enum Data Type
· loading · loading
Hardware Design Verification SystemVerilog Enum FSM RTL Design Testbench Debugging
Hardware Design Verification
SystemVerilog Logic Data Type
· loading · loading
Hardware Design Verification SystemVerilog Logic RTL Design Verilog Synthesis Net vs Variable
Hardware Design Verification
SystemVerilog Tasks and Functions
· loading · loading
Hardware Design Verification SystemVerilog Tasks Functions RTL Design Testbench Reusability
Hardware Design Verification