RISC-V --- A Modern Open ISA
August 3, 2026·27 min read·intermediate
RISC-V originated at the University of California, Berkeley in 2010. A team led by Krste Asanović and David Patterson designed it as a clean-slate instruction set for teaching and research . Unlike ARM, which…
RISC-V originated at the University of California, Berkeley in 2010. A team led by Krste Asanović and David Patterson designed it as a clean-slate instruction set for teaching and research [1]. Unlike ARM, which is licensed from a single company, and unlike x86-64, which is controlled by Intel and AMD, RISC-V is a free and open standard governed by a nonprofit foundation, RISC-V International. Anyone can build a RISC-V processor without paying license fees, and anyone can extend the ISA with custom instructions without seeking permission. The openness is not just a legal arrangement. It is a design philosophy that pervades the ISA’s structure, from its modular extension scheme to its clean encoding that invites experimentation.
This chapter walks through the RISC-V ISA in detail. It begins with the base integer instruction set (RV32I and RV64I), examines each of the six major standard extensions, diagrams the instruction encoding formats, and closes with the privileged specification that governs exception handling and operating-system support. The presentation assumes the vocabulary of Chapter 13.
01.The Rationale for a Clean-Slate ISA
By 2010, every major ISA carried decades of accumulated legacy. x86-64 traced its roots to the 8086 of 1978 and still supported 16-bit real mode. ARM A64 was a clean 64-bit redesign, but ARM Holdings controlled it through a licensing model that charged per- chip royalties or substantial upfront fees. MIPS, SPARC, and PA-RISC had either faded commercially or been acquired.
The Berkeley team wanted an ISA that met four requirements. First, it had to be free of licensing restrictions so that students and researchers could build, modify, and publish implementations without legal barriers. Second, it had to be simple and regular so that a graduate student could build a working processor in a semester. Third, it had to be modular so that a minimal microcontroller and a high-performance server core could share the same base specification and differ only in which extensions they implemented. Fourth, it had to leave room for custom extensions so that domain-specific accelerators could add instructions without polluting the base encoding space.
The resulting design, documented in the unprivileged specification [2] and the privileged specification [3], has since moved from academia into industry. SiFive, Andes, Ventana, Tenstorrent, and dozens of other companies now ship commercial RISC-V silicon. The open-source ecosystem includes the Rocket Chip generator, the Berkeley Out-of-Order Machine (BOOM), the CVA6 core, and many others.
02.The Base Integer ISA: RV32I and RV64I
Every RISC-V processor implements either RV32I (32-bit) or RV64I (64-bit) as its mandatory base. This section walks through the base using RV64I as the primary example, noting differences where RV32I diverges.
Registers
RV64I provides 32 integer registers, x0 through x31, each 64 bits wide (32 bits in RV32I). Register x0 is hardwired to zero. Reading x0 always returns zero, and writing to x0 is silently discarded. This design choice is not unique to RISC-V. MIPS also hardwired register $0 to zero. The hardwired zero register simplifies the encoding of many common operations, as the worked examples in this chapter demonstrate. The program counter is a separate register, not accessible as a GPR. The base ISA does not define floating-point registers. Those are added by the F and D extensions.
Arithmetic and logical instructions
The base provides signed and unsigned addition, subtraction, and the logical operations AND, OR, and XOR. It also provides shift instructions (logical left, logical right, arithmetic right) and comparison instructions that write 1 or 0 to the destination register (SLT for signed less-than, SLTU for unsigned less-than). Multiplication and division are not in the base. They live in the M extension.
Each arithmetic instruction has two forms. The register form (ADD rd, rs1, rs2) takes two source registers. The immediate form (ADDI rd, rs1, imm) takes one source register and a 12-bit signed immediate. This pair of forms covers nearly all integer computation without requiring any memory access.
RISC-V also provides a SUB instruction for register- register subtraction but deliberately omits a SUBI immediate form. Subtracting an immediate is the same as adding its negation, so ADDI rd, rs1, -5 serves the purpose of SUBI rd, rs1, 5. This is a characteristic RISC-V design choice: do not add an instruction when an existing instruction already covers the operation at no extra cost.
A related simplification is the family of pseudo-instructions that the assembler recognizes and translates into base instructions. MV rd, rs is a pseudo-instruction for ADDI rd, rs, 0. NOP is ADDI x0, x0, 0. LI rd, imm is a pseudo-instruction that the assembler expands into one or two instructions depending on the immediate’s size. The pseudo-instruction layer lets assembly code read naturally without inflating the hardware’s instruction set.
Load and store instructions
RISC-V is a strict load-store architecture. The base provides byte, halfword (16-bit), word (32-bit), and doubleword (64-bit, RV64I only) loads and stores. Each load instruction specifies whether the loaded value is sign-extended or zero-extended to fill the full register width. The addressing mode is uniformly base plus a 12-bit signed offset. There are no indexed, scaled, or auto-increment modes in the base ISA. The simplicity of having a single addressing mode is a deliberate design choice: it keeps the address-generation unit minimal and predictable.
The load instructions are LB (load byte, sign-extend), LBU (load byte, zero-extend), LH (load halfword, sign-extend), LHU (load halfword, zero-extend), LW (load word, sign-extend in RV64I), LWU (load word, zero-extend, RV64I only), and LD (load doubleword, RV64I only). The store instructions are SB, SH, SW, and SD. Stores do not need sign-extension variants because they write the appropriate number of low-order bits from the source register to memory.
RISC-V specifies little-endian byte ordering as the default, with an optional big-endian mode that implementations may support. In practice, nearly all RISC-V hardware and software uses little-endian, matching the convention of ARM A64 and x86-64.
Control flow instructions
Conditional branches compare two registers and jump to a PC-relative target if the condition holds. The six branch conditions are equal (BEQ), not equal (BNE), less than signed (BLT), greater than or equal signed (BGE), less than unsigned (BLTU), and greater than or equal unsigned (BGEU). There is no condition-code register. The comparison is performed directly in the branch instruction, as discussed in Chapter 13.
Note the absence of “greater than” and “less than or equal” branch conditions. These are not needed because the opposite operand order provides the same test: BLT x5, x6, label tests whether x5 < x6, so testing x6 < x5 is equivalent to testing x5 > x6. The assembler provides pseudo-instructions BGT, BLE, BGTU, and BLEU that swap the operands automatically. This halves the number of branch encodings the hardware must recognize.
The unconditional jump JAL (jump and link) writes the return address (the address of the instruction following the JAL) to a destination register and jumps to a PC-relative target encoded as a 20-bit signed offset. By convention, a function call uses JAL x1, target, storing the return address in the link register x1 (also known as ra). A plain unconditional jump that does not need to save a return address uses JAL x0, target, discarding the return address into the hardwired zero register. The indirect jump JALR computes the target by adding a 12-bit signed offset to a base register. A function return is JALR x0, 0(x1): jump to the address in the link register, discard the “return address of the return” into x0. Together, JAL and JALR support function calls, returns, indirect jumps, and switch-table dispatch.
Upper-immediate instructions
Two instructions construct large constants. LUI (load upper immediate) places a 20-bit immediate into bits [31:12] of the destination register and zeros bits [11:0]. AUIPC (add upper immediate to PC) adds the same 20-bit immediate, shifted left by 12, to the current PC and writes the result to the destination register. A LUI + ADDI pair loads any 32-bit constant. An AUIPC + JALR pair reaches any address within a GiB range of the current PC, which is sufficient for position-independent code.
System instructions
The base defines ECALL (environment call, used for system calls), EBREAK (environment breakpoint, used by debuggers), and the FENCE instruction for memory ordering. It also defines the CSR access instructions (CSRRW, CSRRS, CSRRC, and their immediate variants), which read and write control and status registers.
03.Instruction Encoding Formats
Every 32-bit RISC-V instruction belongs to one of six formats: R, I, S, B, U, and J. The formats differ in how the 32 bits are allocated among the opcode, register specifiers, function codes, and immediate fields. A critical design principle is that the register specifiers always occupy the same bit positions regardless of format: rd is always at bits [11:7], rs1 at bits [19:15], and rs2 at bits [24:20]. This consistency lets the decoder start reading registers before it has fully determined the instruction type.
R-type: register-register operations
The R-type format carries no immediate. It encodes the opcode (7 bits), two source registers rs1 and rs2 (5 bits each), a destination register rd (5 bits), a 3-bit function code funct3, and a 7-bit function code funct7. All base arithmetic and logical instructions with two register operands use this format.
I-type: immediate operations and loads
The I-type format replaces rs2 and funct7 with a 12-bit signed immediate. It is used for arithmetic immediates (ADDI, ANDI, ORI), loads (LW, LD), and the JALR indirect jump.
S-type: stores
Store instructions need two source registers (rs1 for the base address and rs2 for the data to store) but no destination register. The S-type format splits the 12-bit immediate across two fields (bits [31:25] and bits [11:7]) to keep rs1 and rs2 in their standard positions.
B-type: conditional branches
The B-type format is a variant of S-type used for branches. The 12-bit immediate encodes a signed offset in multiples of 2 bytes (bit 0 of the target is always zero for aligned instructions), giving a branch range of KiB from the current PC. The immediate bits are shuffled so that the sign bit is always in bit 31 of the instruction word, which simplifies sign extension in hardware.
U-type: upper immediate
The U-type format carries a 20-bit immediate and a destination register. It is used by LUI and AUIPC.
J-type: unconditional jumps
The J-type format carries a 20-bit immediate (encoding a signed offset in multiples of 2 bytes, giving a range of MiB) and a destination register for the return address. It is used by JAL. The immediate bits are shuffled, like the B-type, to keep the sign bit at position 31.
04.Standard Extensions
The base integer ISA is deliberately minimal. A RISC-V implementation that supports only RV32I can execute any general-purpose program, but it will perform multiplication through a software routine and will have no hardware floating-point support. The standard extensions add capabilities in well-defined, independently testable groups.
M: integer multiply and divide
The M extension adds four multiply instructions (MUL, MULH, MULHSU, MULHU) and two divide-and-remainder pairs (DIV/REM for signed, DIVU/REMU for unsigned). MUL returns the lower half of the product. MULH and its variants return the upper half, which is needed for multi-word multiplication and for detecting overflow.
A: atomics
The A extension provides two mechanisms for atomic memory operations. The first is the load-reserved/store-conditional (LR/SC) pair. LR loads a value and places a reservation on the address. A subsequent SC to the same address succeeds (writes the value and returns zero in the destination register) only if no other core has written to that address since the reservation was placed. If another core wrote to the address, SC fails (writes nothing and returns a nonzero value). This pair is the foundation for lock-free algorithms and for implementing higher-level synchronization primitives such as compare-and-swap.
The second mechanism is a set of atomic memory operations (AMOADD, AMOSWAP, AMOAND, AMOOR, AMOXOR, AMOMAX, AMOMIN, and their unsigned variants) that atomically read a memory location, perform an operation, and write the result back. Each AMO instruction also takes acquire and release ordering annotations to control memory ordering.
F and D: floating point
The F extension adds 32 floating-point registers (f0 through f31), each 32 bits wide, and a full set of IEEE 754 single-precision arithmetic instructions: add, subtract, multiply, divide, square root, fused multiply-add, comparisons, conversions between integer and floating-point formats, and sign- injection operations. The D extension widens the floating-point registers to 64 bits and adds double-precision versions of all F-extension instructions. A processor that implements D necessarily implements F.
The floating-point control and status register fcsr holds the rounding mode and the accrued exception flags (invalid, divide-by-zero, overflow, underflow, inexact), following the IEEE 754 model described in Chapter 8.
RISC-V floating-point instructions use a 3-bit rm field in each instruction to select the rounding mode for that operation: round to nearest even, round toward zero, round down, round up, or round to nearest (ties away from zero). A special encoding (rm = 111) tells the hardware to use the dynamic rounding mode stored in fcsr. This per- instruction rounding-mode selection is unusual. ARM A64 and x86-64 both use a single global rounding mode stored in a control register, and changing the mode requires a write to that register. The RISC-V approach avoids the control-register write for operations that need a non-default rounding mode, eliminating a potential serialization point in the pipeline.
C: compressed instructions
The C extension defines 16-bit encodings for the most frequently used instructions. A compressed C.ADDI occupies 2 bytes instead of 4. A compressed C.LW occupies 2 bytes instead of 4. The compressed forms use a restricted set of registers (typically x8–x15, the eight callee-saved and argument registers most used in leaf functions) and narrower immediates. The hardware detects the instruction length by checking the lowest 2 bits of each fetch: if both bits are not 11, the instruction is 16 bits.
The C extension reduces code size by 25 to 30 percent on typical workloads, bringing RISC-V code density close to that of ARM Thumb-2 and x86-64. Since instruction-cache misses are a meaningful source of performance loss, smaller code translates directly into fewer cache misses and lower energy consumption.
V: vector operations
The V extension adds variable-length vector processing to RISC-V. It defines 32 vector registers (v0 through v31) whose width is implementation-defined. A processor that provides 128-bit vector registers executes a vector add on two elements at a time. A processor that provides 512-bit vector registers executes the same instruction on eight elements at a time. The same binary runs on both, because the vector length is not encoded in the instruction. Instead, a special VSETVLI instruction configures the number of elements per vector operation at run time.
This length-agnostic approach is the same philosophy that ARM’s SVE extension adopted. It avoids the binary-compatibility trap that plagued x86 SIMD extensions, where each new vector width (SSE at 128 bits, AVX at 256 bits, AVX-512 at 512 bits) required a new set of instructions and a recompile.
The extension naming convention
A RISC-V implementation declares its capabilities through a string such as RV64IMAFDC. The letters are concatenated in a canonical order: base width first, then single-letter extensions in the order fixed by the specification, which runs I, M, A, F, D, Q, L, C, B and onward rather than alphabetically. The shorthand G (general-purpose) abbreviates the combination IMAFD. A processor labeled RV64GC implements the 64-bit base, integer multiply/ divide, atomics, single-precision float, double-precision float, and compressed instructions, which is the standard general-purpose configuration for application processors. This is the configuration that Linux distributions target by default, and it is the minimum for running a full operating system with standard C library support.
Beyond the single-letter extensions, RISC-V defines multi-letter extensions for specialized features. Zicsr provides the CSR access instructions (technically part of the base but given its own extension name for modularity). Zifencei provides the FENCE.I instruction for instruction-cache coherence. Zba, Zbb, and Zbs (the bit-manipulation extensions) add address-generation helpers, basic bit-manipulation operations (count leading/trailing zeros, population count, byte reverse), and single-bit instructions. These extensions fill gaps in the base ISA that compilers encounter frequently in real code.
Table 1. Standard RISC-V extensions and their functions.
| Letter | Name | Function |
|---|---|---|
| I | Base integer | Arithmetic, logic, loads, stores, branches |
| M | Multiply/divide | MUL, DIV, REM |
| A | Atomics | LR/SC, AMO operations |
| F | Single-precision FP | IEEE 754 single-precision arithmetic |
| D | Double-precision FP | IEEE 754 double-precision arithmetic |
| C | Compressed | 16-bit encodings for common instructions |
| V | Vector | Variable-length vector operations |
05.Custom Extensions and the Encoding Space
The RISC-V encoding reserves four opcode ranges (custom-0 through custom-3) for vendor-defined instructions. A company building a domain-specific accelerator (a neural-network inference engine, a cryptographic coprocessor, a signal-processing unit) can add instructions in these ranges without conflicting with any standard extension. The custom instructions share the same 32-bit encoding framework and the same register-specifier positions as the base, so existing decoder infrastructure can route them to the custom execution unit with minimal modification.
This extensibility is one of RISC-V’s most distinctive features relative to ARM and x86-64. ARM extensions must be proposed to and ratified by ARM Holdings. x86 extensions must be proposed by Intel or AMD and adopted by the other. RISC-V custom extensions require no external approval, which has made the ISA popular for research prototypes and for companies building specialized silicon.
The custom-extension mechanism has a practical constraint: the software toolchain must know about the custom instructions. GCC and LLVM both support RISC-V custom instructions through inline assembly or through intrinsics defined in a header file. For more systematic integration, RISC-V supports a .insn assembler directive that lets the programmer encode arbitrary instruction words by specifying the format, opcode, and operand fields directly. This directive bridges the gap between hardware experimentation and software usability, allowing researchers to test new instructions without modifying the compiler’s code-generation backend.
A second practical consideration is that custom extensions must be tested for correctness independently of the base ISA. The RISC-V compliance test suite, maintained by RISC-V International, covers the standard extensions. Custom extensions need their own test suites. The open-source Spike simulator and the QEMU RISC-V port both support pluggable custom-instruction modules, making it possible to simulate and debug custom extensions before committing to silicon.
06.The Privileged Specification
The unprivileged specification defines the instructions that application code uses. The privileged specification [3] defines the system-level architecture: privilege modes, exception handling, virtual memory, and hardware configuration.
Machine, supervisor, and user modes
RISC-V defines three privilege modes. Machine mode (M) is the most privileged. It has full access to all hardware resources and is the mode that firmware and bootloaders run in. Supervisor mode (S) is less privileged. It runs the operating system kernel and has access to virtual memory configuration and most system registers, but certain operations (like configuring the physical memory protection unit) are restricted to M-mode. User mode (U) is the least privileged and runs application code. A simple embedded controller may implement only M-mode. A system running Linux needs all three.
An optional hypervisor extension (H) adds virtualized privilege modes for virtual-machine monitors. The H-extension allows a hypervisor running in HS-mode (hypervisor-extended supervisor mode) to manage guest operating systems running in VS-mode (virtual supervisor mode) and guest applications running in VU-mode (virtual user mode).
Control and status registers (CSRs)
Each privilege mode has its own set of CSRs. The most important are:
-
mstatus/sstatus: global interrupt enable, privilege-mode stack, and other status bits. -
mtvec/stvec: the trap-vector base address, which is the entry point for exception handlers. -
mepc/sepc: the exception program counter, holding the address of the instruction that caused the trap or that was interrupted. -
mcause/scause: the cause code that identifies the exception type. -
mtval/stval: additional exception information (for example, the faulting virtual address on a page fault). -
mie/sieandmip/sip: interrupt-enable and interrupt-pending registers.
Physical memory protection (PMP)
Machine mode can configure a set of PMP (physical memory protection) entries that restrict the physical addresses that supervisor and user mode can access. Each PMP entry specifies a range of physical addresses and a set of permissions (read, write, execute). The PMP mechanism is the foundation for memory isolation on systems that do not implement virtual memory (for example, embedded microcontrollers running a real-time operating system).
Virtual memory
When the S-mode is present, RISC-V supports paged virtual memory through the Sv32 (32-bit, two-level page table), Sv39 (39-bit, three-level), Sv48 (48-bit, four-level), and Sv57 (57-bit, five-level) translation schemes. The page size is 4 KiB, with support for superpages at each intermediate level. The page table entry format includes present, read, write, execute, user, global, accessed, and dirty bits. The active translation mode is selected by writing to the satp CSR.
The Sv39 scheme is the most commonly implemented. It provides a 39-bit virtual address space (512 GiB), which is sufficient for most application workloads. The three-level page table uses 9 bits of virtual address per level, with a 12-bit page offset. Each page table is exactly one 4 KiB page (512 entries of 8 bytes each). The hardware page-table walker reads the satp register to find the root page table’s physical address, then walks three levels to produce the final physical address. A translation lookaside buffer (TLB) caches recent translations to avoid the full walk on most accesses. The SFENCE.VMA instruction flushes TLB entries when the operating system modifies the page tables.
Timer and performance counters
The privileged specification defines a real-time counter (mtime and mtimecmp) for timer interrupts and three read-only counters accessible from user mode: cycle (clock cycles elapsed), time (wall-clock time), and instret (instructions retired). These counters give user-mode code a lightweight mechanism for benchmarking and time-stamping without requiring a system call. Machine mode can disable user-mode access to these counters through the mcounteren CSR if the security policy requires it.
07.RISC-V Compared with ARM and x86-64
The table below places RISC-V alongside ARM A64 and x86-64 on the key dimensions introduced in Chapter 13.
Table 2. RISC-V compared with ARM A64 and x86-64 on major ISA dimensions.
| Dimension | RISC-V (RV64GC) | ARM A64 | x86-64 |
|---|---|---|---|
| Encoding width | 32-bit + 16-bit C | 32-bit | 1–15 bytes |
| Operand model | 3-register GPR | 3-register GPR | 2-operand reg-mem |
| Memory model | Load-store | Load-store | Register-memory |
| Integer registers | 32 | 31 + XZR/SP | 16 (32 with APX) |
| FP registers | 32 (F/D ext.) | 32 | 16 XMM/YMM/ZMM |
| Condition codes | None | NZCV flags | EFLAGS |
| Memory ordering | Relaxed (RVWMO) | Relaxed | TSO |
| Privilege modes | M/S/U (+ H) | EL0–EL3 | Ring 0–3 |
| Licensing | Free, open | Licensed | Proprietary |
The comparison reveals RISC-V’s minimalism. Fewer addressing modes, no condition codes, no register-memory arithmetic. The cost is occasionally higher instruction count. The benefit is decoder simplicity, a smaller verification surface, and unencumbered extensibility. Whether that trade-off favors RISC-V over ARM or x86-64 depends on the application domain. For embedded and education, the simplicity is a strong advantage. For high- performance server workloads, the mature toolchains and microarchitectural investment behind ARM and x86-64 remain formidable.
One dimension that the table does not capture is verification complexity. An ISA with fewer instructions, fewer addressing modes, and fewer implicit side effects (no flag register, no register-memory arithmetic) has a smaller state space for formal verification and testing. The RISC-V designers explicitly cite verification simplicity as a design goal [2]. A clean-slate ISA with 47 base instructions and well-separated extensions is easier to verify exhaustively than an ISA with over a thousand encodings and decades of accumulated corner cases. This matters for safety- critical applications (automotive, medical, aerospace) where functional correctness of the processor must be demonstrated to a certification authority.
Another dimension is the ecosystem of open-source implementations. Because RISC-V is free to implement, a rich landscape of open-source cores exists. The Rocket Chip generator (a configurable in-order core), BOOM (a configurable out-of-order core), CVA6 (formerly Ariane, a 6-stage in-order application processor core), PicoRV32 (a size-optimized 32-bit core for FPGAs), and SweRV (a commercial-quality core originally from Western Digital) are all available as open-source RTL. No comparable ecosystem of open-source cores exists for ARM A64 or x86-64. This openness is what makes RISC-V the preferred ISA for academic computer architecture research and education.
08.Worked Examples
09.Exercises
References
- [1]Patterson, David A. and Hennessy, John L. (2020). “Computer Organization and Design RISC-V Edition: The Hardware Software Interface.” Morgan Kaufmann.
- [2]Waterman, Andrew and Asanovi\'c (2024). “The RISC-V.”
- [3]Waterman, Andrew and Asanovi\'c (2024). “The RISC-V.”