RISC-V Architecture
pronounced as 'RISC-five', represents the fifth major generation of RISC research processors designed by the computer science department at UC Berkeley.
Modular Architecture
RISC-V has extendable modules and a Base. So each chip is named as [Base][Extension]
Ex: RV64GC
Base
each chip must have One Base. This base tells about the registers size, memory address space.
- RV32I: 32-bit registers, standard Integer instruction set (about 40 instructions).
- RV64I: 64-bit registers, standard Integer instruction set (used for modern PCs and servers).
- RV32E: An ultra-tiny "Embedded" base with only 16 registers (instead of the usual 32)
- designed for microcontrollers like the ones in a smart toaster or a toothbrush.
Extensions
letters come after the base indicates included modules in the chip.
| Letter | Name | Description |
|---|---|---|
| M | Multiply | Adds hardware instructions for integer multiplication and division. |
| A | Atomic | Adds instructions that allow multiple CPU cores to talk to memory without stepping on each other's toes. |
| F & D | Floating Point | Adds registers and instructions for single-precision (F) and double-precision (D) decimal math. |
| G | General Purpose | Short form for Combination (I + M + A + F + D). This is not a separate extension, just a abbreviation. |
| C | Compressed | Allows the chip to read 16-bit instructions alongside standard 32-bit ones to save RAM space. |
Ex: RV64GC means same as RV64IMAFDC a 64-bit general-purpose chip that also supports compressed instructions.
Register Convention
Physically, a standard RISC-V chip has 32 registers, labeled simply x0 to x31.
To avoid remembering all the registers by position, RISC-V uses a naming convention known as ABI (Application Binary Interface).
Issue if Regs used randomly
if every programmer or compiler used these registers randomly, code written by person A wouldn't work with code written by person B. For example, your program might call a library function, and that function might accidentally overwrite the data you were right in the middle of using. So if you only use your own programming, its perfectly fine, but the moment you want to use someone else's programming or a team mate comes to collaboration, a lot of hiccups will occur.
Register Naming Convention
Strict ruleset called Application binary interface (ABI) to keep things in same track
| Register | ABI Name | Description / Role | Who Saves It? (Preservation Rule) |
|---|---|---|---|
x0 | zero | Hardwired to the value 0 | N/A (Read-only) |
x1 | ra | Return Address (Where to jump back to after a function ends) | Caller |
x2 | sp | Stack Pointer (Tracks temporary memory on the stack) | Callee |
x3 | gp | Global Pointer (Points to global variables) | N/A (Set once at startup) |
x4 | tp | Thread Pointer (Used in multi-threaded programs) | N/A (Set by Operating System) |
x5 - x7 | t0 - t2 | Temporary registers (Quick scratchpads) | Caller |
x8 | s0 / fp | Saved register 0 / Frame Pointer | Callee |
x9 | s1 | Saved register 1 | Callee |
x10 - x11 | a0 - a1 | Function Arguments / Return Values | Caller |
x12 - x17 | a2 - a7 | Function Arguments | Caller |
x18 - x27 | s2 - s11 | Saved registers | Callee |
x28 - x31 | t3 - t6 | More Temporary registers | Caller |
Understanding "Who Saves it"
function b(){
// stuff
}
function a(){
b();
}
// we call a
a()Imagine a calls b part.
ais the Callerbis the Callee
1. Caller saved registers (t and a registers)
these are unprotected or volatile.
Rule - b() has full authority to delete/read/replace any value of temporary (t) or argument (a) registers
If a() want any of those values to be kept, it must manually put them into the RAM (stack) then load it back again once, b() returns.
2. Callee saved registers (s registers and sp)
these are protected or preserved.
Rule - b() is not allowed to change the values (i.e. forbidden from leaving dirty values) in these registers when it finish work.
This does not mean it cannot change/read them, it can use those registers, but when it exit from stack, the initial values of s registers and sp must be the same.
Ex: If b() wants to use s2 for some math, it must first back up the original value of s2 onto the stack memory. Before it exits and returns to a(), it must restore that original value. a() can confidently leave data in s2 knowing it won't be modified by other functions.
"Saved" means who is responsible for saving the register values from getting nuked.
- Caller-Saved: Caller needs to make sure to pass it to Stack Memory to avoid getting replaced by Callee
- Callee-Saved: Callee is responsible for avoiding the value from changing after exiting from stack
Special Purpose Registers
1. x0 (Zero Register)
This is a hardware level protected register. This cannot be edited by any instruction. A predefined 0 comes in handy in many situations to avoid instruction count from getting high.
Ex: Avoid Move Instruction:
- COPY
x1tox2 - ADD
x0andx0tox1
2. Argument & Return registers (a0-a7 and ra)
result = calculate(5, 10);- The program places the numbers
5and10intoa0anda1. - It notes the line number it is currently on, saves it into
ra(Return Address), and jumps to thecalculatefunction. - The
calculatefunction runs, grabs its arguments out ofa0anda1, does the math, and drops the final answer back intoa0. - The function looks at
ra, jumps back to where it came from, and the main program reads the answer right out ofa0.
RISC-V Instruction Format (32bit)
RISC-V Instruction Format (32bit) check here