RV32IM Under the Prover — a RISC-V instruction visualizer for zkVM designers

RV32IM Under the Prover

A single-page RISC-V machine you can step one data transfer at a time. Not one instruction at a time — one value moving between two components: the register file lights up as it sends, the ALU lights up as it receives, the wire animates, and the value itself rides across on the bus. Every box on the diagram shows what it is currently holding, and keeps holding it until the next instruction overwrites it.

Launch the visualizer Opens in a new tab · no install, no network · works on desktop and mobile

Why another RISC-V simulator? The existing ones answer “what does this program do”. This one answers where did that value come from, and what happened to it on the way — which is the question you actually have when an instruction's behaviour surprises you. It steps at the granularity of a bus transfer, so nothing is ever left to be inferred from an arrow.

Everything on screen is architectural. There is no model of any particular implementation here: no pipeline, no cache, no arithmetization. Just the machine the manual describes.

The instruction set

The machine implements RV32I plus the M extension — the 32-bit base integer instructions and multiply/divide, 45 opcodes, every one of which the pre-loaded program executes at least once in 119 instructions. The omissions are deliberate.

Not implementedWhy
RV64I widthsThis is a 32-bit machine. The *W family exists only to keep 32-bit arithmetic exact on a 64-bit one.
A — atomicsOne hart, no interrupts, no preemption: every atomicity and ordering guarantee is already satisfied by executing in order.
C — compressedEvery instruction is four bytes wide, which keeps address arithmetic and the ROM layout uniform.
ecall, ebreak, fence, CSRsThese are the boundary with an execution environment and privileged software, and there is neither. There is no console either — results are written into RAM, where the memory panel reads them back.
A labelled address space

Memory is byte-addressed and little-endian, split into three regions rather than presented as one anonymous array:

RegionRangeWhat it holds
ROM0x00000000–0x000007FFInstruction memory plus read-only data. Instructions are fetched from it and constants are loaded out of it; a store into it raises a store access fault.
RAM0x00010000–0x000103FFOrdinary read-write memory.
STACK0x00020000–0x000203FFThe same kind of memory, drawn separately because sp is what makes call frames legible. It starts at 0x00020400 and grows downward.

Alignment is enforced, and the exceptions carry the architecture's own names: instruction address misaligned, illegal instruction, load/store address misaligned, load/store access fault. The base ISA leaves misaligned access to the execution environment; this machine declines it, the way most real implementations do.

The pre-loaded program

Instead of printing a greeting, the program in ROM makes one pass through every RV32IM instruction, and the listing runs straight down: reading it top to bottom is reading the ROM top to bottom. Phases you can jump between:

  • Bootlui/addi to set up the stack pointer, and auipc+addi to compute an address from the program counter itself.
  • Arithmetic — including addi on 0x7FFFFFFF, which wraps to the most negative integer with no exception at all: RISC-V has no overflow trap and no flags.
  • Logic and shiftsxori rd,rs,-1 as a bitwise NOT, and a shift by 0x54 that turns out to be a shift by 20, because only the low five bits of the amount are ever used.
  • Comparison — the same register pair read signed and unsigned, plus sltiu's corner where the immediate is sign-extended first and compared unsigned afterwards.
  • Branches and a loop — all six conditions, with one pair of operands producing opposite verdicts signed and unsigned, then a backward branch three times around.
  • Calljal saving a return address and ret (which is just jalr x0, 0(ra)) going back to it.
  • Multiply and divide — one 64-bit product read four ways, and both degenerate division cases the ISA defines rather than traps.
  • Memory — a stack push and pop, lb versus lbu on the same byte, and two byte stores plus a halfword store assembling one word.
What is on screen
  • Datapath — PC, ROM, decode, registers, ALU, memory, writeback and next-PC, each box displaying the values it currently holds. Sender teal, receiver amber, the value on the wire.
  • Transfer readout — which component sent, which received, and a sentence on what just happened. Clickable dots jump to any transfer in the instruction.
  • Bit-field decoder — the 32-bit word split into its actual encoding fields, coloured by role, for whichever of the six formats is in play — and a plain-English description of the opcode, so MULHSU reads as “multiply high, signed × unsigned” rather than as five letters.
  • Operation detail — the operation at the level the architecture defines it: binary before and after for logic and shifts, byte lanes and sign extension for memory, the full 64-bit product for multiply, the quotient/remainder identity for divide.
  • Registers and memory — all 32 registers with a bit and byte breakdown of any one you pin, and a scrollable dump of each region with the accessed word highlighted.
  • Honest timing — the highlighted program line does not advance, a register write shows dashed and still holding its old value, and a stored word stays unchanged, until the transfer that actually does it goes through.
  • Reversible — step backwards a transfer at a time; registers, memory and the program counter all restore exactly.
Launch the visualizer Keys: step · step back · Enter run · P next phase