SystemVerilog randomization also works on array-like structures inside a class — static arrays, dynamic arrays, and queues. As with any other variable, the array itself has to be declared rand or randc to make it, and its elements, randomizable.
6 min read | Intermediate Level
What You'll Learn
- How randomizing a static array differs from randomizing a scalar
- Why a dynamic array or queue needs a size constraint before its elements can be randomized meaningfully
- How to constrain individual array elements with
foreach - What happens when you leave an array's size unconstrained
Randomizing Static Arrays
Randomizing a static array is straightforward and works just like any other rand variable — every element gets a fresh random value, each independently within its declared range:
class Packet;
rand bit [3:0] s_array [7]; // Declare a static array with "rand"
endclass
module tb;
Packet pkt;
// Create a new packet, randomize it and display contents
initial begin
pkt = new();
pkt.randomize();
$display("queue = %p", pkt.s_array);
end
endmodule
Output
ncsim> run
queue = '{'hf, 'hf, 'h2, 'h9, 'he, 'h4, 'ha}
ncsim: *W,RNQUIE: Simulation is complete.
s_array has a fixed size of 7 elements, each a 4-bit value (0 to 15, shown in hex above), and no extra syntax is needed beyond the rand qualifier — the size is already known at declaration time, so there's nothing else for the solver to figure out besides the element values.
Randomizing Dynamic Arrays
Dynamic arrays don't have a size fixed at declaration — that's what the empty square brackets [] mean below. Because of that, the array's length is itself treated as an implicit random variable, and you'll usually want to constrain it explicitly with .size(). In this example, one constraint keeps the size somewhere between 5 and 10 (exclusive on both ends, so anywhere from 6 to 9), and a second constraint, using foreach, sets each element equal to its own index:
class Packet;
rand bit [3:0] d_array []; // Declare a dynamic array with "rand"
// Constrain size of dynamic array between 5 and 10
constraint c_array { d_array.size() > 5; d_array.size() < 10; }
// Constrain value at each index to be equal to the index itself
constraint c_val { foreach (d_array[i])
d_array[i] == i;
}
// Utility function to display dynamic array contents
function void display();
foreach (d_array[i])
$display ("d_array[%0d] = 0x%0h", i, d_array[i]);
endfunction
endclass
module tb;
Packet pkt;
// Create a new packet, randomize it and display contents
initial begin
pkt = new();
pkt.randomize();
pkt.display();
end
endmodule
If you don't constrain a dynamic array's or queue's size at all, the solver is free to pick any legal size for it — including zero, leaving you with an unexpectedly empty array. Always constrain .size() explicitly if you need a predictable length.
Output
ncsim> run d_array[0] = 0x0 d_array[1] = 0x1 d_array[2] = 0x2 d_array[3] = 0x3 d_array[4] = 0x4 d_array[5] = 0x5 d_array[6] = 0x6 d_array[7] = 0x7 d_array[8] = 0x8 ncsim: *W,RNQUIE: Simulation is complete.
The array size was randomized to 9 (satisfying c_array, since 9 is greater than 5 and less than 10), and the element at each index equals the index itself (satisfying c_val).
Quick Check
Q: If you removed constraint c_array entirely from the class above, what could happen to d_array's size?
Show Answer
It would become unconstrained, so the solver could legally pick any size, including 0 — an empty array. c_val's foreach constraint would still hold for whatever size ends up chosen, since it applies per-index rather than fixing the count.
Randomizing Queues
Queues work the same way as dynamic arrays — declare them rand and constrain their .size() if you need a specific length:
class Packet;
rand bit [3:0] queue [$]; // Declare a queue with "rand"
// Constrain size of queue to exactly 4
constraint c_array { queue.size() == 4; }
endclass
module tb;
Packet pkt;
// Create a new packet, randomize it and display contents
initial begin
pkt = new();
pkt.randomize();
// Tip : Use %p to display arrays
$display("queue = %p", pkt.queue);
end
endmodule
Output
ncsim> run
queue = '{'hf, 'hf, 'h2, 'h9}
ncsim: *W,RNQUIE: Simulation is complete.
Here c_array pins the queue's size to exactly 4 elements, so unlike the dynamic array example, every call to randomize() produces the same length — only the element values change.
Where Is Array Randomization Used?
Randomized arrays and queues are a natural fit for variable-length packet payloads and burst transactions — constrain the size to a legal burst length, then let each element (or a foreach constraint tying elements together) generate the payload contents automatically.
Common Beginner Mistakes
Mistake #1: Leaving a dynamic array's or queue's size unconstrained
Without a .size() constraint, the array's length is randomized right along with its elements. Why? This can silently produce an empty array on some runs, which is easy to miss until something downstream (like a loop over the array) behaves unexpectedly with zero iterations.
Mistake #2: Writing a per-element constraint without foreach
A constraint like d_array[i] == i; only makes sense wrapped in a foreach (d_array[i]) loop inside the constraint block. Why? Without foreach, there's no i to loop over, and the constraint simply won't compile or apply to every index the way you intend.
Mistake #3: Assuming static arrays need special randomization syntax
A static array declared rand randomizes exactly like a scalar variable does — no extra constraint or syntax is required just to make it work. Why? Its size is already fixed at declaration time, so the solver only has element values left to figure out, unlike dynamic arrays and queues where size is also in play.
Practice Exercise
Challenge: Constrain a Queue to Even Values
Declare a rand bit [7:0] queue with exactly 3 elements, and add a foreach constraint so that each element equals its index multiplied by 2 (so the queue always ends up {0, 2, 4}).
Hint
Use the same foreach (queue_name[i]) queue_name[i] == i * 2; pattern shown in the dynamic array example, just applied to a queue with a fixed size constraint instead.
Solution
class Packet;
rand bit [7:0] data_q [$];
constraint c_size { data_q.size() == 3; }
constraint c_val {
foreach (data_q[i])
data_q[i] == i * 2;
}
endclass
module tb;
Packet pkt;
initial begin
pkt = new();
pkt.randomize();
$display("data_q = %p", pkt.data_q);
end
endmodule
c_size fixes the queue at exactly 3 elements, and c_val's foreach constraint ties each element to its index, so every call to randomize() produces the same values — {0, 2, 4} — even though the queue is technically still rand.
Summary
In this tutorial, you learned:
- Static arrays randomize like any other
randvariable — their size is already fixed, so only element values are randomized - Dynamic arrays and queues treat their own size as an implicit random variable unless you constrain it with
.size() - Leaving a dynamic array's or queue's size unconstrained can produce an empty array on some runs
foreachinside a constraint block lets you tie each array element to its index or to other elements- A queue with a size fixed by constraint keeps a consistent length across calls to
randomize(), while its element values still vary
Continuing to Constraints
With arrays and queues covered, the natural next step is SystemVerilog Constraints, which goes deeper into the constraint styles briefly introduced across this chapter — ranges, inside, and weighted dist distributions.