SystemVerilog arrays store many values in a single variable. The foreach loop is built just for walking through them, and it is the simplest way to do so. You don't declare an index variable or work out the array size. foreach declares the loop variable for you and steps it through every valid index, so you can never index past the end of the array.
7 min read | Intermediate Level
What You'll Learn
- The syntax of
foreachfor one-dimensional and multidimensional arrays - The order in which
foreachvisits indices, which is not always from 0 - How
foreachworks with dynamic arrays, queues and associative arrays in testbenches - When
foreachcan be used in synthesizable RTL
Syntax
When the loop body has more than one statement, wrap them in begin and end, as with any other procedural block.
foreach(<variable>[<iterator>])
// Single statement
foreach(<variable>[<iterator>]) begin
// Multiple statements
end
- variable: the array to walk through. It can be a fixed-size, dynamic, associative or queue array.
- iterator: the loop variable name, which can be any identifier. For a multidimensional array, list one name per dimension, separated by commas:
[i, j].
The loop variable is declared automatically. It exists only inside the loop and is read-only, so you cannot assign to it.
Example #1: Single-Dimensional Arrays
module tb;
int array[5] = '{1, 2, 3, 4, 5};
int sum;
initial begin
// Here, "i" is the iterator and can be named as anything you like
// Iterate through each element from index 0 to end using a foreach
// loop.
foreach (array[i])
$display ("array[%0d] = %0d", i, array[i]);
// Multiple statements in foreach loop requires begin end
// Here, we are calculating the sum of all numbers in the array
// And because there are 2 statements within foreach there should
// be a begin-end
foreach (array[l_index]) begin
sum += array[l_index];
$display ("array[%0d] = %0d, sum = %0d", l_index, array[l_index], sum);
end
end
endmodule
ncsim> run array[0] = 1 array[1] = 2 array[2] = 3 array[3] = 4 array[4] = 5 array[0] = 1, sum = 1 array[1] = 2, sum = 3 array[2] = 3, sum = 6 array[3] = 4, sum = 10 array[4] = 5, sum = 15 ncsim: *W,RNQUIE: Simulation is complete.
For an array declared as int array[5], which is shorthand for [0:4], the foreach loop does the same job as this for loop:
for (int i = 0; i < $size(array); i++) begin
// Statements inside the for loop
end
The two are only equivalent for arrays whose indices start at 0 and count up. The next section shows why.
Iteration Order
foreach does not always start at 0. It goes from the left bound of the declared range to the right bound:
| Array type | Order of indices |
|---|---|
Fixed-size, written [N] or [0:N-1] | 0 up to N-1 |
Fixed-size, written [N-1:0] (including packed vectors) | N-1 down to 0 |
| Dynamic array, queue | 0 up to size-1 |
| Associative array | Ascending order of the index (key) |
module tb;
bit [3:0] nibble = 4'b0110; // packed vector, declared [3:0]
int arr [3:0]; // unpacked array, declared high-to-low
initial begin
foreach (nibble[i]) // i = 3, 2, 1, 0
$display ("nibble[%0d] = %0b", i, nibble[i]);
foreach (arr[i]) // i = 3, 2, 1, 0
$display ("arr index %0d", i);
end
endmodule
Both loops count down from 3 to 0, because both arrays are declared with the larger number on the left.
Quick Check
Q: In what order does foreach (mem[a]) visit indices if mem is declared as logic [7:0] mem [1:4]?
Show Answer
1, 2, 3, 4. The unpacked range is [1:4], so iteration runs from the left bound 1 to the right bound 4. The packed width [7:0] of each element is not part of the loop.
Example #2: Multidimensional Arrays
The example below walks a 5x2 array by nesting one foreach inside another.
module tb;
int md_array [5][2] = '{'{1,2}, '{3,4}, '{5,6}, '{7,8}, '{9,10}};
initial begin
// First iterate through the first dimension using "i"
foreach (md_array[i])
// For each element in first dimension "i", iterate through the
// second dimension using "j"
foreach (md_array[i][j])
$display("md_array[%0d][%0d] = %0d", i, j, md_array[i][j]);
end
endmodule
ncsim> run md_array[0][0] = 1 md_array[0][1] = 2 md_array[1][0] = 3 md_array[1][1] = 4 md_array[2][0] = 5 md_array[2][1] = 6 md_array[3][0] = 7 md_array[3][1] = 8 md_array[4][0] = 9 md_array[4][1] = 10 ncsim: *W,RNQUIE: Simulation is complete.
The IEEE 1800 standard defines a shorter form: one foreach with a comma-separated list of loop variables. The first variable walks the first dimension and the second walks the second, which gives the same nested order and the same output as above. This is the most portable form across simulators.
module tb;
int md_array [5][2] = '{'{1,2}, '{3,4}, '{5,6}, '{7,8}, '{9,10}};
initial begin
// i walks the first dimension (outer), j walks the second (inner)
foreach (md_array[i, j])
$display ("md_array[%0d][%0d] = %0d", i, j, md_array[i][j]);
end
endmodule
To skip a dimension, leave its slot empty. foreach (md_array[, j]) loops over the second dimension only.
Using foreach in Verification
foreach is most useful with arrays whose size changes at run time. With dynamic arrays and queues it adapts to the current size automatically. With associative arrays it visits only the keys that actually exist.
module tb;
int pkt_count [string]; // associative array indexed by a string
initial begin
pkt_count["write"] = 7;
pkt_count["read"] = 12;
pkt_count["idle"] = 30;
// Visits existing keys in ascending order: "idle", "read", "write"
foreach (pkt_count[key])
$display ("%s : %0d", key, pkt_count[key]);
end
endmodule
For an associative array the loop variable takes the index type, a string here. For fixed-size arrays, dynamic arrays and queues it is an int.
Where is foreach used in verification?
Scoreboards keep expected transactions in queues or associative arrays keyed by address or ID, and use foreach at the end of a test to report anything never matched. Constraint blocks also use foreach to constrain every element of a randomized array, for example to keep every byte of a payload within a legal range.
Using foreach in Digital Design
foreach over a fixed-size array is supported by most current synthesis tools. Its iteration count is known at elaboration time, so it unrolls just like a for loop with constant bounds.
Because the bounds come from the array declaration, a foreach loop resizes itself when a parameter changes the array width. The bit-reversal block below compiles to pure wiring, with no logic gates.
module bit_reverse (
input logic [7:0] din,
output logic [7:0] dout
);
always_comb begin
foreach (din[i]) // i = 7 down to 0, from the [7:0] declaration
dout[i] = din[7 - i]; // swap bit order: dout[7] = din[0], ...
end
endmodule
Dynamic arrays, queues and associative arrays are not synthesizable, so foreach over them is testbench-only. In RTL, use it only on fixed-size packed or unpacked arrays.
Common Beginner Mistakes
Mistake #1: Assuming the index always starts at 0
For logic [7:0] data, foreach (data[i]) visits 7 first and 0 last. Why? foreach follows the declared range from left to right. Order matters whenever the body depends on earlier iterations, such as a running sum written to an output, or "first match wins" logic.
Mistake #2: Assigning to the loop variable
Wrong: foreach (arr[i]) begin if (skip) i++; ... end
Right: foreach (arr[i]) begin if (skip) continue; ... end
Why? foreach loop variables are read-only, so the first version will not compile. Use continue to skip an element.
Mistake #3: Changing the array size inside the loop
Calling q.delete(i) or q.push_back() on the queue being walked gives undefined results. Why? The standard does not define what happens when a dynamically sized array changes size during a foreach. Collect the indices to delete in the loop and remove them afterwards, or use a while loop that checks q.size() on every pass.
Practice Exercise
Challenge: Find the largest value
Given a queue int data[$] = {12, 45, 7, 33};, use foreach to find the largest value and print it.
Hint
Start with max_val set to the first element, then compare every element against it inside the loop.
Solution
module tb;
int data[$] = {12, 45, 7, 33};
int max_val;
initial begin
max_val = data[0]; // start with the first element
foreach (data[i])
if (data[i] > max_val)
max_val = data[i]; // keep the larger value
$display ("max = %0d", max_val); // prints: max = 45
end
endmodule
foreach follows the queue's current size, so the same code works whether the queue holds 4 elements or 4,000.
Summary
In this tutorial, you learned:
foreachwalks every index of an array without a manual index variable or size check- The loop variable is declared automatically, is local to the loop, and is read-only
- Iteration runs from the left bound to the right bound, so
[7:0]arrays are visited from 7 down to 0 - Multidimensional arrays use a comma-separated list such as
[i, j], and a slot can be left empty to skip a dimension - Associative arrays are visited in ascending key order, only for keys that exist
foreachover fixed-size arrays is synthesizable in most tools; over dynamic arrays it is testbench-only