Randomization
Why Randomization?
• Automates generation of test stimuli
• Increases coverage & finds corner cases
• Reduces manual test writing effort
• Works with constraints for valid input generation
Types of Randomization
• $urandom / $urandom_range – simple
procedural random numbers
• rand – class property randomization
• randc – cyclic random values (no repeats until
cycle completes)
• Inline randomize() with with – one-time
constraints
Basic Example
class Packet;
rand bit [7:0] data;
rand bit [3:0] addr;
constraint addr_range { addr inside {[0:9]}; }
endclass
Packet p = new();
[Link]();
$display("addr=%0d data=%0h", [Link], [Link]);
Constraints
• Define valid ranges and relationships
• inside {} for sets/ranges
• Conditional constraints (a -> b)
• solve…before to control solve order
randc Example
• class Counter;
randc bit [3:0] value;
endclass
• Cycles through all values before repeating
Inline randomize()
• bit [7:0] a,b;
randomize(a,b) with { a>b; a inside
{[10:100]}; };
• Adds one-off constraints for variables
Hooks
• pre_randomize() – executed before
randomization
• post_randomize() – executed after
randomization
Best Practices
• Always check if (!randomize()) for failures
• Use seeds for reproducibility
• Use constraints to generate only valid data
Summary
• Randomization is essential for functional
verification
• Combine constraints + coverage for powerful
tests
• Practice with simple examples to master it
1) Simple class-based randomization with constraints
class Packet;
rand bit [7:0] data;
rand bit [3:0] addr;
rand bit write; // 1 = write, 0 = read
// constrained address (0..9)
constraint addr_range { addr inside {[0:9]}; }
// conditional constraint: if write==1 then data must be non-zero
constraint data_when_write {
(write == 1) -> (data != 8'h00);
}
function void post_randomize();
// called by the simulator after successful randomize()
$display("post_randomize: addr=%0d write=%0b data=0x%0h", addr, write, data);
endfunction
endclass
module tb;
Packet p;
initial begin
p = new();
repeat (10) begin
if (![Link]()) begin
$display("randomize failed!");
end
else begin
$display("Packet: addr=%0d
write=%0b data=0x%0h", [Link], [Link],
[Link]);
end
end
$finish;
end
endmodule
2) randc — cyclic random (no repeat until cycle completes)
class SeqGen;
randc bit [3:0] value; // randc cycles through all values before repeating
function void print_one();
if ([Link]()) $display("value = %0d", value);
endfunction
endclass
module tb_randc;
SeqGen s;
initial begin
s = new();
// call more than width to show cycling behavior
repeat (20) begin
s.print_one();
end
$finish;
end
endmodule
3) Inline randomize() with with constraints (one-off constraint)
module tb_with;
bit [7:0] a;
bit [7:0] b;
initial begin
// randomize a and b normally
a = $urandom;
b = $urandom;
// one-off: pick a random a,b such that a > b and a in [10,100]
if (randomize(a, b) with { (a > b); (a inside {[10:100]}); })
$display("randomize with: a=%0d b=%0d", a, b);
else
$display("randomize with failed");
$finish;
end
endmodule
4) Handling impossible constraints (failure detection)
class C;
rand int unsigned x;
constraint impossible { x > 1000; x < 10; } // impossible
function void tryit();
if (![Link]()) begin
$display("Randomize failed as expected (impossible constraints)");
end
endfunction
endclass
module tb_fail;
C c = new();
initial begin
[Link]();
$finish;
end
endmodule
5) Using $urandom and seeding
module tb_seed;
initial begin
// Seed the simulator PRNG for reproducibility
// Provide the same seed to reproduce results across runs
int unsigned seed = 32'hDEADBEEF;
$urandom(seed); // many simulators use this to set the seed
// get pseudo-random numbers
$display("r1 = %0d", $urandom());
$display("r2 = %0d", $urandom_range(0, 9)); // 0..9 inclusive
$finish;
end
endmodule
6) Coverage-friendly constrained sequence generator (useful in UVM-style tests)
class Trans;
rand bit [7:0] data;
rand bit [1:0] type; // 0..3
constraint c_type { type inside {0,1,2}; } // avoid type 3
function void do_rand();
if (randomize()) begin
$display("type=%0d data=0x%0h", type, data);
end
else $display("randomize failed");
endfunction
endclass
module tb_cov;
Trans t = new();
initial begin
repeat (50) t.do_rand();
$finish;
end
endmodule
Introduction
Introduction
• A CRT is made of two parts:
• The test code that uses a stream of random values to
create input to the DUT.
• A seed to the pseudo-random number generator
(PRNG), we can make a CRT behave differently just
by using a new seed.
• This feature allows you to leverage each test so each
is the functional equivalent of many directed tests, just
by changing seeds. You are able to create more
equivalent tests using these techniques than with
directed testing
What to Randomize
When you think of randomizing the stimulus to a
design, the first thing you may think of are the data
fields. These are the easiest to create – just call
$random.
Device configuration
• To test this device, the verification engineer had to write
several dozen lines of Tcl code to configure each channel.
• As a result, never able to try configurations with more
than a handful of channels enabled.
• Using a CRT methodology, wrote a testbench that
randomized the parameters for a single channel, and then
put this in a loop to configure the whole device.
• Now had confidence that tests would uncover bugs that
previously would have been missed.
Environment configuration
• The device that you are designing operates in an
environment containing other devices.
• When you are verifying the DUT, it is connected to a
testbench that mimics this environment.
• You should randomize the entire environment, including
the number of objects and how they are configured.
• Another company was creating an I/O switch chip that
connected multiple PCI buses to an internal memory bus.
• Even though there were many possible combinations, this
company knew all had been covered.
Primary input data
• This is what we probably thought of first when you read
about random stimulus:
• Take a transaction such as a bus write or ATM cell and fill
it with some random values.
• How hard can that be? Actually it is fairly straightforward
as long as you carefully prepare your transaction classes.
• we should anticipate any layered protocols and error
injection.
Encapsulated input data
• Many devices process multiple layers of stimulus.
• For example, a device may create TCP traffic that is then
encoded in the IP protocol, and finally sent out inside
Ethernet packets.
• Each level has its own control fields that can be
randomized to try new combinations.
• So we are randomizing the data and the layers that
surround it.
• we need to write constraints that create valid control fields
but that also allow injecting errors.
Protocol exceptions, errors, and violations
• Anything that can go wrong, will, eventually. The most
challenging part of design and verification is how to handle
errors in the system.
• we need to anticipate all the cases where things can go wrong,
inject them into the system, and make sure the design handles
them gracefully.
• A good verification engineer tests the behavior of the design to
the edge of the functional specification and sometimes even
beyond.
• The random component of these errors is that your testbench
should be able to send functionally correct stimuli.
• with the flip of a configuration bit, start injecting random types
of errors at random intervals.
Delays
• Many communication protocols specify ranges of delays.
The bus grant comes one to three cycles after request.
• Data from the memory is valid in the fourth to tenth bus
cycle.
• However, many directed tests, optimized for the fastest
simulation, use the shortest latency, except for that one test
that only tries various delays.
• The clock generator should be in a module outside the
testbench so that it creates events in the Active region
along with other design events
Randomization in SystemVerilog
• The random stimulus generation in SystemVerilog is most
useful when used with OOP.
• first create a class to hold a group of related random
variables, and then have the random-solver fill them with
random values.
• Create constraints to limit the random values to legal
values, or to test specific features.
Randomization in SystemVerilog
Simple class with Random Variables
Checking the result from randomization
Checking the result from randomization
contd..
The constraint solver
• The process of solving constraint expressions is handled by
the SystemVerilog constraint solver.
• The solver chooses values that satisfy the constraints.
• The values come from SystemVerilog’s PRNG, that is
started with an initial seed.
• If you give a SystemVerilog simulator the same seed and
the same testbench, it always produces the same results.
The constraint solver
Simple Expressions
Equivalence Expressions
Constrain variables to be in a fixed order
Weighted Distributions
Weighted Distributions := operator
Weighted Distributions :/ operator
Weighted Distributions (cont)
Set membership and the inside operator
Set membership and the inside operator
Using an array in a set
Bidirectional Constraints
Bidirectional Constraints
rand logic [15:0] b, c, d;
constraint c_bidir
{ b < d;
c == b;
d < 30;
c > 25; }
Conditional constraints
• SystemVerilog supports two implication operators, -> and
if-else.
• When you are choosing from a list of expressions, such as
an enumerated type, the implication operator, ->, lets you
create a case-like block.
• If you have a true-false expression, the if-else operator
may be better
Constraint block with implication operator
class BusOp;
... constraint c_io {
(io_space_mode) ->
addr[31] == 1’b1; }
Constraint block with if-else operator
class BusOp;
... constraint c_len_rw {
if (op == READ)
len inside {[BYTE:LWRD]};
else
len == LWRD; }
Constraint block with implication operator
Equivalence operator
Solution Probabilities
Whenever we deal with random values, you need to understand
the probability of the outcome. SystemVerilog does not
guarantee the exact solution found by the random constraint
solver, but you can influence the distribution.
Implication
Here are the possible solutions and probability. we can see that the random
solver recognizes that there are eight combinations of x and y, but all the
solutions where x==0 (A–D) have been merged together.
Implication and bidirectional constraints
• Note that the implication operator says that when x==0, y
is forced to 0, but when y==0, there is no constraint on x.
• However, implication is bidirectional in that if y were
forced to a nonzero value, x would have to be 1.
• Example has the constraint y>0, so x can never be 0.
class Imp2;
rand bit x; // 0 or 1
rand bit [1:0] y; // 0, 1, 2, or 3
constraint c_xy {
y > 0;
(x==0) -> y==0; }
endclass
Solution for imp2 Example
Guiding Distribution with solve/before
Controlling Multiple Constraint Blocks
• A class can contain multiple constraint blocks. Your class
might naturally divide into two sets of variables, such as
data vs. control.
• so we may want to constrain them separately. or you
might want to have a separate constraint for each test.
• Perhaps one constraint would restrict the data length to
create small transactions (great for testing congestion),
while another would make long transactions.
Controlling Multiple Constraint Blocks
Valid Constraints
• A good randomization technique is to create several
constraints to ensure the correctness of your random
stimulus, known as “valid constraints.”
• Example, a bus read-modify-write command might only be
allowed for a longword data length.
Valid Constraints
In-line Constraints
• As we write more tests, you can end up with many
constraints.
• They can interact with each other in unexpected ways, and
the extra code to enable and disable them adds to the test
complexity.
• Additionally, constantly adding and editing constraints to
a class could cause problems in a team environment.
• Many tests only randomize objects at one place in the
code. SystemVerilog allows you to add an extra constraint
using randomize() with.
In-line Constraints
The pre_randomize and post_randomize Functions
Building a bathtub distribution
Cont..
• $dist_exponential — Exponential decay, as shown in
Figure
• $dist_normal — Bell-shaped distribution
• $dist_poisson — Bell-shaped distribution
• $dist_uniform — Flat distribution
• $random — Flat distribution, returning signed 32-bit
random
• $urandom — Flat distribution, returning unsigned 32-bit
random
• $urandom_range — Flat distribution over a range
Constraints Tips and Techniques
Using Nonrandom Values
Checking values using constraints
Randomizing Individual Variables
Turn constraints off and on
Use the implication operators (-> or if-else) to build a
single, elaborate constraint controlled by nonrandom
variables.
Common Randomization Problems
• We may be comfortable with procedural code, but
writing constraints and understanding random
distributions requires a new way of thinking.
• Here are some issues you may encounter when
trying to create random stimulus.
Use care with signed variables
Randomizing unsigned 32-bit variables
Randomizing unsigned 8-bit variables
Use Signed Values with care
Solver performance Tips
Randsequence
• The next way to generate a sequence of transactions is by
using the randsequence construct in SystemVerilog. With
randsequence you describe the grammar of the transaction,
using a syntax similar to BNF (Backus-Naur Form).
• SystemVerilog introduces the randsequence construct.
• Uses a BNF like syntax to describe the grammar of the
transaction
Command generator using rand sequence
Example generates a sequence called stream. A stream can be either
cfg_read, io_read, or mem_read. The random sequence engine randomly
picks one. The cfg_read label has a weight of 1, while io_read is twice as
likely to be chosen and mem_read is most likely to be chosen, with a
weight of 5.
Command generator using randsequence
• A cfg_read can be either a single call to the cfg_read_task,
or a call to the task followed by another cfg_read.
• As a result, the task is always called at least once, and
possibly many times.
• One big advantage of randsequence is that it is procedural
code and you can debug it by stepping though the
execution, or adding $display statements.
• When you call randomize for an object, it either all works
or all fails, but you can’t see the steps taken to get to a
result.
Combining sequences
• we can combine multiple sequences together to make a
more realistic flow of transactions.
• For example, for a network device, you could make one
sequence that resembles downloading e-mail.
• second that is viewing a web page, and a third that is
entering single characters into web-based form
Introduction to randcase
• we can use randcase to make a weighted choice between
several actions, without having to create a class and
instance.
• Example chooses one of the three branches based on the
weight.
• SystemVerilog adds up the weights (1+8+1 = 10), chooses
a value in this range, and then picks the appropriate
branch.
• The branches are not order dependent, the weights can be
variables, and they do not have to add up to 100%.
Random control with randcase and
$urandom_range
The $urandom_range function returns a random number in
the specified range.
Equivalent constrained class
Code using randcase is more difficult to override and modify
than random constraints. The only way to modify the
random results is to rewrite the code or use variable weights.
Building a decision tree with randcase
we can use randcase when you need to create a decision tree.
Example has just two levels of procedural code, but you can see
how it can be extended to use more.
Random Generators
• How random is System Verilog? On the one hand, our
testbench depends on an uncorrelated stream of random
values to create stimulus patterns that go beyond any
directed test.
• On the other hand, we need to repeat the patterns over and
over during debug of a particular test, even if the design
and testbench make minor changes.
Pseudorandom number generators
• Verilog uses a simple PRNG that you could access with
the $random function.
• The generator has an internal state that you can set by
providing a seed to $random.
• All IEEE-1364-compliant Verilog simulators use the same
algorithm to calculate values.
Simple pseudorandom number generator
reg [31:0] state = 32’h12345678;
function reg [31:0] my_random;
reg [63:0] s64;
s64 = state * state;
state = (s64 >> 16) + state;
my_random = state;
endfunction
The PRNG has a 32-bit state. To calculate the next random
value, square the state to produce a 64-bit value, take the
middle 32 bits, then add the original value.
Random Stability — multiple generators
▪Verilog has a single PRNG that is used for the entire simulation.
▪ In System Verilog Testbenches often have several stimulus generators
running in parallel.
▪ creating data for the design under test. If two streams share the same
PRNG, they each get a subset of the random values.
▪ There are two stimulus generators and a single PRNG producing
values a, b, c, etc. Gen2 has two random objects, so during every cycle,
it uses twice as many random values as Gen1
Random Stability — multiple generators
Fig. First generator uses additional values
In System Verilog, there is a separate PRNG for every object and thread.
Changes to one object don’t affect the random values seen by others.
Random Device Configuration
▪An important part of your DUT to test is the configuration of both the internal
DUT settings and the system that surrounds it.
▪ The eth_cfg class describes the configuration for a 4-port Ethernet switch. It
is instantiated in an environment class, which in turn is used in the test.
▪The test overrides one of the configuration values, enabling all 4 ports.
Random Device Configuration
Random Device Configuration