Microcode Test Sequence Generation
Definition
Microcode test sequence generation is the practice of automatically producing microcode instruction sequences (stimuli) for the functional verification of microprocessor designs. Rather than authoring individual directed tests by hand, automated generators randomize instruction fields in a controlled manner so that coverage of opcodes and instruction attributes is spread across the meaningful values of the design.
Motivation
As microprocessor designs have grown in complexity, hand-written directed tests have largely been supplanted by automated random test generators that cover the stimulus space more efficiently. These generators create microcode test sequences that emphasize the distribution of stimuli across all meaningful values for opcodes and other instruction attributes, with the goal of reaching corner cases that would be tedious or impossible to enumerate by hand.
Traditional approaches that randomize instruction fields sequentially suffer from several drawbacks:
- Verbose, redundant generation code
- Limited control over the distribution of generated values
- Inefficient coverage of meaningful attribute combinations
Generator Architecture
A typical microcode stimulus generator is organized into two layers:
- Upper layer — a SystemVerilog random sequence with weighted knobs that controls the distribution of high-level items (for example, which families of instructions are exercised and in what proportion).
- Lower layer — the opcode class that is randomized with constraints and weights provided by the upper layer. The opcode class models one instruction and its attributes.
Tests are expressed as a set of weighted values that direct the generator to a required mix of instructions. The constraint solver applies those weights directly to the generator layer, controlling the distribution of the various opcode types produced.
Single-Class Randomization
The simplest coding style places every opcode in a single class. This style is very flexible because constraints may be applied between any of the data members in the opcode class, allowing cross-field implications that guarantee only legal instructions are generated. The trade-off is randomization speed: the constraint solver must work with a large problem consisting of many random variables and a complex set of constraints. In one published implementation, the opcode class contained approximately 100 random variables and 800 constraint equations, with the opcode type acting as a key data member that controlled which kind of instruction was produced.
Hierarchical (Multi-Class) Randomization
To shrink the randomization problem and reduce memory consumption, the opcode class is partitioned into multiple smaller classes using an object-oriented design:
- A base class holds global constraints that apply to every opcode.
- Sub-classes are derived to define groups of related opcodes that share similar constraints.
By partitioning the constraints hierarchically into smaller groups of opcodes, the memory required by the solver is drastically reduced, and overall performance increases. This hierarchical constrained-random formulation is the implementation technique that scales the approach beyond what a single monolithic class can deliver.
Implementation Language and Tooling
SystemVerilog's constraint language constructs provide a clean, concise way to describe microcode instructions in terms of their possible attribute combinations and to control the per-field value distribution precisely. A constraint solver such as the Synopsys VCS constraint solver can directly apply the user-supplied weights to the generator layer, producing biased but legal stimuli that hit corner cases.