计算机体系结构

什么是体系结构

在讨论计算机设计时,有几个概念经常被混在一起。实际上,它们对应的是从不同抽象层次、不同视角去看同一台计算机。自上而下来看,可以大致分为以下几个层级。

1.体系结构(Architecture)/ 指令集架构(ISA)

image-20241209115112416
  • 体系结构 = 软件眼里的CPU
  • 这一层是从软件视角来看计算机,ISA的内容包括:
    • 指令集:属于RISC还是CISC、支持的指令(如 ADDMOV)、指令格式、操作数类型
    • 寄存器结构:通用寄存器、状态寄存器(如 x86EFLAGSARMCPSR),各寄存器宽度
    • 内存管理:虚拟内存映射,地址空间、对齐要求、内存访问语义、字节序
    • 中断与异常模型:异常类型(如缺页、非法指令)、处理流程
    • 特权级别:定义特权模式(如 x86Ring 0-3ARMEL0-EL3
    • 可选拓展:SIMD、虚拟化、DSP…
  • 至于硬件内部是顺序执行还是乱序执行、Cache多大,这层一概不管,只要对外行为不变就行
  • 常见ISA:x86_64、ARMv7a、ARMv7m…

RISC-V、ARM其实都是ISA的统称,具体的ISA应该是RV32IA、ARMv7-M这样的

image-20250503155818886
  • ISA的宽度指的是CPU中通用寄存器的宽度(二进制的位数),这决定了寻址范围的大小,以及数据运算的能力。这与指令的宽度不同,比如RISCV架构CPU有32位也有64位,但指令的宽度都是32位。

2.组成原理 / 微架构(MicroArchitecture)

  • 微架构 = CPU里面怎样组织效率更高

  • 这一层关注的计算机的内部的组织方案,ISA定义了应该支持的功能后,硬件内部该怎么拆模块、怎么并行、怎么减少等待…微架构从硬件组织和性能优化的角度出发,讨论下述内容:

    • 流水线设计:级数、超标量、乱序执行、分支预测等
    • 寄存器重命名与调度机制
    • Cache与TLB的大小、替换策略、全相连还是组相连
    • 访存路径与一致性策略等
  • 这一层已经深入到硬件内部结构,但仍然停留在架构和机制层面,而不是具体电路实现

  • 修改微架构层级的设计,不应该影响程序运行的结果,只应该影响其效率。比如同样的代码可以在ARMv8A的CortexA72,CortexA58上运行,但是换到RISCV ISA的CPU上,肯定没法直接运行了

  • 常见微架构:ARM的Cortex系列,玄铁做的C906…

image-20250510203628233

3.数字电路 / 数字IC前端设计(RTL层)

  • RTL = 把设计的模块用数字电路实现

  • 这一层从电路实现的角度出发,把微架构层的设计转化为可综合、可验证的硬件描述。关注点包括模块划分、时序设计、状态机、寄存器与组合逻辑的组织方式,以及使用VHDL等硬件描述语言来实现具体协议和模块,例如AMBA总线接口、Cache控制逻辑、一致性协议等

  • 这一层的目标是如何设计电路,各条线该怎么连接,而不是再去讨论体系结构层面的取舍

  • 一般买IP核,比如SoC厂买ARM的Cortex内核,最多都只是到这一层级,后端布局布线那些的还是得自己做

4.IC后端、工艺

  • 这一层进一步把RTL实现的电路映射到具体芯片上,涉及综合、布局布线、时序收敛、功耗与面积优化以及制造工艺节点的限制。这一层已经完全脱离软件和体系结构语义,关注的是物理世界中的可制造性和可靠性问题

下面举个例子,从实现虚拟内存的角度,来看看不同层级应该做什么事情

  • ISA层级

    • 定义虚拟内存对软件呈现的模型和语义,包括是否支持分页、页大小、页表格式、相关寄存器和指令、以及在何种条件下触发异常。该层只关心“程序看到什么行为”,而不关心硬件如何加速这些操作。
    • 示例:ARMv8-A 定义 48 位虚拟地址和四级页表;RISC-V Sv39 定义三级页表结构。
  • 微架构层级

    • 在满足ISA语义的前提下,决定地址转换的具体硬件组织和性能优化策略,例如TLB的层级与结构、页表遍历缓存、是否支持并行查表,以及MMU如何与流水线和Load/Store单元协同工作。这一层是厂商进行性能和功耗差异化的核心
    • 示例:Intel Skylake 采用多级TLB和硬件页表遍历器;Cortex-A72支持大页和页表缓存以减少TLB miss
  • SoC系统设计层级

    • 关注地址转换在整个系统中的作用,包括转换结果如何影响片上互连和外设访问,以及如何与安全和虚拟化机制协同,例如TrustZone、二阶段地址转换和IOMMU。这一层通常涉及CPU核之外的系统组件

指令系统

指令集

  • 定义:CPU能够执行的所有机器指令的集合,是软件与硬件交互的基础规范。例如:x86支持复杂指令(如MMX、AVX),ARM支持精简指令(Thumb-2)

  • 分类:

    • CISC(复杂指令集):针对每一种功能都实现特定的指令,导致指令的数量较多,但生成的程序长度较短

    • RISC(精简指令集):只实现基本的指令,复杂的指令由基本指令组成,这导致指令的数目较少,但生成的程序长度较长

机器指令、汇编、编程语言的区别

  • 指令集:**一个CPU真正支持的所有操作的==集合==**。它定义了 CPU 能够执行的指令类型、每条指令的操作码及其格式、操作数类型等。不同架构的CPU(x86、ARM、MIPS等)的主要区别之一就是指令集不同

  • 机器指令:机器指令是CPU可以直接运行的二进制代码,一条机器指令就是指令集中的一条元素,对应CPU支持的特定操作,例如数据传送、算术运算、逻辑操作、控制流等

  • 汇编语言:汇编语言是对机器指令的一种封装,使得用户可读(机器指令是二进制的,用户不可读),汇编是个动词,指的是将汇编语言转成CPU可以运行的机器指令。汇编这个过程类似编译,也需要一个程序(汇编程序)来完成,汇编相较于编译更为简单,通常只涉及将汇编指令映射为机器码。

由此可见汇编不具有跨平台的特性,在X86架构的CPU写的汇编,拿到ARM架构的CPU大概率不可用,因为指令集都不一样

汇编中指令和伪指令的区别

  • 指令:对应于CPU支持的机器指令,汇编时直接映射为机器码
  • 伪指令:辅助汇编程序编写和组织代码的数据定义和控制指令,不对应于CPU支持的机器指令,汇编时被转换为几条机器码

中断与异常

现在的SoC或者MCU内,都会有中断控制器,它们负责接收、筛选和派发中断给CPU。这个中断控制器可能被集成在CPU内核内(比如NVIC),也可能在CPU外部作为一个独立的IP核(比如GIC、PLIC)

中断控制器有点类似CPU内核,也是遵循“公开的规范 + 私有的实现”的形式

我们之前经常看到的被ARMv7-M使用的NVIC中断控制器,ARMv7-A使用的GIC中断控制器,其实都是由ARM提出的一个==规范==(例如GICv1, v2, v3, v4)

这份规范详细规定了:

  • 功能模型:中断的生命周期(产生、分发、优先级仲裁、应答、结束)
  • 编程接口:软件(操作系统、驱动程序)如何与GIC交互。这包括:
    • 寄存器接口:定义了控制寄存器组(如GICD、GICC等)的功能、地址映射和访问方式
    • 中断类型:SGI(软件生成中断)、PPI(私有外设中断)、SPI(共享外设中断)等的定义和行为
    • 中断状态机:每个中断所处的状态( inactive, pending, active, active and pending)
  • 兼容性要求:确保任何遵循此规范的GIC实现,都能被同一版本的软件驱动正确操作

从程序员的角度来看,只要2个SoC用的都是GIC标准,那么对其寄存器地址、优先级配置方法、中断开关流程的配置应该都是一样的,但是硬件上大概率是不一样的

各芯片厂商在获得ARM的授权后,会根据这份规范,用自己的方式设计出具体的GIC硬件电路,比如:

  • Nvidia Tegra X1实现了 GIC-400(基于GICv2架构)
  • Qualcomm Snapdragon 8 Gen 2 手机芯片实现了基于 GICv3 或 GICv4 架构的自研GIC

下面来分析几个不同的ISA,体会一下ISA到底包含哪些内容

ARMv7-A

ARMv7 ISA其实有多个不同的版本

ARMv7-A ARMv7-R ARMv7-M
应用场景 应用处理器,高性能 OS 实时控制 微控制器,低功耗
MMU/MPU 有 MMU MPU MPU
指令集 ARM + Thumb ARM + Thumb Thumb / Thumb-2
常见 CPU 核心 Cortex-A8/A9/A53 Cortex-R4/R5 Cortex-M0/M3/M4

之前用的ARM Cortex A7的CPU内核的指令集架构是ARMv7A,现在对该ISA进行分析

特权等级

与RISCV的特权等级单一分层不同,ARMv7A使用了两层分层模型(特权等级+处理器模式)来设计

具体地,包含7种工作模式(分为2种特权等级),每个特权等级对应不同的处理器工作模式:

  • 非特权等级(Unprivileged):仅 User模式
  • 特权等级(Privileged):所有其他模式
模式 说明 特权级别 典型用途
User (USR) 普通用户模式 用户 应用程序执行
FIQ (Fast Interrupt) 快速中断模式 特权 高速中断处理
IRQ (Interrupt) 普通中断模式 特权 中断处理
Supervisor (SVC) 监控模式 特权 OS 内核、系统调用
Abort (ABT) 内存访问异常(预取/数据)模式 特权 异常处理
Undefined (UND) 未定义指令模式 特权 异常处理
System (SYS) 内核模式,运行在特权级别的用户模式 特权 内核线程运行

小技巧记忆:

  • User = 应用程序执行
  • FIQ/IRQ = 中断处理
  • SVC/System = OS 内核
  • Abort/Undefined = 异常处理
  • User:用户程序的工作模式,运行在操作系统的用户态,所以没有权限操作硬件资源

    • 无法直接切换到别的模式,如果想要访问硬件或者切换到别的模式,只能通过软中断(SWI指令)或者异常
  • IRQ:普通中断模式,用于处理一般的中断请求,延迟稍高于 FIQ

    • R8–R12 与其他模式共享,需要保存/恢复通用寄存器
  • FIQ:高速中断,用于对延迟敏感的场景,优先级高于IRQ

    • 拥有独立的 R8–R14,可以快速响应,避免保存/恢复通用寄存器

GIC内部可以把中断标记成IRQ或FIQ再发给CPU,一般只有Secure World或高优先级中断用的是FIQ。Linux内核通常不使用FIQ模式,直接用IRQ模式处理

  • Abort:用于异常处理,比如访问非法地址或页错误

    • 发生内存访问异常时(Prefetch Abort / Data Abort),CPU切换到该模式
  • Undefined:未定义指令异常

    • 处理未定义指令时,CPU切换到该模式
  • SVC:用于执行系统调用

    • CPU上电时进入的默认模式

    • 由软件中断指令SVC触发

  • System:用于内核线程的执行

    • 和 User 模式共享寄存器(R0–R12)
    • 不会自动保存SPSR寄存器
    • 不需要用SVC指令触发,当OS调度到一个内核线程时,进入该模式

工作模式切换

CPU的工作模式(特权等级)主要通过以下方式==切换==:

  • 中断/异常:
    • 发生中断/异常时,硬件自动切换到对应模式
    • 中断/异常返回时,恢复原模式
  • 软件触发:通过SVC #imm 汇编指令(#imm 是立即数,可用作系统调用编号),CPU自动切到SVC模式。类似RISCV的ecall

寄存器

image-20240904171832275

上面带三角的寄存器,在CPU不同的模式有不同的作用

通用寄存器

一共有13 + 5 + 7 + 7 +1 = 33个

R0~R15都属于通用寄存器,它们又可分为以下几类:

  • R0–R12:普通寄存器,大部分模式共享,除了FIQ有专用的(R8~R12)
    • 未被用于特殊用途,一般就用来暂存数据、传递函数参数、返回值、返回地址等
    • 发生函数跳转时,会造成此类寄存器数据的破坏,因此需要保存当前的数据到栈空间中,即==保存上下文==,实际上就是在进程的栈区里创建一个栈帧保存当前各个寄存器的值
  • R13 (SP):每种模式有独立的堆栈指针
  • R14 (LR):相当于RISCV的ra,发生调转时,保存跳转前的pc. 每种模式有独立的链接寄存器
  • R15 (PC):程序计数器,全局共享

程序状态寄存器

一共1 + 6 = 7个

  • CPSR(Current Program Status Register):当前程序状态寄存器

image-20240904195137643

比较重要的功能有:

  • 中断的开关
  • 工作模式的设置
  • 指令模型

  • SPSR(Saved Program Status Register):备份的程序状态寄存器
    • 发生中断/异常,会在目标模式的SPSR寄存器里备份CPSR的值
    • 异常返回时,CPU用当前模式的目标模式SPSR 恢复CPSR
    • ==每个特权模式都有独立==的SPSR寄存器,所以一共有6个(User无)

系统控制寄存器

寄存器 功能
SCTLR 系统控制寄存器,控制 MMU、Cache、分支预测等
TTBR0 / TTBR1 页表基址寄存器(低地址 / 高地址空间)
DACR 域访问控制寄存器,控制 domain 权限
DFSR / IFSR 数据/指令访问异常状态寄存器
DFAR / IFAR 数据/指令访问异常地址寄存器
CPACR 协处理器访问控制寄存器
其他 TLB、ASID、MPIDR 等多种辅助寄存器

协处理器寄存器

  • ARMv7-A支持协处理器(CP10/CP11 常用于浮点单元或 NEON)
  • VFP / NEON寄存器组:
    • S0–S31(单精度浮点)
    • D0–D31(双精度浮点,S寄存器别名)
  • 通过VMRS / VMSR / VLDR / VSTR指令访问

异常与中断

异常的概念:由CPU执行指令所导致的原来运行的程序的终止

异常类型

不同的异常源会使CPU进入不同的工作模式

异常类型 CPU 模式 触发源(Source) 特征/用途
Reset SVC / Supervisor 上电或复位 初始化系统,CPU启动
Undefined Instruction UND 执行未定义的指令 捕获非法指令异常
Software Interrupt (SVC指令) SVC 执行 SVC 指令 用户程序请求系统调用
Prefetch Abort ABT 指令预取错误(非法地址或权限) 内存访问异常
Data Abort ABT 数据访问错误(非法地址或权限) 内存访问异常
IRQ IRQ 外设普通中断 中断处理,一般优先级低
FIQ FIQ 外设快速中断 高速中断,优先级高

异常处理流程

1.异常发生:CPU 捕捉到异常源(IRQ、FIQ、SVC、Abort、Undefined、Reset 等)

2.中断屏蔽检查:

  • 如果是 IRQ/ FIQ:
    • CPU 检查 CPSR.I / CPSR.F 位 。如果屏蔽位已置位,则不响应该中断

3.CPU自动切换到对应的工作模式

4.硬件自动操作部分寄存器:

  • 将当前LR保存到对应工作模式的LR
  • 将当前CPSR保存到对应工作模式的SPSR
  • 交换当前SP和对应对应工作模式的SP
  • PC跳转到异常向量表对应的位置

image-20240905102604071

5.执行异常处理程序:

  • 手动保存通用寄存器:将R0–R12PUSH到当前模式的栈

  • 执行中断/异常处理逻辑(具体实现与OS相关):

    • 普通IRQ/FIQ:读取外设中断号(GIC/中断控制器寄存器),分发给对应ISR

    • SVC:根据SVC #imm中的立即数查系统调用表,调用内核服务

    • Abort(缺页/存取异常):

      • 访问系统控制寄存器:

      • DFSR/IFSR(Fault Status Register):错误类型

      • DFAR/IFAR(Fault Address Register):出错虚拟地址

      • 内核根据异常类型决定修复(如加载缺页)还是杀死进程

    • Undefined:通常触发内核信号,可能是非法指令

  • 可能需要的系统寄存器操作

    • 内存管理相关:切换TTBR0/TTBR1(换页表)、失效TLB
    • Cache操作:清除/失效Cache(通过CP15)
    • 控制位修改:如修改SCTLR(开关MMU、Cache、对齐检查等)
    • 异常信息获取:访问DFSR/IFSR、DFAR/IFAR

6.异常返回

  • 软件手动恢复通用寄存器R0~R12
  • 执行返回指令,如:
1
MOVS PC, SPSR_svc  ; 将 SPSR_svc 恢复到 CPSR,并跳回异常前的 PC
  • 硬件自动恢复CPSR、PC、SP寄存器,恢复到User模式

一图流:

image-20240905104912888

内存管理

  • 地址空间:ARMv7-A是32位的架构,所以最大4GB地址空间
  • MMU:由系统控制寄存器中的SCTLR.M来控制MMU的开关
  • 页表:
    • 默认2级页表,由页表基址寄存器TTBR0和TTBR1存储页表基址
    • 页表项定义了AP、XN等位进行权限的控制,定义了TEX、C、B等位来表示这块内存是普通内存还是外设的映射(PTE的内容原来是ISA定义的,不同的ISA还不一样)
  • TLB:由专用的系统控制寄存器来控制,提供了刷新某页和全部TLB的指令
  • Cache:由专用的系统控制寄存器来控制,提供了失效所有Cache等指令
  • 异常相关:当内存访问错误时,ARMv7-A会触发Abort异常,并把原因和出错地址保存到DFSR、IFSR等寄存器

ARMv7-M

特权等级

ARMv7M同样使用了两层分层模型(特权等级+处理器模式)来设计

具体地,包含2种工作模式,但是同一个工作模式可以示特权级别的,也可是非特权级别的:

特性 Thread mode Handler mode
特权级 Privileged 或 Unprivileged Privileged
栈指针 MSP或PSP(由CONTROL寄存器控制) MSP
进入方式 复位、异常返回 异常/中断
特权切换 可主动降级到Unprivileged 不可降级,永远特权
典型用途 RTOS线程 中断/异常处理

不同特权级别的区别:

  • Privileged(特权级)

    • 可以访问所有指令和系统控制寄存器(例如 MPU、NVIC、SCB 等)

    • 可以访问所有内存区域(除非 MPU 限制)

    • 可以选择使用MSP或PSP作为栈指针

  • Unprivileged(非特权级)

    • 被限制访问系统控制寄存器

    • 内存访问受MPU限制

    • 只能使用PSP作为栈指针


不同工作模式的区别:

  • Thread mode

    • 异常处理程序之外的运行模式(比如用户应用或OS线程)

    • 可以运行在特权级或非特权级

    • 栈指针可以选择MSP或PSP(由 CONTROL[1] 决定)

    • 系统复位时默认是该模式,且处于特权级,使用MSP

    • 如果是裸机程序,就会一直在特权级+MSP下运行,但如果用RTOS,会把用户线程降级到非特权级+PSP,来进行隔离

  • Handler mode

    • 处理异常/中断时进入的模式

    • 永远运行在特权级

    • 栈指针固定使用MSP(不受CONTROL寄存器影响)

    • RTOS的任务调度器运行在特权级 + MSP下,因为任务调度是靠SysTick/PendSV异常触发的

工作模式的切换

场景 切换方式 结果
Thread → Handler 发生异常/中断 自动切换,Handler mode + Privileged + MSP
Handler → Thread 异常返回(EXC_RETURN) 自动切换回 Thread mode(PSP 或 MSP)

特权等级的切换

Thread mode 本身可以运行在不同的特权等级,可以通过CONTROL寄存器切换:

  • CONTROL[0] = 0 → Privileged
  • CONTROL[0] = 1 → Unprivileged
  • CONTROL[1] = 0 → Thread 使用 MSP
  • CONTROL[1] = 1 → Thread 使用 PSP
场景 切换方式 结果
Thread (Privileged → Unprivileged) MSR CONTROL, Rn 立即生效
Thread (Unprivileged → Privileged) 触发异常,再在 Handler 里改 CONTROL 通过异常回到特权级

注意:

  • 从 Privileged → Unprivileged 可以直接改 CONTROL
  • 从 Unprivileged → Privileged不行,只能通过触发异常(进入Handler mode),然后在异常处理程序里再改回去

寄存器

通用寄存器

R0~R15都属于通用寄存器,它们又可分为以下几类:

  • R0–R12:通用工作寄存器,用于参数传递、临时变量保存
  • R13 (SP):栈指针,有MSP (Main SP)和 PSP (Process SP)两个实现,具体使用哪个由 CONTROL[1] 决定
  • R14 (LR):相当于RISCV的ra,发生调转时,保存跳转前的pc. 每种模式有独立的链接寄存器
  • R15 (PC):程序计数器,全局共享

程序状态寄存器

xPSR (Program Status Register),类似ARMv7-A的CPSR寄存器,但他做了精简,有以下字段:

  • APSR (Application PSR):条件码标志(N, Z, C, V, Q)
  • IPSR (Interrupt PSR):当前异常号(0 = Thread mode)
  • EPSR (Execution PSR):包含 Thumb 状态位 T,IT 块状态

控制/配置寄存器

  • CONTROL

    • 用于控制Thread mode下的特权级别栈指针的选择,与Handler mode无关
  • PRIMASK

    • 1 = 屏蔽所有可屏蔽异常(除了 NMI 和 HardFault),0 = 正常
  • FAULTMASK

    • 1 = 屏蔽所有异常(包括 HardFault),仅 NMI 能打断

    • 一般用于紧急临界区(比如出错恢复)

  • BASEPRI

    • 存放一个优先级阈值

    • 只允许比该值优先级更高的中断被响应

    • 常用于 RTOS 的“临界区实现”

异常相关寄存器

在 System Control Block (SCB) 中,定义了一系列只读/可写寄存器:

  • ICSR (Interrupt Control and State Register):显示当前异常号,支持触发 PendSV、SysTick 等
  • AIRCR (Application Interrupt and Reset Control Register):系统复位、优先级分组配置
  • SCR (System Control Register):睡眠模式控制
  • SHCSR (System Handler Control and State Register):配置/查询系统异常状态
  • CFSR (Configurable Fault Status Register):详细错误状态(分为 MemManage/Bus/Usage Fault)
  • HFSR (HardFault Status Register):HardFault 错误源
  • MMFAR/ BFAR:内存/总线错误的出错地址

NVIC寄存器

  • ISER / ICER:中断使能/清除
  • ISPR / ICPR:中断挂起/清除
  • IPR:中断优先级设置

异常与中断

ARMv7-M 的异常总数最多496 个:

  • 前 16 个是系统异常(System Exceptions)(编号 1–15)

  • 16 之后是外部中断(IRQ, Interrupt Requests),编号 16–(n-1),由 NVIC 管理

异常类型

1.系统异常(固定16个)

异常号 名称 优先级 说明
15 Reset 固定最高 上电或复位时进入,取向量表中的 MSP 初值和 Reset Handler 地址
14 NMI (Non-Maskable Interrupt) 次高,仅次于 Reset 不可屏蔽,常用于紧急错误处理
13 HardFault 次高(仅次于 NMI) 严重错误(比如没有启用的 Fault 或执行错误)
12 MemManage Fault 可配置 内存保护单元(MPU)相关错误
11 BusFault 可配置 总线访问错误(如非法地址访问)
10 UsageFault 可配置 指令执行错误(未定义指令、除 0、未对齐访问等)
9-6 保留 - -
5 SVCall (Supervisor Call) 可配置 通过 SVC 指令触发,常用于系统调用接口
4 Debug Monitor 可配置 调试相关异常
3 保留 - -
2 PendSV (Pendable Service Call) 可配置 用于任务切换,RTOS 核心异常
1 SysTick 可配置 系统定时器中断,通常配合 RTOS 用于调度
  • PendSV:可挂起系统调用
    • 跟SVC很像,都是用于从用户态切换到内核态用的。但是相比于SVC的立即触发,PendSV延迟触发。通过SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;指令开启一个PendSV请求后,CPU会在空闲当前中断完成后响应,并进入Hadler Mode,而不是立即响应
    • ==引入目的==:在RTOS中,任务的调度一般在SysTick中断中被触发,但是如果直接在SysTick中断中执行上下文切换的话,存在以下问题:
      • 高优先级中断的处理延迟:任务调度会消耗一定时间,如果在 SysTick中断中执行任务切换,系统可能无法及时处理其他高优先级中断,影响系统的响应性
      • 实时性问题: 对于实时系统来说,任务切换在中断中执行可能会导致中断服务的执行时间增加,从而影响系统响应外部事件的能力
    • 为了解决这些问题,PendSV 引入了 延迟任务调度的机制,确保任务切换不会影响中断服务例程的执行。通过 PendSV,任务切换操作被 延迟到当前中断处理完成后 执行。具体流程:
      1. SysTick(或其他中断)触发任务调度:当 SysTick 定时器触发中断时,系统会标记需要进行任务切换。但任务切换不会立即在 SysTick ISR 中执行
      2. PendSV 异常挂起:在 SysTick ISR 中,我们 设置 PendSV 异常挂起,而不是直接在 ISR 中执行任务调度。这样做的目的是把任务切换的操作 推迟到当前中断处理完成后,并确保高优先级中断不会被任务调度中断
      3. PendSV 执行任切换:当当前中断处理完成后,系统会自动进入PendSV ISR,执行任务调度操作,并完成上下文切换

中断到线程的上下文切换可以用下图表示:

image-20250830164804689

  • SVC
    • 通过SVC #imm指令可以触发一个软件中断,让CPU立即自动切换到Handler Mode,主要用于触发系统调用(内存分配、任务调度…)

2.外部中断 (IRQ)

  • 从异常号16 开始,每个外部中断对应NVIC 一个输入线
  • ARMv7-M 支持最多 240 个外设中断(具体数量取决于实现)
  • 这些中断对应 SoC 外设(UART、SPI、I2C、GPIO、DMA …)

优先级

1
Reset > NMI > HardFault > 其他 Fault/系统异常 > 外部中断

异常处理流程

1.异常触发

2.CPU自动切换工作模式和特权等级:切到Handler mode + Privileged

3.硬件自动操作部分寄存器:压R0–R3, R12, LR, PC, xPSR 到栈,切换SP为MSP

4.跳转到异常向量表中的异常入口

5.软件执行中断服务程序

6.执行BX LR进行异常返回,LR 中的EXC_RETURN字段决定返回到什么工作模式

STM32的异常向量表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__Vectors     DCD     __initial_sp             ; Top of Stack
DCD Reset_Handler ; Reset 处理函数
DCD NMI_Handler ; NMI 处理函数
DCD HardFault_Handler ; Hard Fault 处理函数
DCD MemManage_Handler ; MPU Fault 处理函数
DCD BusFault_Handler ; Bus Fault 处理函数
DCD UsageFault_Handler ; Usage Fault 处理函数
DCD 0 ; 保留
DCD 0 ; 保留
DCD 0 ; 保留
DCD 0 ; 保留
DCD SVC_Handler ; SVCall 处理函数
DCD DebugMon_Handler ; Debug Monitor 处理函数
DCD 0 ; 保留
DCD PendSV_Handler ; PendSV 处理函数
DCD SysTick_Handler ; SysTick 处理函数

__initial_spxxx_Handler就是各个异常的处理函数,DCD是一个伪指令,但也能完成跳转

内存管理

ARMv7-M 并没有完全实现 MMU(内存管理单元) 和 虚拟内存。它依赖于 MPU(内存保护单元) 来管理内存访问权限,保障系统的稳定性和安全性

MPU 是一个硬件模块,==用于对内存区域的访问进行权限控制==。它的主要目的是提高系统的安全性和可靠性,防止任务对内存的非法访问

1.MPU 的工作原理:

  • MPU 区域(Region):MPU 可以配置多个内存区域。每个区域有一个 起始地址、大小 和 访问权限。

  • MPU 区域的权限控制:

    • 访问权限(AP, Access Permissions):用于控制内存区域的访问权限,常见的权限包括:
      • 可读/可写
      • 只读
      • 执行禁止(XN,Execute Never)
    • 特权级别:内存区域可以对 特权级(privileged)和 非特权级(unprivileged)的访问进行区分。

2.MPU 的作用:

  • 通过定义区域,可以限制任务访问某些特定内存区域,防止非法访问。
  • 防止堆栈溢出或访问越界,保护任务堆栈和数据区。
  • 在某些情况下,MPU 可以防止 代码注入 或 执行恶意代码,提高系统安全性。

3.MPU 配置:

ARMv7-M 支持最多 8 个 MPU 区域,并通过控制寄存器进行配置。每个 MPU 区域由以下几个要素决定:

  • 起始地址
  • 大小(通常是 32 字节、64 字节、128 字节、256 字节等的 2 的幂)
  • 访问权限(读取、写入、执行等)
  • 访问类型(如代码区域、数据区域等)

MPU 配置的寄存器:

  • MPU_CTRL:用于启用/禁用 MPU。
  • MPU_RNR:选择当前操作的 MPU 区域。
  • MPU_RBAR:配置 MPU 区域的起始地址。
  • MPU_RASR:配置 MPU 区域的大小、权限、执行等设置。

面试题

1.中断栈和任务栈有什么区别,保存在哪里

  • 在 Cortex-M 内核上,所有中断共享一个中断栈(MSP),进入中断时硬件会自动保存部分寄存器;而每个任务有独立的任务栈,由 RTOS 管理,用来保存任务上下文和函数调用现场。任务切换时,调度器会把寄存器现场压入任务栈,并在切回时恢复

2.要实现任务调度,可以只有systick中断,不用pendsv吗?pendsv有什么优势

  • 可以的,正常流程是在Systick Handler发起一个PendSV请求然后在中断结束后进行真正地任务调度
  • 不用PendSV的话就直接在Systick Handler内部进行任务切换,这会带来以下问题:
    • 增加中断的处理时间,影响实时性
    • 如果某个任务自己触发了调度(比如调用 taskYIELD()),只能等下一个Systick到来时才能真正的切换
  • PendSV的优势:
    • 调度逻辑和时钟中断解耦,SysTick 只管触发调度请求,PendSV 专心做切换,更加灵活和高效
    • 优先级一般设的比较低,这样它不会打断真正的中断而造成影响
    • 可以暂时挂起延迟执行,等高优先级的中断运行完了才运行
    • 不依赖SysTick, taskYIELD()、信号量释放等情况,都可以设置PendSV的pending,从而触发调度

ARMv8-A

RISC-V

特权等级

RISCV包含4个特权等级

级别 编码 名称
0 00 用户/应用模式 (U, User/Application)
1 01 监督模式 (S, Supervisor)
2 10 H, Hypervisor
3 11 机器模式 (M, Machine)

RISC-V 架构中,只有 M 模式是必须实现的,剩下的特权级则可以根据跑在 CPU 上应用的实际需求进行调整:

  • 简单的嵌入式应用只需要实现 M 模式
  • 带有一定保护能力的嵌入式系统(RTOS)需要实现 M/U 模式
  • 复杂的多任务系统(如Linux)则需要实现 M/S/U 模式
  • 到目前为止,(Hypervisor, H)模式的特权规范还没完全制定好

寄存器

基础整数寄存器

  • RISC-V 基础指令集(RV32I/RV64I)包含32个通用寄存器和一个PC指针,每个寄存器的大小和处理器的位宽相关,可能为32/64/128位
  • 每个寄存器在编程时有特定的用途和别名,由ABI决定
image-20250421162849924
寄存器 ABI 名称 用途 是否调用保存
x0 zero 硬编码为 0,写入无效,读取始终返回 0 -
x1 ra 返回地址(Return Address),用于函数返回(如 ret 指令)
x2 sp 栈指针(Stack Pointer),指向当前栈顶
x3 gp 全局指针(Global Pointer),用于访问全局数据(可选优化) -
x4 tp 线程指针(Thread Pointer),用于线程局部存储(TLS) -
x5-x7 t0-t2 临时寄存器,用于短期存储,函数调用后可能被覆盖
x8 s0/fp 帧指针(Frame Pointer),用于调试或栈帧定位(可选)
x9 s1 保存寄存器,函数调用后需恢复
x10-x11 a0-a1 函数参数/返回值,传递前两个参数或返回值
x12-x17 a2-a7 函数参数,传递第 3~8 个参数
x18-x27 s2-s11 保存寄存器,函数调用后需恢复
x28-x31 t3-t6 临时寄存器,函数调用后可能被覆盖
  • “是否调用保存”指的是,发送函数调用时,是否会保存该寄存器的值,还是直接覆盖

浮点寄存器

如果实现 FD 扩展(单精度/双精度浮点),RISC-V 提供32 个浮点寄存器 f0-f31,位宽由扩展决定:

  • F 扩展:32 位(单精度)
  • D 扩展:64 位(双精度)

状态控制寄存器

  • 不同的特权级别分别对应各自的一套状态控制寄存器CSRs,用于配置和监控 CPU 状态(==具体有哪些==,请看RISC-V ISA手册Part2的Table 2.2)

  • 高级别的特权级别下可以访问低级别的CSR, 譬如 Machine Level 下可以访问 Supervisor/User Level 的 CSR,以此类推, 但反之不可以

  • RISC-V 定义了专门用于操作 CSR 的指令

  • RISC-V 定义了特定的指令可以用于在不同特权级别之间进行切换

异常与中断

异常类型

异常代码 助记符 描述
0 0 指令地址未对齐 (Instruction address misaligned)
0 1 指令访问错误 (Instruction access fault)
0 2 非法指令 (Illegal instruction)
0 3 断点 (Breakpoint)
0 4 加载地址未对齐 (Load address misaligned)
0 5 加载访问错误 (Load access fault)
0 6 存储/原子操作地址未对齐 (Store/AMO address misaligned)
0 7 存储/原子操作访问错误 (Store/AMO access fault)
0 8 用户模式系统调用 (Environment call from U-mode)
0 9 监管模式系统调用 (Environment call from S-mode)
0 11 机器模式系统调用 (Environment call from M-mode)
0 12 指令页错误 (Instruction page fault)
0 13 加载页错误 (Load page fault)
0 15 存储/原子操作页错误 (Store/AMO page fault)

相关寄存器

  • MODE:当前CPU的特权模式
  • SATP:当前MMU所使用页表的基址
  • STVEC:内核中处理Trap的指令的地址
  • SEPC:备份发生Trap时PC指针的值,结束时返回
  • SSCRATCH:临时存储数据,通常用于上下文保存时,切换用户/内核栈,相当于一个cache
  • SCAUSE:保存了trap是什么类型(中断/系统调用/异常)
  • SSTATUS:中断使能控制;trap发生时的特权级保存;sret返回时的特权级设置
  • 32个通用寄存器:作为上下文被保存

需要注意的是这些寄存器是S态的CSR寄存器。M态还有一套自己的CSR寄存器mcause,mtvec…

异常处理流程

1.Trap 发生

  • 可能是 中断 (interrupt):外部中断、定时器中断、软件中断
  • 也可能是 异常 (exception):非法指令、系统调用 (ECALL)、页错误、地址对齐错误等

2.中断屏蔽检查

  • 先检查 mstatus 或 sstatus 里的中断使能位(如 MIE/SIE)
  • 检查 mie/sie 寄存器的具体中断源使能
  • 如果对应中断被屏蔽,则忽略

3.模式切换

  • 如果启用了S,那么发生Trap时优先进入S Mode,否则直接进入M Mode

4.硬件自动操作寄存器(CSR 自动更新)

  • 保存 PC:
    • 当前 PC → mepc / sepc
  • 保存异常原因:
    • Trap 原因号 → mcause / scause
    • 中断 vs 异常有标志位区分
  • 保存出错地址(如果有):
    • mtval / stval ← 出错的虚拟地址 / 指令字
  • 更新控制寄存器:
    • mstatus:保存中断使能位 MIE 到 MPIE,并关闭中断
    • 切换到异常目标模式(M 模式或 S 模式)
  • PC 跳转:
    • PC ← mtvec / stvec(Trap 向量基址 + 偏移方式)

5.执行异常处理程序

  • 手动保存通用寄存器
    • RISC-V 不会自动保存 x1–x31,软件必须把需要用的寄存器保存到栈里
  • 根据Trap类型执行逻辑
    • 中断:查询 mcause/scause,确认是定时器中断、外部中断还是软件中断
    • ECALL(系统调用):根据寄存器 a7(系统调用号)分发到内核服务
    • 页错误(Page Fault):读取 stval/mtval 得到出错地址;OS 做缺页处理或杀进程
    • 非法指令/对齐错误:根据策略决定信号/异常处理
  • 可选的 CSR 操作
    • 修改 satp(页表基址寄存器,切换地址空间)
    • 清除/设置 mip/mie/sip/sie 中断位
    • 可能会调整 mstatus/sstatus

6.异常返回

  • 恢复之前保存的通用寄存器
  • 执行异常返回指令:
    • 从 M 模式返回:MRET
    • 从 S 模式返回:SRET
  • 硬件自动完成:
    • PC ← mepc/sepc(回到异常前指令)
    • mstatus/sstatus 恢复中断使能(MPIE → MIE)
    • 回到原模式(U/S)继续执行

内存管理

  • 地址空间:
    • RISC-V 支持多种虚拟内存方案:
      • Sv32(32 位虚拟地址,用于 RV32,最大 4GB)
      • Sv39(39 位虚拟地址,用于 RV64,最大 512GB)
      • Sv48(48 位虚拟地址,用于 RV64,最大 256TB)
    • 物理地址宽度由具体实现决定(通常 36~56 位)
  • MMU:
    • 由 satp 寄存器控制,保存根页表基址和模式字段
    • satp.MODE=0 时表示关闭 MMU(直接物理地址)
  • 页表:
    • 多级页表(Sv32 两级,Sv39/Sv48 三级/四级)
    • 页表基地址由 satp.PPN 给出
    • 页表项字段(ISA 定义):
      • V/R/W/X/U/G/A/D 等位控制有效性、访问权限、执行权限、是否用户态可见、全局页、是否访问/修改
    • 不同于 ARM,缓存/内存属性不是在页表项里定义,而是通过 PMA/PMP 来定义
  • TLB:
    • RISC-V 定义了 SFENCE.VMA 指令用于 TLB 刷新,没有像 ARM 那样的 CP15 接口,而是通过这条指令通知硬件失效部分或全部 TLB
  • Cache:
    • RISC-V 规范本身没有定义 Cache 控制指令
    • Cache 行失效/清除等属于平台相关扩展(通常由实现或特权扩展提供)
    • ISA 层面只保证内存一致性模型(RVWMO)
  • 异常相关:
    • 发生内存访问错误时会触发 Page Fault 异常(Instruction / Load / Store Page Fault)
    • 出错虚拟地址保存在 stval/mtval寄存器,异常原因写入 scause/mcause 寄存器