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 的关键挑战

  1. 条件执行丢失

    • ARM 的条件执行需要改写为分支或 SLT 序列
    • 可能影响性能(分支预测失败惩罚)
  2. 移位+运算拆分

    • ADD R0, R1, R2, LSL #3 需拆为两条指令
    • 代码体积可能增加
  3. 批量操作替换

    • LDM/STM 需替换为多个 lw/sw
    • 函数序言/尾声代码变长
  4. 地址计算简化

    • 带移位的偏移需要额外指令
    • 数组访问效率可能降低
  5. 乘除法替代

    • MUL 需替换为移位+加法序列,或使用 M 扩展
    • DIV 无基础指令支持
  6. 标志操作替代

    • 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)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