Overview
A verification engine is the component that drives and evaluates tests or proof obligations against a correctness criterion. In the TestRIG RISC-V CPU-testing workflow, the verification engine is represented by QCVEngine, described as the “TestRIG Verification Engine.” QCVEngine leverages Haskell’s QuickCheck library to generate instruction sequences, compare execution traces, and shrink failing examples.
Role in TestRIG
TestRIG uses Direct Instruction Injection (DII) and RVFI traces to compare behavior across implementations. DII directly specifies the instruction sequence expected in the output trace and does not associate instructions with memory addresses. This decouples the instruction stream from control flow, which simplifies sequence generation and shrinking because the program counter does not affect the instruction stream.
QCVEngine takes advantage of this structure by using unmodified QuickCheck utilities. Its core test function receives a list of instructions, sends them over two DII sockets, collects RVFI traces from those sockets, asserts that the traces match, and returns a pass/fail result to QuickCheck.
Test generation and shrinking
QCVEngine provides generators for arbitrary instruction sequences. It also uses convenience functions that resemble the RISC-V ISA manual syntax and tailored generators for instruction fields, including generator choices intended to promote register reuse. QuickCheck discovers and uses these generators through Haskell’s type system.
The engine also supports more targeted generation strategies. The cited TestRIG work describes targeted generators for simple instruction-set subsets and generators based on templates of varying complexity, including templates intended to reach deeper states such as virtual-memory mappings and cache conflicts.
Because QuickCheck searches for failing inputs and shrinks them, QCVEngine can provide reduced counterexamples. The TestRIG paper reports that this tight cycle of reduced counterexamples helped add functionality to Ibex within a month.
Trace replay and equivalence checking
The TestRIG authors report using QCVEngine-related trace features to replay recorded test-suite examples, including examples from riscv-tests and RISCV-DV, while adding full trace-equivalence checking with shrinking. They also report capturing traces of an operating system booting on a model implementation, then using those traces to aid bring-up on implementations, with instruction shrinking helping to highlight problems.
Current limitations and future direction
The TestRIG paper characterizes QCVEngine as the initial TestRIG verification engine and says its generators were still rudimentary at the time of writing. Although its Haskell infrastructure supports rich and complex generators, the authors identify virtual-memory, cache-testing, and floating-point generators as areas that could be enriched with more intelligent directed-random templates for reaching deeper states.
The same paper also identifies memory-model and concurrency testing as future work. It proposes that RVFI-DII instruction streams with specified timestamps, injected into multiple shared-memory cores, could precisely stimulate concurrency behavior. That use case would require a more advanced verification engine that checks RVFI traces not only for equivalence but also against higher-level memory-model semantics.