Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

1Learning Outcomes

2RISC-V Data Transfer Basics

Consider the memory access syntax for loading and storing words shown in Table 1:

Table 1:RV32I Instructions: Load word (lw), Store word (sw).

InstructionNameDescription
lw rd imm(rs1)Load WordR[rd] = M[R[rs1] + imm][31:0][1]
sw rs2 imm(rs1)Store WordM[R[rs1] + imm][31:0] = R[rs2][31:0][1]

2.1Load Word

The load word instruction:

The memory address is computed with register and immediate arithmetic. rs1 is called the base register. The immediate imm is called the offset and is a numeric constant that must be known at assembly time.

Let’s look at an example:

lw x10 12(x5)

In Figure 1, executing this lw instruction loads the word 0x00564253 from memory (at address 0x10C) into register x10.

"TODO"

Figure 1:Illustration of lw x10 12(x5).

2.2Store Word

The store word instruction:

Let’s look at an example:

sw x10 0(x5)

In Figure 2, executing this sw instruction stores the word 0x12345678 in register x10 to memory (at address 0x100).

"TODO"

Figure 2:Illustration of lw x10 12(x5).

2.3Endianness and Alignment

This section is a good time to step back and realize that we have now learned enough to interpret the actual RISC-V specification!

From the RV32I Specification:

In RISC-V, endianness is byte-address invariant.

In a system for which endianness is byte-address invariant, the following property holds: if a byte is stored to memory at some address in some endianness, then a byte-sized load from that address in any endianness returns the stored value.

RISC-V therefore supports both little-endian and big-endian architectures, and loads and stores are consistent with the endianness of the architecture. As an example:

In a little-endian configuration, multibyte stores write the least-significant register byte at the lowest memory byte address, followed by the other register bytes in ascending order of their significance. Loads similarly transfer the contents of the lesser memory byte addresses to the less-significant register bytes.

From the RV32I Specification:

Regardless of EEI[2], loads and stores whose effective addresses are naturally aligned shall not raise an address-misaligned exception. Loads and stores whose effective address is not naturally aligned to the referenced datatype (i.e., the effective address is not divisible by the size of the access in bytes) have behavior dependent on the EEI. ... Misaligned accesses are occasionally required when porting legacy code...

According to the RISC-V standard, loads and stores of words should specify word-aligned addresses. For RV32I, this means R[rs1] + imm should be a multiple of 4. Addresses that are not word-aligned produce behavior that is implementation-dependent. Put another way, while RISC-V technically allows misaligned accesses to support legacy code, it is very slow and messy. You should treat the “should” as a “must” to avoid “scribbling” all over the memory. :-)

3Partial Loads and Stores

We often work with data types smaller than 32 bits, like 8-bit characters[3]. It would be wasteful to use a full word for these, so RISC-V supports instructions for bytewise data transfers.

Recall that registers are themselves are word-sized. The RV32I specification therefore defines what to do with the other bytes when loading or storing byte-sized or half-word-sized data.

3.1Store

Table 2:RV32I store instructions.

InstructionNameDescription
sb rs2 imm(rs1)Store ByteM[R[rs1] + imm][7:0] = R[rs2][7:0]
sh rs2 imm(rs1)Store Half-wordM[R[rs1] + imm][15:0] = R[rs2][15:0]
sw rs2 imm(rs1)Store WordM[R[rs1] + imm][31:0] = R[rs2][31:0]

Table 2 demonstrates the RV32I Specification:

The SW, SH, and SB instructions store 32-bit, 16-bit, and 8-bit values from the low bits of register rs2 to memory.

Consider the instruction:

sb x10 0(x5)

As shown in Figure 3, this store byte instruction then ignores the upper bytes in the source register x10 and only considers the least significant byte R[x10][7:0] (the value 0xEF). This single byte is then stored to memory at address 0x100.

"TODO"

Figure 3:Example store byte instruction in memory.

3.2Load

The Load Byte instruction lb plucks a single byte from memory and (analogous to sb) places the byte in R[rd][7:0], the lowest byte position of the destination register rd. However, unlike stores, loads of sub-word widths must consider what to put in the upper bits, or R[rd][31:8] (see x10 in Figure 4).

"TODO"

Figure 4:Example load byte instruction in memory. lb x10 0(x5) loads in 0xEF but also must determine how to fill the upper 24 bits of x10.

Because assembly operations determine how to interpret operands, we therefore define two “load byte” operations: Load Byte lb and Load Byte Unsigned lbu.

Table 3:RV32I load instructions.

InstructionNameDescription
lb rd imm(rs1)Load ByteR[rd] = M[R[rs1] + imm][7:0] (Sign-extend)
lbu rd imm(rs1)Load Byte (Unsigned)R[rd] = M[R[rs1] + imm][7:0] (Zero-extend)
lh rd imm(rs1)Load Half-wordR[rd] = M[R[rs1] + imm][15:0] (Sign-extend)
lhu rd imm(rs1)Load Half-word (Unsigned)R[rd] = M[R[rs1] + imm][15:0] (Zero-extend)
lw rd imm(rs1)Load WordR[rd] = M[R[rs1] + imm][31:0]
Footnotes
  1. Like before, we use Verilog syntax to more precisely define the operation, but you aren’t expected to learn Verilog for this course.

  2. The execution environment interface (EEI). We do not discuss the EEI in this course.

  3. Additionally, RGB colors are 24-bit values; each of the three color channels is 8-bit wide.

  4. In the lecture video, Professor Nikolic makes the analogy that sign extension is like putting a dollop of avocado on one side of toast, then smearing the top bit all over the upper bits.