Overview
Instruction Stream Generation is a test-generation technique for processor verification in which streams of instructions are produced and supplied to a processor model, simulator, or RTL core. In the cited RISC-V cross-level verification evidence, the stated goal is to generate an endless and unrestricted instruction stream for co-simulation, with the RTL core and instruction-set simulator (ISS) expected to behave identically in their observable architectural state, especially register updates. [c1] [c2]
Role in RISC-V verification and training
The cited evidence discusses instruction stream generation in the context of RISC-V. RISC-V includes mandatory base integer instruction sets such as RV32I, RV64I, and RV128I, plus optional extensions such as M for integer multiplication/division and C for compressed instructions. Instructions are grouped into classes such as computational, load/store, and branch/jump, and use register operands and immediates. [c3]
For the verification setup described in the evidence, unrestricted generation is intended to exercise broad processor behavior. The approach supports memory-access instructions, jump instructions including self-loops from on-the-fly generation, and special RISC-V CSR access instructions. [c4]
Industry training material lists Instruction Stream Generation alongside an overview of the open-source riscv-dv tool as part of an introductory CPU and CPU verification curriculum that also covers functional coverage modeling, test generation, and CPU verification strategies at unit, CPU, and system levels. [c5]
A broader RISC-V verification training programme cited in the evidence positions instruction stream generation together with riscv-dv, constraints, functional coverage, UVM workflows, architectural verification, and software-driven SoC testing, and lists instruction stream generation among the topics of a follow-on course on best-practice CPU and SoC verification that also covers architectures and microarchitectures, ISA and toolchains, CPU integration, SoC feature verification, debug, coverage, and sign-off. [c7] [c8]
A practical blog on verifying custom RISC-V instructions describes extending the generator with constraints that describe when an instruction is legal and stimulus rules that create useful surrounding context, because many processor failures are not visible when an instruction is executed in isolation. Surrounding context that the evidence recommends includes data dependencies on the preceding instruction, immediate consumers, rd/rs aliasing, branches around the custom instruction, exceptions and interrupts near execution, back-to-back custom instructions, transitions between custom and standard instructions, privilege transitions where relevant, and memory-system activity for stateful or memory-related operations. [c6]
Generation strategy
The baseline generator described in the evidence fully randomizes generated instructions. This creates a foundation for testing because unrestricted generation can include legal and illegal encodings; the cited paper notes that a fully random 32-bit word is most likely an illegal instruction because of the significantly larger state space of illegal instructions. [c9]
To steer generation toward interesting cases, the cited cross-level testing paper describes three modifications applied to a fully randomized instruction:
- Opcode injection. With high probability (98%), a random valid opcode is injected into the randomized 32-bit word, while the remaining instruction fields stay randomized. This yields a legal instruction of the selected class. [c9]
- Field mutation. With smaller probability (20%), a mutation rule drawn from the RISC-V instruction format is applied to the fields. Rules inject special immediate values such as {MIN, -1, 0, 1, MAX}, mutate RD to zero (since x0 is hardwired in RISC-V), make RD equal to RS1 and/or RS2, make RS1 equal to RS2, or mutate the CSR selector field to a supported CSR. [c9]
- Instruction sequences. With low probability (1%), the generator begins a new sequence by choosing a random sequence generator and returning the first instruction of that sequence. A sequence consists of a fixed number of instructions. [c9]
The cited paper illustrates opcode injection and mutation using ADDI as an example: starting from a fully randomized 32-bit word (A), injecting the ADDI opcode produces a randomized ADDI with randomized RD, RS1, and immediate (B); then a mutation rule that sets RD = RS1 produces a special ADDI whose fields are still randomized but with RD equal to RS1 (C). [c9]
The combined algorithm (Fig. 5 of the cited paper) is therefore: optionally start a sequence (1%); otherwise, generate a random 32-bit word (Line 11), then with 98% probability inject a random valid opcode (Line 12–14), and with 20% probability additionally apply a random field mutation (Line 15–17). The single returned instruction may either be an independent instruction or the first instruction of a chosen sequence. [c9]
Co-simulation and instruction matching
Unrestricted instruction streams introduce a synchronization problem when the same stream must be supplied to both an RTL core and an ISS. A pipelined RTL core can prefetch instructions, but prefetched instructions may not execute after jumps or traps. As a result, the ISS may fetch a different sequence of program counters than the RTL core if matching is handled naively. [c10]
The evidence describes an instruction matching algorithm that stores generated instructions, together with their program counters, in a queue of pending instructions fetched by the RTL core but not yet consumed by the ISS. The ISS fetch path then searches this pending queue for a matching (PC, instruction) pair. If a match is found, the instruction is returned; otherwise, a mismatch is reported. [c10]
RTL completion detection
The evidence identifies instruction-completion detection in a pipelined RTL core as a key implementation challenge. Illegal instructions may bypass pipeline stages and may not trigger regular register write-back notifications. Pipeline flushes, jumps, traps, and multi-cycle operations can also introduce delays and gaps. To address this, the described setup uses a core adapter that observes internal core signal changes, especially pipeline signals, and notifies the test controller when the RTL core completes an instruction while preserving correct order for illegal instructions. [c10]
Integration with riscv-dv and the OpenHW flow
The cited Imperas slides describe an OpenHW RISC-V DV flow that combines a UVM environment with a RISC-V reference model. In this flow, a random instruction generator (Google-generated) compiles programs with GCC into a test-program environment, which is loaded into instruction memory and memory-mapped virtual peripherals. The UVM RISC-V Reference Model executes the same program as the CORE-V CV32/64 RTL, and a step-and-compare mechanism checks that all CSRs, GPRs and the PC agree. Re-use of assertions from formal verification is supported. The flow uses the Imperas RISC-V reference model as the golden reference and extends this flow to support a step-and-compare DV methodology. [c14]
The slides also describe extending the Imperas RISC-V reference model with Vector and Bitmanip extension instructions for inclusion in the functional coverage model (this extension work is stated as not yet publicly released at the time of the talk). [c14]
A case study presented in the same Imperas slides targets the lowRISC Ibex (previously known as zero-risky / PULP), a small 32-bit RISC-V CPU core supporting RV32IMC / EMC with a two-stage pipeline. Its block diagram shows an instruction fetch stage with prefetch buffer and compressed-instruction decoder feeding an ID stage with decoder, controller and register file, and an execute block containing the ALU, multiplier/divider, CSR module and load/store unit. The core exposes a debug request interface and presents 32-bit interfaces to instruction and data memories. [c14]
Comparison with other generation approaches
The evidence contrasts unrestricted generation with several existing RISC-V test-generation approaches:
- RISC-V Torture Test is described as a Scala-based framework that generates tests from randomized instruction-sequence templates and supports several RISC-V ISA extensions. However, approaches based on predefined instruction-sequence building blocks are said to have limited coverage and to not support illegal instructions or exceptions. [c11]
- Coverage-guided fuzzing at the ISS level loosens some instruction-stream generation restrictions but is reported to have problems with branches and jumps, platform-dependent CSR and memory accesses, and to produce comparatively small test suites. [c11]
- riscv-dv continuously generates RISC-V instruction streams from constrained-random descriptions, with each instruction stream representing a test case. The cited evidence says it supports custom processor targets, user extensions, additional instruction streams, directed assembly/C tests, ISS-based co-simulation, multiple instruction-set simulators, and instruction-generation coverage. Generated streams are restricted to avoid infinite loops and platform-dependent memory access operations. The configuration mechanism allows engineers to adapt the generator to processor capabilities and add new instruction streams rather than modifying upstream classes unnecessarily. [c12] [c13]
Practical significance
Within the cited co-simulation setup, Instruction Stream Generation is significant because unrestricted on-the-fly generation can exercise legal instructions, illegal instructions, memory behavior, jumps, traps, and CSR accesses. The matching and core-adapter mechanisms are necessary to make this practical for pipelined RTL cores and ISS comparison without relying on the RTL core under test to define the executed stream. [c2] [c4] [c10]