The random constraints are built typically specified
on top of an object oriented data abstraction.
that models the data to be randomized as objects that
contain random variables and user-defined constraints. The constraints
determine the legal values that can be assigned to the random variables.
Objects are ideal for representing complex aggregate data types and protocols
such as Ethernet packets.
task
exercise_bus (MyBus
bus);
int
res;
// EXAMPLE 1: restrict to small addresses
res = bus.randomize() with with
{atype == small;};
// EXAMPLE 2: restrict to address between 10 and
20
res = bus.randomize()with with
{10 <= addr && addr
<= 20;};
// EXAMPLE 3: restrict data values to powers-of-two
res = bus.randomize()with with
{data & (data - 1) == 0;};
endtask
— Constraints support only
2-state values. 4-state values (X and or Z) values and or 4-state operators (e.g., ===, !==
) are illegal and shall result in an error.
function
void pre_randomize();
super super.pre_randomize();
$display("Before
randomize x=%0d, y=%0d", x, y);
endfunction
loop_variables ::= [ index_variable_identifier ] { , [ index_ variable_identifier ] } // from Annex A.6.8
— The randomize() task is
virtual. Accordingly, it treats the class constraints in a virtual manner. When
a named constraint is overloaded redefined in an extended class, the previous
definition is overridden.
Constraints support integer
value sets and the set membership operators (as defined in Section 7.20).
Absent any other
constraints, all values (either single values or value ranges), have an equal
probability of being chosen by the inside operator.
The negated form of the inside operator denotes that expression lies outside the set: !(expression
inside { set }).
dist_item ::= value_range
[ dist_weight ]
value_range := expression
| value_range :/
expression
dist_weight ::=
:= expression
| :/ expression
The distribution set is a
comma-separated list of integral expressions and ranges. Optionally, each term
in the list can have a weight, which is specified using the := or :/ operators.
If no weight is specified for an item, the
default weight is := 1. The weight can be any integral
SystemVerilog expression.
In general, distributions
guarantee two properties: set membership and monotonic weighting, which means
that increasing a weight shall increase increases the likelihood of choosing those values.
The boolean
equivalent of the implication operator a -> b is (!a || b). This states that if the expression is true, then random
numbers generated are constrained by the constraint (or constraint block set).
Otherwise the random numbers generated are unconstrained.
The constraint_set
represents any valid constraint or an unnamed constraint block set. If the
expression is true, all of the constraints in the constraint block set must
also be satisfied.
constraint_set represents
any valid constraint or an unnamed constraint block. If the expression is true,
all of the constraints in the first constraint or constraint block set must be
satisfied, otherwise all of the constraints in the optional else
constraint or constraint-block must
be satisfied. Constraint blocks sets can be used to group multiple constraints.
loop_variables ::= [ index_variable_identifier ] { , [ index_ variable_identifier ] } // from Annex A.6.8
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 v. Class B extends class A and represents a heap-node with value v, a left subtree, and a
right subtree. Both subtrees
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 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.
constraint_block ::= // from Annex A.1.9
solve identifier_list before identifier_list ;
| constraint_expression
static constraint constraint_identifier { contraint_expressions }
constraint_declaration ::= //
from Annex A.1.9
[ static ] constraint constraint_identifier { { constraint_block } }
Syntax
12-8—Static constraint syntax (excerpt from Annex A)
— 0 TRUE FALSE Sub-expression evaluates to TRUE FALSE
— 1 FALSE TRUE Sub-expression evaluates to FALSE TRUE
— Negation ( !
): If the sub-expression evaluates to ERROR, then the guard evaluates to ERROR.
Otherwise, if the sub-expression evaluates to TRUE or FALSE then the guard
evaluates to FALSE or TRUE, respectively.
— If the final result of
the evaluation is RANDOM then a guarded conditional constraint is generated.
Conjunction
virtual
function int randomize();
—
The randomize() method shall not is built-in and
cannot be overridden.
—
The randomize() method
implements object random stability. An object can be seeded by the $srandom()
system call calling its srandom() method
(see Section 12.12.3), specifying the object in the
second argument.
blocking_assignment ::= //
from Annex A.6.2
...
| class_variable_identifier . randomize [ ( )
] with constraint_block
inline_constraint _declaration ::= // not in Annex A
class_variable_identifier . randomize [ ( [ variable_identifier_list
| null
] ) ]
with { { constraint_block
} }
Syntax
12-8—In-line constraint syntax (excerpt from not in Annex A)
In this example, the toggle_rand function
first checks the current active state of the constraint filter1 in the
specified Packet object p. If the constraint is active, then the function shall
deactivates it; if it
is inactive, the function shall activates it. Finally, the function calls the randomize
method to generate a new random value for variable source_value.
a.randomize(); //
random variables: x, y. state variables: v, w
a.randomize( x ); //
random variables: x. state variables: y, v, w
a.randomize( v, w ); //
random variables: v, w. state variables: x, y
a.randomize( w, x ); //
random variables: w, x. state variables: y, v
The built-in class
randomize method operates exclusively on class member variables. Using classes
to model the data to be randomized is a powerful mechanism that enables the
creation of generic, reusable objects containing random variables and constraints
that can be later extended, inherited, constrained, overridden, enabled,
disabled, merged with or separated from other objects. The ease with which
classes and their associated random variables and constraints can be
manipulated make classes an ideal vehicle for describing and manipulating
random data and constraints. However, some less-demanding problems that do not
require the full flexibility of classes, can use a
simpler mechanism to randomize data that does not belong to a class. The scope
randomize method function,
std::randomize(), enables
users to randomize data in the current scope, without the need to define a
class or instantiate a class object.
The syntax of the scope randomize method function is:
statement ::= [ block_identifier : ] { attribute_instance } statement_item // from Annex A.6.4
statement_item ::=
...
| scope_randomize
scope_randomize ::= // not in Annex A
[ std:: ] randomize ( [ variable_identifier_list
] ) [ with { { constraint_block } } ]
variable_identifier_list ::= variable_identifier
{ , variable_identifier }
Syntax
12-9—scope randomize method function syntax (excerpt from not in
Annex A)
The scope randomize method function
behaves exactly the same as a class randomize method, except that it operates
on he variables of the current scope instead of class member variables.
Arguments to this method function specify the variables that are to be
assigned random values, i.e., the random variables.
However, for this simple
application, the scope randomize method function
leads to a straightforward implementation.
The scope randomize method function
returns 1 if it successfully sets all the random variables to valid values,
otherwise it returns 0. If the scope randomize method function is
called with no arguments then it behaves as a checker, and simply returns
status.
12.11.1
Adding constraints to scope variables - std::randomize() with
The std::randomize() with form of the scope randomize method function allows users to
specify random constraints to be applied to the local scope variables. When
specifying constraints, the arguments to the scope randomize method function
become random variables, all other variables are considered state variables.
task
stimulus( int
length );
int
a, b, c, success;
success = std::randomize( a,
b, c ) with { a < b ; a + b <
length };
...
success = std::randomize( a, b
) with { b - a > length };
...
endtask
The task stimulus
above calls std::randomize twice
resulting in two sets of random values for its local variables a, b,
and c. In the first call variables a and b are
constrained such that variable a is
less than b, and their sum is less than
the task argument length, which is designated as a state variable. In the
second call, variables a and b are constrained such that their difference is greater
than state variable length.
—
The system
randomization calls, $urandom()
and, $urandom_range(), and $srandom().
—
The object
and process random seeding method, srandom().
—
The object
randomization method, randomize().
integer
x, y, z;
fork //set a seed at the
start of a thread
begin
$process::self.srandom(100);
x = $urandom; end
//set a seed during a thread
begin
y = $urandom; $process::self.srandom(200); end
// draw 2 values from the thread RNG
begin
z = $urandom + $urandom
; end
join
— Objects can be seeded at
any time using the $srandom() system task with an optional object argument method.
class
Foo;
function
new (integer seed);
//set a new seed for this instance
$this.srandom(seed, this);
endfunction
endclass
Sometimes it is desirable
to manually seed an object’s RNG using the $srandom() system call method. This can be done either in a class method, or
external to the class definition:
An example of seeding the
RNG internally, as a class method is:
class
Packet;
rand
bit[15:0] header;
...
function
new (int
seed);
$this.srandom(seed, this);
...
endfunction
endclass
An example of seeding the
RNG externally is:
Packet p = new(200);
// Create p with seed 200.
$p.srandom(300, p); // Re-seed p with seed 300.
Calling $srandom() in an
object’s new() function, assures the object’s RNG is set with the new
seed before any class member values are randomized.
The case production statement is analogous to the procedural
case statement except as noted below. The case expression is evaluated, and its
value is compared against the value of each case-item expression, which are
evaluated and compared in the order in which they are given. The production generated is the one associated with the first
case-item expression that matches matching the case expression is generated. If no matching case-item expression
is found then the production associated with the optional default item is
generated, or nothing if there no default item. Multiple default statements in
one case production statement shall be illegal. Case-item expressions separated
by commas allow multiple expressions to share the production. For example:
GAP: { ## ($urandom_range( 1, 20 )); };
endsequence