Panel For Example Panel For Example Panel For Example
SystemVerilog Randomization Syntax

SystemVerilog Randomization Syntax

Author : Adrian September 03, 2025

Random stability ensures that each thread or object has a private random number generator (RNG). The sequence of random values returned by one thread is independent of the RNGs of other threads or objects. Random stability applies to the following cases:

  • System randomization method calls: $urandom() and $urandom_range()
  • Object and process seed methods: srandom()
  • Object randomization methods: randomize()

This feature allows a testbench to have more stable RNG behavior when minor changes are made to user code. Additionally, by manually adjusting the seeds of threads and objects, you can achieve more precise control over the generation of random values.

Random stability includes the following characteristics:

 

1. Random Stability

Initialization RNG: Each module, interface, program instance, and package has an initialization RNG. Every initialization RNG has a default, implementation-defined seed. An initialization RNG is used to create static processes and static initializers.

Thread Stability: Each thread has an independent RNG that is used by all randomization system calls within that thread. When a new dynamic thread is created, its RNG is seeded with the next random value generated by the parent thread, a method known as hierarchical seeding. When a static process is created, its RNG is seeded with the next random value from the initialization RNG of its declaration scope (module instance, interface instance, program instance, or package). The random stability of programs and threads can be maintained by preserving the creation order of pre-existing threads and RNGs. When adding new threads to an existing test case, they should be added at the end of a code block to maintain the random stability of previously created processes.

Object Stability: Each class instance (object) has an independent RNG used for all randomization methods within that class. When an object is created, its RNG seed is derived from the thread that created it. If an object is created by a static initializer, its RNG seed will be derived from the next random value of the initialization RNG (from the module instance, interface instance, program instance, or package), as there is no active thread to provide the seed. Object stability can be maintained by preserving the creation order of pre-existing objects and RNGs. To maintain RNG stability, new objects, threads, and random number generations should occur after existing objects have been created.

Manual Seeding: All uninitialized RNGs can be manually seeded. Combined with hierarchical seeding, this mechanism can be used to define a single seed at the root thread of a subsystem, which then deterministically affects the entire subsystem's operation.

 

2. Thread Stability Example

integer x, y, z; fork begin process::self.srandom(100); // set a seed at the start of a thread x = $urandom; end begin y = $urandom; process::self.srandom(200); // set a seed during a thread end begin z = $urandom + $urandom; // draw 2 values from the thread RNG end join

This code snippet demonstrates several features:

Thread Locality: The return values of x, y, and z are independent of the thread execution order. This is a crucial feature that allows for the development of independent, controllable, and predictable subsystems.

Hierarchical Seeding: When threads are created, their random state is initialized using the next random value from the parent thread as the seed. Therefore, all three forked threads receive their seeds from the parent thread.

Each thread has a unique seed value determined solely by its parent thread. The root of thread execution determines the random seeds for its children. This allows for setting the root thread to automatically and deterministically control the behavior of child threads.

 

3. Object Stability Example

class C1; rand integer x; endclass class C2; rand integer y; endclass initial begin C1 c1 = new(); C2 c2 = new(); integer z; void'(c1.randomize()); // z = $random; void'(c2.randomize()); end

  • The values returned for `c1.x` and `c2.y` are independent of each other.
  • The `randomize()` call is independent of system calls like `$random`. If the `z=$random` line were uncommented, the values assigned to `c1.x` and `c2.y` would not change.
  • Each object instance has a unique source of random values that can be seeded independently. This random seed is obtained from the parent thread when the instance is created.

class C3 function new (integer seed); this.srandom(seed); endfunction endclass

Once an object is created, there is no guarantee that the creating thread can change the object's random state before another thread accesses it. Therefore, it is best for objects to self-seed from within their `new()` constructor rather than being seeded externally.

An object's seed can be changed from any thread. However, a thread's seed can only be modified from within that thread itself.

 

4. Manual Seeding Example

class Packet; rand bit[15:0] header; ... function new (int seed); this.srandom(seed); ... endfunction endclass

An example of setting an RNG seed externally is:

Packet p = new(200); // Create p with seed 200. p.srandom(300); // Re-seed p with seed 300.

Calling `srandom()` within an object's `new()` function ensures that the object's RNG uses the new seed before any class member values are randomized. Seeding from within a class method is generally more reliable.

 

5. Uniqueness Constraints

The unique keyword can be used to constrain a set of random variables, ensuring that no two variables in the set have the same value after randomization. This feature can be applied to integer scalar variables, a leaf element of an integer unpacked array, a slice of leaf elements, or all leaf elements. A leaf element is the final non-unpacked array type in an unpacked array variable. For example:

rand byte a[5]; rand byte b; rand byte excluded; constraint u { unique {b, a[2:3], excluded}; } constraint exclusion { excluded == 5; }

In this example, the variables `a[2]`, `a[3]`, `b`, and `excluded` will all have unique values after randomization. Because the `exclusion` block constrains `excluded` to 5, the values of `a[2]`, `a[3]`, and `b` will not be 5.

 

6. Iterative Constraints

Iterative constraints allow arrays to be constrained using loop variables, index expressions, or array reduction methods. The `foreach` iterative constraint syntax is as follows:

foreach ( ps_or_hierarchical_array_identifier [ loop_variables ] ) constraint_set loop_variables ::= [ index_variable_identifier ] { , [ index_variable_identifier ] }

An example is shown below:

class C; rand byte A[] ; constraint C1 { foreach ( A [ i ] ) A[i] inside {2,4,8,16}; } constraint C2 { foreach ( A [ j ] ) A[j] > 2 * j; } endclass

The `C1` block constrains each element of A to be in the set {2,4,8,16}, while the `C2` block constrains each element of A to be greater than twice its index.

The mapping between loop variables and array indices is determined by the cardinality of the dimensions, as shown below:

// 1 2 3 3 4 1 2 -> Dimension numbers int A [2][3][4]; bit [3:0][2:1] B [5:1][4]; foreach( A [ i, j, k ] ) ... foreach( B [ q, r, , s ] ) ...

The first `foreach` loop iterates `i` from 0 to 1, `j` from 0 to 2, and `k` from 0 to 3. The second `foreach` loop iterates `q` from 5 down to 1, `r` from 0 to 3, and `s` from 2 down to 1.

For dynamic arrays and queues, the array size can also be constrained. If size constraints and iterative constraints are applied simultaneously, the size constraint is solved first, followed by the iterative constraint. Users must prevent iteration beyond the array bounds. For instance, `k < A.size - 1` in the example below prevents an array index overflow.

class C; rand int A[] ; constraint c1 { A.size inside {[1:10]}; } constraint c2 { foreach ( A[ k ] ) (k < A.size - 1) -> A[k + 1] > A[k]; } endclass

Array reduction methods can produce a single integer value from the elements of an unpacked array, as in:

class C; rand bit [7:0] A[] ; constraint c1 { A.size == 5; } constraint c2 { A.sum() with (int'(item)) < 1000; } endclass

The `c2` constraint block translates to:

( int'(A[0])+int'(A[1])+int'(A[2])+int'(A[3])+int'(A[4]) ) < 1000