Dynamic Constraints Final
Dynamic Constraints Final
Random Constraints
Jeremy Ridgeway
www.lsi.com
ABSTRACT
We present a suite of SystemVerilog constraint building blocks that are instantiated during
simulation. A front-end Flex parser dissects a constraint string, then interacts with VCS through
the direct programming interface (DPI) to instantiate a SystemVerilog constraint at random-time
on some value (in a type-parameterized container class). A test bench-wide resource manager
(e.g., UVM) maintains constraint strings composed before and during simulation.
With the advanced simulation profiler in VCS we show that our constraints are competitive in
solving time and, with some optimizations, in simulation time and memory. A significant
overhead pervades but flexibility to fully interchange constraints without recompilation is
valuable.
Table of Contents
1. Introduction ........................................................................................................................... 3
2. SystemVerilog Constraints ................................................................................................... 3
EXAMPLE: UVM CONSTRAINT OVERRIDE .................................................................................... 4
3. Interchangeable Constraints .................................................................................................. 6
4. Test Case ............................................................................................................................. 11
5. Results ................................................................................................................................. 13
DISTRIBUTION RESULTS ............................................................................................................. 13
PERFORMANCE RESULTS ............................................................................................................ 16
6. Discussion and Future Work ............................................................................................... 20
7. Conclusions ......................................................................................................................... 21
8. References ........................................................................................................................... 21
9. Appendix ............................................................................................................................. 22
BNF GRAMMAR ......................................................................................................................... 22
Table of Figures
Figure 1: Stimulus generation in UVM. ......................................................................................... 5
Figure 2: A new interchangeable constraint. .................................................................................. 7
Figure 3: Random and constraint hierarchies. ................................................................................ 7
Figure 4: User defined distribution: “dist”.................................................................................... 14
Figure 5: Uniform distribution on a set of distinct values: “inside”. ............................................ 15
Figure 6: Uniform distribution on a range of values..................................................................... 16
Table of Tables
Table 1: Cumulative simulation performance using `uvm_do. .................................................. 17
Table 2: Constraint solver performance using `uvm_do. ........................................................... 17
Table 3: Cumulative simulation performance over successive optimizations. ............................. 19
Table 4: Cumulative simulation memory consumption over successive optimizations. .............. 19
The Universal Verification Methodology (UVM) library has provided enhanced ability to modify
constraints at simulation time via factory overrides. From [2], they note that the UVM factory
facilitates “reuse and adjustment of predefined” verification components. This is applicable to
any UVM-registered class and therefore applies to constraints. However, to enable factory
override the verification engineer must (1) extend the class containing the constraint in question,
(2) know the test bench hierarchy to provide the appropriate parameters, and (3) create a new test
and recompile. While on-the-fly constraint modifications (i.e., through a function call) would
require recompilation in any method, we prefer an easier path.
In section 2, current techniques regarding overriding constraints with SystemVerilog and UVM
are discussed. We present our approach to interchangeable constraints in section 3. Then, in
section 4 we present the test bench used for results and discussion in section 5.
2. SystemVerilog Constraints
Random constraints may be manipulated either through inheritance (declarative) or enabled or
disabled at run-time (dynamic) [3]. Good coding practice defines a default constraint block
alongside a declared random value to ensure valid stimulus generation [4]. For example, the
constraint in Listing 1 defines the valid length of a packet to be between one and 1K bytes.
class packet;
rand int unsigned length;
constraint valid {
length inside {[1:1024]};
}
...
endclass: packet
Listing 1: Default constraint is defined alongside its variable declaration.
Test bench components may further constrain the length by extending from the packet class and
defining a new constraint. The two constraints are treated as a logical AND and subjected to
simultaneous solving (i.e., they must not block all possible values). Also, a class extension may
disable the default constraint to allow for invalid stimulus.
In the err_packet in Listing 2, the constraint block name must be known and overridden to
avoid a solving inconsistency, and randomization failure. This becomes a problem when
overriding class constraints in encrypted code, such as from a third party vendor, since the
constraint block may not be accessible. To declaratively disable a constraint, as in
any_packet, the name-collided block must be empty.
During simulation, it is possible to enable and disable whole constraint blocks directly via a task
[3]. For example, pkt.valid.constraint_mode(0) disables the default valid constraint
block for the specific packet instantiation, pkt. The method is defined by SystemVerilog as
both a task and a function. Only the task flavor modifies the current mode on the constraint.
Therefore, some care must be taken to modify the mode within the context of a task.
Assume that some scenario requires transmitting a packet that is exactly 47 bytes in length.
In Listing 3, a new packet class is defined along with a new test to register the packet with the
UVM library. If not using UVM, then a new sequencer should be defined such that the derived
class is instantiated in place of the base class. UVM streamlines this process through its factory
[2].
Each UVM object or component class is registered with the UVM factory at compile-time via
`uvm_object_utils or `uvm_component_utils macros, respectively. Instead of using
the new operator, the user essentially requests the factory to instantiate the class. If an override
UVM provides for class instance and type overriding, each by class type or type name [2].
Listing 3 provides an example of instance by type override. The new class type, packet47, is
registered with the UVM factory to override instances of packet at all test bench paths whose
ancestry is denoted in the string.
Finally, once the code modification has been implemented and new class registered with the
UVM factory, the test bench must be re-compiled and the new test simulated.
This approach requires a fair amount of detailed test bench knowledge and advanced
SystemVerilog and verification methodology understanding. This may be less efficient for quick
“what-if” scenarios or sanity checks (e.g., for bug fixes).
3. Interchangeable Constraints
We have defined a set of general classes in a local verification methodology (LVM) library to
implement a subset of the SystemVerilog constraint language. When coupled with a front-end
parser and test bench-wide resource manager, constraints may be fully interchanged on-the-fly.
Furthermore, neither deep test bench architecture nor verification methodology need be known to
change the constraints.
Continuing through the figure, a static and parameterized constraint factory is accessed for actual
constraint construction. In step 2, the constraint factory instantiates, through the direct
programming interface (DPI) layer, a parser that then generates an abstract syntax tree (AST).
The parser implements a subset of the SystemVerilog constraint language and utilizes a flex
generated lexer to identify keywords in the string and a bison generated parser to build the AST
[6, 7]. 1, 2, 3 Once parsing is complete, the AST reflects the structure of the constraint such that
the constraint factory may, in step 3, instantiate it in SystemVerilog. Finally, a well-formed
constraint is returned to the original lvm_rand instance in step 4.
1
For fast lexical analyzer details, see: http://en.wikipedia.org/wiki/Flex_lexical_analyser.
2
For GNU bison details, see: http://en.wikipedia.org/wiki/GNU_bison.
3
Refer to the appendix for the interchangeable constraints Backus-Naur Form (BNF) grammar.
SystemVerilog class hierarchies are shown in Figure 3 for LVM random variables and
constraints. The vertical arrows in the figure indicate class inheritance. A typed base class,
lvm_param_rand, is extended from an untyped base class for flexibility in reference
comparison. Also, references to a factory and its current constraint (constructed by the factory)
exist in the typed base class. Because the constraint factory is re-entrant and memory-less, its
reference is attributed “static” to limit the number of instances (one per type).
Finally, the lvm_rand class accesses the test bench-wide resource manager. While the manager
may be any table-style lookup, we employ the resource database in the UVM library. The
interchangeable constraints language considers test bench scoping to pinpoint or globalize the
application of new constraints.
Our interchangeable constraints implement constants, inside ranges, inside lists, and user defined
distribution lists. Constraints require a reference to an lvm_static_rand instance or
derivative, as indicated by the left directional arrow in Figure 3.
Each constraint type is a separate SystemVerilog class that is extended from the
lvm_constrain base and maintain necessary members. Then, an in-line randomize call is
employed using those members and targeting the local lvm_static_rand reference. Constant
constraints, lvm_const, have a single typed value to use in randomization, as in Listing 5.
Inside ranges, lvm_range, have a minimum and maximum typed value; inside lists,
lvm_inside, have a typed queue of values; distributions, lvm_dist, have a typed queue of
values and an integer queue of weights.
Randomization follows similar to lvm_const for all constraints except for the distribution. We
employ a common linear two-step strategy for user-defined distributions. First, the chosen
weight is uniformly randomized in the range [1:total_weight]. Then, the chosen weight
corresponds to the chosen value by iterating through the weight queue as in Algorithm 2. The
time required to determine the chosen value queue slot grows linearly with the number of
elements (O(n)). The grammar in the appendix defines range as an alias to inside range and
uniform as a Boolean variant (i.e. min ≤ value ≤ max rather than inside [min:max]).
The constraint factory, from Figure 2, links a flex generated C++ lexer and bison generated C++
parser to SystemVerilog via the DPI. Using C++ allows for multiple instances of the lexer and
parser to coexist [6, 7]. At step 2 in the figure, a single structure encapsulating lexer, parser, and
all necessary data to build an AST from the input string is allocated in C++ and stored on the
SystemVerilog runtime stack as an automatic chandle type. The lexer and parser build the
AST in C++ while instantiation of the type-specific constraint occur in SystemVerilog.
Algorithm 3 presents the general algorithm for stepping through an AST and constructing a new
SystemVerilog constraint container class.
Note that both the interchangeable constraint and the constraint factory are type-parameterized.
While the constraint is dynamic and can change throughout simulation, the type is static. It is
not legal to change the random variable from one type to another. Furthermore, SystemVerilog
supports direct randomization of integral types only [3]. Interchangeable constraints also support
SystemVerilog types only. This implies that interchangeable constraints may not be nested. For
example, the following code is illegal.
Also note, the translation from C++ data structure to uvm_bitstream_t is realized in
SystemVerilog. We ran into issue with UVM regular expressions leaking memory during
simulation and therefore implemented a two step approach. First, the type and arguments to the
Employing SystemVerilog system functions reduce memory consumption and avoided the UVM
library.
The entire process from lexical analysis to constraint construction is handled solely within non-
blocking SystemVerilog and C++ functions. At completion, the chandle is freed, leaking no
memory (although refer to the analysis in section 6). These measures ensure interchangeable
constraint factories are re-entrant and, therefore, may be statically instantiated.
The interchangeable constraints are lazy. Nothing occurs until and only if it is required. For
example, if a constraint string is listed on the command-line, it will only be parsed when and if
the associated lvm_rand calls its next function. Thus, constraints do not exist unless they are
explicitly necessary. Further, each class or function in the constraint interchange process only
handles what is required. For example, the AST generated from parsing is not an exact model of
the constraint because number conversion is not handled in the parser, this task occurs later in
SystemVerilog.
4. Test Case
As a test case of interchangeable random constraints, we implemented two packet generation
only test benches conforming to UVM, similar to Figure 1 (i.e. UVM sequence, sequencer,
driver, and agent). The two test benches differed in the base packet definition used in the
sequence:
Notice that in Listing 6 the default constraint defined the entire valid range, as expected.
The constraint block in scenario2 further constrained the packet length within the valid space
while err_toobig forced the packet length beyond the valid space. A common error is to
inadvertently define an inconsistent constraint, especially through inheritance. Without disabling
or overriding the default constraint, err_toobig will fail.
In contrast, the interchangeable versions of all packets are defined in just the base packet.
In Listing 7, length is declared as an lvm_rand and instantiated at packet construction with the
name PKT_LENGTH. The default valid range is pushed onto the variable. At randomization time,
the default constraint may be overridden by a new shape existing in the UVM resource database
or from the command-line. Since the new constraint will fully replace the existing one, solving
consistency is based only on the new constraint string.
Following UVM guidelines, we generated packets in the sequence in a loop. The number of
packets to be generated was controlled in the test via an uvm_config_db parameter. In the
packet sequence, we generated packets in a loop using the standard UVM macro:
if(starting_phase != null)
starting_phase.raise_objection(this);
endtask
Listing 8: Sequence body task using standard `uvm_do approach.
5. Results
We simulated with VCS version 2013.06-1 full 64-bit mode on shared Linux machines with 32
Intel (R) Xenon(R) E5-2690 processors at 2.90GHz and 378 GB of memory [8]. For the
performance results section, we enabled Synopsys VCS's Unified Simulation Profiler, a limited
customer agreement option [9].
For both test benches, we simulated a single constraint in each of three tests:
Test 1: Distribution,
Test 2: Inside set,
Test 3: Inside range.
Each test was executed five times, randomizing the variable according to the constraint a fixed
number of times: 500; 5,000; 50,000; 500,000; and 5,000,000 randomizations. The random
distributions observed are presented first; while the resultant performance metrics are presented
second.
Distribution Results
In test 1 for test bench #2, the inside set constraint was simulated. The interchangeable constraint
was set in a test through the UVM resource database:
uvm_resource_db#(string)::
set_override(“*”, “PKT_LENGTH”,
“dist{1:=1, 256:=2, 512:=2, 1024:=3,
1280:=3, 1536:=3, 1792:=3, 2048:=1, 2304:=1,
2560:=1, 2816:=1, 3072:=2, 3328:=2, 3584:=2,
3840:=2, 4096:=1 }”, this);
The following command-line argument is equivalent (spacing is for readability, they were not
used in code):
Test bench #1 implemented the corresponding SystemVerilog constraint block. Figure 4 shows
the observed behavior for both test bench #1, SystemVerilog constraints, and test bench #2,
interchangeable constraints. As expected, as the number of randomizations increase, the two
distributions converge to an identical line.
In test 2 for test bench #2, the inside set constraint was simulated. The interchangeable constraint
was set in a test through the UVM resource database:
uvm_resource_db#(string)::
set_override(“*”, “PKT_LENGTH”, “inside{1, 256, 512, 1024,
1280, 1536, 1792, 2048, 2304, 2560,
2816, 3072, 3328, 3584, 3840, 4096 }”, this);
Test bench #1 implemented the corresponding SystemVerilog constraint block. Figure 5 shows
the observed behavior for both test benches. As expected, as the number of randomizations
increase, the two distributions converge to an identical line.
In test 3 for test bench #2, the inside range constraint was simulated. The interchangeable
constraint was set in a test through the UVM resource database:
uvm_resource_db#(string)::
set_override(“*”, “PKT_LENGTH”, “inside [30:50]”, this);
+uvm_config_string=“PKT_LENGTH”,“inside [30:50]”
Test bench #1 implemented the corresponding SystemVerilog constraint block. Figure 6 shows
the observed behavior for both test benches. As expected, as the number of randomizations
increase, the two distributions converge to an identical line.
Performance Results
For performance comparison, we considered the following metrics:
Cumulative simulation time,
Cumulative simulation memory,
Constraint solving time,
Constraint solving memory.
Advanced simulation profiling in VCS was enabled for constraint metrics. For the cumulative
simulation metrics, only a summary report was generated. On the command line, we used the
VCS runtime option -reportstats, for CPU time and memory usage (virtual memory size) at
simulation completion.
First, the standard `uvm_do macro approach, as shown in Listing 8, instantiates a new sequence
item for each loop [5]. During large simulations (e.g. 500,000 packets or more), memory
consumption seems to jump considerably and inversely to performance. Therefore, we modified
the sequence to instantiate the packet once, a persistent instance, and randomize only each loop.
Second, the UVM resource database performs glob or regular expression pattern matching
during lookup. This enables wildcard use in test bench path arguments [2]. We took two
corrective actions to optimize the regression. We internally replaced the UVM resource
database with a very simple associative array, a local constraint database, keyed only on the
random variable name (i.e. lvm_rand.get_name()).
lvm_rand#(int) my_rand;
my_rand.clear_dynamic();
These changes effectively disabled uvm_resource_db access for setting constraints (replaced by
another global API) and allowed only a single dynamic update on the constraint, for the purpose
of testing.
The first set of regressions using `uvm_do, in Table 1, did not seem to follow a logarithmic path
even though the number of randomizations scaled accordingly. However, the time signatures in
Table 3 are obviously logarithmic. Furthermore, once optimization #2 is employed, there is very
little overall difference between the SystemVerilog constraints and interchangeable constraints.
class a;
int min = 4;
lvm_rand#(int) my_val;
function new();
my_range = new(“MY_RANGE”);
my_val.push(“inside [min:10]”); // Illegal!
// Parent class variable access.
my_val.push(“inside [inside [0:10] : inside [100:200]]”);
// Illegal! Nested constraints.
my_val.push(“value > 4”); // Illegal! Math/logical op.
endfunction
endclass
Listing 9: Current interchangeable constraint limitations.
Many times dynamic composition of the constraint string will suffice in overcoming limitations.
For example, instead of using parent class variables within the constraint, use them to compose a
new constraint string.
7. Conclusions
We presented an approach that enables fully interchangeable SystemVerilog constraints by
defining a set of general-use classes coupled with a front-end parser and test bench-wide
resource manager. Overall, our approach provides a straightforward way to specify constraints
as a string either to a function or on the command line. We demonstrated the resultant
distributions, in the macro, converge with SystemVerilog native constraints. However,
interchangeable constraints may incur a significant overhead depending on the use model. When
properly optimized, however, interchangeable constraints are competitive with SystemVerilog
constraints with an important advantage: constraints can be entirely swapped during simulation.
8. References
[1] J. Yuan, C. Pixley and A. Aziz, Constraint-Based Verification, New York: Springer
Science+Business Media, Inc., 2006.
[2] Accellera, "Universal Verification Methodology (UVM) 1.1 User's Guide," 2011.
[3] IEEE Computer Society, "SystemVerilog--Unified Hardware Design, Specification, and
Verification Language (1800-2012)," New York, 2013.
[4] C. Spear, SystemVerilog for Verification, A Guide to Learning the Testbench Language
Features, New York: Springer Science+Business Media, LLC, 2010.
[5] Accellera, "Universal Verification Methodology (UVM) 1.1 Class Reference," 2012.
[6] V. Packson, "Flex, A Fast Scanner Generator," March 1995. [Online]. Available:
http://flex.sourceforge.net.
[7] C. Donnelly and R. Stallman, "Bison, The Yacc-compatible Parser Generator," 2 April 2009.
[Online]. Available: http://www.gnu.org/software/bison/bison.html.
[8] Synopsys, Inc., VCS Mx / VCS MXi User Guide, H-2013.06-1 ed., 2013.
[9] Synopsys, Inc., VCS Mx / VCS MXi LCA Features, H-2013.06-1 ed., 2013.
config_variable: config_var ;
// Constraints
const_value_constraint: stringtoken ;