02 计算机体系结构
计算机体系结构
基本概念
体系结构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和各个外设都集成到了一个芯片里,这是他们很大的一个区别



MCU和SoC的区别主要体现在:
- 处理能力
- 应用场景
- 功耗
- 集成度:MCU集成的资源有限,主要用于控制任务,而非复杂计算;而SOC通常是一个完整的系统集成在一块芯片上,集成度更高,比如有MCU上没有的屏幕驱动部分、GPU等
由于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核拿来用,不能修改或再封装自己的产品
其他的一些概念
指令集
定义: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)
概览
- 定义:规定了CPU对程序员可见的功能和行为,包含指令集、寄存器组织、内存模型、寻址方式、异常或中断的处理方式、软硬件接口、特权模式等的完整规范,是==CPU==设计的抽象类,可以理解为他就是个文档,且不包括具体的电路实现
指令集架构只是一种规范和约束,让编程者无需关心底层的硬件电路。
- 可以理解为指令集(架构)相当于抽象类,硬件电路为子类具体的实现
我们常说的CPU的架构其实就是指的是CPU的指令集架构(ISA),比如x86_64、ARMv7a、ARMv7m..
- 核心内容:
- 指令集:属于RISC还是CISC、支持的指令(如
ADD、MOV)、指令格式、操作数类型 - 寄存器结构:通用寄存器、状态寄存器(如
x86的EFLAGS、ARM的CPSR),各寄存器宽度 - 内存管理:虚拟内存映射,地址空间、对齐要求、内存访问语义、字节序
- 异常与中断模型:异常类型(如缺页、非法指令)、处理流程
- 特权级别:定义特权模式(如
x86的Ring 0-3、ARM的EL0-EL3) - 可选拓展:SIMD、虚拟化、DSP…
- 指令集:属于RISC还是CISC、支持的指令(如
不同ISA包含的内容不完全一样:
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…
这种 “规范 + 实现” 的方式在其他领域也可以看见!
定义:CPU内核是对ISA的==具体实现==,并关注性能、功耗、面积等优化。一般以IP核的形式提供给SOC厂商,IP核又分为软核(提供RTL级实现)和硬核(提供电路布局级实现)。如果有了软核,可以直接将RTL映射至FPGA,即可用FPGA进行物理时序的验证
核心内容:
- 流水线设计:级数、乱序执行(如 Intel 的
Out-of-Order)、分支预测策略 - 缓存层次:各级Cache的大小、替换算法(如 LRU)
- 并行计算单元:超标量(Superscalar)、SIMD(如 NEON/AVX)
- 功耗管理:动态电压频率调整(DVFS)、时钟门控
- 物理设计:晶体管布局、时序优化
- 流水线设计:级数、乱序执行(如 Intel 的
实例:
- Cortex-A77 是 基于ARMv8-A 的一种CPU内核,采用 4 宽解码、128 ROB(重排序缓冲区)
- Apple M1 基于 ARMv8.5-A ISA,但使用自研 Firestorm/Icestorm 微架构
- 玄铁C906是RISCV64GCBV ISA的CPU内核
关系与对比
| 层级 | 定义范围 | 稳定性 | 示例 |
|---|---|---|---|
| ISA | 指令、寄存器、内存模型 | 长期稳定 | ARMv8-A、RISC-V |
| Microarchitecture | 流水线、缓存、功耗优化 | 厂商私有,频繁迭代 | Cortex-A78、Apple M2 |
| Architecture | 多核、总线、外设集成 | 依赖生态需求 | AMBA 总线、Intel Hybrid 架构 |
以“实现内存映射这一目”为例,看一下不同层级分别负责哪些内容
1.ISA层级:只规定内存映射机制,不涉及具体硬件实现
地址转换机制:规定是否支持分页(如 ARMv8-A 的 4KB/64KB 页)、页表格式(如 ARM 的
Descriptor Table)特权控制:定义哪些指令/模式可访问页表(如
x86的CR3寄存器、ARM的TTBR0)异常类型:触发缺页异常(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
寄存器

上面带三角的寄存器,在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):当前程序状态寄存器

比较重要的功能有:
- 中断的开关
- 工作模式的设置
- 指令模型
- 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跳转到异常向量表对应的位置

5.执行异常处理程序:
手动保存通用寄存器:将R0–R12
PUSH到当前模式的栈执行中断/异常处理逻辑(具体实现与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模式
一图流:
内存管理
- 地址空间: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→ PrivilegedCONTROL[0] = 1→ UnprivilegedCONTROL[1] = 0→ Thread 使用 MSPCONTROL[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,任务切换操作被 延迟到当前中断处理完成后 执行。具体流程:
- SysTick(或其他中断)触发任务调度:当 SysTick 定时器触发中断时,系统会标记需要进行任务切换。但任务切换不会立即在 SysTick ISR 中执行
- PendSV 异常挂起:在 SysTick ISR 中,我们 设置 PendSV 异常挂起,而不是直接在 ISR 中执行任务调度。这样做的目的是把任务切换的操作 推迟到当前中断处理完成后,并确保高优先级中断不会被任务调度中断
- PendSV 执行任切换:当当前中断处理完成后,系统会自动进入PendSV ISR,执行任务调度操作,并完成上下文切换
- 跟SVC很像,都是用于从用户态切换到内核态用的。但是相比于SVC的立即触发,PendSV延迟触发。通过
中断到线程的上下文切换可以用下图表示:

- 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 | __Vectors DCD __initial_sp ; Top of Stack |
__initial_sp、xxx_Handler就是各个异常的处理函数,DCD是一个伪指令,但也能完成跳转
内存管理
ARMv7-M 并没有完全实现 MMU(内存管理单元) 和 虚拟内存。它依赖于 MPU(内存保护单元) 来管理内存访问权限,保障系统的稳定性和安全性
MPU 是一个硬件模块,==用于对内存区域的访问进行权限控制==。它的主要目的是提高系统的安全性和可靠性,防止任务对内存的非法访问
1.MPU 的工作原理:
MPU 区域(Region):MPU 可以配置多个内存区域。每个区域有一个 起始地址、大小 和 访问权限。
MPU 区域的权限控制:
- 访问权限(AP, Access Permissions):用于控制内存区域的访问权限,常见的权限包括:
- 可读/可写
- 只读
- 执行禁止(XN,Execute Never)
- 特权级别:内存区域可以对 特权级(privileged)和 非特权级(unprivileged)的访问进行区分。
- 访问权限(AP, Access Permissions):用于控制内存区域的访问权限,常见的权限包括:
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决定
| 寄存器 | 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 |
临时寄存器,函数调用后可能被覆盖 | 否 |
- “是否调用保存”指的是,发送函数调用时,是否会保存该寄存器的值,还是直接覆盖
浮点寄存器
如果实现 F 或 D 扩展(单精度/双精度浮点),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
- 从 M 模式返回:
- 硬件自动完成:
- 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 位)
- RISC-V 支持多种虚拟内存方案:
- 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
- RISC-V 定义了
- Cache:
- RISC-V 规范本身没有定义 Cache 控制指令
- Cache 行失效/清除等属于平台相关扩展(通常由实现或特权扩展提供)
- ISA 层面只保证内存一致性模型(RVWMO)
- 异常相关:
- 发生内存访问错误时会触发 Page Fault 异常(Instruction / Load / Store Page Fault)
- 出错虚拟地址保存在 stval/mtval寄存器,异常原因写入 scause/mcause 寄存器
