Chapter 5: Load-Store Architecture

September 17, 2026 · View on GitHub

Introduction

RISC-V is a load-store architecture: the only instructions that access memory are loads and stores. All arithmetic and logic operates exclusively on registers. This chapter explains the load-store model, shows the key load and store instructions used in our firmware, and demonstrates the fundamental read-modify-write pattern for hardware programming.

Why Load-Store?

In a load-store architecture, computation never operates directly on memory. To add 1 to a value in memory, you must:

  1. Load the value from memory into a register.
  2. Add 1 to the register.
  3. Store the result back to memory.

This constraint simplifies the processor hardware and enables fast, pipelined execution. Every RISC-V instruction either operates on registers or transfers data between registers and memory — never both in the same instruction.

The Load Instruction: lw

The lw (load word) instruction reads a 32-bit value from a memory address into a register:

  lw    t1, 0(t0)                                # read 32-bit value at address in t0

This reads 4 bytes from the address in t0 and places them in t1. Our firmware uses this pattern to read every peripheral register:

  li    t0, RESETS_RESET                         # load RESETS->RESET address
  lw    t1, 0(t0)                                # read current RESETS->RESET value

The Store Instruction: sw

The sw (store word) instruction writes a 32-bit value from a register to a memory address:

  sw    t1, 0(t0)                                # write t1 to address in t0

This writes the 4 bytes in t1 to the address in t0. In peripheral programming, this is how we configure hardware:

  sw    t1, 0(t0)                                # store value into XOSC_CTRL

The Load-Modify-Store Pattern

Most hardware configuration follows a three-step pattern:

  1. Load the current register value.
  2. Modify specific bits (set, clear, or toggle).
  3. Store the modified value back.
  li    t0, CLK_PERI_CTRL                        # load CLK_PERI_CTRL address
  lw    t1, 0(t0)                                # read CLK_PERI_CTRL value
  li    t2, (1<<11)                              # ENABLE bit mask
  or    t1, t1, t2                               # set ENABLE bit
  sw    t1, 0(t0)                                # store value into CLK_PERI_CTRL

This pattern guarantees we only change the bits we intend to, preserving all other bits in the register.

Byte and Halfword Access

RISC-V also supports narrower loads and stores:

InstructionWidthDescription
lb / sb8 bitsLoad/store byte
lh / sh16 bitsLoad/store halfword
lw / sw32 bitsLoad/store word
lbu8 bitsLoad byte unsigned
lhu16 bitsLoad halfword unsigned

Our firmware uses only lw/sw because all RP2350 peripheral registers are 32 bits wide. The lb/lbu variants exist for byte-oriented data processing.

Stack Operations

RISC-V uses explicit sw/lw instructions with the stack pointer for push/pop operations:

  addi  sp, sp, -4                               # allocate stack frame
  sw    ra, 0(sp)                                # save return address
  ...
  lw    ra, 0(sp)                                # restore return address
  addi  sp, sp, 4                                # deallocate stack frame

This is functionally equivalent to ARM's push/pop but makes the stack pointer adjustment explicit.

Memory Access in Our Firmware

Every hardware interaction in our firmware follows the load-store model:

OperationPattern
Read peripheralli t0, ADDR then lw t1, 0(t0)
Write peripheralli t0, ADDR then sw t1, 0(t0)
Set bitsLoad, or, store
Clear bitsLoad, and with inverted mask, store
Test a bitLoad, andi, branch
Push to stackaddi sp, sp, -N then sw ra, 0(sp)
Pop from stacklw ra, 0(sp) then addi sp, sp, N

Summary

  • RISC-V is a load-store architecture — only lw/sw (and variants) access memory.
  • All computations happen in registers.
  • The load-modify-store pattern is the foundation of all hardware configuration.
  • Stack operations use explicit sw/lw with sp adjustments.
  • Our firmware uses word-width access (lw/sw) for all peripheral registers.