RISC-V 32 位基础指令集(RV32I)完整参考手册
一、架构概述
RISC-V 是一个开源的精简指令集计算机(RISC)架构,由加州大学伯克利分校于 2010 年发起设计。
| 项目 |
说明 |
| 指令长度 |
固定 32 位(4 字节) |
| 寄存器数量 |
32 个通用寄存器(x0--x31) + PC |
| 寄存器宽度 |
32 位 |
| 字节序 |
小端(Little-endian) |
| 寻址方式 |
字节寻址 |
| 立即数范围 |
12 位(I/S 型)、20 位(U 型)、21 位(J 型) |
二、寄存器组详解
2.1 完整寄存器表
| 寄存器编号 |
ABI 名称 |
别名 |
用途 |
调用约定 |
| x0 |
zero |
--- |
硬连线零,读取恒为 0,写入无效 |
--- |
| x1 |
ra |
--- |
返回地址(Return Address) |
Caller-saved |
| x2 |
sp |
--- |
栈指针(Stack Pointer) |
Callee-saved |
| x3 |
gp |
--- |
全局指针(Global Pointer) |
--- |
| x4 |
tp |
--- |
线程指针(Thread Pointer) |
--- |
| x5 |
t0 |
--- |
临时寄存器 / 链接暂存 |
Caller-saved |
| x6 |
t1 |
--- |
临时寄存器 |
Caller-saved |
| x7 |
t2 |
--- |
临时寄存器 |
Caller-saved |
| x8 |
s0 |
fp |
保存寄存器 / 帧指针(Frame Pointer) |
Callee-saved |
| x9 |
s1 |
--- |
保存寄存器 |
Callee-saved |
| x10 |
a0 |
--- |
函数参数 0 / 返回值 0 |
Caller-saved |
| x11 |
a1 |
--- |
函数参数 1 / 返回值 1 |
Caller-saved |
| x12 |
a2 |
--- |
函数参数 2 |
Caller-saved |
| x13 |
a3 |
--- |
函数参数 3 |
Caller-saved |
| x14 |
a4 |
--- |
函数参数 4 |
Caller-saved |
| x15 |
a5 |
--- |
函数参数 5 |
Caller-saved |
| x16 |
a6 |
--- |
函数参数 6 |
Caller-saved |
| x17 |
a7 |
--- |
函数参数 7 / 系统调用号 |
Caller-saved |
| x18 |
s2 |
--- |
保存寄存器 |
Callee-saved |
| x19 |
s3 |
--- |
保存寄存器 |
Callee-saved |
| x20 |
s4 |
--- |
保存寄存器 |
Callee-saved |
| x21 |
s5 |
--- |
保存寄存器 |
Callee-saved |
| x22 |
s6 |
--- |
保存寄存器 |
Callee-saved |
| x23 |
s7 |
--- |
保存寄存器 |
Callee-saved |
| x24 |
s8 |
--- |
保存寄存器 |
Callee-saved |
| x25 |
s9 |
--- |
保存寄存器 |
Callee-saved |
| x26 |
s10 |
--- |
保存寄存器 |
Callee-saved |
| x27 |
s11 |
--- |
保存寄存器 |
Callee-saved |
| x28 |
t3 |
--- |
临时寄存器 |
Caller-saved |
| x29 |
t4 |
--- |
临时寄存器 |
Caller-saved |
| x30 |
t5 |
--- |
临时寄存器 |
Caller-saved |
| x31 |
t6 |
--- |
临时寄存器 |
Caller-saved |
| --- |
pc |
--- |
程序计数器,不可直接读写 |
--- |
2.2 寄存器功能分组
复制代码
┌─────────────────────────────────────────────────────────┐
│ 32 个通用寄存器 │
├─────────────┬───────────────────┬───────────────────────┤
│ 特殊寄存器 │ 调用相关寄存器 │ 通用工作寄存器 │
├─────────────┼───────────────────┼───────────────────────┤
│ x0 (zero) │ x1 (ra) │ x5-t0, x6-t1, x7-t2 │
│ x2 (sp) │ x8 (s0/fp) │ x28-t3, x29-t4 │
│ x3 (gp) │ x9 (s1) │ x30-t5, x31-t6 │
│ x4 (tp) │ x10-a0 ~ x17-a7 │ │
│ │ x18-s2 ~ x27-s11 │ │
└─────────────┴───────────────────┴───────────────────────┘
2.3 各寄存器详细说明
x0 (zero) --- 零寄存器
- 最特殊的寄存器,硬件上恒输出 0
- 任何对 x0 的写入操作都会被忽略
- 利用 x0 可以实现许多伪指令,例如:
mv rd, rs → addi rd, rs, 0
neg rd, rs → sub rd, x0, rs
ret → jalr x0, 0(ra)
nop → addi x0, x0, 0
li rd, imm(小立即数)→ addi rd, x0, imm
x1 (ra) --- 返回地址
jal(跳转并链接)和 jalr(寄存器跳转并链接)会自动将返回地址(PC+4)写入 ra
- 函数返回时通过
jalr x0, 0(ra) 跳回调用点
- Caller-saved:被调函数可自由使用,调用者需自行保存
x2 (sp) --- 栈指针
- 指向当前栈顶
- RISC-V 要求栈 16 字节对齐(RV32 中至少 4 字节对齐,ABI 推荐 16 字节)
- 栈从高地址向低地址增长(push 时 sp 减小)
- Callee-saved:被调函数必须保存和恢复
x3 (gp) --- 全局指针
- 通常由链接器设置为指向全局数据区的中间位置
- 允许通过 gp + 12 位有符号偏移(±2KB)访问全局变量
- 在小型嵌入式程序中特别有用
- 调用约定中不归类为 caller/callee-saved,由系统管理
x4 (tp) --- 线程指针
- 指向线程局部存储(TLS)区域
- 多线程环境中每个线程有独立的 tp 值
- 通常由运行时系统管理,用户代码很少直接修改
x5 (t0) --- 临时寄存器 / 链接暂存
- 临时计算、中间结果
- 也被
auipc+jalr 长跳转序列用作暂存
- Caller-saved
x6--x7 (t1, t2) --- 临时寄存器
- 临时计算,不需要跨函数调用保持的值
- Caller-saved
x8 (s0/fp) --- 保存寄存器 / 帧指针
- 双重角色:作为 s0 使用时是普通的 callee-saved 寄存器
- 作为 fp(帧指针)使用时,指向当前栈帧的固定位置
- 帧指针有助于调试器进行栈回溯(stack unwinding)
- Callee-saved
x9 (s1) --- 保存寄存器
- 需要跨函数调用保持的局部变量
- Callee-saved
x10--x11 (a0, a1) --- 参数 / 返回值寄存器
- 传递函数的前 2 个整数参数
- 函数返回值放在这两个寄存器中(可返回 64 位值:a0 低 32 位,a1 高 32 位)
- Caller-saved
x12--x17 (a2--a7) --- 参数寄存器
- 传递函数的第 3--8 个整数参数
- a7 同时用于传递系统调用号(Linux 约定)
- Caller-saved
x18--x27 (s2--s11) --- 保存寄存器
- 需要跨函数调用保持的局部变量(共 10 个)
- 被调函数如果使用了这些寄存器,必须在入口保存、出口恢复
- Callee-saved
x28--x31 (t3--t6) --- 临时寄存器
- 额外的临时寄存器(共 4 个)
- Caller-saved
PC --- 程序计数器
- 指向下一条要执行的指令地址
- 不能作为指令的源/目的寄存器直接访问
- 只能通过分支、跳转、异常等间接修改
2.4 Caller-saved 与 Callee-saved
复制代码
Caller-saved(调用者保存)--- 共 16 个:
ra, t0-t6, a0-a7
┌──────────────────────────────────────────────┐
│ 调用者如果需要在函数调用后继续使用这些寄存器, │
│ 必须在调用前自行保存到栈上。 │
│ 被调函数可以自由使用这些寄存器。 │
└──────────────────────────────────────────────┘
Callee-saved(被调者保存)--- 共 13 个:
sp, s0-s11
┌──────────────────────────────────────────────┐
│ 被调函数如果要使用这些寄存器, │
│ 必须在函数入口保存原值,函数出口恢复原值。 │
│ 保证调用者看到的值不变。 │
└──────────────────────────────────────────────┘
2.5 函数调用示例
assembly
复制代码
# int add(int a, int b) { return a + b; }
# 调用约定:a=a0, b=a1, 返回值=a0
add_func:
add a0, a0, a1 # a0 = a + b
ret # 返回(jalr x0, 0(ra))
# 调用 add(3, 5)
caller:
addi sp, sp, -16 # 分配栈帧
sw ra, 12(sp) # 保存返回地址(ra 是 caller-saved)
sw s0, 8(sp) # 保存 s0(callee-saved)
li a0, 3 # 第一个参数
li a1, 5 # 第二个参数
call add_func # 调用(等价于 jal ra, add_func)
# 返回值在 a0 中,值为 8
lw s0, 8(sp) # 恢复 s0
lw ra, 12(sp) # 恢复返回地址
addi sp, sp, 16 # 释放栈帧
ret
三、指令格式
3.1 六种基本格式
RISC-V 定义了 6 种 32 位指令格式:
复制代码
R-type(寄存器-寄存器运算):
31 25 24 20 19 15 14 12 11 7 6 0
┌─────────┬───────┬───────┬──────┬──────┬─────────┐
│ funct7 │ rs2 │ rs1 │funct3│ rd │ opcode │
└─────────┴───────┴───────┴──────┴──────┴─────────┘
7 位 5 位 5 位 3 位 5 位 7 位
I-type(立即数运算 / 加载):
31 20 19 15 14 12 11 7 6 0
┌─────────────────┬───────┬──────┬──────┬─────────┐
│ imm[11:0] │ rs1 │funct3│ rd │ opcode │
└─────────────────┴───────┴──────┴──────┴─────────┘
12 位 5 位 3 位 5 位 7 位
S-type(存储):
31 25 24 20 19 15 14 12 11 7 6 0
┌─────────┬───────┬───────┬──────┬─────────┬─────────┐
│imm[11:5]│ rs2 │ rs1 │funct3│imm[4:0] │ opcode │
└─────────┴───────┴───────┴──────┴─────────┴─────────┘
7 位 5 位 5 位 3 位 5 位 7 位
B-type(条件分支):
31 30 25 24 20 19 15 14 12 11 8 7 6 0
┌───┬─────────┬───────┬───────┬──────┬───────┬───┬─────────┐
│12 │imm[10:5]│ rs2 │ rs1 │funct3│imm[4:1]│11 │ opcode │
└───┴─────────┴───────┴───────┴──────┴───────┴───┴─────────┘
1 位 6 位 5 位 5 位 3 位 4 位 1 位 7 位
U-type(高位立即数):
31 12 11 7 6 0
┌─────────────────────────────┬──────┬─────────┐
│ imm[31:12] │ rd │ opcode │
└─────────────────────────────┴──────┴─────────┘
20 位 5 位 7 位
J-type(无条件跳转):
31 30 21 20 19 12 11 7 6 0
┌───┬─────────────┬───┬─────────────┬──────┬─────────┐
│20 │ imm[10:1] │11 │ imm[19:12] │ rd │ opcode │
└───┴─────────────┴───┴─────────────┴──────┴─────────┘
1 位 10 位 1 位 8 位 5 位 7 位
3.2 立即数编码说明
| 格式 |
立即数范围 |
说明 |
| I-type |
-2048 ~ +2047(12 位有符号) |
符号扩展到 32 位 |
| S-type |
-2048 ~ +2047(12 位有符号) |
分为 imm[11:5] 和 imm[4:0] |
| B-type |
-4096 ~ +4094(13 位有符号,步长 2) |
最低位恒为 0 |
| U-type |
0 ~ 1048575(20 位) |
直接放在高 20 位 |
| J-type |
-1048576 ~ +1048574(21 位有符号,步长 2) |
最低位恒为 0 |
3.3 操作码(opcode)映射
| opcode[6:0] |
十六进制 |
格式 |
指令类别 |
| 0110111 |
0x37 |
U-type |
LUI |
| 0010111 |
0x17 |
U-type |
AUIPC |
| 1101111 |
0x6F |
J-type |
JAL |
| 1100111 |
0x67 |
I-type |
JALR |
| 1100011 |
0x63 |
B-type |
分支指令 |
| 0000011 |
0x03 |
I-type |
加载指令 |
| 0100011 |
0x23 |
S-type |
存储指令 |
| 0010011 |
0x13 |
I-type |
立即数算术/逻辑 |
| 0110011 |
0x33 |
R-type |
寄存器算术/逻辑 |
| 0001111 |
0x0F |
I-type |
FENCE |
| 1110011 |
0x73 |
I-type |
系统 / CSR |
3.4 funct3 编码
算术/逻辑指令(opcode = 0x13 / 0x33)
| funct3 |
R-type 指令 |
I-type 指令 |
| 000 |
ADD / SUB |
ADDI |
| 001 |
SLL |
SLLI |
| 010 |
SLT |
SLTI |
| 011 |
SLTU |
SLTIU |
| 100 |
XOR |
XORI |
| 101 |
SRL / SRA |
SRLI / SRAI |
| 110 |
OR |
ORI |
| 111 |
AND |
ANDI |
分支指令(opcode = 0x63)
| funct3 |
指令 |
条件 |
| 000 |
BEQ |
rs1 == rs2 |
| 001 |
BNE |
rs1 != rs2 |
| 100 |
BLT |
rs1 < rs2(有符号) |
| 101 |
BGE |
rs1 >= rs2(有符号) |
| 110 |
BLTU |
rs1 < rs2(无符号) |
| 111 |
BGEU |
rs1 >= rs2(无符号) |
加载指令(opcode = 0x03)
| funct3 |
指令 |
宽度 |
扩展方式 |
| 000 |
LB |
8 位 |
符号扩展 |
| 001 |
LH |
16 位 |
符号扩展 |
| 010 |
LW |
32 位 |
--- |
| 100 |
LBU |
8 位 |
零扩展 |
| 101 |
LHU |
16 位 |
零扩展 |
存储指令(opcode = 0x23)
| funct3 |
指令 |
宽度 |
| 000 |
SB |
8 位 |
| 001 |
SH |
16 位 |
| 010 |
SW |
32 位 |
四、RV32I 指令完整列表
4.1 算术指令
寄存器-寄存器(R-type)
| 指令 |
语法 |
操作 |
说明 |
| ADD |
add rd, rs1, rs2 |
rd ← rs1 + rs2 |
有符号加法 |
| SUB |
sub rd, rs1, rs2 |
rd ← rs1 - rs2 |
有符号减法 |
立即数算术(I-type)
| 指令 |
语法 |
操作 |
说明 |
| ADDI |
addi rd, rs1, imm |
rd ← rs1 + sext(imm) |
加立即数(12 位有符号) |
高位立即数(U-type)
| 指令 |
语法 |
操作 |
说明 |
| LUI |
lui rd, imm |
rd ← imm << 12 |
20 位立即数加载到高 20 位 |
| AUIPC |
auipc rd, imm |
rd ← PC + (imm << 12) |
PC 相对高位加载 |
加载 32 位常数示例:
assembly
复制代码
lui a0, 0x12345 # a0 = 0x12345000
addi a0, a0, 0x678 # a0 = 0x12345678
注意: RV32I 没有 MUL、DIV、REM 指令,这些属于 M 扩展。
4.2 逻辑指令
| 指令 |
语法 |
操作 |
| AND |
and rd, rs1, rs2 |
rd ← rs1 & rs2 |
| OR |
or rd, rs1, rs2 |
rd ← rs1 | rs2 |
| XOR |
xor rd, rs1, rs2 |
rd ← rs1 ^ rs2 |
| ANDI |
andi rd, rs1, imm |
rd ← rs1 & sext(imm) |
| ORI |
ori rd, rs1, imm |
rd ← rs1 | sext(imm) |
| XORI |
xori rd, rs1, imm |
rd ← rs1 ^ sext(imm) |
特殊用法:
xori rd, rs, -1 等价于按位取反(NOT)
andi rd, rs, 0xFF 可用于提取低 8 位
andi rd, rs, 1 可用于判断奇偶
4.3 移位指令
| 指令 |
语法 |
操作 |
说明 |
| SLL |
sll rd, rs1, rs2 |
rd ← rs1 << rs2[4:0] |
逻辑左移 |
| SRL |
srl rd, rs1, rs2 |
rd ← rs1 >> rs2[4:0] |
逻辑右移(补 0) |
| SRA |
sra rd, rs1, rs2 |
rd ← rs1 >>> rs2[4:0] |
算术右移(补符号位) |
| SLLI |
slli rd, rs1, shamt |
rd ← rs1 << shamt |
立即数逻辑左移 |
| SRLI |
srli rd, rs1, shamt |
rd ← rs1 >> shamt |
立即数逻辑右移 |
| SRAI |
srai rd, rs1, shamt |
rd ← rs1 >>> shamt |
立即数算术右移 |
- 移位量仅使用低 5 位(0--31)
SRAI 通过 funct7 的最高位(bit 30 = 1)与 SRLI(bit 30 = 0)区分
常见移位技巧:
assembly
复制代码
slli a0, a0, 1 # a0 = a0 * 2
slli a0, a0, 3 # a0 = a0 * 8
srli a0, a0, 2 # a0 = a0 / 4(无符号)
srai a0, a0, 2 # a0 = a0 / 4(有符号,向负无穷取整)
4.4 比较指令
| 指令 |
语法 |
操作 |
说明 |
| SLT |
slt rd, rs1, rs2 |
rd ← (rs1 < rs2) ? 1 : 0 |
有符号比较 |
| SLTU |
sltu rd, rs1, rs2 |
rd ← (rs1 < rs2) ? 1 : 0 |
无符号比较 |
| SLTI |
slti rd, rs1, imm |
rd ← (rs1 < sext(imm)) ? 1 : 0 |
有符号立即数比较 |
| SLTIU |
sltiu rd, rs1, imm |
rd ← (rs1 < sext(imm)) ? 1 : 0 |
无符号立即数比较 |
注意: SLTIU 的立即数先符号扩展,然后按无符号数比较。
4.5 分支指令(B-type)
分支指令比较两个寄存器,若条件成立则跳转到 PC + offset。
| 指令 |
语法 |
跳转条件 |
| BEQ |
beq rs1, rs2, offset |
rs1 == rs2 |
| BNE |
bne rs1, rs2, offset |
rs1 != rs2 |
| BLT |
blt rs1, rs2, offset |
rs1 < rs2(有符号) |
| BGE |
bge rs1, rs2, offset |
rs1 >= rs2(有符号) |
| BLTU |
bltu rs1, rs2, offset |
rs1 < rs2(无符号) |
| BGEU |
bgeu rs1, rs2, offset |
rs1 >= rs2(无符号) |
- 偏移量范围:±4KB(12 位有符号,最低位恒为 0,步长 2 字节)
- 没有"大于"和"小于等于"指令,通过交换操作数实现:
bgt rs1, rs2, offset → blt rs2, rs1, offset
ble rs1, rs2, offset → bge rs2, rs1, offset
循环示例:
assembly
复制代码
li t0, 0 # sum = 0
li t1, 1 # i = 1
li t2, 101 # 上界 + 1
loop:
bge t1, t2, done # if (i >= 101) break
add t0, t0, t1 # sum += i
addi t1, t1, 1 # i++
j loop # 无条件跳回
done:
# t0 = 5050 (1+2+...+100)
4.6 跳转指令
JAL(Jump And Link)--- J-type
| 指令 |
语法 |
操作 |
| JAL |
jal rd, offset |
rd ← PC+4; PC ← PC + sext(offset) |
- 偏移量范围:±1MB(20 位有符号,最低位恒为 0)
- 典型用法:
jal ra, label --- 调用函数,返回地址存入 ra
JALR(Jump And Link Register)--- I-type
| 指令 |
语法 |
操作 |
| JALR |
jalr rd, rs1, imm |
rd ← PC+4; PC ← (rs1 + sext(imm)) & ~1 |
- 跳转目标 =
rs1 + 立即数,最低位清零(保证 2 字节对齐)
- 典型用法:
- 函数返回:
jalr x0, 0(ra)
- 间接调用:
jalr ra, 0(a0)
- 函数指针调用
4.7 加载指令(I-type)
| 指令 |
语法 |
操作 |
| LB |
lb rd, imm(rs1) |
rd ← sext(M[rs1+imm][7:0]) |
| LBU |
lbu rd, imm(rs1) |
rd ← zext(M[rs1+imm][7:0]) |
| LH |
lh rd, imm(rs1) |
rd ← sext(M[rs1+imm][15:0]) |
| LHU |
lhu rd, imm(rs1) |
rd ← zext(M[rs1+imm][15:0]) |
| LW |
lw rd, imm(rs1) |
rd ← M[rs1+imm][31:0] |
- 偏移量:12 位有符号(-2048 ~ +2047)
LB/LH 符号扩展到 32 位,LBU/LHU 零扩展
4.8 存储指令(S-type)
| 指令 |
语法 |
操作 |
| SB |
sb rs2, imm(rs1) |
M[rs1+imm][7:0] ← rs2[7:0] |
| SH |
sh rs2, imm(rs1) |
M[rs1+imm][15:0] ← rs2[15:0] |
| SW |
sw rs2, imm(rs1) |
M[rs1+imm][31:0] ← rs2[31:0] |
- 偏移量:12 位有符号(-2048 ~ +2047)
4.9 系统指令
| 指令 |
语法 |
说明 |
| ECALL |
ecall |
环境调用(系统调用),陷入操作系统 |
| EBREAK |
ebreak |
断点异常,用于调试器 |
| FENCE |
fence pred, succ |
内存屏障,保证多核内存一致性 |
| FENCE.I |
fence.i |
指令缓存同步,用于自修改代码 |
系统调用约定(Linux RISC-V):
assembly
复制代码
# write(1, buffer, length)
li a7, 64 # 系统调用号 64 = write
li a0, 1 # fd = stdout
la a1, buffer # 缓冲区地址
li a2, 13 # 长度
ecall # 陷入内核
# 返回值在 a0 中
4.10 CSR 指令
CSR(Control and Status Register)用于访问特权寄存器。
| 指令 |
语法 |
操作 |
| CSRRW |
csrrw rd, csr, rs1 |
rd ← CSR; CSR ← rs1 |
| CSRRS |
csrrs rd, csr, rs1 |
rd ← CSR; CSR ← CSR | rs1 |
| CSRRC |
csrrc rd, csr, rs1 |
rd ← CSR; CSR ← CSR & ~rs1 |
| CSRRWI |
csrrwi rd, csr, uimm |
rd ← CSR; CSR ← uimm(5 位) |
| CSRRSI |
csrrsi rd, csr, uimm |
rd ← CSR; CSR ← CSR | uimm |
| CSRRCI |
csrrci rd, csr, uimm |
rd ← CSR; CSR ← CSR & ~uimm |
常用 CSR 寄存器:
| 地址 |
名称 |
说明 |
| 0x000 |
ustatus |
用户模式状态 |
| 0x005 |
utvec |
用户模式陷阱向量基址 |
| 0x041 |
uepc |
用户模式异常 PC |
| 0x042 |
ucause |
用户模式异常原因 |
| 0x043 |
utval |
用户模式异常附加信息 |
| 0x044 |
uip |
用户模式中断挂起 |
| 0x100 |
sstatus |
监管者模式状态 |
| 0x104 |
sie |
监管者中断使能 |
| 0x105 |
stvec |
监管者陷阱向量 |
| 0x141 |
sepc |
监管者异常 PC |
| 0x142 |
scause |
监管者异常原因 |
| 0x143 |
stval |
监管者异常附加信息 |
| 0x144 |
sip |
监管者中断挂起 |
| 0x180 |
satp |
监管者地址转换与保护 |
| 0x300 |
mstatus |
机器模式状态 |
| 0x301 |
misa |
ISA 能力寄存器 |
| 0x302 |
medeleg |
机器异常委托 |
| 0x303 |
mideleg |
机器中断委托 |
| 0x304 |
mie |
机器中断使能 |
| 0x305 |
mtvec |
机器陷阱向量 |
| 0x340 |
mscratch |
机器临时寄存器 |
| 0x341 |
mepc |
机器异常 PC |
| 0x342 |
mcause |
机器异常原因 |
| 0x343 |
mtval |
机器异常附加信息 |
| 0x344 |
mip |
机器中断挂起 |
| 0xB00 |
mcycle |
周期计数器(低 32 位) |
| 0xB02 |
minstret |
退休指令计数器(低 32 位) |
| 0xB80 |
mcycleh |
周期计数器(高 32 位) |
| 0xB82 |
minstreth |
退休指令计数器(高 32 位) |
| 0xF11 |
mvendorid |
厂商 ID |
| 0xF12 |
marchid |
架构 ID |
| 0xF13 |
mimpid |
实现 ID |
| 0xF14 |
mhartid |
硬件线程 ID |
五、完整伪指令表
伪指令由汇编器展开为一条或多条真实指令。
5.1 数据传输伪指令
| 伪指令 |
展开形式 |
说明 |
NOP |
addi x0, x0, 0 |
空操作 |
LI rd, imm |
addi 或 lui+addi |
加载立即数(自动选择最短序列) |
MV rd, rs |
addi rd, rs, 0 |
寄存器复制 |
NOT rd, rs |
xori rd, rs, -1 |
按位取反 |
NEG rd, rs |
sub rd, x0, rs |
算术取反 |
NEGW rd, rs |
subw rd, x0, rs |
算术取反(RV64,32 位) |
SEXT.W rd, rs |
addiw rd, rs, 0 |
符号扩展字(RV64) |
5.2 算术伪指令
| 伪指令 |
展开形式 |
说明 |
SEQZ rd, rs |
sltiu rd, rs, 1 |
rs == 0 时 rd = 1 |
SNEZ rd, rs |
sltu rd, x0, rs |
rs != 0 时 rd = 1 |
SLTZ rd, rs |
slt rd, rs, x0 |
rs < 0 时 rd = 1 |
SGTZ rd, rs |
slt rd, x0, rs |
rs > 0 时 rd = 1 |
5.3 分支伪指令
| 伪指令 |
展开形式 |
说明 |
BEQZ rs, offset |
beq rs, x0, offset |
rs == 0 时跳转 |
BNEZ rs, offset |
bne rs, x0, offset |
rs != 0 时跳转 |
BLEZ rs, offset |
bge x0, rs, offset |
rs <= 0 时跳转 |
BGEZ rs, offset |
bge rs, x0, offset |
rs >= 0 时跳转 |
BLTZ rs, offset |
blt rs, x0, offset |
rs < 0 时跳转 |
BGTZ rs, offset |
blt x0, rs, offset |
rs > 0 时跳转 |
BGT rs1, rs2, offset |
blt rs2, rs1, offset |
rs1 > rs2(有符号) |
BLE rs1, rs2, offset |
bge rs2, rs1, offset |
rs1 <= rs2(有符号) |
BGTU rs1, rs2, offset |
bltu rs2, rs1, offset |
rs1 > rs2(无符号) |
BLEU rs1, rs2, offset |
bgeu rs2, rs1, offset |
rs1 <= rs2(无符号) |
5.4 跳转伪指令
| 伪指令 |
展开形式 |
说明 |
J offset |
jal x0, offset |
无条件跳转(不保存返回地址) |
JAL offset |
jal ra, offset |
调用函数(保存返回地址到 ra) |
JR rs |
jalr x0, 0(rs) |
寄存器间接跳转 |
JALR rs |
jalr ra, 0(rs) |
寄存器间接调用 |
RET |
jalr x0, 0(ra) |
函数返回 |
CALL offset |
auipc ra, offset_hi + jalr ra, ra, offset_lo |
长距离函数调用 |
TAIL offset |
auipc t0, offset_hi + jalr x0, t0, offset_lo |
尾调用(不保存返回地址) |
LA rd, symbol |
auipc rd, offset_hi + addi/lw rd, rd, offset_lo |
加载符号地址 |
5.5 CSR 伪指令
| 伪指令 |
展开形式 |
说明 |
CSRR rd, csr |
csrrs rd, csr, x0 |
读取 CSR |
CSRW csr, rs |
csrrw x0, csr, rs |
写入 CSR |
CSRS csr, rs |
csrrs x0, csr, rs |
置位 CSR 位 |
CSRC csr, rs |
csrrc x0, csr, rs |
清除 CSR 位 |
CSRWI csr, uimm |
csrrwi x0, csr, uimm |
立即数写入 CSR |
CSRSI csr, uimm |
csrrsi x0, csr, uimm |
立即数置位 CSR |
CSRCI csr, uimm |
csrrci x0, csr, uimm |
立即数清除 CSR |
RDCYCLE rd |
csrrs rd, cycle, x0 |
读周期计数器 |
RDTIME rd |
csrrs rd, time, x0 |
读时间计数器 |
RDINSTRET rd |
csrrs rd, instret, x0 |
读退休指令计数器 |
六、内存模型
6.1 内存布局(典型 Linux 用户空间)
复制代码
高地址 0xFFFF_FFFF
┌─────────────────────┐
│ 内核空间 │ ← 用户不可访问
├─────────────────────┤
│ 栈 (Stack) │ ← sp(向下增长)
│ ↓ │
│ │
│ (空闲区域) │
│ │
│ ↑ │
│ 堆 (Heap) │ ← brk/sbrk(向上增长)
├─────────────────────┤
│ .bss(未初始化数据) │
├─────────────────────┤
│ .data(已初始化数据) │ ← gp 指向附近
├─────────────────────┤
│ .rodata(只读数据) │
├─────────────────────┤
│ .text(代码段) │ ← 程序入口
└─────────────────────┘
低地址 0x0000_0000
6.2 栈操作约定
assembly
复制代码
# 函数序言(Prologue)
addi sp, sp, -N # 分配 N 字节栈帧(N 必须 16 字节对齐)
sw ra, (N-4)(sp) # 保存返回地址
sw s0, (N-8)(sp) # 保存需要使用的 callee-saved 寄存器
...
# 函数尾声(Epilogue)
lw ra, (N-4)(sp) # 恢复返回地址
lw s0, (N-8)(sp) # 恢复 callee-saved 寄存器
addi sp, sp, N # 释放栈帧
ret # 返回
七、特权模式
RISC-V 定义了 3 个特权级别:
| 级别 |
编码 |
名称 |
典型用途 |
| 0 |
00 |
User (U) |
用户应用程序 |
| 1 |
01 |
Supervisor (S) |
操作系统内核 |
| 3 |
11 |
Machine (M) |
固件 / 引导程序 |
- M 模式是所有实现必须支持的最低特权模式
- 嵌入式系统可仅使用 M 模式
- Linux 系统通常使用 U/S/M 三级
特权指令
| 指令 |
可用模式 |
说明 |
| ECALL |
U, S, M |
环境调用 |
| EBREAK |
U, S, M |
断点 |
| MRET |
M |
从机器模式陷阱返回 |
| SRET |
S, M |
从监管者模式陷阱返回 |
| WFI |
S, M |
等待中断(低功耗) |
| SFENCE.VMA |
S, M |
刷新 TLB |
八、分支预测提示
RISC-V 规范不强制要求分支预测,但常见实现会采用:
- 静态预测:后向分支预测为跳转(循环),前向分支预测为不跳转
- 动态预测:BHT(Branch History Table)/ BTB(Branch Target Buffer)
- RISC-V 的规整编码格式有利于快速分支目标计算
九、完整指令速查
9.1 按格式分类
| 格式 |
指令列表 |
| R-type |
ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND |
| I-type |
ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI, LB, LH, LW, LBU, LHU, JALR, ECALL, EBREAK, FENCE, FENCE.I, CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI |
| S-type |
SB, SH, SW |
| B-type |
BEQ, BNE, BLT, BGE, BLTU, BGEU |
| U-type |
LUI, AUIPC |
| J-type |
JAL |
9.2 按功能分类
复制代码
┌────────────────────────────────────────────────────────┐
│ RV32I 指令集总览 │
├──────────┬─────────────────────────────────────────────┤
│ 算术 │ ADD, SUB, ADDI, LUI, AUIPC │
├──────────┼─────────────────────────────────────────────┤
│ 逻辑 │ AND, OR, XOR, ANDI, ORI, XORI │
├──────────┼─────────────────────────────────────────────┤
│ 移位 │ SLL, SRL, SRA, SLLI, SRLI, SRAI │
├──────────┼─────────────────────────────────────────────┤
│ 比较 │ SLT, SLTU, SLTI, SLTIU │
├──────────┼─────────────────────────────────────────────┤
│ 分支 │ BEQ, BNE, BLT, BGE, BLTU, BGEU │
├──────────┼─────────────────────────────────────────────┤
│ 跳转 │ JAL, JALR │
├──────────┼─────────────────────────────────────────────┤
│ 加载 │ LB, LBU, LH, LHU, LW │
├──────────┼─────────────────────────────────────────────┤
│ 存储 │ SB, SH, SW │
├──────────┼─────────────────────────────────────────────┤
│ 系统 │ ECALL, EBREAK, FENCE, FENCE.I │
├──────────┼─────────────────────────────────────────────┤
│ CSR │ CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI│
└──────────┴─────────────────────────────────────────────┘
十、完整示例程序
10.1 计算数组元素之和
assembly
复制代码
# int array_sum(int *arr, int n) {
# int sum = 0;
# for (int i = 0; i < n; i++)
# sum += arr[i];
# return sum;
# }
# 参数:a0 = arr 指针, a1 = n
# 返回值:a0 = sum
array_sum:
li t0, 0 # sum = 0
li t1, 0 # i = 0
.loop:
bge t1, t1, .done # if (i >= n) break
lw t2, 0(a0) # t2 = arr[i]
add t0, t0, t2 # sum += arr[i]
addi a0, a0, 4 # arr++(int 为 4 字节)
addi t1, t1, 1 # i++
j .loop
.done:
mv a0, t0 # 返回值 = sum
ret
10.2 字符串长度
assembly
复制代码
# int strlen(const char *s) {
# int len = 0;
# while (*s++) len++;
# return len;
# }
# 参数:a0 = s
# 返回值:a0 = 长度
strlen:
li t0, 0 # len = 0
.loop:
lb t1, 0(a0) # t1 = *s
beq t1, x0, .done # if (*s == 0) break
addi t0, t0, 1 # len++
addi a0, a0, 1 # s++
j .loop
.done:
mv a0, t0 # 返回 len
ret
10.3 递归阶乘
assembly
复制代码
# int factorial(int n) {
# if (n <= 1) return 1;
# return n * factorial(n-1);
# }
# 参数:a0 = n
# 返回值:a0 = n!
factorial:
addi sp, sp, -8 # 分配栈帧
sw ra, 4(sp) # 保存返回地址
sw s0, 0(sp) # 保存 s0
mv s0, a0 # s0 = n
addi t0, x0, 1 # t0 = 1
ble a0, t0, .base # if (n <= 1) goto base
addi a0, s0, -1 # a0 = n - 1
call factorial # 递归调用
# a0 = factorial(n-1)
mul a0, s0, a0 # a0 = n * factorial(n-1)(需要 M 扩展)
# 若无 M 扩展,需用移位和加法实现乘法
j .epilogue
.base:
li a0, 1 # 返回 1
.epilogue:
lw s0, 0(sp) # 恢复 s0
lw ra, 4(sp) # 恢复返回地址
addi sp, sp, 8 # 释放栈帧
ret
10.4 系统调用示例(Linux)
assembly
复制代码
.section .data
msg:
.string "Hello, RISC-V!\n"
.section .text
.globl _start
_start:
# write(1, msg, 15)
li a7, 64 # syscall: write
li a0, 1 # fd: stdout
la a1, msg # buf: 消息地址
li a2, 15 # count: 15 字节
ecall
# exit(0)
li a7, 93 # syscall: exit
li a0, 0 # status: 0
ecall
十一、与其他 RISC 架构对比
| 特性 |
RV32I |
ARM (A32) |
MIPS32 |
x86-32 |
| 指令长度 |
固定 32 位 |
固定 32 位 |
固定 32 位 |
变长 1--15 字节 |
| 通用寄存器 |
32 个 |
16 个 |
32 个 |
8 个 |
| 条件执行 |
分支指令 |
条件码 |
无 |
条件码 |
| 延迟槽 |
无 |
无 |
有(MIPS I) |
无 |
| 零寄存器 |
x0 = 0 |
无 |
$zero = 0 |
无 |
| 编码复杂度 |
极低 |
中等 |
低 |
高 |
| 授权 |
开源免费 |
商业 |
商业 |
商业 |
附录:RV32I 指令编码汇总
复制代码
指令 | opcode | funct3 | funct7 | 格式
----------|---------|--------|----------|--------
LUI | 0110111 | --- | --- | U
AUIPC | 0010111 | --- | --- | U
JAL | 1101111 | --- | --- | J
JALR | 1100111 | 000 | --- | I
BEQ | 1100011 | 000 | --- | B
BNE | 1100011 | 001 | --- | B
BLT | 1100011 | 100 | --- | B
BGE | 1100011 | 101 | --- | B
BLTU | 1100011 | 110 | --- | B
BGEU | 1100011 | 111 | --- | B
LB | 0000011 | 000 | --- | I
LH | 0000011 | 001 | --- | I
LW | 0000011 | 010 | --- | I
LBU | 0000011 | 100 | --- | I
LHU | 0000011 | 101 | --- | I
SB | 0100011 | 000 | --- | S
SH | 0100011 | 001 | --- | S
SW | 0100011 | 010 | --- | S
ADDI | 0010011 | 000 | --- | I
SLTI | 0010011 | 010 | --- | I
SLTIU | 0010011 | 011 | --- | I
XORI | 0010011 | 100 | --- | I
ORI | 0010011 | 110 | --- | I
ANDI | 0010011 | 111 | --- | I
SLLI | 0010011 | 001 | 0000000 | I
SRLI | 0010011 | 101 | 0000000 | I
SRAI | 0010011 | 101 | 0100000 | I
ADD | 0110011 | 000 | 0000000 | R
SUB | 0110011 | 000 | 0100000 | R
SLL | 0110011 | 001 | 0000000 | R
SLT | 0110011 | 010 | 0000000 | R
SLTU | 0110011 | 011 | 0000000 | R
XOR | 0110011 | 100 | 0000000 | R
SRL | 0110011 | 101 | 0000000 | R
SRA | 0110011 | 101 | 0100000 | R
OR | 0110011 | 110 | 0000000 | R
AND | 0110011 | 111 | 0000000 | R
FENCE | 0001111 | 000 | --- | I
FENCE.I | 0001111 | 001 | --- | I
ECALL | 1110011 | 000 | 0000000 | I
EBREAK | 1110011 | 000 | 0000001 | I
CSRRW | 1110011 | 001 | --- | I
CSRRS | 1110011 | 010 | --- | I
CSRRC | 1110011 | 011 | --- | I
CSRRWI | 1110011 | 101 | --- | I
CSRRSI | 1110011 | 110 | --- | I
CSRRCI | 1110011 | 111 | --- | I