Opcode Generator
An Opcode Generator is a constrained-random verification component used to generate legal microcode instruction sequences with controlled distributions across opcodes and instruction attributes. In the described implementation, the generator was built in SystemVerilog and used the Synopsys VCS constraint solver to improve stimulus distribution, bias generation toward corner cases, reduce memory usage, and improve performance compared with sequential instruction-field randomization approaches.[1]
Background
As microprocessor designs became more complex, verification teams moved away from hand-written directed tests toward automated random test generators that can cover a larger stimulus space more efficiently.[1] These generators create microcode test sequences and emphasize distributing stimulus across meaningful opcode values and other instruction attributes.[1]
Traditional approaches randomized instruction fields sequentially, but this could produce verbose and redundant code and gave limited control over value distributions.[1] The opcode generator addressed these limitations by using hierarchical constrained-random generation in SystemVerilog, allowing instruction legality and field distributions to be expressed as constraints and weights.[1]
Design Goals
The generator architecture was intended to:
- Generate legal opcode and instruction-attribute combinations.[1]
- Provide precise control over distributions for individual fields.[1]
- Support weighted biasing to hit corner cases.[1]
- Reduce memory consumption by partitioning constraints hierarchically.[1]
- Improve generation performance through smaller randomization problems.[1]
- Allow test writers to control instruction mix using high-level knobs or switches.[2]
SystemVerilog Constraint-Based Modeling
SystemVerilog constraint language constructs were used to describe microcode instructions in terms of valid combinations of instruction attributes.[1] This enabled concise expression of opcode legality and controlled distribution of field values.[1]
An initial prototype used a single class containing constraints for all opcodes.[1] This design overcame problems associated with sequential field randomization, but it created a large constraint-solving problem because all opcode-related variables and constraints were contained in one class.[1]
Generator Architecture
The opcode generator used a two-layer architecture:
- Upper layer — implemented using a SystemVerilog random sequence construct. This layer used weighted knobs to control the distribution of high-level instruction categories.[1]
- Lower layer — implemented as opcode classes randomized with additional constraints and weights supplied by the upper layer.[1]
Tests supplied sets of weighted values that directed the generator toward the desired instruction mix.[1] The VCS constraint solver applied these weights at the generator layer to control the distribution of opcode types.[1]

Figure: Test/generator architecture.[1]
Single-Class Randomization
The simplest implementation placed all opcodes into a single opcode class.[1] This approach was flexible because constraints could be applied between any data members in the class.[1] However, it also produced a large and complex constraint problem, which could slow randomization.[1]
In the described implementation, the single opcode class contained approximately:
- 100 random variables.[1]
- 800 constraint equations.[1]
The class included random variables and implication constraints to ensure that generated opcodes were legal.[1] A key data member was the opcode type, which controlled which instruction type was generated.[1]

Figure: Single-class architecture.[1]
Advantages
- High flexibility for cross-field constraints.[1]
- Direct expression of legality rules across the entire opcode model.[1]
Disadvantages
- Large randomization problem.[1]
- Many random variables and constraints in a single solve.[1]
- Potentially slower constraint-solving performance.[1]
Multi-Class Randomization
To reduce the size of the constraint-solving problem, the opcode class was split into multiple smaller classes.[2] Opcodes were divided into categories that mapped well to the knobs or weights exposed through the test interface.[2]
The multi-class design used:
- A base instruction class containing data members common to all child classes.[2]
- Common methods for setting, printing, and packing data.[2]
- Global constraints shared by all opcodes.[1]
- Child classes for related opcode groups with category-specific constraints.[1][2]
Each opcode-category child class used a structure similar to the original single-class design, with implication operators based on opcode type.[2]

Figure: Multi-class architecture.[2]
Benefits
Partitioning constraints into smaller opcode groups drastically reduced memory requirements and increased performance.[1] The design also aligned opcode categories with the high-level knobs used by tests, making the generator easier to control at the test layer.[2]
Architectural Considerations
The instruction generator was controlled by knobs or switches that allowed test writers to generate constrained stimulus.[2] In the described architecture, the test layer did not directly constrain items inside lower-level subclasses.[2] Instead, the upper-layer random sequence selected an opcode category first, then allocated the correct subclass object and inserted it into the sequence.[2]
If a test layer directly controls lower-level subclass items, the subclass-selection decision must be made before the lower-level object can be randomized.[2] In that case, a wrapper class may be required to constrain variables controlled by the tests.[2] The wrapper would be randomized first, after which the correct subclass object would be allocated and randomized in a second generation phase.[2]

Figure: Wrapper-class concept for two-phase generation.[2]
Constraint Profiling
The VCS constraint profiler was used to analyze generator runtime and memory performance.[2] It reported runtime data in three categories:
| Profiling category | Purpose |
|---|---|
| Cumulative randomize calls | Shows total CPU impact across repeated randomization calls.[2] |
| Per randomize call | Shows the slowest individual randomize invocations.[2] |
| Per partition | Shows performance of individual constraint partitions created by VCS.[2] |
The profiler showed that a randomization call in op_gen.sv at line 4308 had the greatest cumulative CPU-time impact because it was executed 7,104 times and consumed 44 seconds of CPU time.[2] Another randomize call took 3.2 seconds individually, but because it occurred only twice, optimizing it would have had limited total simulation impact.[2]
VCS can divide a randomize call into multiple partitions when unrelated random variables occur in the same call.[2] This lets unrelated variables be solved independently and helps identify the slowest constraint partitions.[2]

Figure: Cumulative randomize CPU runtime profile.[2]

Figure: Individual randomize CPU runtime profile.[2]

Figure: Individual partition CPU runtime profile.[2]
Summary
The opcode generator used hierarchical constrained-random generation to improve instruction stimulus generation for complex microprocessor verification.[1] A single-class implementation provided flexibility but created a large constraint-solving problem.[1] Refactoring the model into a base class and multiple opcode-category subclasses reduced memory requirements and improved performance while preserving controlled opcode distribution through upper-layer weighted knobs.[1][2] Constraint profiling in VCS helped identify high-impact randomization calls and partitions for optimization.[2]
References
[1]: Gregory Tang and Rajat Bahl, AMD, Inc.; Alex Wakefield and Padmaraj Ramachandran, Synopsys Inc. Evidence ID: 4de14aa6-a0c6-4115-8dcd-2be6148018dc.
[2]: Gregory Tang and Rajat Bahl, AMD, Inc.; Alex Wakefield and Padmaraj Ramachandran, Synopsys Inc. Evidence ID: 78097f25-52a5-4808-ad76-fce988303d2d.