RISC-V Instruction Set(ISA)

RISC

  • Fixed-length instructions
  • Load-store architecture: Only load (lw) and store (sw) instructions access memory
    • All ALU operations work on registers only.

Data Store

Register Operands

RISC-V has a 32 $\times$ 64-bit register file: x0 to x31

  • Use for frequently accessed data
  • word: 32-bit data
  • double word: 64-bit data
Register(s) Purpose Notes
x0 the constant value 0 0
x1 return address ra
x2 stack pointer sp
x3 global pointer
x4 thread pointer
x5-x7 temporaries t0-t2
x8 frame pointer
x9 saved registers
x10-x11 function arguments/results
x12-x17 function arguments
x18-x27 saved registers
x28-x31 temporaries
  • x5 – x7, x28 – x31: temporary registers
    • Not preserved by the callee
  • x8 – x9, x18 – x27: saved registers
    • If used, the callee saves and restores them

Memory Operands

  • Main memory used for composite data
    • Arrays, structures, dynamic data
  • To apply arithmetic operations
    • Load values from memory into registers
    • Store result from register to memory
  • Memory is byte addressed
    • Each address identifies an 8-bit byte
  • RISC-V is Little Endian
    • Least-significant byte at least address of a word
  • RISC-V does not require words to be aligned in memory
ld x9, 64(x22)
sd x9, 46(x22)

Compare Registers and Memory:

  • Registers are faster to access than memory
  • Operating on memory data requires loads and stores
  • Compiler must use registers for variables as much as possible

Format of Instructions

All RISC-V base instructions are 32 bits long, encoded in one standard formats.

Format Purpose Key Fields (bit layout, [31:0]) Immediate Reassembly Opcode (hex) Opcode (bin) Mnemonic Examples
R-type Register-register ALU ops
(寄存器-寄存器运算)
[funct7][rs2][rs1][funct3][rd][opcode]
(7)(5)(5)(3)(5)(7)
(no immediate) 0x33 0110011 addsuband
I-type Immediate ALU, loads
(立即数运算、加载)
[imm[11:0]][rs1][funct3][rd][opcode]
(12)(5)(3)(5)(7)
imm = sign_extend(imm[11:0]) 0x13 0010011 addiorilw
S-type Store instructions
(存储指令)
[imm[11:5]][rs2][rs1][funct3][imm[4:0]][opcode]
(7)(5)(5)(3)(5)(7)
imm = sign_extend(imm[11:5] | imm[4:0]) 0x23 0100011 swshsb
SB-type Branches
(条件分支)
[imm[12]][imm[10:5]][rs2][rs1][funct3][imm[4:1]][imm[11]][opcode]
(1)(6)(5)(5)(3)(4)(1)(7)
imm = sign_extend(imm[12] | imm[11] | imm[10:5] | imm[4:1] | 0)
→ lowest bit is always 0(对齐到半字)
0x63 1100011 beqbneblt
U-type LUI, AUIPC
(加载高 20 位)
[imm[31:12]][rd][opcode]
(20)(5)(7)
imm = imm[31:12] << 12 0x37 / 0x17 0110111 / 0010111 lui / auipc
UJ-type JAL (jump and link)
(无条件跳转并链接)
[imm[20]][imm[10:1]][imm[11]][imm[19:12]][rd][opcode]
(1)(10)(1)(8)(5)(7)
imm = sign_extend(imm[20] | imm[19:12] | imm[11] | imm[10:1] | 0)
→ lowest bit is always 0
0x6F 1101111 jal

R-format

![[images/Pasted image 20251126161123.png]]

Fields Purpose
opcode operation code
rd destination register number
funct3 3-bit function code (additional opcode)
rs1 the first source register number
rs2 the second source register number
funct7 7-bit function code (addtional opcode)
add x9, x20, x21

![[images/Pasted image 20251126161335.png]]

I-format

![[images/Pasted image 20251126161414.png]]

Immediate arithmetic and load instructions

Fields Purpose
rs1 source or base address register number
immediate constant operand, or offset added to base address 2s-complement, sign extended

S-format

![[images/Pasted image 20251126161558.png]]
Different immediate format for store instructions

Fields Purpose
rs1 base address register number
rs2 source operand register number
immediate offset added to base address

Core Instructions

Arithmetic & Logic

R-type

