Overview
The BDD Constraint Solver is a solving mode within the Synopsys VCS constraint solver that uses a Binary Decision Diagram (BDD) representation of a randomize() call's constraint system. It is the alternative to the default RACE solver mode and is exposed to users through the VCS constrained-random verification flow. Its defining behavior, performance profile, and applicability are documented in case studies of CPU/microcode stimulus generation, most notably the AMD microcode stimuli work published on Design & Reuse.
How It Works
Unlike incremental solvers that reason about constraints on the fly, the BDD solver first elaborates the entire solution space of a randomize() call before selecting a single solution. Once the solution space is built, the solver picks a value from it. The full solution space is then cached, which speeds up subsequent randomize() calls that pose the same problem (e.g., randomized the same way repeatedly in a CPU opcode generator). The trade-off is that the elaboration step itself can require a significant amount of memory and CPU time, because the full set of legal solutions must be constructed up front.
Memory and Performance Characteristics
- Memory: Because the BDD solver materializes the entire solution space, it is typically the memory-dominant mode. Memory use is reported separately in the VCS Constraint Profiler because RACE memory use is generally not a limiting factor and is usually not measured for comparison.
- Runtime: The cost of elaborating the solution space is amortized over many
randomize()calls. It pays off when the samerandomize()call is invoked repeatedly. - Architectural fit: The BDD solver works well for specific architectures, particularly when the randomize problem does not consume excessive memory and the same
randomize()call is executed many times — a pattern that is common in CPU opcode generation.
Comparison with the RACE Solver
When a stimulus architecture was refactored from a single-class to a multiple-class layout (choosing an opcode category first, then randomizing within it), both solvers benefited, but the magnitudes differed:
- RACE solver: 4x speedup moving from single-class to multiple-class.
- BDD solver: 2x speedup moving from single-class to multiple-class.
- The multiple-class layout also produced significantly better BDD-solver memory consumption than the single-class layout.
The performance gains are attributed to the smaller set of variables and constraints present in the multiple-class implementation, which reduces the size of the problem the solver must work on. In the published study, the multiple-class implementation had roughly 7x fewer constraints than the original single-class implementation.
Use Cases
The BDD solver is documented in the context of generating stimuli for complex instruction sets:
- AMD microcode / x86 opcodes: The BDD solver mode is used to randomize x86 instructions, where the same opcode generation problem is solved many times and the amortization of the upfront solution-space elaboration is favorable.
- General constrained-random verification: Any flow in which a
randomize()problem of manageable size is called many times can benefit from the BDD solver's caching behavior.
Integration With Other Tools and Techniques
- The VCS Constraint Profiler reports per-
randomize()and per-partition CPU runtime and memory data. Memory profiling is highlighted as especially valuable when the BDD solver is enabled, because BDD elaboration is the main source of memory pressure; cumulative randomize CPU runtime, individual randomize CPU runtime, individual partition CPU runtime, and memory data are all reported. - The VCS 2009.12 release added a testcase extraction feature that automatically extracts the slowest partition from each
randomize()call, which is useful for diagnosing BDD-solver hot spots. - The BDD solver is employed by Hierarchical Constrained-Random Stimulus Generation techniques, in which a higher-level randomize call (e.g., choosing an opcode category) gates a lower-level randomize call (e.g., the operands/encodings of that category), naturally creating the pattern of repeated, narrowly-scoped
randomize()calls that the BDD solver serves efficiently.
When to Choose the BDD Solver
Based on the documented evidence, the BDD solver is the preferred mode when:
- The constraint problem is small enough that elaborating the full solution space fits in memory.
- The same
randomize()problem is invoked many times, so the cached solution space is reused. - The application domain (such as CPU opcode generation) calls for solving the same structured problem repeatedly across thousands of randomized instructions.
The default RACE solver is generally a better fit for one-shot, larger, or more varied randomize problems where the cost of building the full BDD solution space is not amortized.