In digital IC verification, arrays are everywhere — register models, TLM port lists, expected data queues in scoreboards, coverage bin definitions… pretty much every verification component depends on arrays.
SystemVerilog offers far richer array types and methods than traditional Verilog. Use them well and your productivity skyrockets. Use them poorly and performance tanks.
This guide covers every array type from the ground up.
1. Array Type Overview
SystemVerilog provides four main array categories:
| Type | Declaration | Size | Best For |
|---|---|---|---|
| Fixed-Size | [n] | Compile-time fixed | Fixed-width signals, register banks |
| Dynamic | [] + new[n] | Runtime variable | Unknown-count transactions, config tables |
| Associative | [*] or [type] | Key-indexed | Sparse storage, address maps |
| Queue | [$] | Grows/shrinks on demand | Scoreboards, FIFOs, ordered data streams |
2. Packed vs Unpacked — The First Fork
Before diving into arrays, internalize one core concept: packed vs unpacked.
Packed Arrays
// Width on the left side of name → packed, physically contiguous
bit [7:0] byte_vec; // Single-dimension packed: an 8-bit vector
bit [3:0][7:0] word_vec; // 2-D packed: 4 bytes, 32 bits total
- Essence: A contiguous chunk of bits, always treated as a whole
- Operations: Can participate in arithmetic/logic ops as a unit; bit-slicing supported
- Storage: Contiguous in memory, zero overhead
Unpacked Arrays
// Width on the right side → unpacked, each element is independent
bit byte_mem [0:7]; // 8 unpacked elements, each 1 bit
int data_buf [256]; // 256 unpacked ints
- Essence: A collection of independent elements
- Operations: Must operate element-by-element, no bulk arithmetic
- Storage: Elements may not be contiguous in memory
⚠️ Common Gotcha
bit [7:0] a; // packed: one 8-bit variable
bit b [7:0]; // unpacked: eight 1-bit variables
a = 8'hFF; // ✅ Legal
b = 8'hFF; // ❌ Compilation error! Can't bulk-assign unpacked
bit [7:0] c [0:3]; // Hybrid: 4 unpacked elements, each 8-bit packed
Rule of thumb: Prefer packed when possible — better simulation performance. But beyond 64 bits, synthesis results may degrade.
3. Fixed-Size Arrays
The most basic array type. All dimensions are known at compile time.
Declaration
// 1-D fixed
int arr1 [0:7]; // 8 elements, indices 0~7
int arr2 [8]; // Equivalent to [0:7]
int arr3 [6:1]; // 6 elements, indices 6,5,4,3,2,1
// Multi-dimensional
int matrix [0:3][0:7]; // 4×8 two-dimensional
int cube [0:1][0:3][0:7]; // 2×4×8 three-dimensional
Initialization
int arr [0:3] = '{0, 1, 2, 3}; // List init
int arr2[0:3] = '{default: -1}; // All to -1
int arr3[0:3] = '{0:10, 1:20, default: 0}; // Specific + defaults
int matrix[0:2][0:1] = '{ '{0,1}, '{2,3}, '{4,5} }; // Multi-dim
Iteration
// C-style for (discouraged)
for (int i = 0; i < $size(arr); i++)
$display("arr[%0d] = %0d", i, arr[i]);
// foreach (recommended ✅)
foreach (arr[i])
$display("arr[%0d] = %0d", i, arr[i]);
// Multi-dimensional foreach
foreach (matrix[i, j])
$display("matrix[%0d][%0d] = %0d", i, j, matrix[i][j]);
Why foreach? No manual boundary calculation, works regardless of dimension ordering, cleaner code.
System Functions Quick Reference
| Function | Meaning | Example |
|---|---|---|
$size(arr) | Size of first dimension | $size(arr) → 8 |
$size(arr, 2) | Size of specific dimension | $size(matrix, 2) → 8 |
$dimensions(arr) | Number of dimensions | 2 |
$left(arr) / $right(arr) | Left/right bounds | $left(arr[6:1]) → 6 |
$low(arr) / $high(arr) | Min/max index | $low(arr[6:1]) → 1 |
4. Dynamic Arrays
Size determined at runtime. Allocated via the new[] constructor.
Core Operations
int dyn[]; // Declaration: empty, size 0
dyn = new[10]; // Allocate 10 elements (default 0)
dyn = new[20](dyn); // Expand to 20, preserving old data ✅
dyn = new[5]; // Shrink to 5, old data lost ⚠️
dyn = '{1, 2, 3, 4, 5}; // Direct assignment (auto-reallocates)
dyn.delete(); // Clear, size → 0
$display("size = %0d", dyn.size()); // Get current size
Useful Trick: array ↔ queue
int dyn[];
dyn = new[5];
foreach (dyn[i]) dyn[i] = i * 10;
// Dynamic array → queue
int q[$] = dyn; // Direct assignment ✅
// Queue → dynamic array
dyn = new[q.size()];
dyn = {>>{q}}; // Streaming operator
⚠️ Common Pitfalls
// ❌ Error: accessing without allocation
int dyn[];
dyn[0] = 5; // Runtime fatal error!
// ❌ Error: resize loses data
int dyn[] = '{1,2,3,4,5};
dyn = new[10]; // First 5 elements lost! New elements are all 0
// ✅ Correct: expand and preserve
dyn = new[10](dyn);
Golden rule: Always check
dyn.size()before accessing, or ensurenew[]has been called.
5. Queues
Queues are SystemVerilog’s most flexible data structure — similar to C++ std::deque.
Declaration & Initialization
int q1[$]; // Unbounded queue
int q2[$:255]; // Bounded queue, max 256 elements
string names[$] = '{"alice", "bob", "charlie"};
Insertion & Deletion
int q[$] = '{10, 20, 30};
// Tail operations
q.push_back(40); // {10,20,30,40}
q.push_back(50); // {10,20,30,40,50}
q.pop_back(); // {10,20,30,40}
// Head operations
q.push_front(0); // {0,10,20,30,40}
q.pop_front(); // {10,20,30,40}
// Arbitrary position
q.insert(2, 25); // {10,20,25,30,40} insert at index 2
q.delete(1); // {10,25,30,40} delete index 1
q.delete(); // Clear entire queue
Classic Verification Pattern: Scoreboard
class scoreboard;
transaction exp_q[$]; // Expected queue
function void add_expected(transaction tr);
exp_q.push_back(tr);
endfunction
function void check_actual(transaction tr);
if (exp_q.size() == 0) begin
`uvm_error("SB", "unexpected transaction!")
end
else begin
transaction exp = exp_q.pop_front();
if (!exp.compare(tr))
`uvm_error("SB", "mismatch!")
end
endfunction
endclass
// Sliding window: keep last N samples
int sample_window[$:15]; // Max 16 elements
function void add_sample(int val);
if (sample_window.size() == 16)
sample_window.pop_front();
sample_window.push_back(val);
endfunction
6. Associative Arrays
Indexed by keys rather than sequential integers. Ideal for sparse data.
Declaration
int aa_wild [*]; // Wildcard index (any integral type)
int aa_int [int]; // int key
int aa_str [string]; // string key
int aa_cls [some_class]; // class handle key (rare)
bit [63:0] aa_addr [bit [63:0]]; // Large-width key: address maps
Basic Operations
int aa [string];
aa["foo"] = 10;
aa["bar"] = 20;
aa["baz"] = 30;
$display("aa[bar] = %0d", aa["bar"]); // 20
// Check key existence
if (aa.exists("foo"))
$display("foo exists");
// Deletion
aa.delete("bar"); // Delete single entry
aa.delete(); // Delete all
// Traversal
string key;
if (aa.first(key)) begin
do
$display("aa[%s] = %0d", key, aa[key]);
while (aa.next(key));
end
// Size
$display("size = %0d", aa.num());
Typical Use Case: Register Address Map
class reg_model;
uvm_reg reg_map [bit [31:0]];
function void build();
// 200 registers in a 4GB address space
// Associative array stores only what exists
reg_map[32'h4000_1000] = reg_ctrl;
reg_map[32'h4000_1004] = reg_status;
reg_map[32'h4000_2000] = reg_data;
endfunction
function uvm_reg get_reg(bit [31:0] addr);
if (reg_map.exists(addr))
return reg_map[addr];
else begin
`uvm_error("REG", $sformatf("No register at addr 0x%h", addr))
return null;
end
endfunction
endclass
⚠️ Watch Out
exists()on a missing key does not create an entry- Direct read on a missing key does create an entry with default value
[*]index keys must be integral (not class objects)- Associative arrays don’t support
foreach— usefirst()/next()
7. Array Methods Reference
SystemVerilog provides a powerful set of methods for unpacked arrays (fixed, dynamic, queues).
7.1 Reduction Methods
Apply an accumulation operation across all elements, returning a single value.
int arr[] = '{1, 2, 3, 4, 5};
arr.sum(); // 15 Sum
arr.product(); // 120 Product
arr.and(); // Bitwise AND
arr.or(); // Bitwise OR
arr.xor(); // Bitwise XOR
7.2 Locator Methods — the find Family
int arr[] = '{1, 3, 5, 7, 2, 4, 6, 8};
// find → queue of all matching elements
// find_first → queue of first matching element (single)
// find_last → queue of last matching element
// find_index → queue of matching element indices
// find_first_index→ first matching index
// find_last_index → last matching index
int q[$];
q = arr.find(x) with (x > 4); // {5, 7, 6, 8}
q = arr.find_first(x) with (x > 4); // {5}
q = arr.find_index(x) with (x > 4); // {2, 3, 5, 7}
q = arr.find_first_index(x) with (x > 4);// {2}
7.3 Advanced with Clauses
// 'item' is the default iterator variable — you can rename it
q = arr.find(item) with (item > 5 && item < 8); // {7, 6}
// Multi-condition with objects
class packet;
int addr;
bit is_write;
endclass
packet pkt_q[$];
// Find addresses of all write transactions
int addr_q[$];
addr_q = pkt_q.find(p) with (p.is_write);
foreach (addr_q[i]) addr_q[i] = addr_q[i].addr;
7.4 Sorting Methods
int arr[] = '{5, 2, 8, 1, 3};
arr.reverse(); // {3, 1, 8, 2, 5} In-place reverse
arr.sort(); // {1, 2, 3, 5, 8} Ascending
arr.rsort(); // {8, 5, 3, 2, 1} Descending
arr.shuffle(); // Random shuffle
// Custom sort with 'with' clause
class packet;
int id;
int priority;
endclass
packet pkt_q[$];
pkt_q.sort(p) with (p.priority); // Ascending by priority
pkt_q.rsort(p) with (p.priority); // Descending by priority
7.5 Other Useful Methods
int arr[] = '{1, 2, 3, 4, 5};
arr.min(); // 1 Minimum
arr.max(); // 5 Maximum
arr.unique(); // Deduplicate, returns queue
arr.unique_index(); // Indices of unique elements
int idx = arr.min(item) with (item % 2); // Min even number: 2
8. foreach Loop Advanced Usage
Multi-dimensional Traversal
int arr[3][4]; // 3 rows × 4 columns
// ✅ Recommended: foreach
foreach (arr[i, j])
arr[i][j] = i * 4 + j;
// ❌ Discouraged: nested for
for (int i = 0; i < 3; i++)
for (int j = 0; j < 4; j++)
arr[i][j] = i * 4 + j;
foreach with Queues & Dynamic Arrays
int dyn[] = '{10, 20, 30, 40};
int q[$] = dyn;
// All three are legal
foreach (dyn[i]) $display(dyn[i]);
foreach (q[i]) $display(q[i]);
// ⚠️ Warning: Don't resize during iteration! Behavior is undefined.
// ❌ foreach (q[i]) q.push_back(i); // Don't do this
9. Synthesis vs Simulation — Key Differences
As a verification engineer, most of your code runs in simulation. But if it needs synthesis, know these limits:
| Feature | Simulation | Synthesis |
|---|---|---|
| Fixed-size packed array | ✅ | ✅ |
| Fixed-size unpacked array | ✅ | ✅ (limited) |
| Dynamic array | ✅ | ❌ Not synthesizable |
| Associative array | ✅ | ❌ Not synthesizable |
| Queue | ✅ | ❌ Not synthesizable |
foreach | ✅ | ✅ (SV 2017+) |
find/sort methods | ✅ | ❌ Not synthesizable |
$size / $dimensions | ✅ | ✅ |
Simulation advice: Use dynamic arrays and queues freely — performance is fine, expressiveness is great.
Synthesis advice: Stick to fixed-size packed/unpacked arrays; be cautious with 2+ dimensions.
10. Quiz Time — Common Interview Traps
Trap 1: new[] Without Argument
int arr[] = '{1, 2, 3, 4, 5};
arr = new[10]; // arr becomes {0,0,0,0,0,0,0,0,0,0} — data lost!
arr = new[10](arr); // ✅ Expand and preserve
Trap 2: Reading a Missing Associative Array Key
int aa[string];
$display("aa[key] = %0d", aa["nonexistent"]); // Prints 0, AND creates the entry!
// aa.num() is now 1, not 0!
Trap 3: find Returns a Queue
int arr[] = '{1, 2, 3, 4, 5};
int result = arr.find_first(x) with (x > 3); // ❌ Type mismatch!
int result_q[$] = arr.find_first(x) with (x > 3); // ✅ Use a queue
int result = result_q[0]; // ✅ Then extract
Trap 4: Packed Array Dimension Order
bit [3:0][7:0] a; // 4 bytes, each 8 bits
// a[3] = most significant byte, a[0] = least significant byte
a = 32'hDEAD_BEEF;
$display("a[3] = %h", a[3]); // DE
$display("a[0] = %h", a[0]); // EF
$display("a[3][7] = %b", a[3][7]); // 1 (MSB of DE)
11. Summary
| Need | Pick |
|---|---|
| Fixed-count signals/DUT ports | Fixed-size unpacked |
| Concatenate into a bus | Fixed-size packed |
| Size unknown until runtime | Dynamic array |
| FIFO / Scoreboard ordering | Queue [$] |
| Sparse address mapping | Associative array |
| Stats / search / sort | Array methods (find, sort) |
| Array iteration | foreach (always first choice) |
SystemVerilog’s array system is well-designed and thorough. Master the right type for each scenario and your verification code will be cleaner by an order of magnitude. Don’t simulate C-style double for loops where you don’t need to — SV gives you better tools. Use them.
If you found this helpful, follow me on Bilibili for more digital IC verification content.