add  x1, x2, x3    # x1 = x2 + x3
sub  x1, x2, x3    # x1 = x2 - x3
mul  x1, x2, x3    # x1 = x2 * x3
and  x1, x2, x3    # bitwise AND
or   x1, x2, x3    # bitwise OR
slt  x1, x2, x3    # set if less than (signed): x1 = (x2 < x3) ? 1 : 0
sltu x1, x2, x3    # set if less than (unsigned)
  • Uses two source registers (rs1, rs2) and one destination (rd).
  • funct3 / funct7 distinguish between add / sub, slt / sltu

Immediate Operands

I-type

Constant data specified in an instruction

addi x1, x2, 10    # x1 = x2 + 10
andi x1, x2, 0xF   # x1 = x2 & 0xF
ori  x1, x2, 1     # x1 = x2 | 1
slli x1, x2, 3	   # x1 = x2 << 3
srli x1, x2, 3	   # x1 = x2 >> 3

Memory Access

Load Word

I-type

lw x1, 8(x2)	# x1 = Mem[x2 + 8] as int32, load word, need sign extension
ld x1, 8(x2)	# x1 = Mem[x2 + 8] as int64, load double word

Store Word

S-type

sw x1, 8(x2)	# Mem[x2 + 8] = x1, load low 32-bit to memory
sd x1, 8(x2)	# Mem[x2 + 8] = x1

Important: RISC-V uses base + offset addressing. No register indirect or scaled indexing in base ISA.

Load 扩展看符号,Store 原样写内存

Control Flow

Conditional Branches

SB-type

beq  x1, x2, Label   # branch if x1 == x2
bne  x1, x2, Label   # branch if x1 != x2
blt  x1, x2, Label   # branch if x1 < x2 (signed)
bge  x1, x2, Label   # branch if x1 >= x2
bltu x1, x2, Label   # unsigned comparison
  • Offset is PC-relative, in multiples of 2 bytes (must be even).
  • Encoded as a 12-bit signed offset, shifted left by 1 (so actual range: ±4 KiB).

Unconditional Jumps

jal  x1, Label	# jump and link: x1 = PC+4; PC = Label
jalr x0, 0(x1)	# jump register (often used for return, x1 is ra): PC = x1 + 0

Recursive

addi sp, sp, -16    # Allocate 16 bytes on stack

sd ra, 8(sp)        # Save return address at sp + 8
sd s0, 0(sp)        # Save callee-saved register s0 at sp + 0

ld ra, 8(sp)        # Restore return address
ld s0, 0(sp)        # Restore s0
addi sp, sp, 16     # Deallocate 16 bytes (move sp back up)
  • When your function calls another function (e.g., recursively), the jal instruction overwrites ra.
  • So you must save the original ra before making the call.
  • On RV64, pointers/addresses are 8 bytes → needs 8 bytes in memory.
long long int fact(long long int n) {
	if (n < 1) return 1;
	else return n * fact(n - 1);
}

To assmebly:

fact:
	addi sp,sp,-16 		# Save return address and n on stack
	sd x1,8(sp)
	sd x10,0(sp)
	addi x5,x10,-1 		# x5 = n - 1
	bge x5,x0,L1 		# if n >= 1, go to L1
	addi x10,x0,1 		# Else, set return value to 1
	addi sp,sp,16 		# Pop stack, don’t bother restoring values
	jalr x0,0(x1) 		# Return
L1: addi x10,x10,-1 	# n = n - 1
	jal x1,fact 		# call fact(n-1)
	addi x6,x10,0 		# move result of fact(n - 1) to x6
	ld x10,0(sp) 		# Restore caller’s n
	ld x1,8(sp) 		# Restore caller’s return address
	addi sp,sp,16 		# Pop stack
	mul x10,x10,x6 		# return n * fact(n-1)
	jalr x0,0(x1) 		# return

