typedef enum {low, mid, high} AddrType;
class MyBus extends Bus;
rand AddrType atype;
constraint addr_range
{
(atype
== low ) =>
-> addr inside { [0
: 15] };
(atype
== mid ) =>
-> addr inside { [16
: 127]};
(atype
== high) =>
-> addr inside {[128
: 255]};
}
endclass
—
Constraints interact bidirectionally. 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, including the
implication operator (=> ->).
Occasionally,
it is desirable to perform operations immediately before or after
randomization. That is accomplished via two built-in methods, pre_randomize() and post_randomize(), which are automatically called before and after
randomization. These methods can be overridden overloaded
with the desired functionality:
By
default, pre_randomize() and post_randomize() call
their overridden overloaded parent class
methods. When pre_randomize() or post_randomize() are
overridden overloaded, care must be taken
to invoke the parent class’ methods, unless the class is a base class (has no
parent class), otherwise the base class methods shall not be called.
constraint_declaration ::=
[ static ] constraint
constraint_identifier { { constraint_block } }
constraint_block ::=
solve [ priority ] identifier_list before identifier_list ;
| expression dist { dist_list
} ;
| constraint_expression
constraint_expression ::=
expression ;
| expression
=> -> constraint_set
| if
( expression ) constraint_set [ else constraint_set ]
| expression dist { dist_list
} ;
| foreach ( array_identifier
[ loop_variables
] ) constraint_set
constraint_set ::=
constraint_expression
| { {
constraint_expression } }
dist_list ::= dist_item
{ , dist_item }
dist_item ::=
value_range := expression
| value_range :/ expression
constraint_prototype ::= [ static
] constraint constraint_identifier
extern_constraint_declaration ::=
[ static ] constraint
class_identifier :: constraint_identifier
{ { constraint_block } }
identifier_list ::= identifier { , identifier
}
loop_variables ::= [ index_identifier ] { , [ index_identifier
] } // from Annex A.6.8
constraint_block is a list
of expression statements that restrict the range of a variable or define
relations between variables. A constraint_expression
is any SystemVerilog expression, or one of the constraint-specific
operators: => -> and dist (see Sections 12.4.4 and 12.4.5).
The declarative nature of
constraints imposes the following restrictions on constraint expressions:
— Calling tasks or functions is not allowed. Functions are allowed with certain
limitations (see Section 12.4.11).
— Operators with side
effects, such as ++ and -- are not allowed.
— randc variables cannot be specified in ordering constraints
(see solve...before
in Section 12.4.8).
— dist expressions cannot appear in other expressions (unlike inside); they can only be top-level expressions.
Limitations:
— A dist operation shall not be applied to randc variables.
— A dist expression requires that expression contain at least
one rand variable.
— A dist expression
can only be a top-level constraint (not a predicated constraint).
The implication
operator ( => -> ) can be used to declare an expression
that implies a constraint.
The syntax to
define an implication constraint is:
constraint_expression ::= //
from Annex A.1.9
...
| expression => -> constraint_set
Syntax 12-5—Constraint implication syntax (excerpt from Annex A)
The expression
can be any integral SystemVerilog expression.
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).
Otherwise the random numbers generated are unconstrained.
The constraint_set represents any valid
constraint or an unnamed constraint block. If the expression is true, all of
the constraints in the constraint block must also be satisfied.
For example:
mode == small => -> len < 10;
mode == large => -> len > 100;
In this
example, the value of mode implies that the value of len shall be constrained to less than 10 (mode == small),
greater than 100 (mode == large), or unconstrained (mode != small and mode != large).
In the
following example:
bit [3:0] a, b;
constraint c { (a == 0) => -> (b == 1);
}
Both a and b are 4 bits, so
there are 256 combinations of a and b. Constraint c says that a == 0 implies
that b == 1,
thereby eliminating 15 combinations: {0,0}, {0,2}, … {0,15}. Therefore, the
probability that a == 0 is thus 1/(256-15) or 1/241.
mode == small => -> len < 10 ;
mode == large => -> len > 100 ;
12.4.7 Iterative Constraints
Iterative constraints allow arrayed
variables to be constrained in a parameterized manner using loop variables and
indexing expressions.
The syntax to define an iterative constraint is:
constraint_expression
::= //
from Annex A.1.9
…
| foreach (array_identifier [ loop_variables ] ) constraint_set
loop_variables ::= [ index_identifier ]{ , [ index_identifier
] }
//
from Annex A.6.8
The foreach construct specifies iteration over the elements of
an array. Its argument is an identifier that designates any type of array
(fixed-size, dynamic, associative, or queue) followed by a list of loop
variables enclosed in square brackets. Each loop variable corresponds to one of
the dimensions of the array.
For example:
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
C1 constrains each element of the array A to be in the set [2,4,8,16]. C2 constrains each element of the array A to be greater than twice its index.
The number of loop variables must not exceed
the number of dimensions of the array variable. The scope of each loop variable
is the foreach
constraint construct, including its constraint_set.
The type of each loop variable is implicitly declared to be consistent with the
type of array index. An empty loop variable indicates no iteration over that
dimension of the array. As with default arguments, a list of commas at the end can
be omitted, thus, foreach(
arr [ j ] ) is a shorthand for foreach( arr
[ j, , , , ] ). It shall be an error for any loop variable to have the same
identifier as the array.
The mapping of loop variables to array
indexes is determined by the dimension cardinality, as described in Section 22.4.
// 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 causes i to iterate
from 0 to 1, j from 0 to 2, and k from 0 to 3. The second foreach causes q to iterate from 5
to 1, r from 0 to 3, and s from 2 to 1.
Iterative constraints can include predicates. For example:
class C;
rand int
A[] ;
constraint c1 { arr.size
inside {[
constraint c2 { foreach
( A[ k ] ) (k < A.size – 1) => A[k + 1] > A[k]; }
endclass
The first constraint, c1, constrains the
size of the array A to be between 1 and 10. The second constraint, c2,
constrains each array value to be greater than the preceding one, i.e., an
array sorted in ascending order.
Within a foreach, predicate expressions involving only constants, state
variables, object handle comparisons, loop variables, or the size of the array
being iterated behave as guards against the creation of constraints, and not as
logical relations. For example, the implication in constraint c2 above involves
only a loop variable and the size of the array being iterated, thus, it allows
the creation of a constraint only when k < A.size()
– 1, which in this case prevents an out-of-bounds access in the constraint.
Guards are described in more detail in Section 12.4.12
Index expressions can include loop
variables, constants, and state variables. Invalid or out or bound array
indexes are not automatically eliminated; users must explicitly exclude these
indexes using predicates.
The size method of a
dynamic or associative array can be used to constrain the size of the array
(see constraint c1 above). If an array is constrained by both size constraints
and iterative constraints, the size constraints are solved first, and the
iterative constraints next. As a result of this implicit ordering between size
constraints and iterative constraints, the size method shall be treated as a state variable within the foreach block of
the corresponding array. For example, the expression A.size
is treated as a random variable in constraint c1, and as a state variable in
constraint c2. This implicit ordering can cause the solver to fail in some
situations.
class B;
rand bit s;
rand bit [31:0]
d;
constraint c { s => -> d == 0;
}
endclass
class B;
rand bit s;
rand bit [31:0]
d;
constraint c { s => -> d == 0;
}
constraint order
{ solve s before d; }
endclass
12.4.11 Functions in Constraints
Some properties are unwieldy or impossible to express in a single
expression. For example, the natural way to compute the number of 1’s in a
packed array uses a loop:
function int
count_ones ( bit [9:0] w );
for( count_ones
= 0; w != 0; w = w >> 1 )
count_ones += w & 1’b1;
endfunction
Such a function could be used to constrain
other random variables to the number of 1 bits:
constraint C1 { length == count_ones(
v ) };
Without the ability to call a function, this
constraint requires the loop to be unrolled and expressed as a sum of the
individual bits:
constraint C2
{
length
== ((w>>9)&1) + ((w>>8)&1) + ((w>>7)&1) +
((w>>6)&1) + ((w>>5)&1) +
((w>>4)&1)
+ ((w>>3)&1) + ((w>>2)&1) +
((w>>1)&1) + ((w>>0)&1);
}
Unlike the count_ones
function, more complex properties, which require temporary state or unbounded
loops, may be impossible to convert into a single expression. The ability to
call functions, thus, enhances the expressive power of the constraint language
and reduces the likelihood of errors. Note that the two constraints above are
not completely equivalent; C2 is bidirectional (length can constrain w and
vice-versa), whereas C1 is not.
To handle these common cases, SystemVerilog
allows constraint expressions to include function calls, but it imposes certain
semantic restrictions.
¾ Functions that appear in constraint
expressions cannot contain output or ref arguments (const ref are allowed).
¾ Functions that appear in constraint
expressions should be automatic (or
preserve no state information) and have no side effects.
¾ Functions that appear in constraints cannot
modify the constraints, for example, calling rand_mode
or constraint_mode methods.
¾ Functions shall be called before constraints
are solved, and their return values shall be treated as state variables.
¾
Random
variables used as function arguments shall establish an implicit variable
ordering or priority. Constraints that include only variables with higher
priority are solved before other, lower priority, constraints. Random variables
solved as part of a higher priority set of constraints become state variables
to the remaining set of constraints. For example:
class B;
rand int x, y;
constraint C {
x <= F(y); };
constraint D {
y inside { 2, 4, 8 } };
endclass
Forces y to be solved before x. Thus, constraint D is solved separately
before constraint C, which uses the values of y
and F(y) as state variables.
Within each prioritized set of constraints,
cyclical (randc)
variables are solved first.
¾ Circular dependencies created by the
implicit variable ordering shall result in an error.
¾
Function
calls in active constraints are executed an unspecified number of times (at
least once), in an unspecified order.
12.4.12 Constraint guards
Constraint guards are predicate expressions
that function as guards against the creation of constraints, and not as logical
relations to be satisfied by the solver. These predicate expressions are
evaluated before the constraints are solved, and are characterized by involving
only the following items:
¾ constants
¾ state variables
¾ object handle comparisons (comparisons
between two handles or a handle and the constant null)
In addition to the above, iterative
constraints (see Section 12.4.7) also consider loop variables and the size of
the array being iterated as state variables.
Treating these predicate expressions as
constraint guards prevents the solver from generating evaluation errors,
thereby failing on some seemingly correct constraints. This enables users to
write constraints that avoid errors due to nonexistent object handles or array
indices out of bounds. For example, the sort constraint of
the singly-linked list, SList, shown
below is intended to assign a random sequence of numbers that is sorted in
ascending order. However, the constraint expression will fail on the last
element when next.n
results in an evaluation error due to a non-existent handle.
class SList;
rand int n;
rand Slist next;
constraint sort
{ n < next.n;
}
endclass
The error condition above can be avoided by
writing a predicate expression to guard against that condition:
constraint sort { if( next != null ) n
< next.n; }
In the sort constraint
above, the if prevents the
creation of a constraint when next == null,
which in this case avoids accessing a non-existent object. Both implication (->)
and if…else can be used as guards.
Guard expressions can themselves include
sub-expressions that result in evaluation errors (e.g., null references), and
they are also guarded from generating errors. This logical sifting is
accomplished by evaluating predicate sub-expressions using the following
4-state representation:
¾ 0 TRUE Sub-expression evaluates to TRUE
¾ 1 FALSE Sub-expression evaluates to FALSE
¾ E ERROR Sub-expression
causes an evaluation error
¾ R RANDOM Expression
includes random variables and cannot be evaluated
Every sub-expression within a predicate
expression is evaluated to yield one of the above four values. The
sub-expressions are evaluated in an arbitrary order,
and the result of that evaluation plus the logical operation define the outcome
in the alternate 4-state representation. A conjunction (&&),
disjunction (||), or negation (!) of sub-expressions can include some
(perhaps all) guard sub-expressions. The following rules specify the resulting
value for the guard:
¾ Conjunction
(&&): If any one of the sub-expressions
evaluates to FALSE then the guard evaluates to FALSE. Otherwise, if any one
sub-expression evaluates to ERROR then the guard evaluates to ERROR, else the
guard evaluates to TRUE.
¾ If the guard evaluates to FALSE then the
constraint is eliminated.
¾ If the guard evaluates to TRUE then a
(possibly conditional) constraint is generated.
¾ If the guard evaluates to ERROR then an
error is generated and randomize fails.
¾ Disjunction
(||): If any one of the sub-expressions
evaluates to TRUE then the guard evaluates to TRUE. Otherwise, if any one
sub-expression evaluates to ERROR then the guard evaluates to ERROR, else the
guard evaluates to FALSE.
¾ If the guard evaluates to FALSE then a
(possibly conditional) constraint is generated.
¾ If the guard evaluates to TRUE then an
unconditional constraint is generated.
¾ If the guard evaluates to ERROR then an
error is generated and randomize fails.
¾
¾ Negation
(!): If the sub-expression evaluates 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.
These rules are codified by the following truth tables:
|
&& |
0 |
1 |
E |
R |
|
|| |
0 |
1 |
E |
|