Overview
Object-Oriented Constraint Partitioning is a technique for structuring constrained-random generators by moving global constraints into a base class and placing category-specific constraints into derived subclasses. In the AMD/Synopsys microcode stimulus generator example, the original generator used one opcode class containing constraints for all opcodes; the later object-oriented version used a base instruction class plus child classes for groups of related opcodes. This hierarchical partitioning reduced the size of each randomization problem, drastically reduced memory requirements, and increased performance.
Motivation
A single-class constrained-random generator is flexible because constraints can be applied across any data members in the class. However, the evidence describes a single opcode class with approximately 100 random variables and 800 constraint equations, which made the randomization problem large and potentially slow for the constraint solver.
The partitioned approach addresses this by splitting the opcode class into multiple smaller classes. Opcode categories were chosen to map well to the knobs or weights exposed through the test interface, allowing tests to continue controlling high-level instruction distributions while reducing the complexity of each solve.
Architecture
The referenced generator used two layers:
- Upper layer: a SystemVerilog random sequence with weighted knobs controlling the distribution of high-level items.
- Lower layer: an opcode class or subclass randomized with additional constraints and weights supplied by the upper layer.
In the object-oriented partitioned design:
- A base instruction class contains data members common to all child classes.
- The base class also contains common methods, such as methods used to set, print, and pack data.
- Data members and constraints that apply to every opcode are placed in the base class.
- Each opcode-category subclass contains constraints specific to that opcode group.
- Each subclass can retain the same general implication-constraint style as the original single-class generator, but only for its own opcode category.
Randomization flow
The upper-layer random sequence chooses an opcode category first using knobs or weights. Once the category is known, the generator can allocate the correct subclass object and randomize that smaller object. This design works cleanly when tests control only high-level knobs and do not directly constrain fields inside lower-level subclasses.
If test-layer constraints directly control lower-level subclass fields, the evidence notes that subclass selection decisions must be made before randomizing the subclass. In that situation, a wrapper class may be required. The wrapper class constrains the variables controlled by the tests, is randomized first, and then the correct subclass object is allocated and randomized in a second generation phase.
Relationship to solver-level partitioning
The evidence also describes Synopsys VCS constraint solver profiling. VCS can partition a randomize call into independent partitions when unrelated random variables appear in the same randomize call. This solver-level partitioning allows unrelated variables to be solved independently and can be analyzed using profiler views such as cumulative randomize calls, per-randomize-call data, and per-partition data.
Object-Oriented Constraint Partitioning is complementary to this solver behavior: it reduces the problem size at the generator architecture level, while the solver may further partition unrelated variables within a randomize call.
Benefits
Reported benefits in the evidence include:
- smaller randomization problems by splitting one large opcode class into multiple category-specific classes;
- reduced memory requirements through hierarchical partitioning;
- increased performance compared with the large single-class formulation;
- continued use of weighted knobs to control opcode-category distributions; and
- better profiling visibility through VCS constraint profiler reports for runtime and memory.
Implementation considerations
Use this technique when a constrained-random class has grown into a large monolithic solve problem with many random variables and constraints. A practical implementation pattern is:
- Identify fields, methods, and constraints common to all generated items.
- Move those shared members into a base class.
- Divide generated items into categories aligned with test-level knobs or weights.
- Implement one subclass per category, containing only category-specific constraints.
- Randomize the category first, allocate the matching subclass, and then randomize the subclass object.
- If tests directly constrain subclass internals, introduce a wrapper randomization phase to choose the subclass consistently before solving subclass-specific constraints.