计算机体系结构

基本概念

体系结构vs组成原理

  • 体系结构:能被程序员看到的计算机系统的属性,即概念性的结构和功能特性(主要是被汇编程序员所看到的属性,包括指令集、数据类型、存储器寻址技术、IO机理等…)

    • 示例:

      • 冯诺依曼架构

      • 哈佛架构

      • 核心内容:

        • 多核协同:一致性协议(如 MESI)、互联总线(如 AMBA ACE)

        • 内存子系统:NUMA 支持、内存控制器设计

        • 外设与扩展:PCIe、USB 控制器、加速器(如 NPU)的集成

        • 安全与虚拟化:TrustZone、IOMMU、虚拟化扩展(如 ARM SMMU)

  • 组成原理:指如何实现计算机体系结构所体现的属性,它包含了很多对程序员来说是透明的硬件细节。比如规定一台计算机有哪些属性属于体系结构的问题,但如何通过电路实现这些指令,属于组成原理的问题

对比维度 体系结构 组成原理
关注点 软件可见的属性 硬件如何实现这些属性
面向对象 程序员 / 编译器 硬件工程师
内容 指令集、寄存器、内存模型 流水线、Cache、总线、预测
稳定性 长期稳定(几十年不变) 厂商私有,频繁迭代
举例 RISC-V 指令集、ARMv8 ISA Cortex-A72 的流水线设计

参考链接:

CPU/SoC/MCU/MPU的区别

  • CPU就是上面提到的CPU内核,包括运算器、控制器、寄存器、Cache等

  • SoC是将一个完整的系统整合到单一芯片上的集成电路,包括 CPU、GPU、存储控制器、外设接口、专用加速器(GPU/NPU/VOP)等

    • SoC 不一定有片上 RAM(有些会集成 LPDDR),但一定有外部内存接口
  • MCU是一种集成了CPU、内存(如闪存、RAM)、以及多种外设(如ADC、DAC、串行通信接口、定时器等)于一体的芯片,可以看成简易版的SoC。与SoC的核心区别:

    • MCU通常不跑复杂OS(最多RTOS),但是SoC一般跑Linux/Android之类的

    • MCU的片上RAM/ROM都比较小,功耗比较低

  • MPU:不带片上 Flash/RAM,需要外部存储器的处理器芯片,一般只包括CPU核心+外设控制器,必须配合外部的RAM+ROM才能运行,适合跑Linux/Android之类的复杂操作系统。(注意:ARM里还有个叫内存保护单元的MPU,和这里不是一个东西)

x86架构的电脑通常会有个主板,将CPU芯片和各个外设芯片通过总线连接,而嵌入式系统的SOC/MCU是把CPU和各个外设都集成到了一个芯片里,这是他们很大的一个区别

image-20241211101851896image-20241211101903102image-20241211101954770

MCU和SoC的区别主要体现在:

  • 处理能力
  • 应用场景
  • 功耗
  • 集成度:MCU集成的资源有限,主要用于控制任务,而非复杂计算;而SOC通常是一个完整的系统集成在一块芯片上,集成度更高,比如有MCU上没有的屏幕驱动部分、GPU等
image-20240901211121011

由于SOC中集成了很多模块,那么软硬件工程师的工作量也会减少,如果用裸CPU,那么什么RAM、FLASH的电路都得自己画,但是如果SOC自带了,那么就不需要了啊

ARM授权模式

ARM公司其实是CPU内核的设计厂商,只做CPU内核的设计(IP核),并不制造芯片本身。我们经常听到ARM架构,那么为什么别的芯片公司都能用ARM架构的CPU呢?

而其他半导体厂商,比如ST、华为之类的,要生产的SOC肯定里面要用到CPU内核,那么就得向ARM公司买他们设计好的内核(可以理解为ARM只卖DNF装备的设计图)

授权又分为多种:

  • ISA层级授权:可以对ARM内核进行大幅度改造,甚至可以修改指令集,总而得到一个新的架构(苹果的Swift架构)
  • 微架构层级授权:可以以ARM内核为基础再加上自己的外设,最后形成一个MCU或SOC(TI、ST公司)
  • 使用层级授权:只能把定义好的IP核拿来用,不能修改或再封装自己的产品
image-20240901212713201

其他的一些概念

指令集

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

  • 分类:

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

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

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

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

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

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

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

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

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

指令集架构(ISA)

image-20241209115112416

概览

  • 定义:规定了CPU对程序员可见的功能和行为,包含指令集、寄存器组织、内存模型、寻址方式、异常或中断的处理方式、软硬件接口、特权模式等的完整规范,是==CPU==设计的抽象类,可以理解为他就是个文档,且不包括具体的电路实现

指令集架构只是一种规范和约束,让编程者无需关心底层的硬件电路。

  • 可以理解为指令集(架构)相当于抽象类,硬件电路为子类具体的实现

