Understanding Arrays in SystemVerilog

Arrays are an essential part of SystemVerilog (SV), allowing designers and verification engineers to work with collections of variables in an efficient and structured way. SystemVerilog extends traditional Verilog arrays by adding features like dynamic arrays, associative arrays, and queues, which are useful in modern VLSI (Very Large Scale Integration) design and verification. In this article, we will dive deep into arrays in SystemVerilog, including multidimensional arrays, packed and unpacked arrays, and their usage in practical scenarios.

What is an Array?

In SystemVerilog, an array is a collection of variables of the same data type, stored in contiguous memory locations. Arrays provide a way to handle large sets of data using a single variable name with indexing. SystemVerilog supports both fixed-size and dynamically-sized arrays, and it allows you to create arrays that can have multiple dimensions.

Example: Simple Array

int array[5];  // Declares a fixed-size array of 5 integers

In this example, array[5] declares an array with 5 elements. Each element can be accessed individually using an index starting from 0. For instance, array[0] refers to the first element, array[1] to the second, and so on.

Types of Arrays in SystemVerilog

SystemVerilog supports several types of arrays, including:

  • Fixed-size arrays: Arrays with a fixed number of elements.
  • Dynamic arrays: Arrays whose size can be changed dynamically at runtime.
  • Associative arrays: Arrays that use an index of any data type (not just integers) to access the elements.
  • Queues: Arrays that grow or shrink dynamically with the ability to add or remove elements.

While these types of arrays are useful in specific scenarios, this article will focus on fixed-size arrays and their multidimensional, packed, and unpacked forms. Rest we will discuss in next chapters.

Multidimensional Arrays

A multidimensional array in SystemVerilog is an array with more than one dimension, which is useful for modeling complex data structures like matrices or grids. Each dimension adds an additional level of indexing.

Example: 2D array (Matrix)

int matrix[3][4];  // 2D array with 3 rows and 4 columns

Here, matrix is a 2D array, which can be visualized as a table with 3 rows and 4 columns. Each element is accessed by specifying both row and column indices, such as matrix[1][2], which refers to the element in the second row and third column.

Example: 3D array

int cube[3][3][3];  // 3D array (a 3x3x3 cube)

In this example, cube represents a 3D array that can be visualized as a cube, with each element identified by three indices. For instance, cube[1][1][1] refers to the element in the center of the cube.

Packed and Unpacked Arrays

SystemVerilog introduces the concept of packed and unpacked arrays, which is not available in traditional Verilog. These types of arrays are crucial for hardware modeling because they determine how data is stored and accessed at the bit-level.

Packed Arrays

Packed arrays are stored as contiguous bits, meaning that the entire array is treated as a single vector of bits. Packed arrays are primarily used for bit-level operations and interfacing with hardware.

Packed arrays are declared to the left of the variable name, and their size affects the number of bits allocated to the variable. They are useful when you need to model hardware data types, such as a bus or a register, where each element of the array directly represents a specific bit or group of bits in hardware.

Example: Packed Array

bit [7:0] byte_array;    // A packed array of 8 bits (1 byte)
bit [3:0][7:0] byte_matrix;  // A 4x8 matrix (4 bytes, each 8 bits wide)

In the above examples:

  • byte_array is an 8-bit packed array, which can be seen as a byte.
  • byte_matrix is a 4×8 packed array (4 rows of 8-bit values), commonly used for representing hardware structures like memory or buses.

Since packed arrays are stored as contiguous bits, they are essential for precise hardware control.

Unpacked Arrays

Unpacked arrays are more flexible and are stored as separate, independent elements in memory. Unpacked arrays allow you to model higher-level structures that don’t necessarily need to map directly to hardware but need to represent collections of data in software simulation.

Unpacked arrays are declared to the right of the variable name, and their size does not affect the bit-level layout of the individual elements.

Example: Combining packed and unpacked arrays

bit [7:0] memory [4];    // A 4-element unpacked array, where each element is a packed 8-bit value

In this example:

  • The packed part ([7:0]) defines the bit-width of each element (8 bits).
  • The unpacked part ([4]) defines an array of 4 elements.

Usage of Packed and Unpacked Arrays in Hardware Design

Packed and unpacked arrays are essential for representing hardware constructs like register files, buses, and memory structures. Packed arrays are particularly useful when you need to perform operations at the bit-level, such as shifting, masking, or extracting bits, which are common in digital design.

Unpacked arrays, on the other hand, are suitable for higher-level data organization and simulation. They allow you to represent more abstract data structures such as arrays of transactions, buffers, or collections of packets in a verification environment.

Example: Register File representation

logic [31:0] registers [7];  // A register file with 8 registers, each 32 bits wide

Multidimensional Packed and Unpacked Arrays

SystemVerilog allows mixing packed and unpacked arrays, which is useful for modeling complex hardware architectures.

Example: Mixed pack and unpacked array

bit [15:0][3:0] register_bank [10];  // 10 unpacked arrays, each containing 4 packed arrays of 16 bits

Here, register_bank is a multidimensional array where:

  • [15:0] is a 16-bit packed array.
  • [3:0] represents 4 such packed arrays (4 registers of 16 bits each).
  • [10] is an unpacked array that contains 10 of these 4×16 register structures.

This kind of array declaration is useful in hardware designs that need to store complex data structures like register banks or memory blocks.