The Problem: Fitting Large Values into Fixed-Width Instructions
Every RISC-V instruction is packed into a fixed 32-bit word. Since some of those bits must encode the opcode and register fields, only a limited number of bits remain available for an embedded constant, known as an Immediate Value. This creates a problem: how can a program work with a 32-bit or 64-bit constant, or a memory address far larger than what fits in the leftover bits of one instruction?
Building Wide Constants from Smaller Pieces
RISC-V solves this by splitting a large constant across two instructions rather than trying to fit it into one.
lui a, upperBits
addi a, a, lowerBitslui (load upper immediate) places a set of bits into the upper portion of a register and clears the lower portion to zero. The following addi instruction then adds a smaller immediate value to fill in the lower bits. Combined, these two ordinary instructions construct a value far larger than either instruction could encode on its own.
Reaching Distant Memory Addresses and Labels
The same size limitation applies to jump and branch instructions when the destination is far away in memory. Since a branch's target offset must also fit within the instruction's limited immediate field, very distant jumps are handled either by combining multiple instructions similarly to constant-building, or by using instruction variants specifically designed to reach a wider address range at the cost of extra encoding bits dedicated to the offset.
Why Multiple Processors Need Synchronization
When a single program runs on one core, instructions execute in a predictable order. But when a program is split across multiple cores that share the same memory, a new problem arises: two cores might try to read and update the same memory location at nearly the same moment, a situation called a Race Condition, which can silently corrupt shared data if left unmanaged.
Atomic Instructions: Read-Modify-Write Without Interruption
To prevent this, RISC-V provides Atomic Instructions, which perform a read, a modification, and a write to memory as a single indivisible step that no other core can interrupt partway through.
A representative atomic instruction used for synchronization:
amoswap.d a, b, (c)This instruction atomically swaps the value in register b with the value currently stored at the memory address held in register c, placing the old memory value into register a. Because this entire sequence is guaranteed to complete without another core interfering in the middle, it can be used to build higher-level coordination tools such as a Lock, which ensures only one core at a time can access a shared resource.
Why These Two Topics Belong Together
Both wide-value construction and atomic synchronization address the same underlying constraint: a fixed, narrow instruction format. Just as large constants must be assembled from smaller pieces across multiple instructions, safe coordination between cores must be built from small, guaranteed-indivisible hardware operations rather than assumed to happen correctly by default.