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}
| Method | Does |
|---|---|
.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.