RISC-V 指令集深度解析:从 ISA 到 Zephyr

RISC-V 经常被称为"开源指令集架构",但如果真正开始做 RISC-V BSP、Bootloader 或 Zephyr 驱动,仅仅知道 ADDLWSW 并不够。

本文从 ISA 的基本概念出发,理解 RISC-V 的寄存器、指令编码、Load/Store、CSR 和扩展机制,并最终结合 Zephyr,看看这些 ISA 能力是如何真正被软件使用的。


一、ISA 到底是什么?

ISA,即:

复制代码
Instruction Set Architecture

中文通常叫指令集架构

它实际上定义的是:

软件和 CPU 硬件之间的接口。

例如:

复制代码
add a0, a1, a2

软件看到的含义就是:

复制代码
a0 = a1 + a2

至于 CPU 内部究竟采用几级流水线、有没有乱序执行、ALU 有几个,这些都不属于 ISA,而属于微架构。

可以简单理解为:

复制代码
软件
  │
  ▼
  ISA
  │
  ▼
CPU Microarchitecture
  │
  ▼
硬件

所以同一个 RISC-V ISA,可以由不同厂商实现出完全不同的 CPU。


二、RISC-V 为什么叫 RISC?

RISC 是:

复制代码
Reduced Instruction Set Computer

即精简指令集计算机。

RISC-V 的一个重要设计思想就是:

让指令格式尽可能规则,让硬件解码和执行更加简单。

例如:

复制代码
add a0, a1, a2

主要完成寄存器之间的加法。

而访问内存则使用:

复制代码
lw a0, 0(a1)

或者:

复制代码
sw a0, 0(a1)

这体现了 RISC-V 非常重要的 Load/Store Architecture

复制代码
CPU Register
             ▲     │
             │     │
           Load   Store
             │     │
             ▼     ▼
              Memory

也就是说,大多数算术运算发生在寄存器之间,而内存访问由专门的 Load/Store 指令完成。


三、RV32、RV64 到底是什么意思?

RISC-V 经常看到:

复制代码
RV32I
RV64I
RV64IMAC
RV64GC

这里最前面的:

复制代码
RV32
RV64

代表 CPU 的 XLEN

例如:

复制代码
RV32 → XLEN = 32
RV64 → XLEN = 64

因此 RV64 CPU 的通用整数寄存器宽度通常为 64 bit。

RISC-V 一共有 32 个整数通用寄存器:

复制代码
x0 ~ x31

其中最特殊的是:

复制代码
x0 = 0

无论向 x0 写入什么,读出来永远是 0。


四、x10 为什么又叫 a0?

做 Zephyr 或 Linux RISC-V 开发时,经常看到:

复制代码
a0
a1
a2
sp
ra
t0
s0

而不是:

复制代码
x10
x11
x12
x2
x1
x5
x8

这是因为 RISC-V ABI 给寄存器定义了更加方便的软件名称。

例如:

复制代码
x0   → zero
x1   → ra
x2   → sp

x5~x7   → t0~t2
x10~x17 → a0~a7
x8~x9   → s0~s1

其中:

复制代码
ra = Return Address
sp = Stack Pointer
a0~a7 = Function Arguments

所以:

复制代码
int add(int a, int b)
{
    return a + b;
}

在 RISC-V ABI 下,可以很自然地对应:

复制代码
a0 = a
a1 = b

执行 add

a0 = return value

这也是为什么你在 Zephyr 汇编代码里经常看到 a0


五、RISC-V 指令到底长什么样?

程序员看到的是:

复制代码
add a0, a1, a2

但 CPU 真正看到的是二进制编码。

最典型的 R-type 指令可以抽象成:

复制代码
31          25 24    20 19    15 14   12 11     7 6       0
┌────────────┬────────┬────────┬───────┬────────┬─────────┐
│   funct7   │  rs2   │  rs1   │funct3 │   rd   │ opcode  │
└────────────┴────────┴────────┴───────┴────────┴─────────┘

其中:

复制代码
rs1 / rs2 → 输入寄存器
rd        → 目标寄存器
opcode    → 指令大类
funct3
funct7    → 进一步确定具体操作

例如:

复制代码
add a0, a1, a2

实际上就是告诉 CPU:

复制代码
rs1 = a1
rs2 = a2
rd  = a0

operation = ADD

RISC-V 的一个重要特点就是指令格式比较规则,这对 CPU Decode 非常重要。


六、RISC-V 的模块化扩展

RISC-V 和传统的一些 ISA 一个很大的区别是:

它不是一个固定不变的指令集合,而是采用模块化扩展。

最基础的是:

复制代码
I = Integer

然后可以增加:

复制代码
M = Multiply / Divide
A = Atomic
F = Single Precision Floating Point
D = Double Precision Floating Point
C = Compressed
V = Vector

例如:

复制代码
RV64IMAC

可以理解成:

复制代码
RV64
 + I
 + M
 + A
 + C

而:

复制代码
RV64GC

中的 G 是一组常用扩展的组合。

这种设计非常适合 SoC:

复制代码
简单 MCU
    ↓
RV32I

需要乘除法
    ↓
RV32IM

需要 RTOS / SMP / 原子操作
    ↓
RV64IMA...

需要向量计算
    ↓
+ V

CPU 可以根据应用场景选择不同的 ISA 能力。


七、为什么 -march 对 RISC-V 特别重要?

编译 RISC-V 程序时,经常会看到:

复制代码
-march=rv64imac

它告诉编译器:

目标 CPU 支持哪些 ISA 扩展。

例如 CPU 实际只有:

复制代码
RV64IMAC

但编译器却生成了:

复制代码
V Extension

中的向量指令,那么 CPU 执行时就可能产生:

复制代码
Illegal Instruction

整个过程就是:

复制代码
Compiler
   │
   │ -march
   ▼
生成目标 ISA
   │
   ▼
RISC-V CPU Decode
   │
   ├── 支持 → Execute
   │
   └── 不支持 → Illegal Instruction

这也是 BSP 开发中遇到"非法指令"时,一个非常值得首先检查的地方。


八、CSR:RISC-V 不只有 x0~x31

如果开始研究 Zephyr、异常和中断,会遇到大量:

复制代码
mstatus
mie
mip
mtvec
mcause
mepc
mtval
satp

这些并不是普通通用寄存器,而是:

复制代码
CSR
Control and Status Register

CSR 是 RISC-V 特权架构非常重要的一部分。

例如:

复制代码
mstatus

保存 Machine Mode 下的重要状态。

复制代码
mie

控制哪些 Machine Interrupt 被允许。

复制代码
mip

表示哪些 Interrupt 当前处于 pending 状态。

而:

复制代码
mtvec

则决定发生 Trap 后 CPU 应该跳到哪里。

所以发生一个异常时,可以粗略理解成:

复制代码
CPU
 │
 │ Exception / Interrupt
 ▼
mtvec
 │
 ▼
Trap Handler
 │
 ├── mcause
 ├── mepc
 └── mtval

其中:

复制代码
mcause → 为什么进入 Trap
mepc   → Trap 发生时执行到哪里
mtval  → 与异常相关的附加信息

这几个 CSR 后面分析 Zephyr Trap 和 Interrupt 时会反复出现。


九、RISC-V ISA 和 Zephyr Driver 是什么关系?

到这里,就可以把 ISA 和我们实际写的 Zephyr Driver 联系起来了。

例如一个非常普通的 Zephyr Driver:

复制代码
static int foo_enable(const struct device *dev)
{
    const struct foo_config *cfg = dev->config;

    sys_write32(FOO_ENABLE,
                cfg->base + FOO_CTRL);

    return 0;
}

表面上我们只是调用:

复制代码
sys_write32()

但往下追:

复制代码
Zephyr Driver
      │
      ▼
  sys_write32()
      │
      ▼
    MMIO
      │
      ▼
RISC-V Store Instruction
      │
      ▼
     Bus
      │
      ▼
Peripheral Register

最终 CPU 执行的其实就是类似:

复制代码
sw t0, 0(t1)

也就是:

RISC-V Store Word。

所以一个看似普通的:

复制代码
sys_write32(value, addr);

实际上已经直接落到了 ISA。


十、为什么理解 Load/Store 对 BSP 很重要?

假设 DTS 中定义:

复制代码
uart0: uart@10000000 {
    reg = <0x10000000 0x1000>;
};

那么 Driver 得到:

复制代码
base = 0x10000000

然后:

复制代码
sys_write32(value, base + UART_CTRL);

最终的数据路径就是:

复制代码
DTS
 │
 ▼
Zephyr Driver
 │
 ▼
MMIO Address
 │
 ▼
RISC-V sw
 │
 ▼
SoC Bus
 │
 ▼
UART Register

这也是为什么做 BSP 时,下面几个东西不能割裂开来看:

复制代码
DTS
Driver
MMIO
ISA
Hardware

它们实际上是一条完整的数据通路。


十一、A 扩展:Zephyr 为什么需要 Atomic?

RISC-V 的:

复制代码
A Extension

提供原子操作,例如:

复制代码
LR/SC
AMOADD
AMOSWAP
AMOAND
AMOOR

这和 RTOS 中的:

复制代码
atomic
spinlock
SMP synchronization

关系非常密切。

例如:

复制代码
atomic_add(&counter, 1);

在支持 A 扩展的 RISC-V CPU 上,底层就可能映射到:

复制代码
amoadd.w

于是:

复制代码
Zephyr API
    │
    ▼
Architecture Layer
    │
    ▼
RISC-V A Extension
    │
    ▼
Atomic Instruction

这就是一个非常典型的:

RTOS API → ISA

映射。


十二、把 RISC-V ISA 和 Zephyr 串起来

现在可以重新看最开始的问题:

RISC-V ISA 到底和 Zephyr 有什么关系?

实际上可以总结成:

复制代码
Zephyr
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
      Driver      Kernel      Scheduler
        │           │           │
       MMIO       Atomic    Context Switch
        │           │           │
        ▼           ▼           ▼
     Load/Store   A Extension   Registers
        │           │           │
        └───────────┼───────────┘
                    ▼
               RISC-V ISA
                    │
             ┌──────┼──────┐
             ▼      ▼      ▼
            CSR    Trap   Privilege
             │      │
             └──┬───┘
                ▼
               CPU

所以:

复制代码
Zephyr Driver
     ↓
Architecture Layer
     ↓
RISC-V ISA
     ↓
CPU
     ↓
SoC Hardware

这其实就是 RISC-V BSP 最核心的一条链路。


十三、最后:应该怎么看 RISC-V?

如果只是学习 RISC-V,可以从:

复制代码
ADD
SUB
LW
SW

开始。

但是如果你的目标是:

复制代码
Zephyr
BSP
Bootloader
CPU
SoC

那么更值得重点理解的是:

复制代码
ISA
 │
 ├── Register / ABI
 │
 ├── Instruction Encoding
 │
 ├── Load / Store
 │
 ├── ISA Extensions
 │
 ├── CSR
 │
 ├── Trap
 │
 └── Privilege

然后再进入:

复制代码
PLIC
CLINT
CLIC
Timer
Interrupt
MMU / MPU
Context Switch
相关推荐
wuhuhuan1 小时前
Day17:HTTP 接口测试与链式调用 — sendRequest 从入门到实战
网络·网络协议·http
showyoui2 小时前
K8s 集群迁移踩坑:Istio 升级后 nginx 403
网络·docker·kubernetes·istio·service_mesh
执笔画流年呀2 小时前
网络原理(http)(https)
网络·http·https
caimouse2 小时前
tcpip.sys 传输层 (TCP) 详细分析
网络·网络协议·tcp/ip
SendTomo3 小时前
send.wang私传网:P2P直连传大文件首选工具
javascript·网络·网络协议·webrtc·p2p
caimouse3 小时前
tcpip.sys 传输层 (UDP) 详细分析
网络·网络协议·udp
珠***格14 小时前
双碳目标下:四可装置如何助力光伏消纳与碳数据上报
网络·人工智能·分布式·安全·边缘计算
mounter62514 小时前
绕过主机:通过 Devmem TCP 运行 RDMA 应用
网络·网络协议·tcp/ip·rdma·devmem
我星期八休息15 小时前
网络编程—网络层
开发语言·前端·网络·人工智能·智能路由器