递归时需要手动移动栈顶指针,存储函数返回地址(x1),以及函数的返回值(在这里指定为 x10

Number Representation

Two’s Complement

  • Unsigned: Range $0$ to $2^n−1$
  • Two’s Complement Signed(补码有符号整数): Range $−2^{n−1}$ to $2^{n−1}−1$

Sign Extension

  • When loading a byte into a 64-bit register:
    • lb (load byte): sign-extends the byte → preserves signed value
    • lbu (load byte unsigned): zero-extends → treats as unsigned
# x2 = 0xFF
lb x1, 0(x2) # x1 = 0xFFFFFFFFFFFFFFFF (= -1)
lbu x1, 0(x2)	# x1 = 0x00000000000000FF (= 255)

Hexadecimal

  • Instructions are stored in memory as 32-bit binary words.
  • For readability, we use hexadecimal(十六进制):
    • Each hex digit = 4 bits → 8 hex digits per instruction
    • Example: 0x00128313 might decode to addi x6, x5, 1

Little/Big-Endian

Little-Endian:

  • Least Significant Byte (LSB) is stored at the lowest memory address.
  • Most Significant Byte (MSB) is stored at the highest memory address.
  • Used by: x86, x86-64, RISC-V (by default), ARM (configurable but often little-endian).

Big-Endian:

  • Most Significant Byte (MSB) is stored at the lowest memory address.
  • Least Significant Byte (LSB) is stored at the highest memory address.
  • Used by: Motorola 68k, SPARC (historically), network protocols (TCP/IP = “network byte order” = big-endian).

Show how the value 0xabcdef12 would be arranged in memory of a little-endian  and a big-endian machine. Assume the data are stored starting at address 0 and that  the word size is 4 Bytes.

First, split the value to: AB, CD, EF, 12

In little-endian, it would be:

0 1 2 3
12 EF CD AB

In big-endian, it would be:

0 1 2 3
AB CD EF 12

Exam

Translate the following C code to RISC-V.  Assume that the variables f , g , h , i , and j are assigned to registers x5, x6, x7, x28, and x29, respectively. Assume that the base address of the arrays A and B are in registers x10 and x11, respectively. Assume that the elements of the arrays A and B are 8-byte words:

B[8] = A[i] + A[j];

we need:

  • ld: load double word from arrays A and B. Notes: ld rd, offset(rs1),offset 必须是 12 位有符号立即数
  • add: calculate the result.
  • sd: store double word to array B.
  • slli: convert index to offset. offset = index $\times$ 8. Can be replaced by mul

We can use x5 - x7 / x28 - x31 to store temporary variables.

# get offset
slli x30, x28, 3	# x30 = x28 << 3 = x28 * 8
slli x31, x29, 3	# x31 = x29 << 3 = x29 * 8

# get address
add x30, x10, x30	# x30 = x10 + x30 = &A + x30 = &A[i]
add x31, x10, x31	# x31 = &A[j]

# load from arrays
ld x5, 0(x30)		# x5 = A[i]
ld x6, 0(x31)		# x6 = A[j]

# add
add x7, x5, x6		# x7 = x5 + x6

# store to arrays
sd x7, 64(x11)		# B[8] = x7

Translate the following RISC-V code to C. Assume that the variables f , g , h , i , and j are assigned to registers x5, x6, x7, x28,  and x29 , respectively. Assume that the base address of the arrays A and B are in  registers x10 and x11 , respectively.

addi x30, x10, 8 
addi x31, x10, 0 
sd x31, 0(x30)
ld x30, 0(x30)
add x5,x30, x31

long long int x30 = &A[1];
long long int x31 = &A[0];

x31 = *(x30);
x30 = *(x30);
f = x30 + x31;

Find the shortest sequence of RISC-V instructions that extracts bits 16 down to 11  from register x5 and uses the value of this field to replace bits 31 down to 26 in  register x6 without changing the other bits of registers x5 or x6 . (Be sure to test your  code using x5 = 0 and x6 = 0xffffffffffffffff . Doing so may reveal a common  oversight.)

srli a0, x5, 11
andi a0, a0, 0x3f
slli a0, a0, 26

li a1, 0x3f
slli a1, a1, 26
not a1, a1
and x6, x6, a1

or x6, x6, a0

Suppose the program counter (PC) is set to 0x20000000 . 

a) What range of addresses can be reached using the RISC-V jump-and-link ( jal )  instruction? (In other words, what is the set of possible values for the PC after the  jump instruction executes?) 

b)  What range of addresses can be reached using the RISC-V branch if equal ( beq )  instruction? (In other words, what is the set of possible values for the PC after the  branch instruction executes?)

jal 使用 20-bit 有符号立即数,跳转范围为 $[-2^{19},2^{19}-1]$,立即数左移 1 位,得

$$
[-2^{20},2^{20}-2]
$$

beq 使用 12-bit 有符号立即数,跳转范围为 $[-2^{11}, 2^{11}-1]$,立即数左移 1 位,得

$$
[-2^{12},2^{12}-2]
$$