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.
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 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 implemented | Why |
|---|---|
| RV64I widths | This is a 32-bit machine. The *W family exists only to keep 32-bit arithmetic exact on a 64-bit one. |
| A — atomics | One hart, no interrupts, no preemption: every atomicity and ordering guarantee is already satisfied by executing in order. |
| C — compressed | Every instruction is four bytes wide, which keeps address arithmetic and the ROM layout uniform. |
ecall, ebreak, fence, CSRs | These 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. |
Memory is byte-addressed and little-endian, split into three regions rather than presented as one anonymous array:
| Region | Range | What it holds |
|---|---|---|
| ROM | 0x00000000–0x000007FF | Instruction 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. |
| RAM | 0x00010000–0x000103FF | Ordinary read-write memory. |
| STACK | 0x00020000–0x000203FF | The 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.
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:
- Boot —
lui/addito set up the stack pointer, andauipc+addito compute an address from the program counter itself. - Arithmetic — including
addion0x7FFFFFFF, which wraps to the most negative integer with no exception at all: RISC-V has no overflow trap and no flags. - Logic and shifts —
xori rd,rs,-1as a bitwise NOT, and a shift by0x54that 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.
- Call —
jalsaving a return address andret(which is justjalr 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,
lbversuslbuon the same byte, and two byte stores plus a halfword store assembling one word.
- 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
MULHSUreads 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.
