Skip to content
Rivoryxa Technologies

Proving a single step stall in a RISC-V core never happens

A coverage hole in a RISC-V core's controller during debug single step. We proved with formal verification that the uncovered branch can never run.

Open source RISC-V core, coverage hole 1007Outcome: Dead code, proven

1The issue

Line 675 of the core's controller checks whether the decode stage is ready while the core runs one instruction in debug single step. The branch for a stalled decode stage was never covered. The core's maintainers waived it after an equivalence check showed that removing the lines leaves the outputs unchanged, but there was no direct proof that the stall cannot happen.

The points on the issue

Single step, decode stage readyCan happen. A formal search reaches it in 16 cycles.
Single step, decode stage stalledCan never happen. Proven with formal verification.

2What we did

  1. 1Found why. Single step can only be turned on in debug mode, and the core leaves debug mode by flushing its pipeline. The stepped instruction is the first one after the flush, so nothing older is left to stall it.
  2. 2Proved this with an unbounded formal proof, which checks every reachable state, not only the ones a test happens to try.
  3. 3Checked the proof is meaningful. The single step block is reached in 16 cycles, and a stalled decode stage is reached in 7 cycles outside single step, so the stall path exists, just not under single step.

3Result

The stalled branch is dead code and can be excluded from coverage, backed by an open source proof in place of the equivalence argument.

The proof shows the branch cannot be reached in the build without the floating point unit. It does not show the logic around it is correct.

Have a coverage hole like this one?

Describe the problem in a few lines. An engineer replies, and we agree the scope together.

Discuss a verification problem