2.5-8Control Transfer, Loads & Stores, FENCE, and Environment Calls

Part I Linux boot: required Vol. I (Unprivileged) pp. 30–36 · ~8 min read

  • return-address stack (RAS)
  • load-store architecture
  • byte-address invariance
  • natural alignment
  • fence predecessor/successor sets
  • ecall
  • ebreak

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.

imm[20]31imm[10:1]3021imm[11]20imm[19:12]1912rd117JAL60JAL
Click a field for its role.

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.

offset[11:0]3120base (rs1)19150001412rd117JALR60JALR
Click a field for its role.

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:

RAS prediction hints in JALR operands (Table 3)
rs1 is x1/x5rd = rs1RAS action
rd not linkNoNone
rd not linkYesPop
rd is x1/x5NoPush
rd is x1/x5YesNoPop, then push
rd is x1/x5YesYesPush
Dotted-underlined cells have explanations — click one.

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.

imm[12]31imm[10:5]3025rs22420rs11915funct31412imm[4:1]118imm[11]7BRANCH60B-Type
Click a field for its role.

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 . 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 : 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 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 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.

fm3128pred2724succ2320rs119150001412rd117MISC-MEM60FENCE
Click a field for its role.

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 requests a service from the execution environment (arguments per the EEI’s ABI, usually in registers); EBREAK 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?

6 questions