ARM32 vs RISC-V 32 指令集架构全面对比分析
·
ARM32 vs RISC-V 32 指令集架构全面对比分析
对比范围: ARM32 (ARMv7-A, A32 指令集) vs RISC-V (RV32I 基础整数指令集)
文档版本: v1.0 | 2026-05-12
一、架构设计哲学对比
| 维度 | ARM32 (ARMv7-A) | RISC-V (RV32I) |
|---|---|---|
| 起源 | 1985 年 Acorn 公司,商业闭源 | 2010 年 UC Berkeley,开源免费 |
| 设计目标 | 低功耗嵌入式 → 移动/服务器 | 教学/研究 → 全场景通用 |
| 授权模式 | 商业授权(ARM 授权费) | 开源免费,任何人可实现 |
| 复杂度 | 中等偏高(大量遗留特性) | 极简(从零设计,无历史包袱) |
| 扩展策略 | 配置文件式(A/R/M Profile) | 模块化基础 + 可选扩展 |
| 向后兼容 | 强(ARMv4 → ARMv7 代码兼容) | 无历史包袱(全新设计) |
二、寄存器对比
2.1 通用寄存器数量与宽度
| 项目 | ARM32 | RISC-V |
|---|---|---|
| 通用寄存器 | 16 个(R0–R15) | 32 个(x0–x31) |
| 寄存器宽度 | 32 位 | 32 位 |
| 专用零寄存器 | 无(任何寄存器可存任意值) | x0 硬连线为 0 |
| 程序计数器 | R15 (PC),可直接参与运算 | PC,不可直接读写 |
| 栈指针 | R13 (SP) | x2 (sp) |
| 链接寄存器 | R14 (LR) | x1 (ra) |
| 帧指针 | 无专用(R11 常用) | x8 (s0/fp) |
| CPSR/标志寄存器 | 有(CPSR,含 N/Z/C/V 标志) | 无(直接比较寄存器) |
2.2 ARM32 寄存器详解
R0 – 通用参数/返回值(无固定约定,AAPCS 约定)
R1 – 通用参数
R2 – 通用参数
R3 – 通用参数
R4 – 通用(callee-saved)
R5 – 通用(callee-saved)
R6 – 通用(callee-saved)
R7 – 通用(callee-saved)/ Thumb 模式系统调用号
R8 – 通用(callee-saved)
R9 – 通用(平台相关)
R10 – 通用(callee-saved)
R11 – 帧指针 FP(callee-saved)
R12 – IP(过程调用间暂存,caller-saved)
R13 – SP 栈指针
R14 – LR 链接寄存器(函数返回地址)
R15 – PC 程序计数器(可直接读写!)
ARM32 特殊之处:
- R15 (PC) 可作为源操作数参与运算(读取值 = 当前指令地址 + 8,因为 3 级流水线)
- 任何寄存器都可作为栈指针、帧指针、链接寄存器(只是约定)
- 通过 CPSR 的 T 位区分 ARM 模式(32 位指令)和 Thumb 模式(16 位指令)
2.3 RISC-V 寄存器详解
x0 (zero) – 硬连线零,读恒为 0,写入无效
x1 (ra) – 返回地址(jal/jalr 自动写入)
x2 (sp) – 栈指针(ABI 要求 16 字节对齐)
x3 (gp) – 全局指针
x4 (tp) – 线程指针
x5 (t0) – 临时寄存器(caller-saved)
x6 (t1) – 临时寄存器
x7 (t2) – 临时寄存器
x8 (s0/fp)– 保存寄存器 / 帧指针
x9 (s1) – 保存寄存器
x10-x11 – (a0-a1) 参数 / 返回值
x12-x17 – (a2-a7) 参数
x18-x27 – (s2-s11) 保存寄存器
x28-x31 – (t3-t6) 临时寄存器
PC – 程序计数器(不可直接读写)
2.4 寄存器设计差异关键点
| 差异点 | ARM32 | RISC-V | 影响 |
|---|---|---|---|
| 零寄存器 | 无 | x0 = 0 | RISC-V 可用 x0 简化伪指令 |
| PC 可读性 | 可直接读取(值 = 当前地址+8) | 不可直接读取 | ARM 可做 PC 相对寻址,RISC-V 需 AUIPC |
| 标志寄存器 | CPSR(N/Z/C/V 四个标志位) | 无 | ARM 用条件码后缀,RISC-V 用分支指令 |
| 条件执行 | 所有指令可条件执行(后缀) | 仅分支指令有条件跳转 | ARM 减少分支,RISC-V 简化流水线 |
| 寄存器数量 | 16 个 | 32 个 | RISC-V 寄存器压力更小 |
| Banked 寄存器 | 有(IRQ/FIQ 各有独立 bank) | 无 | ARM 中断响应更快(无需手动保存) |
三、指令格式对比
3.1 ARM32 指令格式
ARM32 采用固定 32 位指令长度,但格式种类更多:
数据处理指令:
31 28 27 26 25 24 21 20 19 16 15 12 11 0
┌──────┬──┬──┬──┬────┬──┬──────┬──────┬────────────┐
│cond │00│I │opcode│S │ Rn │ Rd │ operand2 │
└──────┴──┴──┴────┴──┴──────┴──────┴────────────┘
4位 2 1 4位 1 4位 4位 12位
加载/存储指令:
┌──────┬──┬──┬──┬──┬──┬──────┬──────┬────────────┐
│cond │01│I │P │U │B │W │L │ Rn │ Rd │ offset12 │
└──────┴──┴──┴──┴──┴──┴──┴──┴──────┴──────┴────────────┘
分支指令:
┌──────┬──┬──────────────────────────────────────┐
│cond │10│L │ offset24 │
└──────┴──┴──────────────────────────────────────┘
ARM32 编码特点:
- 所有指令都包含 4 位条件码字段(cond),支持条件执行
- 操作码和条件码在同一位置,便于快速译码
- 移位操作嵌入在 operand2 中,可实现"移位+运算"单指令完成
3.2 RISC-V 指令格式
RISC-V 采用 6 种基本格式,固定 32 位:
R-type:| funct7 | rs2 | rs1 | funct3 | rd | opcode | 7+5+5+3+5+7
I-type:| imm[11:0] | rs1 | funct3 | rd | opcode | 12+5+3+5+7
S-type:| imm[11:5]| rs2 | rs1 | funct3 | imm[4:0] | opcode |
B-type:| imm[12|10:5]| rs2 | rs1 | funct3 | imm[4:1|11] | opcode |
U-type:| imm[31:12] | rd | opcode | 20+5+7
J-type:| imm[20|10:1|11|19:12] | rd | opcode | 20+5+7
3.3 格式差异关键点
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 条件码 | 每条指令都有 4 位条件码 | 无,仅分支指令有条件 |
| 格式种类 | 约 10+ 种变体格式 | 6 种基本格式 |
| 字段位置 | Rd/Rn 位置因指令类型而异 | rs1/rs2/rd 位置固定 |
| 移位编码 | 嵌入 operand2(5+5 位) | 需单独移位指令 |
| 立即数 | 8 位基数 + 4 位旋转(复杂) | 12/20/21 位直接编码(简单) |
四、指令集对比
4.1 算术指令
ARM32
ADD R0, R1, R2 @ R0 = R1 + R2
ADD R0, R1, R2, LSL #3 @ R0 = R1 + (R2 << 3) ← 移位+加法一条指令!
ADD R0, R1, #100 @ R0 = R1 + 100
ADDS R0, R1, R2 @ R0 = R1 + R2,并更新 CPSR 标志
ADC R0, R1, R2 @ R0 = R1 + R2 + C(带进位加法)
SUB R0, R1, R2 @ R0 = R1 - R2
RSB R0, R1, #0 @ R0 = 0 - R1(反向减法,即取反)
MUL R0, R1, R2 @ R0 = R1 * R2(ARMv7 基础指令)
RISC-V
add a0, a1, a2 # a0 = a1 + a2
addi a0, a1, 100 # a0 = a1 + 100
sub a0, a1, a2 # a0 = a1 - a2
# 没有 MUL、ADC、RSB(需要 M 扩展才有乘法)
# 移位+加法需要两条指令:
slli t0, a2, 3 # t0 = a2 << 3
add a0, a1, t0 # a0 = a1 + t0
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 移位+运算 | 单条指令(ADD R0, R1, R2, LSL #3) |
需两条指令 |
| 带进位运算 | ADC, SBC 等 |
无(无标志寄存器) |
| 反向减法 | RSB R0, R1, #0 = 取反 |
sub a0, x0, a1(用零寄存器) |
| 乘法 | MUL 基础指令 |
需要 M 扩展 |
| 标志更新 | 用 S 后缀(ADDS)自动更新 |
无标志,用 SLT 比较 |
4.2 逻辑指令
ARM32
AND R0, R1, R2 @ R0 = R1 & R2
AND R0, R1, #0xFF @ R0 = R1 & 0xFF(位掩码)
ORR R0, R1, R2 @ R0 = R1 | R2
EOR R0, R1, R2 @ R0 = R1 ^ R2(异或)
BIC R0, R1, R2 @ R0 = R1 & ~R2(位清除,RISC-V 没有的)
MVN R0, R1 @ R0 = ~R1(按位取反)
RISC-V
and a0, a1, a2 # a0 = a1 & a2
andi a0, a1, 0xFF # a0 = a1 & 0xFF
or a0, a1, a2 # a0 = a1 | a2
xor a0, a1, a2 # a0 = a1 ^ a2
# 没有 BIC(位清除),需组合:and + not
xori a0, a1, -1 # a0 = ~a1(取反,等价于 NOT)
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 位清除 | BIC R0, R1, R2 单条指令 |
需 not + and 两条 |
| 取反 | MVN R0, R1 |
xori a0, a1, -1 |
| 移位+逻辑 | AND R0, R1, R2, LSL #4 |
需两条指令 |
| 操作数数量 | 三操作数(Rd, Rn, Op2) | 三操作数(rd, rs1, rs2) |
4.3 比较与条件分支
这是两个架构差异最大的地方。
ARM32:基于标志寄存器 + 条件码
CMP R0, R1 @ 比较 R0 和 R1,设置 CPSR 标志
BEQ label @ Z=1 时跳转(相等)
BNE label @ Z=0 时跳转(不等)
BLT label @ N≠V 时跳转(有符号小于)
BGE label @ N=V 时跳转(有符号大于等于)
BHI label @ C=1 且 Z=0(无符号大于)
BLS label @ C=0 或 Z=1(无符号小于等于)
@ 条件执行(ARM 独有!)
CMP R0, #0
MOVEQ R1, #1 @ if (R0 == 0) R1 = 1
MOVNE R1, #0 @ if (R0 != 0) R1 = 0
ADDEQ R2, R2, #1 @ if (R0 == 0) R2 += 1
RISC-V:直接比较寄存器,无标志
beq a0, a1, label # if (a0 == a1) goto label
bne a0, a1, label # if (a0 != a1) goto label
blt a0, a1, label # if (a0 < a1) goto label(有符号)
bge a0, a1, label # if (a0 >= a1) goto label(有符号)
bltu a0, a1, label # if (a0 < a1) goto label(无符号)
bgeu a0, a1, label # if (a0 >= a1) goto label(无符号)
# 条件赋值需要分支或 SLT 指令
slti t0, a0, 1 # t0 = (a0 == 0) ? 1 : 0
beq t0, x0, skip # if (a0 != 0) skip
li a1, 1 # a1 = 1
skip:
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 标志寄存器 | CPSR(N/Z/C/V 四标志) | 无 |
| 比较指令 | CMP/CMN/TST/TEQ 设置标志 |
直接在分支指令中比较两个寄存器 |
| 条件执行 | 所有指令都可加条件后缀 | 无条件执行 |
| 分支范围 | ±32MB(24 位,B 指令) | ±4KB(B-type)/ ±1MB(JAL) |
| 带标志算术 | ADDS, SUBS 等 |
无 |
| 无符号比较 | BHI, BLS, BHS, BLO(基于 C/Z 标志) |
BLTU, BGEU(直接比较) |
条件执行是 ARM32 最独特的优势:
@ ARM32:无分支的条件赋值
CMP R0, #0
MOVEQ R1, #1 @ if (R0 == 0) R1 = 1
MOVNE R1, #2 @ if (R0 != 0) R1 = 2
@ 无分支,流水线不被打断
@ RISC-V 等价实现(需要分支)
beq a0, zero, is_zero
li a1, 2
j done
is_zero:
li a1, 1
done:
@ 分支可能被预测错误,导致流水线冲刷
4.4 移位指令
ARM32
LSL R0, R1, #3 @ R0 = R1 << 3(逻辑左移)
LSR R0, R1, #5 @ R0 = R1 >> 5(逻辑右移)
ASR R0, R1, R2 @ R0 = R1 >> R2(算术右移,寄存器移位量)
ROR R0, R1, #8 @ R0 = R1 循环右移 8 位
RRX R0, R1 @ R0 = (C << 31) | (R1 >> 1)(带进位右移)
@ 移位作为操作数的一部分(嵌入在指令中)
ADD R0, R1, R2, LSL #3 @ R0 = R1 + (R2 << 3)
LDR R0, [R1, R2, LSL #2]@ R0 = M[R1 + R2*4](数组索引)
RISC-V
slli a0, a1, 3 # a0 = a1 << 3
srli a0, a1, 5 # a0 = a1 >> 5(逻辑)
srai a0, a1, 5 # a0 = a1 >> 5(算术)
# 没有循环右移(ROR)和带进位右移(RRX)
# 移位量不能是寄存器值以外的立即数
sll a0, a1, a2 # a0 = a1 << a2[4:0]
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 移位+运算 | 嵌入操作数,单条指令 | 需要单独移位指令 |
| 循环移位 | ROR, RRX |
无 |
| 带进位移位 | RRX |
无(无标志寄存器) |
| 移位量范围 | 0–31(立即数或寄存器) | 0–31(立即数或寄存器低 5 位) |
| 复合操作 | ADD R0, R1, R2, LSL #3 |
需两条指令 |
4.5 加载/存储指令
ARM32
@ 加载
LDR R0, [R1] @ R0 = M[R1](字加载)
LDRB R0, [R1] @ R0 = M[R1](字节加载,零扩展)
LDRSB R0, [R1] @ R0 = M[R1](字节加载,符号扩展)
LDRH R0, [R1] @ R0 = M[R1](半字加载)
LDR R0, [R1, #4] @ R0 = M[R1 + 4](立即数偏移)
LDR R0, [R1, R2] @ R0 = M[R1 + R2](寄存器偏移)
LDR R0, [R1, R2, LSL #2] @ R0 = M[R1 + R2*4](带移位的偏移!)
LDR R0, [R1], #4 @ R0 = M[R1]; R1 = R1 + 4(后索引)
LDR R0, [R1, #4]! @ R1 = R1 + 4; R0 = M[R1](前索引,写回)
@ 存储
STR R0, [R1] @ M[R1] = R0
STRB R0, [R1] @ M[R1] = R0[7:0]
STRH R0, [R1] @ M[R1] = R0[15:0]
@ 批量加载/存储(ARM 独有!)
LDMIA R0, {R1-R4} @ R1=M[R0], R2=M[R0+4], R3=M[R0+8], R4=M[R0+12]
STMDB SP!, {R4-R7, LR} @ 批量压栈(函数序言)
LDMIA SP!, {R4-R7, PC} @ 批量出栈并返回(函数尾声)
RISC-V
# 加载
lw a0, 0(a1) # a0 = M[a1](字加载)
lb a0, 0(a1) # a0 = M[a1](字节,符号扩展)
lbu a0, 0(a1) # a0 = M[a1](字节,零扩展)
lh a0, 0(a1) # a0 = M[a1](半字,符号扩展)
lhu a0, 0(a1) # a0 = M[a1](半字,零扩展)
lw a0, 4(a1) # a0 = M[a1 + 4]
# 存储
sw a0, 0(a1) # M[a1] = a0
sb a0, 0(a1) # M[a1] = a0[7:0]
sh a0, 0(a1) # M[a1] = a0[15:0]
# 没有带移位的偏移、前/后索引、批量加载/存储
# 数组访问需要手动计算地址:
slli t0, a2, 2 # t0 = index * 4
add t0, a1, t0 # t0 = base + index*4
lw a0, 0(t0) # a0 = arr[index]
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 偏移方式 | 立即数 / 寄存器 / 寄存器+移位 | 仅 12 位立即数偏移 |
| 前/后索引 | [Rn, #offset]! 前索引写回 / [Rn], #offset 后索引 |
无 |
| 批量加载/存储 | LDM/STM 一次操作多个寄存器 |
无(需逐个 lw/sw) |
| 符号扩展 | LDRSB, LDRSH 显式 |
LB 符号扩展, LBU 零扩展 |
| 非对齐访问 | 可配置对齐要求 | 规范允许但可能异常 |
批量加载/存储是 ARM32 的显著优势:
@ ARM32 函数序言/尾声(高效!)
STMDB SP!, {R4-R11, LR} @ 一次压栈 9 个寄存器(36 字节)
... @ 函数体
LDMIA SP!, {R4-R11, PC} @ 一次出栈并返回
@ RISC-V 等价实现(需要逐个操作)
addi sp, sp, -40 # 分配栈帧(40 字节,16 对齐)
sw ra, 36(sp)
sw s0, 32(sp)
sw s1, 28(sp)
sw s2, 24(sp)
sw s3, 20(sp)
sw s4, 16(sp)
sw s5, 12(sp)
sw s6, 8(sp)
sw s7, 4(sp)
sw s8, 0(sp)
... # 函数体
lw s8, 0(sp)
lw s7, 4(sp)
lw s6, 8(sp)
lw s5, 12(sp)
lw s4, 16(sp)
lw s3, 20(sp)
lw s2, 24(sp)
lw s1, 28(sp)
lw s0, 32(sp)
lw ra, 36(sp)
addi sp, sp, 40
jalr x0, 0(ra) # 返回
4.6 分支与跳转
ARM32
@ 无条件分支
B label @ 跳转到 label(±32MB)
BL label @ 跳转并保存返回地址到 LR(函数调用)
BX R0 @ 跳转到 R0(可切换 ARM/Thumb 模式)
BLX R0 @ 调用 R0(可切换模式)
@ 条件分支(基于 CPSR 标志)
CMP R0, R1
BEQ label
BNE label
BLT label
BGE label
BGT label
BLE label
@ 条件执行的"分支"(无分支版本)
CMP R0, #0
ADDNE R1, R1, #1 @ if (R0 != 0) R1++(不跳转!)
RISC-V
# 无条件分支
j label # 跳转(jal x0, label,±1MB)
jal ra, label # 调用函数(保存返回地址到 ra)
jr a0 # 跳转到 a0(jalr x0, 0(a0))
jalr ra, 0(a0) # 调用 a0 指向的函数
ret # 返回(jalr x0, 0(ra))
# 条件分支
beq a0, a1, label # 相等
bne a0, a1, label # 不等
blt a0, a1, label # 有符号小于
bge a0, a1, label # 有符号大于等于
bltu a0, a1, label # 无符号小于
bgeu a0, a1, label # 无符号大于等于
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 条件执行 | 所有指令可条件执行 | 无 |
| 分支范围 | ±32MB(B/BL) | ±4KB(B-type)/ ±1MB(JAL) |
| 间接跳转 | BX R0 / BLX R0 |
JALR |
| 模式切换 | BX/BLX 可切换 ARM/Thumb | 无模式切换 |
| 长跳转 | 直接 B 指令(24 位偏移) | AUIPC + JALR 组合 |
4.7 函数调用约定对比
ARM32 (AAPCS)
@ 参数传递:R0-R3(前 4 个参数)
@ 返回值:R0-R1
@ 调用者保存:R0-R3, R12, LR
@ 被调者保存:R4-R11
my_func:
@ 函数序言
STMDB SP!, {R4-R7, LR} @ 保存 callee-saved 寄存器
MOV R4, R0 @ 保存参数
@ 函数体
BL other_func @ 调用其他函数
ADD R0, R4, R0 @ 计算结果
@ 函数尾声
LDMIA SP!, {R4-R7, PC} @ 恢复寄存器并返回(PC = LR)
RISC-V
# 参数传递:a0-a7(前 8 个参数)
# 返回值:a0-a1
# 调用者保存:ra, t0-t6, a0-a7
# 被调者保存:s0-s11
my_func:
# 函数序言
addi sp, sp, -16
sw ra, 12(sp)
sw s0, 8(sp)
mv s0, a0 # 保存参数
# 函数体
call other_func # 调用其他函数
add a0, s0, a0 # 计算结果
# 函数尾声
lw s0, 8(sp)
lw ra, 12(sp)
addi sp, sp, 16
ret
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 参数寄存器 | 4 个(R0-R3) | 8 个(a0-a7) |
| 返回值寄存器 | 2 个(R0-R1) | 2 个(a0-a1) |
| 栈帧保存 | STMDB SP!, {regs} 批量 |
逐个 sw 指令 |
| 返回方式 | LDMIA SP!, {regs, PC} |
lw ra + ret |
| 栈对齐要求 | 8 字节(AAPCS) | 16 字节 |
五、内存模型与地址计算
5.1 地址计算能力
ARM32:强大的地址计算
@ ARM32 支持复杂的地址计算,单条指令完成
LDR R0, [R1, R2, LSL #2] @ R0 = M[R1 + R2*4](数组访问)
LDR R0, [R1, R2, LSL #1]! @ R1 = R1 + R2*2; R0 = M[R1](链表遍历)
STR R0, [R1], #4 @ M[R1] = R0; R1 += 4(指针后移)
@ 等价的 RISC-V 实现(需要多条指令)
slli t0, a2, 2 # t0 = index * 4
add t0, a1, t0 # t0 = base + index*4
lw a0, 0(t0) # a0 = arr[index]
RISC-V:简单的立即数偏移
# RISC-V 仅支持基址 + 12 位立即数偏移
lw a0, 0(a1) # a0 = M[a1 + 0]
lw a0, 4(a1) # a0 = M[a1 + 4]
# 复杂地址计算需要额外指令
5.2 内存对齐
| 项目 | ARM32 | RISC-V |
|---|---|---|
| 默认对齐 | 要求对齐(可配置) | 允许非对齐(可能性能低或异常) |
| 非对齐访问 | 可通过 CP15 配置行为 | 取决于具体实现 |
| 硬件支持 | 非对齐访问由硬件自动处理 | 部分实现硬件处理,部分触发异常 |
六、中断与异常处理
6.1 异常模型
ARM32
异常向量表(固定地址,通常从 0x00000000 开始):
0x00: Reset
0x04: Undefined Instruction
0x08: SVC (Supervisor Call) / SWI
0x0C: Prefetch Abort
0x10: Data Abort
0x14: Reserved
0x18: IRQ
0x1C: FIQ
异常处理流程:
1. 保存 CPSR 到 SPSR_<mode>
2. 保存返回地址到 LR_<mode>
3. 切换到对应模式(修改 CPSR 的 M 位)
4. 跳转到向量表地址
5. 返回时用 SUBS PC, LR, #4 恢复
RISC-V
异常向量基址由 mtvec CSR 指定(可配置)
异常处理流程:
1. 硬件自动保存 PC 到 mepc
2. 硬件自动保存异常原因到 mcause
3. 硬件自动保存附加信息到 mtval
4. 跳转到 mtvec 指向的处理程序
5. 返回时用 MRET 指令恢复
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 向量表 | 固定地址(0x00–0x1C) | mtvec CSR 可配置 |
| Banked 寄存器 | 有(IRQ/FIQ 各有独立 LR/SP) | 无(需软件保存) |
| 模式切换 | 硬件自动切换(修改 CPSR) | 硬件自动(修改 CSR) |
| 快速中断 | FIQ 有独立 bank(R8-R14) | 无快速中断机制 |
| 嵌套中断 | 需手动管理 | 需手动管理 |
| 返回指令 | SUBS PC, LR, #offset |
MRET |
七、特权模式对比
7.1 模式层级
ARM32(ARMv7-A)
User (USR) – 用户模式(无特权)
System (SYS) – 系统模式(与 User 相同寄存器,但有特权)
Supervisor (SVC)– 管理模式(操作系统内核)
Abort (ABT) – 中止模式(内存访问异常)
Undefined (UND)– 未定义模式(未定义指令异常)
IRQ – 中断模式(普通中断)
FIQ – 快速中断模式(高优先级中断,banked 寄存器最多)
Monitor (MON) – 安全监视模式(TrustZone)
Hyp (HYP) – 超级监管模式(虚拟化)
RISC-V
User (U, 0) – 用户模式
Supervisor (S, 1) – 监管者模式(OS 内核)
Machine (M, 3) – 机器模式(固件/引导程序)
关键差异:
| 差异点 | ARM32 | RISC-V |
|---|---|---|
| 模式数量 | 9 种 | 3 种 |
| 安全扩展 | TrustZone(安全/非安全世界) | 无(需扩展) |
| 虚拟化 | HYP 模式 | 无(需 H 扩展) |
| 快速中断 | FIQ 模式(独立 bank) | 无 |
| 复杂度 | 高(大量模式和 banked 寄存器) | 极低 |
八、编码效率与代码密度
8.1 指令编码对比
| 项目 | ARM32 | RISC-V |
|---|---|---|
| 指令长度 | 固定 32 位 | 固定 32 位 |
| Thumb 模式 | 有(16 位指令,代码密度更高) | 需 C 扩展(16 位压缩指令) |
| 混合模式 | ARM/Thumb 可随时切换(BX 指令) | 无(基础 RV32I 固定 32 位) |
8.2 典型操作的指令数对比
| 操作 | ARM32 指令数 | RISC-V 指令数 | 说明 |
|---|---|---|---|
a = b + c |
1 | 1 | 相同 |
a = b + (c << 3) |
1 | 2 | ARM 移位+加法单条 |
a = b * c |
1 | 需 M 扩展 | ARM 基础有 MUL |
a = b & ~c |
1 (BIC) | 2 | ARM 有位清除 |
if (a == 0) b = 1 |
2(条件执行) | 3-4(分支) | ARM 条件执行减少分支 |
arr[i] |
1(带移位偏移) | 2-3 | ARM 地址计算更灵活 |
| 函数序言(保存 5 寄存器) | 1 (STM) | 6 (5×sw + addi) | ARM 批量操作 |
while (n--) body |
2(条件执行) | 3-4 | ARM 可条件更新 |
绝对值 abs(x) |
2(条件执行) | 3(分支) | ARM 更紧凑 |
三目运算 a ? b : c |
3(条件执行) | 5-6(分支) | ARM 优势明显 |
8.3 代码密度示例
示例:计算两个数的最大值
@ ARM32(3 条指令,12 字节)
CMP R0, R1
MOVLT R0, R1 @ if (R0 < R1) R0 = R1
BX LR
# RISC-V(3-4 条指令,12-16 字节)
bge a0, a1, done
mv a0, a1
done:
ret
示例:数组求和
@ ARM32
MOV R2, #0 @ sum = 0
MOV R3, #0 @ i = 0
loop:
CMP R3, R1 @ 比较 i 和 n
LDRGE R0, [SP, #ret] @ 如果 i >= n,加载返回地址
BXGE LR @ 并返回
LDR R4, [R0, R3, LSL #2] @ R4 = arr[i](单条!)
ADD R2, R2, R4 @ sum += arr[i]
ADD R3, R3, #1 @ i++
B loop
# RISC-V
li t0, 0 # sum = 0
li t1, 0 # i = 0
loop:
bge t1, a1, done # if (i >= n) break
slli t2, t1, 2 # t2 = i * 4
add t2, a0, t2 # t2 = &arr[i]
lw t3, 0(t2) # t3 = arr[i]
add t0, t0, t3 # sum += arr[i]
addi t1, t1, 1 # i++
j loop
done:
mv a0, t0
ret
九、系统编程对比
9.1 系统调用
ARM32 (Linux)
@ 系统调用通过 SVC #0(旧称 SWI)
@ 调用号在 R7 中(Thumb 模式)或通过 R0 传递(ARM 模式)
@ 参数在 R0-R6 中
@ 返回值在 R0 中
MOV R7, #1 @ syscall: write
MOV R0, #1 @ fd: stdout
LDR R1, =msg @ buf: 消息地址
MOV R2, #13 @ count: 13 字节
SVC #0 @ 陷入内核
MOV R7, #0 @ syscall: exit
MOV R0, #0 @ status: 0
SVC #0
RISC-V (Linux)
# 系统调用通过 ECALL 指令
# 调用号在 a7 中
# 参数在 a0-a5 中
# 返回值在 a0 中
li a7, 64 # syscall: write
li a0, 1 # fd: stdout
la a1, msg # buf: 消息地址
li a2, 13 # count: 13 字节
ecall # 陷入内核
li a7, 93 # syscall: exit
li a0, 0 # status: 0
ecall
9.2 内存屏障
| 项目 | ARM32 | RISC-V |
|---|---|---|
| 屏障指令 | DMB, DSB, ISB |
FENCE, FENCE.I |
| 粒度 | 可选读/写/全屏障 | pred/succ 参数控制 |
| 指令同步 | ISB |
FENCE.I |
十、调试支持对比
| 项目 | ARM32 | RISC-V |
|---|---|---|
| 断点指令 | BKPT |
EBREAK |
| 硬件断点 | 有(通过 DBG 寄存器配置) | 有(通过 CSR 配置) |
| 调试接口 | JTAG / SWD | JTAG / 两种可选 |
| 单步执行 | 通过 DBG 寄存器 | 通过 CSR(dcsr.step) |
| 跟踪 | ETM(嵌入式跟踪宏单元) | 需扩展 |
十一、完整对比汇总表
| 维度 | ARM32 (ARMv7-A) | RISC-V (RV32I) |
|---|---|---|
| 寄存器数量 | 16 个 | 32 个 |
| 零寄存器 | 无 | x0 = 0 |
| PC 可读 | 是(R15) | 否 |
| 标志寄存器 | CPSR(N/Z/C/V) | 无 |
| 条件执行 | 所有指令 | 无 |
| 乘除法 | 基础指令(MUL) | 需 M 扩展 |
| 批量加载/存储 | LDM/STM | 无 |
| 移位+运算 | 单条指令 | 需两条 |
| 位清除 | BIC 单条 | 需两条 |
| 循环移位 | ROR, RRX | 无 |
| 前/后索引 | 有 | 无 |
| 参数寄存器 | 4 个(R0-R3) | 8 个(a0-a7) |
| 分支范围 | ±32MB | ±4KB / ±1MB |
| 指令长度 | 固定 32 位 | 固定 32 位 |
| 压缩指令 | Thumb(16 位) | 需 C 扩展 |
| 特权模式 | 9 种 | 3 种 |
| 异常向量 | 固定地址 | mtvec 可配置 |
| 快速中断 | FIQ(独立 bank) | 无 |
| 安全扩展 | TrustZone | 无(需扩展) |
| 虚拟化 | HYP 模式 | 需 H 扩展 |
| 内存屏障 | DMB/DSB/ISB | FENCE/FENCE.I |
| 系统调用 | SVC #0 | ECALL |
| 授权模式 | 商业授权 | 开源免费 |
| 编码复杂度 | 中等偏高 | 极低 |
| 硬件实现复杂度 | 中等 | 低 |
| 代码密度 | 较好(尤其 Thumb) | 一般(C 扩展可改善) |
十二、移植注意事项
12.1 从 ARM32 移植到 RISC-V 的关键挑战
-
条件执行丢失
- ARM 的条件执行需要改写为分支或 SLT 序列
- 可能影响性能(分支预测失败惩罚)
-
移位+运算拆分
ADD R0, R1, R2, LSL #3需拆为两条指令- 代码体积可能增加
-
批量操作替换
LDM/STM需替换为多个lw/sw- 函数序言/尾声代码变长
-
地址计算简化
- 带移位的偏移需要额外指令
- 数组访问效率可能降低
-
乘除法替代
MUL需替换为移位+加法序列,或使用 M 扩展DIV无基础指令支持
-
标志操作替代
ADC/SBC(带进位运算)需要软件模拟- 多精度算术需要重新设计
12.2 移植代码示例
ARM32 条件执行 → RISC-V 分支:
@ ARM32:计算绝对值
CMP R0, #0
RSBLT R0, R0, #0 @ if (R0 < 0) R0 = -R0
BX LR
# RISC-V:
bltz a0, negate # if (a0 < 0) goto negate
ret
negate:
neg a0, a0 # a0 = -a0
ret
ARM32 带进位加法 → RISC-V:
@ ARM32:64 位加法(R0:R1 + R2:R3 → R0:R1)
ADDS R0, R0, R2 @ 低 32 位相加,设置进位
ADC R1, R1, R3 @ 高 32 位加进位
BX LR
# RISC-V:需要额外逻辑处理进位
add t0, a0, a2 # t0 = low_a + low_b
sltu t1, t0, a0 # t1 = (t0 < low_a) ? 1 : 0(进位检测)
add a1, a1, a3 # high = high_a + high_b
add a1, a1, t1 # high += carry
mv a0, t0 # low = t0
ret
十三、总结
| 方面 | ARM32 优势 | RISC-V 优势 |
|---|---|---|
| 代码密度 | 条件执行、移位+运算、批量操作 | — |
| 硬件简洁性 | — | 无标志寄存器、无条件码、格式统一 |
| 中断响应 | FIQ 独立 bank、Banked 寄存器 | — |
| 可扩展性 | — | 模块化设计、开源生态 |
| 编译器友好 | — | 寄存器多、ABI 清晰 |
| 学习曲线 | — | 极简设计,易于理解 |
| 生态成熟度 | 成熟(30+ 年积累) | 快速成长中 |
| 授权成本 | — | 完全免费 |
一句话总结: ARM32 是经过 30 年商业优化的"瑞士军刀",功能丰富但复杂;RISC-V 是从零设计的"极简工具",简洁但需要扩展才能达到同等能力。选择取决于项目需求、成本预算和生态依赖。
文档版本: v1.0
对比范围: ARMv7-A (A32) vs RV32I
未覆盖: ARMv8 (AArch64)、RISC-V 扩展指令(M/A/F/D/C/V)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)