Overview
In the Ibex core verification flow, Debug Mode is a privileged execution state entered by the core when it receives an external debug request. Verifying correct entry into Debug Mode, and verifying that the appropriate Control and Status Registers (CSRs) are updated, is a distinct concern from normal instruction-level correctness checking.
The RISC-V Debug Specification exists precisely because hardware debug needs a common architecture across diverse implementations, and Debug Mode is the architectural vehicle by which a RISC-V core transitions into that debug-specific execution context.
Verification Challenge
The standard RTL/ISS co-simulation flow compares every register writeback produced by the core against a trace log produced by a golden-model ISS. Both the core trace log and the ISS trace log are parsed to collect information about every register writeback, and the two sets of writeback data are compared to verify that the core writes the correct data to the correct registers in the correct order.
This works well for ordinary execution, but it breaks down for situations driven by external stimulus such as interrupts and debug requests.
ISS models can simulate traps that arise from architectural exceptions, but they cannot model traps that arise from external stimulus. This means there is no reference trace to compare against once the core enters Debug Mode or an interrupt handler, and the ISS trace log will not contain any execution information for code that runs in the debug ROM or in any interrupt handler.
Projects frequently bring up lockstep comparison, trace hooks, and Debug Mode validation too late, after they have already focused on instruction execution. By that point, missing retirement comparators, poor trace visibility, or incomplete debug-state checking can turn what would have been a one-day bug into a one-week investigation. Effective lockstep co-simulation therefore depends not only on comparison logic but also on a well-defined debug and trace strategy that provides sufficient visibility into internal state, control flow, and exception-handling paths, so that the project knows early on how Debug Mode will be entered and observed and which CSRs and memory accesses must be checked.
Verification Approach
To provide support for these scenarios and verify that the core has:
- entered the proper interrupt handler,
- entered Debug Mode properly, and
- updated any CSRs correctly,
the handshaking mechanism provided by the RISCV-DV instruction generator is heavily used. This mechanism effectively allows the core to send status information to the testbench during program execution, enabling any analysis required to increase verification effectiveness. The signature address used by the Ibex testbench for this handshaking is 0x8ffffffc.
A small set of API tasks provided in dv/uvm/core_ibex/tests/core_ibex_base_test.sv enable easy and efficient integration of this mechanism into the test environment, and real-simulation usage can be inspected in dv/uvm/core_ibex/tests/core_ibex_test_lib.sv. The mechanism is extensively used to provide runtime verification for situations involving external debug requests, interrupt assertions, and memory faults.
Adapted Trace Comparison
Because the handshake mechanism already provides a correctness check for these external-stimulus scenarios, the trace log comparison is adjusted rather than abandoned. A naive per-writeback comparison would produce incorrect results, since the ISS trace log will not contain any execution information for code running in the debug ROM or in any interrupt handler code.
As a result, only the final values contained in every register at the end of the test are compared against each other. Code executed in the debug ROM and trap handlers is not expected to corrupt register state in the rest of the program, so this end-of-test comparison remains a valid correctness signal.
RISCV-DV Debug Support
The RISCV-DV instruction generator provides first-class support for Debug Mode. Among its supported features it lists "Debug mode support, with fully randomized debug ROM", which provides a source of randomized Debug Mode stimulus for verifying Debug Mode entry, debug ROM execution, and CSR updates. RISCV-DV also supports handshake communication with the testbench and co-simulation with multiple ISSes (spike, riscv-ovpsim, whisper, sail-riscv), all of which underpin the Ibex Debug Mode verification approach.
Driving the Flow
The full end-to-end RTL/ISS co-simulation flow that exercises Debug Mode scenarios is driven by the Makefile in dv/uvm/core_ibex/Makefile. Common invocations include:
make— run a full regressionmake OUT=xxx— run a full regression, redirect the output directorymake TEST=riscv_machine_mode_rand_test ITERATIONS=1— run a single testmake TEST=riscv_machine_mode_rand_test ITERATIONS=1 SEED=123 WAVES=1— run a test with a specific seed and dump waveformsmake ... VERBOSE=1— verbose loggingmake ... LSF_CMD="bsub -Is"— run multiple tests in parallel through LSFpython3 sim.py --help— get the command reference of the simulation scriptmake gen— generate the assembly tests onlymake TEST=xxx compile,rtl_sim— compile and run RTL simulation onlymake COV=1— run a full regression with coverage