Skip to content
STIMSMITH

Instruction Opcode Class Hierarchy

Concept WIKI v1 · 6/5/2026

A hierarchical class-based modeling approach for instruction opcodes in constrained-random verification environments, in which a base instruction class encapsulates data members and methods common to every opcode, and derived child classes encode constraints specific to individual opcode categories. This hierarchy is used to scale the opcode randomization problem down into smaller, more tractable per-category randomize calls and to enable knob-driven stimulus selection at the test layer.

Instruction Opcode Class Hierarchy

Overview

In SystemVerilog constrained-random verification, the set of legal instruction opcodes for a processor is typically modeled as a class hierarchy rather than as a single monolithic randomizable object. The hierarchy is centered on a base instruction class that holds the data members, constraints, and methods (set, print, pack) common to every opcode, and a set of opcode category child classes that extend the base class with category-specific constraints and fields. Randomization is performed against an instance of whichever child class corresponds to the selected opcode category, which keeps each individual randomize() call small and allows the verification environment to choose opcode families through weighted test-layer knobs.

Motivation: From a Single Class to a Hierarchy

The initial implementation placed all opcodes into a single large randomizable class. The combined implication structure across every opcode type made the resulting constraint problem large, and the VCS constraint solver spent disproportionate time on the resulting randomize() calls. Splitting the opcodes into a hierarchy of smaller, category-scoped classes reduces the per-call problem size and localizes opcode-specific implications to the class where they are meaningful.

Structure of the Hierarchy

Base Instruction Class

The base class is the parent of every opcode category class. It owns:

  • All data members common to every opcode (those that appear in every instruction word, regardless of category).
  • All constraints that apply universally to every opcode.
  • Most of the methods used to set, print, and pack instruction data.

Placing shared fields and constraints in the base class means each child class only needs to add what is unique to its opcode category, and shared behavior is implemented once and inherited by all categories.

Opcode Category Child Classes

Each child class corresponds to a category of opcodes (for example, the categories used in the AMD microcode generator). Within a child class:

  • Constraints specific to that opcode category are declared.
  • A structure similar to the original single-class code is preserved, with a set of implication operators keyed on the opcode type to express the legal encodings for that family.

Because each child class randomizes only the variables relevant to its category, the constraint solver works on a smaller problem per call, and the partitions VCS can extract are typically tighter.

Architectural Integration with the Test Layer

The instruction generator is driven by a set of knobs (switches) exposed to the test layer. The hierarchy is exercised as follows:

  • The upper-layer random sequence is controlled by knobs only and chooses the opcode category first. This allows the correct object type to be allocated at that location so the chosen sub-class can be inserted into the sequence.
  • The test layer has no direct constraints on items inside the sub-classes, which keeps the two-phase "pick category, then randomize category" flow clean.

When the test layer does need to constrain variables that live in the sub-classes, the hierarchy must be fronted by a wrapper class:

  • The wrapper class constrains all variables that the test layer wants to control and is randomized first.
  • After the wrapper resolves, the decision of which sub-class to allocate is made and the appropriate sub-class object is randomized in a second phase.

This two-phase flow is required because the choice of sub-class and the values of sub-class variables are otherwise inseparable in a single randomize() call.

Performance Profiling of the Hierarchy

The VCS constraint profiler is used to analyze the runtime and memory behavior of the generators that consume the hierarchy. The profiler reports three categories of runtime data, illustrated in the source article:

  • Cumulative randomize calls — total CPU time consumed by each randomize() site, summed across all invocations. In the AMD example the dominant site is op_gen.sv:4308, which executes quickly per call but is invoked 7,104 times, accumulating roughly 44 seconds of CPU time.
  • Per-call (individual) randomize calls — the slowest individual invocations, often correlating with the cumulative table. The same site op_gen.sv:4308@162 (line 4308, 162nd execution of that line in a loop) is reported as the slowest single call at 3.2 seconds, but with only two such calls in the entire simulation, optimizing it has little overall impact.
  • Per-partition randomize calls — VCS splits a randomize() call into partitions when unrelated random variables coexist, allowing them to be solved independently. The slowest partitions typically correlate with the slowest individual calls and with the cumulative hotspots, and are the natural targets for further hierarchy or constraint decomposition.

Similar profiler output is available for memory consumption.

Why a Hierarchy Helps the Solver

Splitting one large randomizable class into a base class plus per-category child classes gives the constraint solver three concrete benefits:

  1. Smaller per-call problem size, because each child class carries only the constraints for its own opcode family.
  2. Cleaner partition boundaries, because the unrelated variables that previously coexisted in one big class now live in separate sub-class objects.
  3. Localized implications, because opcode-type-keyed implications live in the child class where they apply, rather than being entangled in a global implication set.

These benefits are what the hierarchy is designed to deliver, and the profiler metrics above are how that delivery is measured in practice.

Implementation Techniques That Use This Hierarchy

  • Hierarchical Constrained-Random Stimulus Generation — the overarching methodology that uses class inheritance, knobs, and two-phase randomization to scale constrained-random stimulus production for large instruction sets. The instruction opcode class hierarchy is the concrete class structure on which that methodology rests.
  • Multi-Class Randomization — the specific refactoring in which the single opcode class is split into a base class and per-category child classes, the architectural pattern described above.

CITATIONS

8 sources
8 citations
[1] To reduce the size of the randomization problem, the opcode class was split into multiple smaller classes, with opcodes divided into categories mapped to the knobs or weights in the test interface. Generating AMD microcode stimuli using VCS constraint solver
[2] A base instruction class contains all data members common to every child class and most of the set/print/pack methods, with universal data members and constraints placed into that base class. Generating AMD microcode stimuli using VCS constraint solver
[3] Each opcode category child class contains constraints specific to that set of opcodes, with a similar structure to the single-class code, including a set of implication operators based on the opcode type. Generating AMD microcode stimuli using VCS constraint solver
[4] The instruction generator is controlled by knobs/switches; the test layer has no direct constraints on sub-class items, and the upper-layer random sequence (knob-controlled) chooses the opcode category first so the correct object type can be allocated and added to the sequence. Generating AMD microcode stimuli using VCS constraint solver
[5] If the test layer must directly control items in lower levels, a wrapper class is required: it constrains all test-controlled variables, is randomized first, and then the correct sub-class object is allocated and randomized in a second phase. Generating AMD microcode stimuli using VCS constraint solver
[6] The VCS constraint profiler provides runtime details in three categories: cumulative randomize calls, per randomize call, and per partition. Generating AMD microcode stimuli using VCS constraint solver
[7] In the AMD example, the call with the greatest overall CPU-time impact is in op_gen.sv at line 4308: 7,104 invocations consuming roughly 44 seconds of CPU time, while the slowest single call at op_gen.sv:4308@162 takes 3.2 seconds but occurs only twice in the entire simulation. Generating AMD microcode stimuli using VCS constraint solver
[8] VCS partitions a randomize call into several partitions when unrelated random variables occur within the same call, allowing those variables to be solved independently; the partition table often correlates with the individual and cumulative randomize tables. Generating AMD microcode stimuli using VCS constraint solver