我们常说的CPU的架构其实就是指的是CPU的指令集架构(ISA),比如x86_64、ARMv7a、ARMv7m..

  • 核心内容:
    • 指令集:属于RISC还是CISC、支持的指令(如 ADDMOV)、指令格式、操作数类型
    • 寄存器结构:通用寄存器、状态寄存器(如 x86EFLAGSARMCPSR),各寄存器宽度
    • 内存管理:虚拟内存映射,地址空间、对齐要求、内存访问语义、字节序
    • 异常与中断模型:异常类型(如缺页、非法指令)、处理流程
    • 特权级别:定义特权模式(如 x86Ring 0-3ARMEL0-EL3
    • 可选拓展:SIMD、虚拟化、DSP…

不同ISA包含的内容不完全一样:

image-20250503155818886

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

  • 实例:
    • RISC-V:开源指令集架构,可扩展性强(RISC-V其实也是统称,它有很多具体版本比如RV32I)

    • ARMv8-A:支持64位计算,定义AArch64执行状态和异常模型

ISA的宽度

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

中断控制器

现在的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

CPU内核/微架构

  • 英文:CPU Core/Microarchitecture

前面提到了ISA层只是给出了一些接口规范,但具体实现是需要各个厂家通过具体的硬件电路来做,这属于ISA层之下的MicroArchtecture层

比如常见的ISA的实现有x86、ARM、RISC-V、MIPS…,但是ISA的实现也只是给出了一些规范和接口,后面就是通过硬件电路实现这些接口:ARM的Cortex系列,玄铁做的C906…

这种 “规范 + 实现” 的方式在其他领域也可以看见!

image-20241209120423829
  • 定义:CPU内核是对ISA的==具体实现==,并关注性能、功耗、面积等优化。一般以IP核的形式提供给SOC厂商,IP核又分为软核(提供RTL级实现)和硬核(提供电路布局级实现)。如果有了软核,可以直接将RTL映射至FPGA,即可用FPGA进行物理时序的验证

  • 核心内容:

    • 流水线设计:级数、乱序执行(如 Intel 的 Out-of-Order)、分支预测策略
    • 缓存层次:各级Cache的大小、替换算法(如 LRU)
    • 并行计算单元:超标量(Superscalar)、SIMD(如 NEON/AVX)
    • 功耗管理:动态电压频率调整(DVFS)、时钟门控
    • 物理设计:晶体管布局、时序优化
  • 实例:

    • Cortex-A77 是 基于ARMv8-A 的一种CPU内核,采用 4 宽解码、128 ROB(重排序缓冲区)
    • Apple M1 基于 ARMv8.5-A ISA,但使用自研 Firestorm/Icestorm 微架构
    • 玄铁C906是RISCV64GCBV ISA的CPU内核
image-20250510203628233

关系与对比

层级 定义范围 稳定性 示例
ISA 指令、寄存器、内存模型 长期稳定 ARMv8-A、RISC-V
Microarchitecture 流水线、缓存、功耗优化 厂商私有,频繁迭代 Cortex-A78、Apple M2
Architecture 多核、总线、外设集成 依赖生态需求 AMBA 总线、Intel Hybrid 架构

以“实现内存映射这一目”为例,看一下不同层级分别负责哪些内容

1.ISA层级:只规定内存映射机制,不涉及具体硬件实现

  • 地址转换机制:规定是否支持分页(如 ARMv8-A 的 4KB/64KB 页)、页表格式(如 ARM 的 Descriptor Table

  • 特权控制:定义哪些指令/模式可访问页表(如 x86CR3 寄存器、ARMTTBR0

  • 异常类型:触发缺页异常(Page Fault)的条件(如权限错误、非法地址)

  • 示例:

    • ARMv8-A 要求 MMU 支持 48 位虚拟地址和 4 级页表
    • RISC-V 的 Sv39 模式定义三级页表结构

2.微架构层级:决定 MMU 具体实现与优化,是厂商优化的重点

  • TLB设计:容量方式(全关联/组相联)、替换策略(LRU/Random)
  • 多级页表缓存:硬件预取页表项(如 ARM 的 Walk Cache
  • 并行查表:支持多级页表并行访问以降低延迟
  • 物理实现:与流水线的协同(如 MMU 与 Load/Store 单元的交互)
  • 示例:
    • Intel Skylake 的 MMU 采用 多级 TLB(L1/L2)和 硬件页表遍历器
    • Cortex-A72 的 MMU 支持 2MB 大页缓存以减少 TLB 缺失

3.体系结构层级:关注 MMU 在系统中的角色,而非具体电路设计

  • 总线协议:MMU 如何与系统总线(如 AMBA AXI)交互
  • 多核一致性:MMU 在 Cache 一致性中的作用(如 ARM 的 SCU 监听控制)
  • 安全扩展:MMU 如何支持 TrustZone 或虚拟化(如第二阶段地址转换)

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,从而触发调度

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 寄存器