RISC-V 32 位基础指令集(RV32I)完整参考手册

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, rsaddi rd, rs, 0
    • neg rd, rssub rd, x0, rs
    • retjalr x0, 0(ra)
    • nopaddi 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, offsetblt rs2, rs1, offset
    • ble rs1, rs2, offsetbge 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 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 addilui+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
相关推荐
一枝小雨1 天前
RISC-V架构sp寄存器 & RISC-V架构下FreeRTOS任务上下文保存与恢复
单片机·架构·嵌入式·risc-v·rtos·内核原理
一枝小雨1 天前
RISC-V架构的中断与异常处理机制学习笔记
单片机·架构·嵌入式·risc-v·内核原理·中断与异常
一枝小雨2 天前
什么是标准C函数:以RISC-V架构下的C函数为例
c语言·risc-v·内核原理
咕咚.萌西2 天前
RISCV AS汇编器
risc-v
嵌入式小企鹅3 天前
UiPath推出AI编程“总指挥台”,SiFive发布RISC-V第三代猛兽
人工智能·学习·google·程序员·ai编程·risc-v·开源工具
阿祖_in_coding6 天前
RISC-V ACT测试
risc-v
yusur6 天前
开芯院院长唐丹一行来访中科驭数 共探RISC-V与DPU算力协同创新之路
risc-v
加强洁西卡6 天前
【RISC-V】RVV选摘
risc-v
加强洁西卡6 天前
【RISC-V】fclass数值类型对照表
risc-v