RV32I’s remaining instructions are where the ISA meets the memory system and the outside world: two jumps, six branches, eight loads/stores, one fence, and two trap generators. No delay slots anywhere — and a subtle rule worth pinning immediately: an access-fault or page-fault on the target of a jump/taken branch is reported on the target instruction, while a misaligned target is reported on the jump/branch itself.
2.5 Unconditional jumps
JAL uses the J-format: a ±1 MiB signed offset in multiples of 2, added
to the JAL’s own address; pc+4 is written to rd (the link value).
J offset is just JAL with rd=x0.
JALR uses the I-format: target = (rs1 + sext(imm12)) & ~1 — the
least-significant bit is cleared, which slightly simplifies hardware
and lets software stash a tag bit in function pointers. pc+4 goes to rd.
RET = JALR x0, x1, 0.
Both raise instruction-address-misaligned if the target isn’t 4-byte aligned (impossible once C gives you IALIGN=16).
Return-address prediction is encoded implicitly. There are no hint bits; the register numbers are the hint. x1 and x5 are the two link registers, and Table 3 is the contract your RAS must implement:
| rs1 is x1/x5 | rd = rs1 | RAS action | |
|---|---|---|---|
| rd not link | No | — | None |
| rd not link | Yes | — | Pop |
| rd is x1/x5 | No | — | Push |
| rd is x1/x5 | Yes | No | Pop, then push |
| rd is x1/x5 | Yes | Yes | Push |
JAL pushes only when rd is x1/x5.
2.5.2 Conditional branches
B-format, ±4 KiB in multiples of 2. All six compare two registers:
BEQ/BNE, BLT/BLTU, BGE/BGEU — the missing four (BGT/BGTU/
BLE/BLEU) are operand swaps. A neat idiom: one BLTU index, bound checks
a signed array index against a bound, because negative indices compare
huge unsigned.
The misaligned-target exception fires only if the branch is taken. Software guidance baked into the ISA: lay out code so fall-through is the common path; static predictors should assume backward-taken, forward-not-taken. Use JAL (rd=x0), never an always-true branch, for unconditional jumps — wider range and no pollution of conditional predictors.
2.6 Loads and stores
RV32I is a load-store architecture load-store architecture Only loads and stores access memory; arithmetic operates solely on registers.
defined in ch. I·2 — open in glossary
.
Effective address = rs1 + sext(imm12); loads are I-type
(LW, LH/LHU, LB/LBU — sign- vs zero-extending), stores are S-type
(SW, SH, SB, storing the low bits of rs2). Loads with rd=x0 still
raise exceptions and side effects — the value is discarded, the access is
not.
Endianness is EEI-defined, and always byte-address invariant byte-address invariance Endianness property RISC-V mandates: a byte stored at an address is returned by a byte load from that address in ANY endianness. defined in ch. I·2 — open in glossary : a byte stored at an address reads back identically in any endianness — only multi-byte ordering differs.
Alignment is a two-level contract. Accesses with natural alignment natural alignment An access is naturally aligned when its effective address is divisible by the access size in bytes. Naturally aligned loads/stores never raise address-misaligned exceptions and are atomic; misaligned behavior is EEI-defined. defined in ch. I·2 — open in glossary never misalign-trap, and are guaranteed atomic. Misaligned accesses are the EEI’s choice:
- EEI guarantees full support → delivered by hardware, by an invisible trap into the environment (M-mode emulation), or a mix by address range. Software never sees a misalignment trap — but the access may be very slow, and is not guaranteed atomic.
- EEI doesn’t guarantee it → the access may succeed or raise address-misaligned or access-fault (the latter when emulation would be wrong, e.g. a side-effecting I/O region); the EEI defines whether the trap is contained or fatal.
2.7 FENCE
FENCE pred, succ orders the
predecessor set fence predecessor/successor sets FENCE names two subsets of {device Input, device Output, memory Reads, memory Writes}: no other hart/device may observe any successor-set operation before any predecessor-set operation.
defined in ch. I·2 — open in glossary
before
the successor set, each any combination of Input, Output,
Read, Write — as observed by other harts, devices, and
coprocessors. Memory-mapped I/O is ordered via I/O bits (device input/
output), ordinary cacheable memory via R/W. One explicit hole: a device
observing a write through a non-memory channel (say, an interrupt wire)
is outside FENCE’s jurisdiction entirely.
FENCE.TSO (fm=1000, RW,RW) orders predecessor loads before all successors and predecessor stores before successor stores — everything except store-to-load. Implementing it as a plain full FENCE is explicitly correct, since full ordering is a superset. The forward-compatibility rule generalizes: all reserved fm/pred/succ configurations execute as normal fences, and a simple core may execute every FENCE as a full conservative fence.
2.8 ECALL and EBREAK
The SYSTEM major opcode splits into CSR read-modify-writes (ch. I·6) and the rest. RV32I’s two are precise requested traps: ECALL ecall SYSTEM instruction raising a precise requested trap to the execution environment — the service-call mechanism (parameters per EEI, usually in registers). defined in ch. I·2 — open in glossary requests a service from the execution environment (arguments per the EEI’s ABI, usually in registers); EBREAK ebreak SYSTEM instruction returning control to a debugging environment; also used for semihosting via the fixed sequence slli x0,x0,0x1f; ebreak; srai x0,x0,7 (all 32-bit forms). defined in ch. I·2 — open in glossary hands control to a debugger. Simple cores may funnel all SYSTEM instructions to one trap handler.
Semihosting reuses EBREAK inside a fixed, uncompressed 32-bit sequence —
slli x0,x0,0x1f; ebreak; srai x0,x0,7 — so a debugger can distinguish a
semihosting call from an ordinary breakpoint (the shift-NOPs are
themselves HINT encodings).
Hardware Designer Notes
Two traps hide in the details here. First, exception attribution: misaligned target → the branch; target fetch fault → the target. Get the epc value right for both or trap handlers will loop. Second, FENCE decode: the tempting “unknown fm → illegal instruction” breaks forward compatibility — reserved configurations must silently strengthen to a full fence. For a single-issue in-order core with one outstanding access, FENCE and FENCE.TSO are literally free (already ordered); they start costing area only when you add store buffers (ch. 18, RVWMO, makes the ordering model precise).
Minimal Linux-boot hart MUST
- Clear bit 0 of every JALR target; check bit 1 against IALIGN and report misalignment on the jump/branch (taken only)
- Report target-page/access faults on the target instruction, not the transfer instruction
- Loads to x0: perform the access, deliver the exceptions, discard the data
- Execute reserved FENCE configurations as full fences and HINT-FENCEs as no-ops — never trap either
- Make ECALL/EBREAK precise requested traps
MAY simplify / trap-and-emulate
- Implement every FENCE as a conservative full fence (ignore pred/succ/fm entirely)
- Handle misaligned loads/stores by invisible M-mode emulation instead of hardware — Linux only requires that they complete
- Skip RAS prediction entirely on a minimal core — Table 3 is a performance contract, not a correctness one
Check yourself — control transfer, memory & environment
1.JALR computes rs1 + sext(imm) and then clears the least-significant bit. Why clear it instead of trapping on odd targets?
2.Per Table 3, what should your return-address stack do for JALR rd=x1, rs1=x5 (rd≠rs1, both link registers)?
3.A BEQ with a misaligned target address evaluates not-taken. What happens?
4.Your core doesn't implement misaligned loads in hardware, but Linux userspace expects them to just work. What's the standard resolution?
5.Your decoder encounters FENCE with a reserved fm value and nonzero rs1. What must a base implementation do?
6.Why can FENCE never guarantee when a device sees a memory-mapped interrupt-control write via its interrupt wire?