RISC-V is a modular ISA: a small mandatory base integer instruction set (RV32I or RV64I) plus optional, individually frozen standard extensions — M, A, F, D, C and dozens more. The spec deliberately defines only the software-visible interface, not a microarchitecture: your pipeline may be microcoded, in-order, or out-of-order; the ISA is written so that all of those can implement it efficiently. This first chapter pins down the vocabulary and ground rules that every later chapter builds on — and several of them are direct statements about what your hardware must and may do.
1.1 Platform terminology: cores, harts, and friends
A RISC-V hardware platform is anything from a microcontroller to a many-node server: RISC-V-compatible processing cores plus non-RISC-V cores, accelerators, memories, I/O devices, and an interconnect.
The spec’s component vocabulary is precise, and worth internalizing because Volume II and the whole software stack use it constantly:
- A core core A component containing an independent instruction-fetch unit; one core may support multiple harts via multithreading. defined in ch. I·1 — open in glossary is a component with an independent instruction-fetch unit.
- A hart hart A hardware thread: the abstract resource that autonomously fetches and executes RISC-V instructions within an execution environment; may be time-multiplexed onto physical hardware but must behave like an independent hardware thread. defined in ch. I·1 — open in glossary (hardware thread) is the resource that autonomously fetches and executes instructions. One multithreaded core can expose several harts.
- A coprocessor coprocessor A unit attached to a RISC-V core, mostly sequenced by the core's instruction stream, with additional architectural state and extensions and possibly limited autonomy. defined in ch. I·1 — open in glossary is attached to a core and mostly sequenced by its instruction stream, with some extra architectural state.
- An accelerator accelerator A non-programmable fixed-function unit, or an autonomous specialized core (often RISC-V-based), used to offload work such as I/O processing. defined in ch. I·1 — open in glossary is a fixed-function or autonomous specialized unit — often itself a RISC-V core with custom extensions.
One platform, two cores, three harts. A core = an independent fetch unit; a hart = one architectural thread of execution.
1.2 Execution environments and harts
A RISC-V program never runs against “the hardware” in the abstract — it runs against an execution environment interface (EEI) execution environment interface (EEI) The contract a RISC-V program runs against: initial state, number/type of harts and their privilege modes, memory and I/O accessibility, behavior of legal instructions, and trap handling. Examples: Linux ABI, RISC-V SBI. defined in ch. I·1 — open in glossary : the definition of its initial state, how many harts it sees (and their privilege modes), which memory and I/O regions are accessible, the behavior of every legal instruction, and what happens on traps. The Linux ABI is an EEI; so is the SBI your firmware exposes to the kernel; so is a bare-metal platform starting at power-on reset.
EEIs stack: bare metal at the bottom, an OS multiplexing user-level harts above it, perhaps a hypervisor in between, or an emulator (Spike, QEMU) implementing the whole environment in software. The crucial obligation flows downward: the environment is responsible for the eventual forward progress of each of its harts — retirement of an instruction, a trap, or an extension-defined event constitutes progress. Software inside the environment never has to “yield” to keep its own harts alive.
1.3 One ISA, four bases, many extensions
RISC-V is really a family of four base ISAs: RV32I and RV64I (the primary ones, 32-/64-bit address spaces), plus the register-reduced RV32E / RV64E for tiny microcontrollers. XLEN XLEN The width in bits of an integer register (32 for RV32, 64 for RV64); also sizes the byte-addressable address space (2^XLEN, circular). defined in ch. I·1 — open in glossary names the integer-register width — 32 or 64. Bases are deliberately separate ISAs, not strict subsets of each other: RV64I is free to drop RV32-only workarounds, and RV32I reuses encoding space RV64I needs.
Extensions are categorized by who owns their encodings:
- Standard — defined by RISC-V International; never conflict with each other.
- Reserved — unallocated, saved for future standard use.
- Custom — guaranteed never to be touched by standard extensions: vendor playground. A custom extension custom extension A non-standard extension that uses only custom encoding space; a non-conforming extension is one that uses standard or reserved encodings without being standard. defined in ch. I·1 — open in glossary uses only these; a non-conforming one squats on standard/reserved space.
The general-purpose software target is the base plus M (multiply/divide), A (atomics), F/D (single/double float) and C (compressed) — the bundle Linux distributions assume. Each extension, once ratified, stays frozen; new function arrives as new extensions, never as a “v2” of the base.
1.4 Memory: one circular address space, two kinds of contents
A hart sees a single byte-addressable address space of 2XLEN bytes. It is circular: address arithmetic wraps modulo 2XLEN, so your AGU simply ignores carry-out — no overflow fault exists. Terminology is fixed forever: a word word 32 bits (4 bytes) in RISC-V terminology, regardless of XLEN; halfword = 16 bits, doubleword = 64, quadword = 128. defined in ch. I·1 — open in glossary is 32 bits, a halfword 16, a doubleword 64, a quadword 128 — regardless of XLEN.
The environment maps each address range as main memory or I/O. The architectural difference is sharp: main-memory accesses can have no visible side effects; I/O reads and writes may. That distinction is what your memory system’s speculation logic must respect.
Every instruction execution involves an implicit fetch read; loads and stores add explicit accesses; address translation (Volume II) adds more implicit ones. The license that shapes fetch pipelines: implicit reads implicit memory access A memory access an instruction performs beyond its explicit semantics — instruction fetch, and (per the EE) things like page-table walks. Side-effect-free non-excepting implicit reads may occur arbitrarily early and speculatively. defined in ch. I·1 — open in glossary that raise no exception and have no side effects may occur arbitrarily early and speculatively. An implementation may legally prefetch and cache every fetchable byte once and never look at main memory again — software that modifies code must explicitly synchronize with FENCE.I (Vol I ch. 5, Zifencei). Ordering across harts is governed by the RVWMO memory model (ch. 18) — the weakest model any RISC-V implementation may expose, so RVWMO-correct software runs on every implementation.
1.5 Instruction-length encoding
Base instructions are fixed 32-bit and 32-bit aligned ( IALIGN IALIGN The instruction-address alignment constraint an implementation enforces: 32 bits in the base ISA, relaxed to 16 by extensions like C; no other values allowed. defined in ch. I·1 — open in glossary = 32; the C extension relaxes it to 16 — those are the only two legal values). The encoding scheme, though, is built for variable length: an instruction is one or more 16-bit parcels instruction parcel A 16-bit unit of instruction encoding. Instructions are one or more parcels, stored little-endian at increasing halfword addresses regardless of data-memory endianness. defined in ch. I·1 — open in glossary , up to the implementation’s ILEN ILEN The maximum instruction length an implementation supports; a multiple of IALIGN (32 bits for base-only implementations). defined in ch. I·1 — open in glossary . The first parcel’s low bits encode the length:
Two encodings are illegal by design: all-zeros (treated as 16 bits if any 16-bit extension exists, else 32) and all-ones (ILEN bits). Zeroed memory and erased flash therefore self-trap on execution.
Parcels are stored little-endian, lowest parcel first, always — even on a big-endian or bi-endian machine. Instruction encoding is thereby decoupled from data endianness: your fetch unit sees the length bits first in halfword order, unconditionally.
1.6 Exceptions, traps, and interrupts
Fixed vocabulary, used identically in both volumes: an exception exception An unusual condition occurring at run time, associated with an instruction in the current hart (synchronous). defined in ch. I·1 — open in glossary is a synchronous unusual condition tied to an instruction in the current hart; an interrupt interrupt An external asynchronous event that may cause a hart to experience an unexpected transfer of control. defined in ch. I·1 — open in glossary is an external asynchronous event; a trap trap The transfer of control to a trap handler caused by an exception or an interrupt. From inside an execution environment a trap is contained, requested, invisible, or fatal. defined in ch. I·1 — open in glossary is the resulting transfer of control to a handler. (Floating-point exceptions are the odd ones out: in the standard F/D extensions they set flags, they do not trap.)
How a trap looks from inside an execution environment falls into exactly four classes:
| Contained | Requested | Invisible | Fatal | |
|---|---|---|---|---|
| Execution terminates | No | No¹ | No | Yes |
| Software is oblivious | No | No | Yes | Yes² |
| Handled by environment | No | Yes | Yes | Yes |
The same physical event classifies differently depending on the environment you stand in: a page fault is invisible to the faulting application under demand paging, but very visible (“contained”) to the kernel handling it. Precision is an EEI choice: contained and requested traps may be observed imprecise, but the spec recommends preciseness wherever possible — and Volume II’s machinery (mepc/mcause, ch. II·3) assumes it.
Hardware Designer Notes
This chapter hands the fetch-and-decode designer three load-bearing guarantees: length is decidable from parcel 0’s low bits (your predecoder’s critical path is short by construction); zeroed/erased memory self-traps (cheap wild-jump containment); and icache coherence with stores is software’s responsibility. The main-memory/I-O split prefigures Volume II’s PMAs — decide your memory map early, because speculation legality hangs off it.
Minimal Linux-boot hart MUST
- Guarantee eventual forward progress of every hart (fair arbitration; WFI-style waiting is the sanctioned exception)
- Raise illegal-instruction on the all-zeros and all-ones encodings
- Determine instruction length from the low bits of the first parcel (fetch/predecode)
- Keep speculation out of I/O regions — only side-effect-free implicit reads may go early
MAY simplify / trap-and-emulate
- Run a non-coherent icache: never snoop stores; FENCE.I is software’s problem
- Prefetch any fetchable bytes arbitrarily early
- Ignore address carry-out — the address space wraps mod 2^XLEN by definition
Check yourself — platform, memory & traps
1.Your single-issue in-order core time-multiplexes two architectural threads onto one pipeline. From the ISA's point of view, what have you built?
2.Which of these is your fetch unit architecturally allowed to do without any special enabling state?
3.Your decoder sees a 32-bit-aligned word that is all zeros. What must happen?
4.A user-mode ECALL lands in your supervisor's handler and the process later resumes. From inside the OS execution environment, what kind of trap effect is this?
5.Why can your fetch unit determine an instruction's length from only the first 16-bit parcel?