0%

硬件平台-架构对比

一、总论

这篇文章的目的是为了比较各个硬件架构之间的异同点,这是因为我的朋友提到了“硬件是围绕软件来设计的”的观点,所以我觉得许多硬件的差异如果进行对比,可以更好得了解软件需求,看清各式各样的硬件的设计意图。

但是这篇文章似乎因为选题过于宏大了,导致非常难以下笔,所以这篇文章应该是一个长期维护的文章。

维护考虑:

  • 或许可以根据 Linux 的命名方式来区分一些概念?

二、异常处理

2.1 异常概念

Arm64

RISCV

RISCV 的异常分为同步异常和中断两种,可以根据 mcause 的最高位来区分。

RISCV 的同步异常分为 5 种:

  • 访问错误异常:当物理内存的地址不支持访问类型时发生(例如尝试写入 ROM)。
  • 断点异常:在执行 ebreak 指令,或者地址或数据与调试触发器匹配时发生。
  • 环境调用异常:在执行 ecall 指令时发生。
  • 非法指令异常:在译码阶段发现无效操作码时发生。
  • 非对齐地址异常:在有效地址不能被访问大小整除时发生。

中断源分为 3 种:

  • 软件:向内存映射寄存器中写入数来触发,由一个 hart 中断另一个 hart 。
  • 时钟:当 hart 的时间比较器(一个名为 mtimecmp 的内存映射寄存器)大于实时计数器
    mtime 时,会触发时钟中断。
  • 外部:由平台级中断控制器(大多数外部设备连接到这个中断控制器)引发。

不过我也不知道为啥,这些种类并不会一一对应 mcause 的编码,而是一对多,总结如下:

image-20240320232613310

2.2 相关寄存器

Arm64

RISCV

在 M-mode 下,一共有 8 个 CSR 是 RISCV 异常处理所必须的,如下表所示:

CSR 功能
mtvec(Machine Trap Vector) 异常发生时处理器跳转到的地址
mepc(Machine Exception PC) 发生异常的指令 PC
mcause(Machine Exception Cause) 异常发生的种类
mie(Machine Interrupt Enable) 处理器目前能处理和忽略的中断
mip(Machine Interrupt Pending) 处理器正在准备处理的中断
mtval(Machine Trap Value) 保存附加信息,比如地址异常中出错的地址、发非法指令异常中指令本身
mscratch(Machine Scratch) 暂时存放数据
mstatus 运行状态

前面 6 个寄存器都专用于异常处理,mscratch 寄存器在异常处理中没有明确意义,软件开发者可以当成一个指针来使用它,比如说在 Exaros 中它用来保存 TrapFramemstatus 是运行状态寄存器,它在异常处理中只有一些域发挥作用,如下所示:

  • MIE: Machine Interrupt Enable 全局中断使能
  • MPIE: Previous Machine Interrupt Enable 在异常发生后保存 MIE 的旧值

需要区分 mstatusMIE 位和 mie 寄存器, MIE 只有一位,置 1 表示允许中断,mie 是一个完整的寄存器,其中每一位都对应一个中断的使能,比如说 mie[7] 对于 M 模式的时钟中断。而 mip 也是一个完整寄存器,每一位对应一个中断“当前是否待处理”。如果 mstatus.MIE & mie[7] & mip[7] == 1 ,则处理器可以处理时钟中断。

mstatus 这些寄存器只有一个副本,那是不是意味着没法进行中断嵌套了呢?并不是的,只需要保存到栈上即可。

默认情况下,所有异常(无论在什么权限模式)都会被转发到 M 态。不过 M-mode 可以通过委托机制交给 S-mode

2.3 异常路由

Arm64

  • svc supervisor call 用户进程请求 OS 服务
  • hvc hypervisor call 客户 OS 请求虚拟机监视器服务
  • smc Secure Monitor call
  • eret 异常返回,从 SPSR 中恢复 PSTATE

RISCV

  • 使用 mret 从 M-mode 返回。
  • 使用 sret 从 S-mod 返回。
  • 使用 ecall 请求服务

arm64 有多个请求指令,只有 1 个返回指令,RISCV 有多个返回指令,只有 1 个请求指令。arm64 返回的异常等级根据 SPSR 决定,RISCV 的请求等级默认是 M-mode, M-mode 可以委托给 S-mode 处理。

2.4 硬件机制

Arm64

RISCV

异常发生时,RISCV 的处理流程如下:

  • 将异常指令的 PC 保存在 mepc 中,将 PC 设置成 mtvec
  • 根据异常来源设置 mcausemtval
  • 将先前的 mstatus.MIE 值写入 mstatus.PMIE ,将 mstatus.MIE 置零来禁用中断。
  • 将发生异常时的权限模式写入 mstatus.MPP ,将权限模式改为 M-mode

mret 返回时:

  • mepc 写入 PC
  • mstatus.PMIE 写入 mstatus.MIE
  • mstatus.MPP 设置成权限模式

三、权限

3.1 权限等级

Arm64

Arm64 中,具有 4 个异常等级(Exception Level),可以通过 CurrentEL 寄存器进行查询。如下所示:

等级 含义
EL0 用户态,运行用户进程
EL1 内核态,运行操作系统
EL2 虚拟机监视器态(Hypervisor)
EL3 安全监视器态(Secure Monitor)

在 Arm64 中的异常等级更像是一种从增长式的设计,最开始有 El0 和 EL1 (这个在 arm32 中甚至有多种更加细分的模式),为了支持虚拟机,引入了 EL2 ,为了支持可信计算,引入了 EL3 。

软件栈结构如下:

arm-arch1.png

Riscv

RSICV 中,具有 4 个特权模式

模式 含义
U User/Application Mode, 运行用户进程
S Supervisor Mode, 运行操作系统
H Hypervisor Mode, 运行虚拟机监视器
M Machine Mode

软件栈结构如下:

image-20240320210506365

可以看到 RISCV 的软件栈的“层数”要更加多,这是因为它引入了 BI 层和 EE 层来增加可移植性。EE 是 Execute Environment 的意思,表示为上层软件提供服务的实体,而 BI 是 Binary Interface 的意思,表示服务具体的协议。举个例子,OS 是用户的 EE , 系统调用是 BI 。我们常说的 openSBI ,其实是是一种 SEE 和 SBI 的集合体。

软件栈最下层是 HAL(Hardware Abstract Layer) ,它是一个软件,提供了硬件平台的封装。

需要注意的是,一块 RISCV 的板子并不需要完全实现这四种特权模式以及对应的 ISA ,硬件厂商完全可以不实现 Hypervisor Mode 。这些特权模式共有 4 种组合模式,硬件厂商可以选择其中的一种来实现(必须实现 Machine Mode):

名称 组合
Simple Embedded M
Secure Embedded M + U
Unix-like OS capable M + S + U
Virtual Machine M + H + S + U

这些特权等级并不平等,比如说 H-mode 这个特权等级,需要通过 H 拓展来获得(主要提供第二级地址翻译和保护),否则一般是没有的。S-mode 也必须通过 M-mode 的异常委托机制才能发挥作用,M-mode 具有一种“拥有全部硬件权限”的语义,而不是像 Arm64 的 EL3 一样只是安全世界的一个守门人。将原本的 M 变成 M + U 似乎只是不信任的软件可以被隔离,提高系统的安全性,而将 M + U 编程 M + S + U ,似乎只是希望借用 S-mode 下虚拟内存功能,这是 M-mode 所不具备的。

我个人感觉因为 RISCV 的诞生比较靠后,所以可能在设计之初就对市场里的需求有一个总体的把握,不像 X86 或者 ARM 一样需要一步步迭代。此外 RISCV 在一开始就不是一种增量一体式的 ISA 设计,而是一个模块化的 ISA ,所以对于各种硬件功能的解耦更加彻底。

3.2 特权指令

Arm64

Arm64 的特权指令非常好记,系统寄存器写指令是 move system from register ,就是那种谁是 dst ,是在前的汇编常见风格。

msr csr, r

系统寄存器读指令是 move register from system

mrs r, csr

RISCV

RISCV 的系统寄存器被称为 CSR(Control and Status Register, 控制和状态寄存器) 。RISCV 的基础指令都具有 3 个操作数,而不是像 Arm64 一样的 2 个。

  • csrrw (CSR read and write):读写,csr 中的值写入 rdrs1 的值写入 csr
  • csrrs (CSR read and set):读并置位,csr 中的值写入 rdrs1的值或上 csr 中的值再写入 csr
  • csrrc (CSR read and clear):读并清除,csr 中的值读入 rd,根据 rs1 的值对 csr 中的值按位清零再写入csr 中。

csrrwicsrrsicsrrci 分别于 csrrwcsrrscsrrc 相似,除了它们是使用一个处于 rs1 字段的、零扩展到 XLEN 位的 5 位立即数。

我们常用的系统寄存器读指令 csrr ,系统寄存器写指令 csrw ,系统寄存器清空指令 csrc 和系统寄存器置位指令 csrs 都是伪指令,如下所示:

csrr rd, csr    // csrrs rd, csr, zero
csrw csr, rs1   // csrrw zero, csr, rs1
csrc csr, rs1   // csrrc zero, csr, rs1
csrs csr, rs1   // csrrs zero, csr, rs1

3.3 运行状态

Arm64

不能认为 SPSRPSTATE 等价,就像不能认为 Trapframex0, x1, x2, ..., sp, pc 等价。

RISCV

在某种简单的实现中,sstatus 就是 mstatus 的一部分,而不是完全不同的 banked register。从这幅图可以看出些许端倪。


四、指令集

4.1 GPR

4.2 指令

五、内存管理

5.1 内存同步

5.2 页表基址

Arm64

用户使用 ttbr0 寄存器,而内核使用 ttbr1 寄存器。

RISCV

用户和内核都使用 satp 寄存器。