Structures & Unions in SystemVerilog

Structures let you group related data together under one name. Unions allow different data types to share the same memory. Both are useful for organizing complex data in verification.

What is a Structure?

A structure (struct) is a collection of variables grouped together under a single name. Each variable in the struct is called a member and can have a different data type.

Think of a structure like a form you fill out. A job application form has fields for name, age, address, phone - all different types of data, but grouped together as one "application".
Basic Structure
// Define a structure
typedef struct {
    bit [31:0] addr;
    bit [31:0] data;
    bit        write;
    bit [3:0]  id;
} transaction_t;

// Use the structure
transaction_t txn;

txn.addr  = 32'h1000;
txn.data  = 32'hDEADBEEF;
txn.write = 1;
txn.id    = 4'hA;

$display("Transaction: addr=0x%h, data=0x%h, wr=%b, id=%h",
         txn.addr, txn.data, txn.write, txn.id);

typedef is Your Friend

Always use typedef with structures to create a reusable type name. The _t suffix is a common convention to indicate a type definition.

Packed vs Unpacked Structures

SystemVerilog structures can be packed or unpacked (default). This affects how the data is stored in memory.

Packed Structures

Packed structures store all members in a contiguous block of bits. You can treat the entire structure as a single vector.

Packed Structure
// Packed structure - stored as continuous bits
typedef struct packed {
    bit [7:0]  header;    // Bits [31:24]
    bit [15:0] payload;   // Bits [23:8]
    bit [7:0]  checksum;  // Bits [7:0]
} packet_t;              // Total: 32 bits

packet_t pkt;

// Assign members individually
pkt.header   = 8'hAB;
pkt.payload  = 16'h1234;
pkt.checksum = 8'hCD;

// Or treat as a single 32-bit value!
$display("As vector: 0x%h", pkt);  // 0xAB1234CD

// Assign entire structure at once
pkt = 32'hFF0000AA;

Unpacked Structures (Default)

Unpacked structures don't guarantee contiguous storage. You can't treat them as a single vector, but they're more flexible.

Unpacked Structure
// Unpacked (default) - can have any types
typedef struct {
    string     name;      // Strings can't be packed
    int        value;
    real       percentage;
} config_t;

config_t cfg;
cfg.name = "timeout";
cfg.value = 100;
cfg.percentage = 0.95;

When to Use Packed?

Use packed when you need to treat the structure as a bit vector (for DUT signals, protocol fields). Use unpacked for general data grouping in testbenches.

What is a Union?

A union is similar to a structure, but all members share the same memory. Only one member can hold a value at a time. Unions are useful for viewing the same data in different ways.

Basic Union
// Union - all members share same memory
typedef union packed {
    bit [31:0] word;           // View as single 32-bit word
    bit [3:0][7:0] bytes;      // View as 4 bytes
    bit [1:0][15:0] halfwords; // View as 2 halfwords
} data_view_t;

data_view_t d;

// Write as word
d.word = 32'h12345678;

// Read as bytes
$display("Byte 0: 0x%h", d.bytes[0]);  // 0x78
$display("Byte 1: 0x%h", d.bytes[1]);  // 0x56
$display("Byte 2: 0x%h", d.bytes[2]);  // 0x34
$display("Byte 3: 0x%h", d.bytes[3]);  // 0x12

// Read as halfwords
$display("Halfword 0: 0x%h", d.halfwords[0]);  // 0x5678
$display("Halfword 1: 0x%h", d.halfwords[1]);  // 0x1234

Tagged Unions

Use tagged union when you need type safety. It tracks which member is currently valid and prevents reading the wrong member.

Practical Examples

AXI Transaction Structure

AXI Transaction
typedef struct packed {
    bit [3:0]  id;
    bit [31:0] addr;
    bit [7:0]  len;      // Burst length - 1
    bit [2:0]  size;     // Bytes per beat = 2^size
    bit [1:0]  burst;    // FIXED, INCR, WRAP
} axi_addr_t;

typedef struct {
    axi_addr_t aw;       // Write address channel
    axi_addr_t ar;       // Read address channel
    bit [31:0] wdata[$]; // Write data (queue)
    bit [31:0] rdata[$]; // Read data (queue)
} axi_txn_t;

Register Field Extraction

Register Fields
// Control register fields
typedef struct packed {
    bit        enable;     // Bit 31
    bit [2:0]  mode;       // Bits 30:28
    bit [3:0]  reserved;   // Bits 27:24
    bit [23:0] threshold;  // Bits 23:0
} ctrl_reg_t;

// Read register value
bit [31:0] reg_value = read_register(CTRL_ADDR);

// Cast to structure for field access
ctrl_reg_t ctrl = reg_value;

if (ctrl.enable) begin
    $display("Mode: %0d, Threshold: %0d", ctrl.mode, ctrl.threshold);
end

Struct vs Class

Feature Struct Class
Memory Value type (copied) Reference type (handle)
Methods No Yes
Randomization Limited Full support
Inheritance No Yes
Best For Simple data grouping, protocol fields Complex objects with behavior

Quick Summary

  • struct = group related data together
  • union = multiple views of the same memory
  • packed = stored as contiguous bits, can treat as vector
  • unpacked = default, flexible but not contiguous
  • Use typedef to create reusable type names
  • Use structs for protocol fields, classes for complex objects

What's Next?

Continue learning about SystemVerilog: