Section 12.2

LRM-71

Changes (change in red):

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

LRM-71

Changes (change in red):

      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 (=> ->).

LRM-54

Changes (change in red):

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:

LRM-54

Changes (change in red):

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.

Section 12.4

LRM-63 LRM-70 LRM-71

Changes in Syntax 12-2 (change in red):

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

LRM-71

Changes (change in red):

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).

LRM-63

Changes (change in red):

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.

Section 12.4.4

LRM-63

Changes (change in red):

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).

Section 12.4.5

LRM-71

Changes (change in red):

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.

Section 12.4.6

LRM-71

Changes (change in red):

mode == small => -> len < 10 ;

mode == large => -> len > 100 ;

Section 12.4.7 (New)

LRM-63

Add new Section and renumber succeeding (change in red):

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 {[1:10]}; }

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.

Section 12.4.8

LRM-71

Changes (change in red):

class B;

rand bit s;

rand bit [31:0] d;

 

constraint c { s => -> d == 0; }

endclass

LRM-71

Changes (change in red):

class B;

rand bit s;

rand bit [31:0] d;

constraint c { s => -> d == 0; }

constraint order { solve s before d; }

endclass

Section 12.4.11 (New)

LRM-63

Add (change in red):

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.

Section 12.4.12 (New)

LRM-63

Add (change in red):

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

R

 

!

 

0

0

0

0

0

 

0

0

1

E

R

 

0

1

1

0

1

E

R

 

1

1

1

1

1

 

1

0

E

0

E

E

E

 

E

E

1

E

E

 

E

E

R

0

R

E

R

 

R

R

1

E

R

 

R

R

 

 

 

 

 

Conjunction

 

Disjunction

 

Negation

 

These above rules are applied recursively until all sub-expressions are evaluated. The final value of the evaluated predicate expression determines the outcome as follows:

¾      If the result is TRUE then an unconditional constraint is generated.

¾      If the result is FALSE then the constraint is eliminated, and can generate no error.

¾      If the result is ERROR then an unconditional error is generated and the constraint fails.

¾      If the final result of the evaluation is RANDOM then a guarded constraint is generated.

 

When final value is RANDOM a traversal of the predicate expression tree is needed to collect all conditional guards that evaluate to RANDOM. When the final value is ERROR, a subsequent traversal of the expression tree is not required, allowing implementations to issue only one error.

 

Example 1:

 

class D;

int x;

endclass

 

class C;

rand int x, y;

D a,b;

constraint c1 { (x < y || a.x > b.x || a.x == 5 ) -> x+y == 10; }

endclass

 

In the example above, the predicate sub-expressions are (x < y), (a.x > b.x), and (a.x == 5), which are all connected by disjunction. Some possible cases are:

 

Case 1:    a is non-null, b is null, a.x is 5.

Since (a.x==5) is true, the fact that b.x generates an error does not result in an error.

The unconditional constraint (x+y == 10) is generated.

Case 2:    a is null

 This always results in error, irrespective of the other conditions.

Case 3:    a is non-null, b is non-null, a.x is 10, b.x is 20.

All the guard sub-expressions evaluate to FALSE.

The conditional constraint(x<y) => (x+y == 10)is generated.

 

Example 2:

 

class D;

int x;

endclass

class C;

rand int x, y;

D a,b;

constraint c1 { (x < y && a.x > b.x && a.x == 5 ) -> x+y == 10; }

endclass

 

In the example above, the predicate sub-expressions are (x < y), (a.x > b.x), and (a.x == 5), which are all connected by conjunction. Some possible cases are:

 

Case 1:    a is non-null, b is null, a.x is 6.

Since (a.x==5) is false, the fact that b.x generates an error does not result in an error.

The constraint is eliminated.

Case 2:    a is null

 This always results in error, irrespective of the other conditions.

Case 3:    a is non-null, b is non-null, a.x is 5,  b.x is 2.

All the guard sub-expressions evaluate to TRUE, producing constraint (x<y) -> (x+y == 10).

 

Example 3:

 

class D;

int x;

endclass

 

class C;

rand int x, y;

D a,b;

constraint c1 { (x < y && (a.x > b.x || a.x ==5)) -> x+y == 10; }

endclass

 

In the example above, the predicate sub-expressions are (x < y) and (a.x > b.x || a.x == 5), which is connected by disjunction. Some possible cases are:

 

Case 1:    a is non-null, b is null, a.x is 5.

The guard expression evaluates to (ERROR || a.x==5), which evaluates to (ERROR || TRUE).

The guard sub-expression evaluates to TRUE.

The conditional constraint(x<y) -> (x+y == 10)is generated.

Case 2:    a is non-null, b is null, a.x is 8.

The guard expression evaluates to (ERROR || FALSE), and generates an error.

Case 3:    a is null

 This always results in error, irrespective of the other conditions.

Case 4:    a is non-null, b is non-null, a.x is 5,  b.x is 2.

All the guard sub-expressions evaluate to TRUE.

The conditional constraint(x<y) -> (x+y == 10)is generated.

Section 12.5.2

LRM-54

Changes (change in red):

Users can override overload the pre_randomize() in any class to perform initialization and set pre-conditions before the object is randomized.

 

Users can override overload the post_randomize() in any class to perform cleanup, print diagnostics, and check post-conditions after the object is randomized.

 

If these methods are overridden overloaded, they must call their associated parent class methods, otherwise their pre- and post-randomization processing steps shall be skipped.

Section 12.5.3

LRM-54

Changes (change in red):

      The randomize() method shall not be overridden overloaded.

Section 12.6

LRM-59

Changes in Syntax 12-8 (change in red):

 

blocking_assignment ::=

...

| class_variable_identifier . randomize [ ( ) ] with constraint_block ;

LRM-59

Changes (change in red):

class_variable_identifier is the name of an instantiated object.

Section 12.10 (New)

LRM-63

Add new Section and renumber following (change in red):

12.10 In-line random variable control

 

The randomize() method can be used to temporarily control the set of random and state variables within a class instance or object. When the randomize method is called with no arguments, it behaves as described in the previous sections, that is, it assigns new values to all random variables in an object --- those declared as rand or randc --- such that all of the constraints are satisfied. When randomize is called with arguments, those arguments designate the complete set of random variables within that object; all other variables in the object are considered state variables. For example, consider the following class and calls to randomize:

 

       class CA;

              rand byte x, y;

              byte v, w;

 

              constraint c1 { x < v && y > w );

       endclass

 

       CA a = new;

 

       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

 

This mechanism controls the set of active random variables for the duration of the call to randomize, which is conceptually equivalent to making a set of calls to the rand_mode() method to disable or enable the corresponding random variables. Calling randomize() with arguments allows changing the random mode of any class property, even those not declared as rand or randc. This mechanism, however, does not affect the cyclical random mode: it cannot change a non-random variable into a cyclical random variable (randc), and cannot change a cyclical random variable into a non-cyclical random variables (change from randc to rand).

 

The scope of the arguments to the randomize method is the object class. Arguments are limited to the names of properties of the calling object; expressions are not allowed. The random mode of local class members can only be changed when the call to randomize has access to those properties, that is, within the scope of the class in which the local members are declared.

 

12.10.1 In-line constraint checker

 

Normally, calling the randomize method of a class that has no random variables causes the method to behave as a checker, that is, it assigns no random values, and only returns a status: one if all constraints are satisfied and zero otherwise. The in-line random variable control mechanism can also be used to force the randomize() method to behave as a  checker.

 

The randomize method accepts the special argument null to indicate no random variables for the duration of the call, that is, all class members behave as state variables. This causes the randomize method to behave as a checker instead of a generator. A checker evaluates all constraints and simply returns one if all constraints are satisfied, and zero otherwise. For example, if class CA defined above executes the following call:

 

       success = a.randomize( null );           // no random variables

 

Then the solver considers all variables as state variables and only checks whether the constraint is satisfied, namely, that the relation  (x < v && y > w) is true using the current values of  x, y, v, and w.

Section 12.11 (New)

LRM-63

Add new Section and renumber following (change in red):

12.11 Randomization of scope variables - ::randomize()

 

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, ::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 is:

 

statement ::= [ block_identifier : ] { attribute_instance } statement_item                                                                                                                 // from Annex A.6.4

 

statement_item ::=

  ...

| scope_randomize

 

scope_randomize ::= [::] randomize ( [ variable_identifier_list ] ) [ with {  constraint_block  } ]

 

variable_identifier_list ::= variable_identifier {, variable_identifier }

 

The scope randomize method behaves exactly the same as a class randomize method, except that it operates on the variables of the current scope instead of class member variables. Arguments to this method specify the variables that are to be assigned random values, i.e., the random variables.

 

For example:

 

       module stim;

              bit[15:0] addr;

bit[31:0] data;

 

              function bit gen_stim();

                     bit success, rd_wr;

 

                     success = ::randomize( addr, data, rd_wr );

                     return rd_wr ;

              endfunction

 

              ...

       endmodule

 

The function gen_stim calls ::randomize() with three variables as arguments: addr, data, and rd_wr. Thus, ::randomize() assigns new random variables to those variables that are visible in the scope of the gen_stim function. Note that addr and data have module scope, whereas rd_wr has scope local to the function. The above example can also be written using a class:

 

       class stimc;

              rand bit[15:0] addr;

rand bit[31:0] data;

rand bit rd_wr;

       endclass

 

       function bit gen_stim( stimc p );

              bit success;

              success = p.randomize();

              addr = p.addr;

              data = p.data;

              return p.rd_wr;

       endfunction

 

However, for this simple application, the scope randomize method leads to a straightforward implementation.

 

The scope randomize method returns 1 if it successfully sets all the random variables to valid values, otherwise it returns 0. If the scope randomize method is called with no arguments then it behaves as a checker, and simply returns status.

 

12.11.1 Adding constraints to scope variables - ::randomize() with

 

The ::randomize() with form of the scope randomize method allows users to specify random constraints to be applied to the local scope variables. When specifying constraints, the arguments to the scope randomize method become random variables, all other variables are considered state variables.

 

       task stimulus( int length );

    int a, b, c, success;

 

              success = ::randomize( a, b, c ) with { a < b ; a + b < length };

              ...

              success = ::randomize( a, b ) with { b – a > length };

              ...

endtask

 

The task stimulus above calls ::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.

Section 12.10

LRM-63

Changes (change in red):

12.10 Random number system functions and methods

Section 12.10.3

LRM-63

Changes (change in red):

12.10.3 $srandom()

The system function $srandom() method allows manually seeding the Random Number Generator (RNG) of objects or threads. The RNG of a process can be seeded using the srandom() method of the process (see Section 9.9).

                                                                                                                                                                                                                                        

The syntax for the $ prototype of the srandom() system task method  is:

 

task $ function void srandom( int seed, [class_identifier obj] );

 

The $srandom() system task initializes the local random number generator using the value of the given seed.

The optional object argument is used to seed an object instead of the current process (thread). The top level

randomizer of each program is initialized with $srandom(1) prior to any randomization calls.

 

The srandom() method initializes an object's random number generator using the value of the given seed.

Section 12.10.4 (New)

LRM-63

Changes (Old Section 12.10) (change in red):

12.10.4 get_randstate()

 

The get_randstate() method retrieves the current state an object's Random Number Generator (RNG). The state of the RNG associated with a process is retrieved using the get_randstate()method of the process (see Section 9.9).

 

The prototype of  the get_randstate() method is:

 

function string get_randstate();

 

The  get_randstate() method returns a copy of the internal state of the RNG associated with the given object.

 

The RNG state is a string of unspecified length and format. The length and contents of the string are implementation dependent.

Section 12.10.5 (New)

LRM-63

Changes (Old Section 12.10) (change in red):

12.10.5 set_randstate()

 

The set_randstate()method sets the state of an object's Random Number Generator (RNG). The state of the RNG associated with a process is set using the set_randstate()method of the process (see Section 9.9).

 

The prototype of the set_randstate() method is:

 

function void set_randstate( string state );

 

The  set_randstate() method copies the given state into the internal state of an object's RNG.

 

The RNG state is a string of unspecified length and format. Calling set_randstate() with a string value that was not obtained from get_randstate() — or from a different implementation of  get_randstate() — is undefined.

Section 12.11.3

LRM-108

Changes (change in red):

initial begin

Foo foo = new();

Bar bar = new();

integer z;

void(foo.randomize());

// z = $random;

void(bar.randomize());

end

Section 12.14 (New)

LRM-62 LRM-66

Add new Section 12.14 (change in red):

12.14 Random sequence generation - randsequence

 

Parser generators, such as yacc, use a Backus-Naur Form (BNF) or similar notation to describe the grammar of the language to be parsed. The grammar is thus used to generate a program that is able to check if a stream of tokens represents a syntactically correct utterance in that language. SystemVerilog’s sequence generator reverses this process: it uses the grammar to randomly create a correct utterance (i.e., a stream of tokens) of the language described by the grammar. The random sequence generator is useful for randomly generating structured sequences of stimulus such as instructions or network traffic patterns.

 

The sequence generator uses a set of rules and productions within a randsequence block. The syntax of the randsequence block is:

 

randcase_statement ::=                                                                        // from Annex A.6.4

randcase randcase_item { randcase_item } endcase

randcase_item ::= expression : statement_or_null

 

randsequence ::=  randsequence  ( [ production_ identifier ] ) // from Annex A.6.12

                                                  production { production }

                                                  endsequence

 

production ::=  [ function_data_type ] production_name [ ( tf_port_list ) ] : rs_rule { | rs_rule } ;

 

rs_rule ::= rs_production_list [ :=  expression  [ rs_code_block ] ]

 

rs_production_list ::=

  rs_prod { rs_prod }

| rand join [ (expression) ] production_item  production_item { production_item }

 

rs_ code_block ::= { {      data_declaration } { statement_or_null } }

 

rs_prod ::=

 production_item

| rs_code_block

| rs_if_else

| rs_repeat

| rs_case

 

production_item ::= production_identifier [ ( list_of_arguments ) ]

 

rs_if_else ::= if  ( expression ) production_item [ else production_item ]

 

rs_repeat ::= repeat ( expression ) production_item

 

rs_case ::= case ( expression )  rs_case_item { rs_case_item } endcase

 

rs_case_item ::=

  expression { , expression } : production_item

| default [:]   production_item

Syntax 12-9 —Randsequence syntax (excerpt from Annex A)

 

 

A randsequence grammar is composed of one or more productions. Each production contains a name and a list of production items. Production items are further classified into terminals and nonterminals. Nonterminals are defined in terms of terminals and other nonterminals. A terminal is an indivisible item that needs no further definition than its associated code block. Ultimately, every nonterminal is decomposed into its terminals. A production list contains a succession of production items, indicating that the items must be streamed in sequence. A single production can contain multiple production lists separated by the | symbol. Production lists separated by a |  imply a set of choices, which the generator will make at random.

 

A simple example illustrates the basic concepts:

 

                randsequence( main )

main        : first second done ;

first : add | dec ;

second      : pop | push ;

done        : { $display(“done”); } ;

add         : { $display(“add”);  } ;

dec         : { $display(“dec”);  } ;

pop         : { $display(“pop”);  } ;

push        : { $display(“push”); } ;

       endsequence

 

The production main is defined in terms of three nonterminals: first, second, and done. When main is chosen, it generates the sequence, first, second, and done. When first is generated, it is decomposed into its productions, which specifies a random choice between add and dec. Similarly, the second production specifies a choice between pop and push. All other productions are terminals; they are completely specified by their code block, which in the example displays the production name. Thus, the grammar leads to the following possible outcomes:

 

add pop done

add push done

dec pop done

dec push done

 

When the randsequence statement is executed, it generates a gramar-driven stream of random productions.  As each production is generated, the side effects of executing its associated code blocks produce the desired stimulus. In addition to the basic grammar, the sequence generator provides for random weights, interleaving and other control mechanisms. Although the randsequence statement does not intrinsically create a loop, a recursive production will cause looping.

 

The randsequence statement creates an automatic scope. All production identifiers are local to the scope. In addition, each code block within the randsequence block creates an anonymous automatic scope. Hierarchical references to the variables declared within the code blocks are not allowed. To declare a static variable, the static prefix must be used. The randsequence keyword can be followed by an optional production name (inside the parenthesis) that designates the name of the top-level production. If unspecified, the first production becomes the top-level production.

 

12.14.1 Random production weights

 

The probability that a production list is generated can be changed by assigning weights to production lists. The probability that a particular production list is generated is proportional to its specified weight.

 

production ::= [ function_data_type ] production_name [ ( tf_port_list ) ] : rs_rule { | rs_rule } ;

 

rs_rule ::= rs_production_list [ :=  expression  [ rs_code_block ] ]

 

The := operator assigns the weight specified by the expression to its production list. Weight expression must evaluate to integral non-negative values. A weight is only meaningful when assigned to alternative productions, that is, production list separated by a |. Weight expressions are evaluated when their enclosing production is selected, thus allowing weights to change dynamically. For example, the first production of the previous example can be re-written as:

 

first     : add := 3

| dec := 2

;

 

This defines the production first in terms of two weighted production lists add and dec. The production add will be generated with 60% probability and the production dec will be generated with 40% probability.

 

If no weight is specified, a production shall use a weight of 1. If only some weights are specified, the unspecified weights shall use a weight of 1.

 

12.14.2 If..else production statements

 

A production can be made conditionally by means of an if..else production statement. The syntax of the if..else production statement is:

 

rs_if_else ::= if  ( expression ) production_item [ else production_item ]

 

The expression can be any expression that evaluates to a boolean value. If the expression evaluates to true, the production following the expression is generated, otherwise the production following the optional else statement is generated. For example:

 

randsequence()

...

PP_PO : if ( depth < 2 ) PUSH else POP ;

PUSH  : { ++depth; do_push(); };

POP   : { --depth; do_pop(); };

endsequence

 

This example defines the production PP_OP. If the variable depth is less than 2 then production PUSH is generated, otherwise production POP is generated. The variable depth is updated by the code blocks of both the PUSH and POP productions.

 

12.14.3 Case production statements

 

A production can be selected from a set of alternatives using a case production statement. The syntax of the case production statement is:

 

rs_case ::= case ( expression )  rs_case_item { rs_case_item } endcase

 

rs_case_item ::=

  expression { , expression } : production_item

| default [ : ] production_item

 

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 associated with the first case-item expression that matches 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:

 

randsequence()

SELECT : case ( device & 7 )

   0       : NETWORK

   1, 2    : DISK

   default : MEMORY

 endcase ;

...

endsequence

 

This example defines the production SELECT with a case statement. The case expression (device & 7) is evaluated and compared against the two case-item expressions. If the expression matches 0, the production NETWORK is generated, and if it matches 1 or 2 the production DISK is generated. Otherwise the production MEMORY is generated.

 

12.14.4 Repeat production statements

 

The repeat production statement is used to iterate over a production a specified number of times. The syntax of the repeat production statement is:

 

rs_repeat ::= repeat ( expression ) production_item

 

The repeat expression must evaluate to a non-negative integral value. That value specifies the number of times that the corresponding production is generated. For example:

 

randsequence()

...

PUSH_OPER : repeat( $urandom_range( 2, 6 ) ) PUSH ;

PUSH  : ...

endsequence

 

In this example the PUSH_OPER production specifies that the PUSH production be repeated a random number of times (between 2 and 6) depending on by the value returned by $urandom_range().

 

The repeat production statement itself cannot be terminated prematurely. A break statement will terminate the entire randsequence block (see Section 12.14.6).

 

12.14.5 Interleaving productions – rand join

 

The rand join production control is used to randomly interleave two or more production sequences while maintaining the relative order of each sequence. The syntax of the rand join production control is:

 

rs_production_list ::=

  rs_prod { rs_prod }

| rand join [ (expression) ] production_item  production_item { production_item }

 

For example:

 

randsequence( TOP )

TOP : rand join S1 S2 ;

S1  : A B ;

S2  : C D ;

endsequence

 

The generator will randomly produce the following sequences:

 

       A B C D

       A C B D

       A C D B

       C D A B

       C A B D

       C A D B

 

The optional expression following the rand join keywords must be a real number in the range 0.0 to 1.0. The value of this expression represents the degree to which the length of the sequences to be interleaved affects the probability of selecting a sequence. A sequence’s length is the number of productions not yet interleaved at a given time. If the expression is 0.0, the shortest sequences are given higher priority. If the expression is 1.0, the longest sequences are given priority. For instance, using the previous example:

 

TOP : rand join (0.0) S1 S2 ;

 

Gives higher priority to the sequences:           A B C D   C D A B

 

TOP : rand join (1.0) S1 S2 ;

 

Gives higher priority to the sequences:           A C B D    A C D B    C A B D    C A D B

 

If unspecified, the generator used the default value of 0.5, which does not prioritize any sequence length.

 

At each step, the generator interleaves nonterminal symbols to depth of one.

 

12.14.6 Aborting productions - break and return

 

Two procedural statements can be used to terminate a production prematurely: break and return. These two statements can appear in any code block; they differ in what they consider the scope from which to exit.

 

The break statement terminates the sequence generation. When a break statement is executed from within a production code block, it forces a jump out of the randsequence block. For example:

 

randsequence()

   WRITE   : SETUP DATA ;

SETUP   : { if( fifo_length >= max_length ) break; } COMMAND ;

DATA    : ...

       endsequence

       next_statement : ...

 

When the example above executes the break statement within the SETUP production, the COMMAND production is not generated, and execution continues on the line labeled next_statement. Use of the break statement within a loop statement behaves as defined in Section 8.6. Thus, the break statement terminates the smallest enclosing looping statement, otherwise the randsequence block.

 

The return statement aborts the generation of the current production. When a return statement is executed from within a production code block, the current production is aborted. Sequence generation continues with the next production following the aborted production. For example:

 

randsequence()

   TOP : P1 P2 ;

P1  : A B C ;

P2  : A { if( flag == 1 ) return; } B C ;

A   : { $display( “A” ); } ;

B   : { if( flag == 2 ) return; $display( “B” ); } ;

C   : { $display( “C” ); } ;

       endsequence

 

Depending on the value of variable flag, the example above displays the following:

                flag == 0     ==>  A B C A B C

                flag == 1     ==>  A B C A

                flag == 2     ==>  A C A C

 

When flag == 1, production P2 is aborted in the middle, after generating A. When flag == 2, production B is aborted twice (once as part of P1 and once as part of P2), but each time, generation continues with the next production, C.

 

12.14.7 Value passing between productions

 

Data can be passed down to a production about to be generated, and generated productions can return data to the nonterminals that triggered their generation. Passing data to a production is similar to a task call, and uses the same syntax. Returning data from a production requires that a type be declared for the production, which uses syntax similar to a function declaration.

 

Productions that accept data include a formal argument list. The syntax for declaring the arguments to a production is similar to a task prototype; the syntax for passing data to the production is the same as a task call.

 

production ::= [ function_data_type ]  production_name [ ( tf_port_list ) ] : rs_rule { | rs_rule } ;

 

production_item ::= production_identifier [ ( list_of_arguments ) ]

 

For example, the first example above could be written as:

 

                randsequence( main )

main                         : first second gen ;

first                  : add | dec ;

second                   : pop | push ;

add                      : gen(“add”) ;

dec                       : gen(“dec”) ;

pop                          : gen(“pop”) ;

push                     : gen(“push”) ;

gen( string s = “done” )  : { $display( s }; } ;

       endsequence

 

In this example, the production gen accepts a string argument whose default is “done”. Five other productions generate this production, each with a different argument (the one in main uses the default).

 

A production creates a scope, which encompasses all its rules and code blocks. Thus, arguments passed down to a production are available throughout the production.

 

Productions that return data require a type declaration. The optional return type precedes the production. Productions that do not specify a return type shall assume a void return type.

 

A value is returned from a production by using the return with an expression. When the return statement is used with a production that returns a value, it must specify an expression of the correct type, just like non-void functions. The return statement assigns the given expression to the corresponding production. The return value can be read in the code blocks of the production that triggered the generation of the production returning a value. Within these code blocks, return values are accessed using the production name plus an optional indexing expression. Within each production, a variable of the same name is implicitly declared for each production that returns a value. If the same production appears multiple times then a one-dimensional array that starts at 1 is implicitly declared. For example:

 

                randsequence( bin_op )

void bin_op        : value operator value           // void type is optional

                     { $display( “%s %b %b”, operator, value[1], value[2] ); }

                           ;

bit [7:0] value  : { return $urandom } ;

string operator  : add  := 5 { return “+” ; }

 | dec  := 2 { return “-” ; }

         | mult := 1 { return “*” ; }

 ;

       endsequence

 

In the example above, the operator and value productions return a string and an 8-bit value, respectively. The production bin_op includes these two value-returning productions. Therefore, the code block associated with production bin_op has access to the following implicit variable declarations:

 

       bit [7:0] value [1:2];

       string operator;

 

Accessing these implicit variables yields the values returned from the corresponding productions. When executed, the example above displays a simple three-item random sequence: an operator followed by two 8-bit values. The operators +, -, and *are chosen with a distribution of  5/8, 2/8, and 1/8, respectively.

 

Only the return values of productions already generated (i.e., to the left of the code block accessing them) can be retrieved. Attempting to read the return value of a production that has not been generated results in an undefined value. For example:

 

X : A {int y = B;} B ;                                    // invalid use of B

X : A {int y = A[2];} B A ;                               // invalid use of A[2]

X : A {int y = A;} B {int j = A + B;} ;            // valid

 

The sequences produced by randsequence can be driven directly into a system, as a side effect of production generation, or the entire sequence can be generated for future processing.  For example, the following function generates and returns a queue of random numbers in the range given by its arguments. The first and last queue item correspond to the lower and upper bounds, respectively. Also, the size of the queue is randomly selected based on the production weights.

 

function int[$] GenQueue(int low, int high);

int[$] q;

 

randsequence()

TOP  : BOUND(low) LIST BOUND(high) ;

LIST : LIST ITEM  := 8   { q = { q, ITEM }; }

 | ITEM       := 2   { q = { q, ITEM }; }

     ;

int ITEM : { return $urandom_range( low, high ); } ;

 

BOUND(int b) : { q = { q, b }; } ;

endsequence

GenQueue = q;

endfunction

 

When the randsequence in function GenQueue executes, it generates the TOP production, which causes three productions to be generated: BOUND with argument low, LIST, and BOUND with argument high. The BOUND production simply appends its argument to the queue. The LIST production consists of a weighted LIST ITEM production and an ITEM production. The LIST ITEM production is generated with 80% probability, which causes the LIST production to be generated recursively, thereby postponing the generation of the ITEM production. The selection between LIST ITEM and ITEM is repeated until the ITEM production is selected, which terminates the LIST production. Each time the ITEM production is generated, it produces a random number in the indicated range, which is later appended to the queue.

 

The following example uses a randsequence block to produce random traffic for a DSL packet network.

 

class DSL; ... endclass        // class that creates valid DSL packets

 

randsequence (STREAM)

STREAM : GAP DATA := 80

| DATA     := 20 ;

 

DATA : PACKET(0)  := 94 { transmit( PACKET ); }

| PACKET(1)  := 6  { transmit( PACKET ); } ;

 

DSL PACKET(bit bad) : { DSL d = new;

if( bad ) d.crc ^= 23;     // mangle crc

return d;

                             } ;

GAP: { ## $urandom_range( 1, 20 ); };

endsequence

 

In this example, the traffic consists of a stream of (good and bad) data packets and gaps. The first production, STREAM, specifies that 80% of the time the traffic consists of a GAP followed by some DATA, and 20% of the time it consists of just DATA (no GAP). The second production, DATA, specifies that 94% of all data packets are good packets, and the remaining 6% are bad packets. The PACKET production implements the DSL packet creation; if the production argument is 1 then a bad packet is produced by mangling the crc of a valid DSL packet. Finally, the GAP production implements the transmission gaps by waiting a random number of cycles between 1 and 20.