The random constraints are built
on top of an object-oriented class system framework that
models the data to be randomized as objects that contain random variables and
user-defined constraints.
class
Bus;
rand
bit[15:0] addr;
rand
bit[31:0] data;
constraint
word_align {addr[1:0]
== ‘2’b0;}
endclass
The MyBus class
inherits all of the random variables and constraints of the Bus class, and adds a random variable called atype that is used
to control the address range using another constraint. The addr_range constraint
uses implication to select one of three range constraints depending on the
random value of atype. When a MyBus object is randomized, values for addr, data, and atype are computed
such that all of the constraints are satisfied. Using inheritance to build
layered constraint systems enables the development of general-purpose models
that can be constrained to perform application-specific functions.
Using inheritance to build layered constraint systems enables
the development of general-purpose models that can be constrained to perform
application-specific functions.
Objects can be further
constrained using the randomize()
with construct, which declares
additional constraints in-line with the call to randomize():
— Constraints interact bidirectionally bi-directionally. In this example, the value
chosen for addr depends on atype and how it is constrained, and the value chosen for atype depends on
addr and how it is constrained. All expression operators
are treated bidirectionally bi-directionally, including the implication
operator (=>).
class
MyYXPair extends
XYPair
function
void pre_randomize();
super.pre_randomize();
$display("Before randomize
x=%0d, y=%0d", x, y);
endfunction
function
void post_randomize();
super.post_randomize();
$display("After
randomize x=%0d, y=%0d\n", x, y);
endtask endfunction
endfunction endclass
— The solver can randomize singular
variables of any integral type such as integer,
enumerated types, and packed array variables of any size.
Editor’s Note:
This new operator needs to be added to the operator precedence table and other
sections describing operator rules (signed/unsigned/2-state/4-state/real
operands, etc.).
value_range_list is a comma-separated list of integral expressions and ranges. Value ranges Ranges are specified in ascending order with a
low and high bound, enclosed by square braces [ ], and separated by a colon ( :
), as in [low_bound:high_bound].
Ranges include all of the integer elements between the bounds. If the bound to
the left of the colon is greater than the bound to the right the range is empty
and contains no values.
Editor’s Note:
This new operator needs to be added to the operator precedence table and other
sections describing operator rules (signed/unsigned/2-state/4-state/real
operands, etc.).
The distribution operator dist
evaluates to true if the value of the expression is contained in the set;
otherwise it evaluates to false.
Editor’s Note: These
new operators need to be added to the operator precedence table and other
sections describing operator rules (signed/unsigned/2-state/4-state/real
operands, etc.).
— A dist
expression expression requires
that expression contain at least one rand variable.
— A dist
expression expression can only be
a top-level constraint (not a predicated constraint).
Editor’s Note:
This new operator needs to be added to the operator precedence table and other
sections describing operator rules (signed/unsigned/2-state/4-state/real
operands, etc.).
Just like
implication, if...else style constraints are bidirectional bi-directional. In the declaration above, the value of mode constraints
the value of len,
and the value of len constrains the
value of mode.
Just like
implication, if...else style constraints are bi-directional. In the declaration above, the value of mode constraints
the value of len, and
the value of len constrains the
value of mode.
Because the else part of an if-else style constraint declarations is optional, there can
be confusion when an else is omitted from a nested if sequence. This is
resolved by always associating the else with the closest previous if that lacks
an else. In the example below, the else goes with the inner if, as shown by
indentation:
if (mode != large)
if (mode == small)
len < 10;
else // else applies to preceding if
len > 100;
This example uses global
constraints to define the legal values of an ordered binary tree. Class A represents a leaf node with an 8-bit value x. Class B extends class A and represents a heap-node with value v, a left subtree sub-tree, and a right subtree sub-tree.
Both subtrees sub-trees are declared as rand
in order to randomize them at the
same time as other class variables. The constraint block named heapcond has two
global constraints relating the left and right subtree sub-tree
values to the heap-node value. When an instance of class B is randomized, the solver simultaneously solves for B and its left and right children, which in turn can be
leaf nodes or more heap-nodes.
The following rules determines which objects, variables, and constraints
are to be randomized:
First, determine the set of
objects that are to be randomized as a whole. Starting with the object that
invoked the randomize()
method, add all objects that are
contained within it, are declared rand, and are active (see rand_mode
in Section 12.7.1). The definition is
recursive and includes all of the active random objects that can be reached
from the starting object. The objects selected in this step are referred to as
the active random objects.
The solver must assure that
the random values are selected to give a uniform value distribution over legal
value combinations (that is, all combinations of legal values
have the same probability of being the solution). This important property
guarantees that all legal value combinations are
equally probable, which allows randomization to better explore the whole design
space.
The constraint c says “s implies
d equals zero”. Although this
reads as if s determines d, in fact s and d are
determined together. There are 232 232 valid combinations of {s,d}, but s is only true for {1,0}. Thus, the probability that s is true is 1/232 1/232, which is practically zero.
The constraints provide a
mechanism for ordering variables so that s can be chosen independently
of d.
Editor’s Note: Verilog
syntax puts “static” after the data type, the opposite of C. Should the
declaration for constraint be consistent with Verilog, or with C? Note that the
Co-design SystemSim simulator allows the static
declaration of SystemVerilog variables to be in either order. I submitted a
request to the BC committee asking if the SystemVerilog LRM was intended to
allow the same. I do not know the result of that request.
The built-in definition for
pre_randomize()
is:
function
void pre_randomize;
if
(super) super.pre_randomize();
// super is being tested to see if the
object handle exists
// Optional programming before randomization goes
here
endfunction
The built-in definition for
post_randomize()
is:
function
void post_randomize;
if
(super) super.post_randomize();
// super
is being tested to see if the object handle exists
// Optional programming after randomization goes
here
endfunction
— If randomize(), fails post_randomize() is not called.
— The randomize()
method implements object random
stability. An object can be seeded by the $srandom()
system call (see
Section 12.10.3), specifying the object in the second argument.
The randomize()...with constraint block can also reference local variables
and task and function parameters arguments, eliminating the need for mirroring a local
state as member variables in the object class. The scope for variable names in
a constraint block, from inner to outer, is: randomize()...with object class, automatic and local variables, task and
function parameters arguments, class variables, variables in the
enclosing scope. The randomize()... with class is brought into scope at the innermost nesting
level.
Editor’s Note:
“parameter” is a Verilog keyword, and “parameterized” models refer to the usage
of Verilog “parameters” (see Sections 11.21, 19.6 and 20). Use of the word
“parameterized” in this context is not consistent with the Verilog LRM. Suggest
using “arguments” (as in Verilog LRM), “formal arguments” or “formals”.
In the f.randomize() with constraint
block, x is a member of class Foo, and hides the x in class Bar. It also hides the x argument parameter in
the doit() task. y is a
member of Bar. z is a local argument parameter.
The function
form of rand_mode() only accepts singular variables, thus, if the specified variable is an unpacked array, a single
element must be selected via its index.
— Constraint
blocks can be made active or inactive using the constraint_mode() built-in method system task. Initially, all constraint blocks are
active. Inactive constraints are ignored by the randomize() function.
— Random
variables can be made active or inactive using the rand_mode() built-in method system task. Initially, all rand and randc variables
are active. Inactive variables are ignored by the randomize() function.
The seed is an
optional argument that determines which the sequence of random numbers
is generated. The
seed can be any integral expression. The random number generator shall generate the same sequence of random numbers every time the same seed
is used.
function
int unsigned int $urandom [ (int seed ) ] ;
Editor’s Note:
Verilog syntax is “function int unsigned” instead of
“function unsigned int”. Co-design’s SystemSim allows both Verilog and C styles.
function
int unsigned int $urandom_range(int unsigned int maxval, int unsigned
int minval
= 0 );
Editor’s Note:
Verilog syntax is “function int unsigned” instead of
“function unsigned int” ?
task
$srandom( int
seed, [object class_identifier
obj] );
Editor’s Note:
Is “object” a data type? There is no keyword “object”
anywhere else in the LRM.
Testbenches Test-benches with
this feature exhibit more stable RNG behavior in the face of small changes to
the user code. Additionally, it enables more precise control over the
generation of random values by manually seeding threads and objects.
The Random Number Generator
(RNG) is localized to threads and objects. Because the sequence
stream of random values returned by a thread or object is
independent of the RNG in other threads or objects, this property is called random
stability. Random stability applies to:
Each thread has an
independent RNG for all randomization system calls invoked from that thread.
When a new thread is created, its RNG is seeded with the next random value from
its parent thread. This property is called “hierarchical
seeding.” hierarchical seeding.
All RNG’s
can be manually seeded. Combined with hierarchical seeding, this facility
allows users to define the operation of a subsystem (hierarchy subtree sub-tree) completely with a single seed at the
root thread of the system.
Each thread is seeded with
a unique value, determined solely by its parent. The root of a thread execution
subtree determines the random seeding of its
children. This allows entire subtrees to be moved,
and preserves their behavior by manually seeding
their root thread.
class
Foo;
function
void new (integer
seed);
//set a new seed for this instance
$srandom(seed, this);
endfunction
endclass
Each object maintains its
own internal random number generator, which is used exclusively by its randomize()
method. This allows objects to be
randomized independent of each other and of calls to other system randomization
functions. When an object is created, its random number generator (RNG) is
seeded using the next value from the RNG of the thread that creates the object.
This process is called hierarchical object seeding hierarchical object seeding.
class
Packet;
rand
bit[15:0] header;
...
function
void new (int
seed);
$srandom(seed, this);
...
endfunction
endclass