Skip to main content

Arrays

Verilog's Arrays and Memories page introduced Verilog's one array form — reg [7:0] mem [0:255] — a fixed-size collection declared at compile time, with no built-in operations beyond indexing. SystemVerilog keeps that form (it's now called a packed array of an unpacked array, more precisely) and adds real array-manipulation methods on top, plus, in the next page, array types that can actually resize at runtime.

Packed vs. unpacked, precisely​

SystemVerilog gives the two bracket forms in a declaration like logic [7:0] mem [0:255] formal names:

  • Packed dimensions ([7:0], immediately after the type) describe bits that are guaranteed contiguous — the element itself can be treated as one vector, sliced, and used in arithmetic.
  • Unpacked dimensions ([0:255], after the variable name) describe a collection of separate elements — indexable, but the collection as a whole isn't one contiguous bit vector.
logic [7:0] byte_reg; // packed only — an 8-bit vector
logic flags [0:15]; // unpacked only — 16 separate 1-bit elements
logic [7:0] mem [0:255]; // packed element (8 bits) × unpacked collection (256 of them)
logic [3:0][7:0] nibbles_in_word; // packed × packed — one 32-bit vector, viewable as four 8-bit lanes

This is the exact ROM/RAM declaration from Arrays and Memories — reg [7:0] mem [0:255] there is logic [7:0] mem [0:255] here, unchanged in meaning, just spelled with logic per the previous page.

Multi-dimensional unpacked arrays​

Multiple unpacked dimensions stack directly:

logic [7:0] frame_buffer [0:479][0:639]; // 480 rows × 640 columns, each pixel 8 bits

Built-in array methods​

Plain Verilog arrays support indexing and nothing else — finding a value, summing a set of elements, or sorting requires a hand-written loop every time. SystemVerilog adds a family of built-in methods that work on any array (packed or unpacked, fixed or dynamic):

int data [0:7] = '{3, 1, 4, 1, 5, 9, 2, 6};

int total = data.sum(); // 31
data.sort(); // data is now {1, 1, 2, 3, 4, 5, 6, 9} — in place
int matches[$] = data.find(x) with (x > 4); // queue of elements > 4: {5, 9, 6}
MethodDoes
.sum(), .product(), .and(), .or(), .xor()Reduce the whole array to one value with the named operator
.sort(), .rsort()Sort in place, ascending/descending
.reverse()Reverse element order in place
.find(), .find_first(), .find_index()Locate elements matching a with expression
.min(), .max(), .unique()Extremes, or a de-duplicated copy

These methods return a queue (covered next page) when the result is itself a collection (.find()), or a single scalar when reducing to one value (.sum()). The main payoff over hand-rolled loops is exactly what the table shows: data.sort() replaces a whole bubble/insertion-sort loop, data.sum() replaces an accumulator loop — testbench code that only cares about what to compute, not how to loop over the array, reads far closer to its actual intent.

One more built-in method worth knowing: .shuffle() randomly reorders an array's elements in place — commonly reached for in testbench code that needs a randomized ordering of otherwise fixed stimulus (e.g. shuffling a queue of pre-built transactions before driving them). And unlike the reduction methods, .find() and its variants require a with clause — calling data.find() with no filter expression at all is a compile-time error, since the method has nothing to test each element against.

What's next​

Every array on this page has a fixed size, decided at compile time. Real testbenches often don't know in advance how many transactions a test will generate — the next page covers SystemVerilog's three answers to that: dynamic arrays, queues, and associative arrays.