SystemVerilog Arrays

Arrays are essential in verification for storing collections of data like transactions, addresses, or test vectors. SystemVerilog offers several array types, each suited for different use cases. Let's understand when to use which type.

Types of Arrays in SystemVerilog

SystemVerilog provides four main types of arrays:

  • Fixed-Size Arrays - Size is fixed at compile time
  • Dynamic Arrays - Size can change at runtime
  • Associative Arrays - Indexed by any data type (like dictionaries)
  • Queues - Variable-size arrays with easy push/pop operations
Think of it like storage: Fixed arrays are like a shelf with fixed compartments. Dynamic arrays are like expandable storage boxes. Associative arrays are like labeled drawers where you can find items by name. Queues are like a line at a ticket counter where people join and leave.

Fixed-Size Arrays

Fixed-size arrays (also called static arrays) have a size that's determined at compile time and cannot change. They're the simplest and most efficient type.

Fixed-Size Array Examples
// Single-dimensional array
bit [7:0] mem [0:255];        // 256 elements, indexed 0 to 255
int scores [10];               // 10 integers, indexed 0 to 9
// Multi-dimensional array
bit [7:0] matrix [4][4];       // 4x4 matrix
int cube [2][3][4];            // 3D array
// Accessing elements
mem[0] = 8'hFF;
matrix[1][2] = 8'h42;
// Looping through array
foreach(scores[i]) begin
    scores[i] = i * 10;
end

When to Use Fixed Arrays?

Use fixed arrays when you know the exact size at compile time, like register banks, lookup tables, or memory models with known sizes.

Dynamic Arrays

Dynamic arrays can be resized at runtime. They're declared with empty brackets [] and must be sized using new[] before use.

Dynamic Array Examples
// Declaration
int dyn_array [];              // Empty dynamic array
// Sizing
dyn_array = new[10];           // Create 10 elements
// Resizing (preserving old values)
dyn_array = new[20] (dyn_array);  // Grow to 20, keep first 10
// Get size
int size = dyn_array.size();   // Returns 20
// Delete array
dyn_array.delete();            // Size becomes 0

Pro Tip

Dynamic arrays are great for storing variable-length data like burst transactions or test vectors read from a file.

Associative Arrays

Associative arrays are like hash tables or dictionaries. You can use any data type as the index. Memory is only allocated for elements that exist.

Associative Array Examples
// Indexed by integer
int age [string];              // String keys, int values
age["Amit"] = 25;
age["Priya"] = 28;
// Indexed by address
bit [31:0] memory [bit [31:0]];   // Sparse memory model
memory[32'h1000] = 32'hDEAD;
memory[32'h2000] = 32'hBEEF;
// Check if key exists
if (age.exists("Amit")) begin
    $display("Amit's age: %0d", age["Amit"]);
end
// Iterate through all entries
foreach(age[name]) begin
    $display("%s is %0d years old", name, age[name]);
end
// Get number of entries
int num_entries = age.num();

Perfect for Sparse Data!

Associative arrays are ideal for sparse memory models where you only access a few locations out of a huge address space. No need to allocate memory for the entire 4GB address space!

Array Methods

SystemVerilog provides powerful built-in methods for arrays:

Reduction Methods

Reduction Methods
int arr[] = '{1, 2, 3, 4, 5};
int sum = arr.sum();           // 15
int product = arr.product();   // 120
int bitwise_and = arr.and();   // Bitwise AND of all
int bitwise_or = arr.or();     // Bitwise OR of all
int bitwise_xor = arr.xor();   // Bitwise XOR of all

Locator Methods

Finding Elements
int arr[] = '{5, 10, 15, 20, 25};
// Find elements matching condition
int gt15[] = arr.find with (item > 15);        // {20, 25}
int indices[] = arr.find_index with (item > 15); // {3, 4}
// Find first/last
int first = arr.find_first with (item > 10);   // 15
int last = arr.find_last with (item < 20);     // 15
// Min and Max
int minimum = arr.min();       // 5
int maximum = arr.max();       // 25

Sorting and Ordering

Sorting Arrays
int arr[] = '{30, 10, 50, 20, 40};
arr.sort();                    // {10, 20, 30, 40, 50}
arr.rsort();                   // {50, 40, 30, 20, 10}
arr.shuffle();                 // Random order
arr.reverse();                 // Reverse current order

Multidimensional Arrays: Packed vs Unpacked

One of the trickiest parts of SystemVerilog arrays is understanding the difference between Packed and Unpacked dimensions.

  • Packed Dimensions: Declared before the variable name (e.g., bit [3:0][7:0] p_array). These are stored as a single contiguous set of bits in memory. They are extremely efficient for bit-slicing and arithmetic.
  • Unpacked Dimensions: Declared after the variable name (e.g., byte u_array [4]). These can store any data type (including objects or other arrays) and may not be contiguous in memory.
Indexing Rules
// 2D Packed Array (4 elements of 8 bits each)
bit [3:0][7:0] data; 
data[0] = 8'hAA; // Accesses the first byte
// 2D Unpacked Array
int table [2][3];
table[1][2] = 50; // Row 1, Column 2

Memory Layout: Think of Packed arrays as a single register and Unpacked arrays as a bank of separate registers.

Advanced Locator Methods for Verification

In verification, we often use array methods to find specific transactions in a scoreboard or a queue.

Complex Search Examples
typedef struct {
    int id;
    bit [7:0] data;
} trans_s;
trans_s list [$] = '{ '{1, 8'hA}, '{2, 8'hB}, '{3, 8'hA} };
// 1. Find all items where data is 0xA
trans_s res[$] = list.find() with (item.data == 8'hA); 
// 2. Find the index of the transaction with id=2
int idx[$] = list.find_index(x) with (x.id == 2);
// 3. Keep only unique data values
int unique_data[$] = list.unique(x) with (x.data);

Memory Performance: When to use which?

Choosing the right array type can significantly impact your simulation speed and memory footprint.

  • Fixed/Dynamic Arrays: Lowest memory overhead. Best for high-speed access to a known number of elements.
  • Queues: Perfect for FIFOs and scoreboards. Fast insertion/deletion at the ends, but slower for middle-insertions.
  • Associative Arrays: High memory overhead per element due to hash-map overhead. Only use these for truly sparse data (e.g., a 4GB memory model where only 100 addresses are ever used).

Efficiency Hack: If you find yourself frequently using dynamic_array = new[new_size](old_array), switch to a Queue. Resizing dynamic arrays is an O(N) operation that copies the entire array every time.

Quick Comparison Table

Size Compile-time Runtime Runtime
Index Type Integer only Integer only Any type
Memory Pre-allocated Allocated on new[] On-demand
Best For Known size data Variable length data Sparse data, lookups

What's Next?

Now that you understand arrays, continue learning: