RISCV ISA

RISCV ISA

innocentzero

2026-06-15

#hardware #riscv #isa #software | Status: Complete

Notes on the RISCV ISA and associated hardware concepts.

RISCV ISA

Open source. Has 32 int and 32 FP registers. Also has XLEN variable, which is either 32 or 64 for 32-bit and 64-bit processors respectively.

There are 13 callee saved registers. 12 of them are explicitly called saved registers, and the first one of them (s0) is the frame pointer (equivalent to base pointer in AMD64). The 13 one is the stack register.

Register ABI Name Description Saver
x0 zero Zero always
x1 ra Return address caller
x2 sp Stack Pointer callee
x3 gp Global Pointer
x4 tp Thread Pointer
x5-x7 t0-t2 Temps caller
x8 s0 /fp saved / frame pointer (base pointer effectively) callee
x9 s1 saved register callee
x10-x11 a0-a1 Fn args/ return values callee
x12-x18 a2-a7 Fn args caller
x18-x27 s2-s11 Saved registers callee
x27-x31 t3-t6 Temporaries caller

Maximum memory depends on the size of the address bus from the load store unit to the memory.

The caller saved registers have to be explicitly saved by the caller function in a stack frame before calling the other function. On the other hand, callee saved functions are guaranteed to stay the same across function calls and there is no need for functions to save them.

RISCV Instructions

The ones listed below are called R-type or register-type instructions.

All of these also have an immediate version where the last argument is a hardcoded literal that is 12 bits.

These are called I-type or immediate-type instructions.

These are also I-type instructions.

The below one instruction is S-type instruction (for obvious reasons).

The below instruction is called B-type or branch-type instruction.

The above instructions have greater than, greater than equal to variants as well. Also immediate variants of all.

Function call instructions

All of these can jump to at most 1 MiB as they take an immediate value of 20 bits apart from the lsb that's always 0.

The below type is called a J-type instruction.

The below one is an I-type and NOT J-type.

Both of the ones below are aliases.

For function calls we also need a stack-based execution. x2 register is the stack pointer that points to the top of the stack. Frame pointer s0 saves the base of the stack. These mark the stack frame in the stack.

Always load and save values relative to the frame pointer.

Registers a0 to a7 are used to pass function arguments from one function to another. These are 8 parameters. If we have more, then you need to use the stack.

Control and status registers

Atomic means that interrupts won't stop the entire set of operations that is going on. If one of the operands is x0/zero, then that copying doesn't happen.

Executing instructions

If we are using an operating system:

If we are without an operating system:

Register Bank

Has all the registers and 2 mux + 1 demux to select the register to use. There is dual porting to read two source registers at once (hence two muxes).

Program Counter

This keeps track of the next instruction to be executed. Usually incremented by 4 unless branches. It resets to a fixed value called as reset vector.

Instructions and data are fetched every rising edge of the clock and that is when the program counter is also incremented by 4. The instruction fetched is later stored in the instruction register.

The instruction register sends it to the control unit which later sends signals to whatever is responsible.

It also gets reset at interrupts to an interrupt vector and begins to consume instructions from there.

ALU

Performs arithmetic (no shit). Has the gen module before it that generates the immediate instruction from the opcode, regardless of whether it was an I-type or not.

After this, there is a 2:1 mux that has a select line coming from the Control Unit that decides whether or not the immediate values has to be selected or not, the other option being 0.