ARMv7-A小系统实践(二):处理器模式、寄存器组与CPSR
本章记录什么
这一章解释 ARMv7-A 程序最先遇到的一组概念:处理器模式、可见寄存器、banked 寄存器、CPSR 和 SPSR。目标不是背诵一张寄存器表,而是能够回答三个工程问题:为什么启动代码要多次切换模式设置 sp,为什么 IRQ 到来后同名的 lr 和 sp 会指向另一份物理寄存器,以及异常返回时为什么必须同时恢复程序地址和处理器状态。
在这一章之前做了什么
上一章确定了整个小系统的能力边界和资料分工,但还没有进入任何一条具体执行路径。现在从 CPU 的“当前身份”开始,因为启动、异常和调度最终都要落到寄存器可见性上。若这一层没有建立,看到 cps #0x12、mrs r0, spsr 或 rfeia 时只能记住指令表面,无法解释状态为什么能够恢复。
本章会先建立模式与寄存器组的基础,再把规则放进启动代码和一次 IRQ 中推演,最后列出可以在调试器里验证的观察点。任务切换只在结尾用于对比,不会提前混入。
本章的阅读顺序
先读基础概念,确认每个术语解决什么问题;再看设计推导,理解实现为什么采用这种保存集合、初始化顺序或状态机;随后逐段阅读代码和执行轨迹;最后用验证方法与错误案例检查理解。文中保留寄存器名、指令名和标准缩写,叙述部分统一使用简体中文。
动手前的基础:模式、bank 与程序状态
适用平台:Zynq-7000 / 双核 Cortex-A9 / ARMv7-A
学习目的:读懂启动代码、异常入口和每核各模式栈,为上下文切换与故障定位建立底座
阅读方式:正文可以替代本节英文原文;只在“原图对照”处打开 PDF 看图
编写日期:2026-07-30
0. 这一目标到底要学会什么
原定目标所说的“寄存器切换”,第一层确实是读者理解的:
处理器模式改变后,同一个汇编寄存器名会映射到不同的物理寄存器 bank。
但为了能做移植和 Debug,不能只记一张 bank 图,还要把下面四件事连起来:
- 当前模式记录在
CPSR.M[4:0]。 - 哪些模式拥有自己的
SP/LR/SPSR。 - 异常发生时,硬件怎样保存旧状态并切换寄存器 bank。
- 异常返回时,怎样恢复
CPSR,从而重新看到原模式的寄存器 bank。
本目标不是 OS 任务切换。两者的区别是:
1 | |
二者会在 IRQ 尾部调度时汇合,但概念不能混用。
1. 本文覆盖的原始资料和准确范围
| 原始资料 | 准确范围 | 页数 | 本文用途 |
|---|---|---|---|
| Programmer’s Guide 第 4 章 | PDF/手册页 47–56 | 10 页 | 建立模式、权限、寄存器和 CPSR 的直观框架 |
| ARM ARM B1.3 | PDF/手册页 B1-1139–B1-1154 | 16 页 | 补齐架构定义、bank 关系和严格语义 |
| ARM ARM B1.8.3–B1.8.5 | B1-1169–B1-1182 中的相关内容 | 14 页范围内选读 | 说明模式切换怎样真正发生在异常入口 |
这不是逐句翻译,而是按上述原文逻辑整理的中文工程精读稿。B1.3 中与 Zynq 第一版移植关系很弱的 Jazelle、虚拟化路由边角和保留值,只说明用途,不要求背诵。
原图对照
第一次读正文时,只建议看以下 5 张图:
| 原文图名 | 所在页 | 看什么 |
|---|---|---|
| Programmer’s Guide Figure 4-3: Privilege levels | 49 | User 与 PL1 特权模式的关系 |
| Programmer’s Guide Figure 4-5: The ARM register set | 51 | 最直观的 banked register 全图 |
| Programmer’s Guide Figure 4-6: CPSR bits | 53 | CPSR 位域位置 |
| ARM ARM Figure B1-1: Modes, privilege levels, and security states | B1-1141 | 模式、特权级和安全状态的规范关系 |
| ARM ARM Figure B1-2: ARM core registers, PSRs, and ELR_hyp, showing register banking | B1-1144 | 最完整的寄存器 bank 规范图 |
需要核对模式编码时看:
- Programmer’s Guide Table 4-2: ARM Mode Encodings and Privilege Levels,第 49 页。
- ARM ARM Table B1-1: ARM processor modes,B1-1139。
2. 处理器模式:为什么需要它们
ARMv7-A 不是让所有代码都以相同权限、使用同一套异常栈运行。不同模式承担不同职责:
| 模式 | 编码 | 典型来源和用途 | 特权 |
|---|---|---|---|
User (usr) |
0x10 |
普通应用代码 | PL0 |
FIQ (fiq) |
0x11 |
快速中断 | PL1 |
IRQ (irq) |
0x12 |
普通外部中断 | PL1 |
Supervisor (svc) |
0x13 |
复位后、SVC 系统调用、内核主模式 | PL1 |
Monitor (mon) |
0x16 |
TrustZone 安全监控 | PL1 Monitor |
Abort (abt) |
0x17 |
预取或数据访问异常 | PL1 |
Hyp (hyp) |
0x1A |
虚拟化管理 | PL2 |
Undefined (und) |
0x1B |
未定义指令异常 | PL1 |
System (sys) |
0x1F |
特权软件使用 User 寄存器组 | PL1 |
对 Zynq-7020 第一版单核移植,优先掌握:
1 | |
Monitor 要知道它属于 TrustZone;Hyp 要能识别,但没有明确虚拟化需求时不作为第一版实现重点。
User 与 System 最容易混淆
二者使用相同的 R0-R14 寄存器 bank,但权限不同:
- User 是非特权模式,不能随意修改控制寄存器或中断屏蔽。
- System 是 PL1 特权模式,通常供内核在“用户寄存器视图”下工作。
- 两者都没有自己的
SPSR。 - System 不是异常目标模式,不会因为某种异常被硬件自动进入。
所以:
“共享寄存器”不等于“拥有相同权限”。
3. 可见寄存器与实际物理寄存器
汇编程序在任意时刻最多看到:
1 | |
但芯片内部的物理寄存器数量更多,因为部分名字在不同模式下有不同 bank。
3.1 必须记住的 bank 规律
1 | |
可压缩成一句口诀:
FIQ 多 bank
R8-R12;异常模式各 bankSP/LR/SPSR;R0-R7和PC不 bank;User/System 共用一组且没有 SPSR。
3.2 “切换”不是复制
假设:
1 | |
在 SVC 模式执行:
1 | |
r0 得到 0x00108000。切换到 IRQ 模式后再执行同一条指令,r0 得到 0x00104000。
硬件没有把 SVC 栈指针复制到 IRQ 栈指针。发生的是:
1 | |
这也是为什么启动代码必须逐个模式设置栈。如果 SP_irq 没初始化,第一次 IRQ 一执行 push 就会把现场写到未知地址。
3.3 为什么 FIQ 多出五个 bank
FIQ 额外拥有 R8_fiq-R12_fiq,目的是让小型快速处理程序少保存一些共享寄存器,降低入口开销。现代 OS 往往仍会建立规范现场,所以不要把它理解成“FIQ 永远不保存寄存器”,只能说架构给了更低延迟的可能。
4. CPSR:只按用途记,不要机械背 32 个格子
CPSR 同时回答四类问题:
- 上一条运算结果怎样:
N/Z/C/V/Q。 - 当前采用什么指令状态:主要是
T。 - 哪些异步异常被屏蔽:
A/I/F。 - 当前处于什么处理器模式:
M[4:0]。
4.1 CPSR 位域
1 | |
第一优先级:移植和异常代码天天使用
| 位 | 名称 | 工程含义 |
|---|---|---|
9 |
E |
数据访问端序 |
8 |
A |
异步 Abort 屏蔽,1 表示屏蔽 |
7 |
I |
IRQ 屏蔽,1 表示屏蔽 |
6 |
F |
FIQ 屏蔽,1 表示屏蔽 |
5 |
T |
1 为 Thumb 状态,0 为 ARM 状态 |
4:0 |
M |
当前处理器模式 |
先牢牢记:
1 | |
第二优先级:读普通汇编经常出现
| 位 | 含义 |
|---|---|
N |
结果为负 |
Z |
结果为零 |
C |
加减的进位/借位语义,也用于移位移出的位 |
V |
有符号溢出 |
Q |
饱和运算累计溢出 |
CMP r0, r1 本质上更新标志而不保存减法结果,随后 BEQ/BNE/BHI/... 根据这些位判断。
第三优先级:知道存在即可
IT:ThumbIT条件执行块状态,分成两段存放。GE[3:0]:某些并行 SIMD 指令的比较结果。J:Jazelle 状态,当前 Zynq OS 移植不需要作为记忆重点。- 保留位:按手册要求保留,不自行写入任意值。
4.2 修改 CPSR 时要避免的错误
不能为了切模式而随手构造整个 CPSR 常量,覆盖条件标志、指令状态或屏蔽位。常用方法是:
1 | |
或者在允许的特权环境使用:
1 | |
cps #0x12 只改变模式,不会自动生成一次 IRQ 异常现场。
5. SPSR:异常前 CPSR 的停车位
进入普通 PL1 异常时,硬件做的关键动作之一是:
1 | |
例如 IRQ:
1 | |
因此在 IRQ handler 中:
- 当前
CPSR描述“正在 IRQ 模式执行”的状态。 SPSR_irq描述“被打断代码当时”的模式、T 位、屏蔽位和标志。
二者不能互换。
5.1 为什么每种异常模式各有 SPSR
IRQ、Abort、Undefined 等异常可能在不同时间保存不同的旧状态。如果只有一个共享 SPSR,它们之间会互相覆盖。独立 bank 让每个异常模式至少拥有自己的架构保存位置。
这并不自动解决任意嵌套问题:同一种模式再次嵌套,仍可能覆盖自己的 LR_mode/SPSR_mode。所以 OS 入口通常尽快把它们压入内存异常帧。
5.2 异常返回的本质
异常返回必须同时完成:
1 | |
一旦旧 CPSR.M 被恢复,当前寄存器映射也随之回到旧模式。例如从 IRQ 回到 SVC 后,名字 sp 再次表示 SP_svc。
常见形式:
1 | |
具体 LR 修正量属于异常主题,不能把所有异常都机械写成 lr - 4。
6. 一次 IRQ 中真正发生的寄存器切换
假设 CPU0 正在 SVC 模式运行,IRQ 未屏蔽。一个外设 IRQ 到来后,架构入口可理解为:
1 | |
注意硬件没有自动保存 R0-R12。入口汇编必须在改写这些寄存器前,把所需现场放入 IRQ 栈或任务异常帧。
返回时:
1 | |
恢复 CPSR 后模式映射回到异常前的 bank,于是原任务继续。
cps #mode 与异常入口的对照
| 行为 | cps #mode |
真正异常入口 |
|---|---|---|
改变 CPSR.M |
是 | 是 |
| 切换可见 bank | 是 | 是 |
| 旧 CPSR 自动存入 SPSR | 否 | 是 |
| 自动产生返回地址 | 否 | 是 |
| 自动跳到向量 | 否 | 是 |
这是本目标最重要的辨析题。
7. 双核 Zynq:模式 bank 之外还有“每核一套”
Zynq-7020 的两个 Cortex-A9 核,各自拥有完整架构状态:
1 | |
所以要同时理解两个维度:
1 | |
第一版单核启动只初始化 CPU0;启动 CPU1 前,必须给 CPU1 单独准备各模式栈和 per-CPU 数据。CPU0 的 SP_irq 不能供 CPU1 共用。
8. 启动代码应该怎样初始化各模式栈
典型思路:
1 | |
这段代码的意义不是反复修改同一个 SP,而是逐一选择并初始化不同的物理 SP_mode。
实际移植还要:
- 根据 MPIDR 选择 CPU0/CPU1 对应栈;
- 保证栈顶满足 AAPCS 8 字节对齐;
- 栈之间不能重叠;
- 在向量和 IRQ 栈准备好之前不能开放 IRQ;
- 记录链接脚本中栈区边界,便于越界诊断。
9. 移植与 Debug:从症状反推哪一层错了
症状 1:一开 IRQ 就 Data Abort 或跑飞
优先检查:
SP_irq是否初始化。- 当前 CPU 是否用了自己的 IRQ 栈。
- IRQ 栈地址是否可写、对齐且未与
.bss/其他栈重叠。 - 向量基址是否正确。
症状 2:异常返回后进入错误模式
优先检查:
- 异常帧里的
SPSR是否被破坏。 MSR SPSR...或RFE使用的值是否正确。- 返回现场的 PC/CPSR 顺序是否与保存布局一致。
- 是否把当前 CPSR 错当成异常前 CPSR。
症状 3:返回地址差 4 或 8 字节
这是异常类型和 LR 修正问题,不是 GIC 问题。记录:
1 | |
再查 ARM ARM B1.8.3 的 Table B1-6: Exception return addresses 和 B1-1172 的 Table B1-7: Offsets applied to Link value for exceptions taken to PL1 modes。
症状 4:改了 sp,切回模式后值“消失”
通常不是写失败,而是读者写的是另一个 bank。调试器停下后先读取 CPSR.M,再判断当前显示的是哪一个 SP_mode。
最小现场打印
异常诊断至少记录:
1 | |
不要在尚未确认 IRQ 栈有效时直接调用复杂 printf。
10. 本目标的掌握标准
不要求现在背下 CPSR 所有冷门位。达到下面标准即可进入后续实践:
- 能默写主要模式编码:
usr=10, fiq=11, irq=12, svc=13, abt=17, und=1b, sys=1f。 - 能画出
R0-R7、R8-R12_fiq、各模式SP/LR/SPSR的 bank 关系。 - 能解释模式切换是“映射变化”,不是寄存器值复制。
- 能区分
CPSR与SPSR_irq。 - 能说明 IRQ 硬件入口保存什么、没有保存什么。
- 能说明为什么每个 CPU、每种异常模式都需要自己的栈。
- 能区分
cps #0x12与真正 IRQ 入口。 - 看到
RFE或带S的异常返回序列时,知道它在恢复 PC 和 CPSR。
30 秒复述版
ARMv7-A 当前模式由 CPSR.M 决定。模式改变时,同名 SP/LR 可能映射到不同的 bank;FIQ 还额外 bank 了 R8-R12。异常进入会把旧 CPSR 保存到目标模式 SPSR,把返回地址放到该模式 LR,切模式并跳向量,但不会自动保存 R0-R12。入口汇编用该模式 SP 建现场,返回时同时恢复 PC 和 CPSR;旧模式恢复后,原来的寄存器 bank 自动重新可见。双核则每个核各自拥有完整的一套上述状态。
11. 下一主题的接口
本目标解决的是“硬件为什么会切换当前可见寄存器”。下一主题 AAPCS32 解决:
1 | |
之后才能合理回答:普通任务切换为什么常保存 R4-R11/SP/LR,而异常入口为什么需要保存更完整的现场。
把概念放回真实启动与异常路径
五、目前已经明确的核心概念
1. “寄存器切换”的含义
当前目标中的“寄存器切换”,主要指:
ARMv7-A 改变处理器模式后,相同的寄存器名会映射到不同的物理 banked register。
例如,在汇编中始终写 sp,但是:
- IRQ 模式下对应
SP_irq - SVC 模式下对应
SP_svc - Abort 模式下对应
SP_abt - User/System 模式下对应
SP_usr
这不是把一个 SP 的值自动复制到另一个 SP,而是当前模式改变了寄存器名到物理寄存器的映射。
2. 模式切换与任务上下文切换不同
- 处理器模式切换:
- ARM 架构硬件/特权指令机制
- 例如 SVC → IRQ
- 重点是 CPSR.M、SPSR、banked SP/LR
- 操作系统任务上下文切换:
- OS 软件机制
- 把一个任务的寄存器保存到内存,再恢复另一个任务
- 对应后续的“上下文与汇编指令”学习目标
两者都涉及寄存器,但不能混为一谈。
3. banked register 规则
1 | |
4. CPSR 的当前理解
CPSR 保存当前处理器状态,重点字段包括:
1 | |
当前最需要优先记忆的是低 10 位:
1 | |
5. SPSR 的当前理解
异常进入时,处理器把异常前的 CPSR 自动保存到目标异常模式的 SPSR,例如:
1 | |
SPSR 的两个主要用途:
- 异常处理程序检查异常发生前的状态。
- 异常返回时恢复原 CPSR,从而恢复原模式、屏蔽状态和指令集状态。
七、多核与模式 bank 的关系
已经明确这是两个不同维度:
1 | |
- “CPU0/CPU1”表示每个核独立的处理器状态。
- “IRQ/SVC/FIQ 等”表示一个核内部的模式 bank。
- 工程启动代码按“CPU ID × 处理器模式”分别初始化栈。
九、交流中得到的重要纠正
纠正 1:cps #mode 不等于异常进入
cps #mode:
- 是特权软件主动修改模式。
- 改变 CPSR.M。
- 使当前可见的 banked register 随模式变化。
- 不会自动保存旧 CPSR。
- 不会自动生成异常返回地址。
异常进入:
- 由处理器硬件按照异常类型改变模式。
- 自动把旧 CPSR 保存到目标模式 SPSR。
- 自动把首选返回地址保存到目标模式 LR,Hyp 除外。
- 跳到相应异常向量。
纠正 2:硬件不会自动保存所有通用寄存器
异常发生时,硬件主要保存 CPSR 和返回地址,并切换模式。R0-R12 等工作寄存器仍需异常入口汇编根据软件设计保存。
纠正 3:System 模式不是异常模式
System 与 User 共享核心寄存器,但 System 是 PL1 特权模式。没有异常会默认进入 System,它通常由操作系统显式切换使用。
从规则走到实际实现
下面回到本章对应的小系统实现。这里不再假定读者已经打开源码,而是把关键地址、数据结构、指令序列和返回路径直接放在文章中解释。
模式、指令状态、安全状态是三个问题
处理器模式由 CPSR.M[4:0] 表示,它影响权限与 banked 寄存器的选择。ARM/Thumb 指令状态主要由 CPSR.T 等状态位体现,决定指令如何解释。TrustZone 的 Secure/Non-secure 是安全状态,不能从“正在 SVC 模式”推导出来。
例如一个任务可以处于 SVC 模式、ARM 指令状态,并使用普通小端数据访问。切到 IRQ 模式是异常处理;从 ARM 函数调用 Thumb 函数是指令状态切换;进入安全监控路径又涉及另一套规则。
当前工程统一使用 -marm,任务运行在 SVC 模式,没有实现用户态和安全监控固件。
Cortex-A9 需要认识哪些模式
| 模式 | M 编码 | 寄存器特点 | 典型用途 |
|---|---|---|---|
| User | 0x10 | 使用 usr 组 SP/LR,无 SPSR | 非特权应用 |
| FIQ | 0x11 | 独立 r8-r14、SPSR_fiq | 快速中断 |
| IRQ | 0x12 | 独立 SP/LR、SPSR_irq | 普通中断 |
| SVC | 0x13 | 独立 SP/LR、SPSR_svc | 内核与 SVC 异常 |
| Monitor | 0x16 | 安全扩展相关 bank | 安全状态切换 |
| Abort | 0x17 | 独立 SP/LR、SPSR_abt | 取指或数据访问异常 |
| Undefined | 0x1b | 独立 SP/LR、SPSR_und | 未定义指令 |
| System | 0x1f | 与 User 共享寄存器组,无 SPSR | 特权下操作 usr 组寄存器 |
User 与 System 的寄存器组相同,权限不同。System 是特权模式,但不是某类异常的自动入口目标。
ARMv7-A 架构手册还会列出 Hyp 模式。那属于实现虚拟化扩展的处理器范围,Cortex-A9 不实现该扩展。不能因为 ARM ARM 画出了 ELR_hyp,就给 Zynq-7020 安排一条 Hyp 启动路径。
同一个寄存器名,可能对应不同存储
对本工程涉及的经典模式,可按下面的规律记:
1 | |
“共享”限定在同一个核上。CPU0 与 CPU1 各自拥有寄存器文件;CPU0 的 r0 不会因为 CPU1 写 r0 而变化。寄存器银行切换与多核是两个不同维度。
假定 SVC 模式下 sp_svc=0x00120000、sp_irq=0x00130000、r4=0x12345678。执行:
1 | |
之后通过名字 sp 看到的是 0x00130000,r4 仍是 0x12345678。sp_svc 没有消失,只是当前名字 sp 不再选中它。切回 SVC 又能看到原值。
这一步没有把寄存器压进内存,也没有保存一个软件任务。硬件 bank 的数量是固定的,不可能为每一个线程分配一个 SVC bank。
启动代码怎样初始化这些 bank
基础版 src/start.S 先读 MPIDR,随后按模式和 CPU 选择栈。下面摘出两个模式:
1 | |
第一段写当前 CPU 的 sp_irq,第二段写 sp_svc。链接脚本为两个核分别预留内存,两份栈既不能地址相同,也不能互相重叠。
这里有两个条件需要同时满足:处理器已处于允许执行 CPS 的特权环境;对应 RAM 已经能访问,链接符号落在实际可用内存里。SP 初始化只意味着寄存器装入了一个数,能否安全压栈,要到第一次访存才真正验证。
CPSR 各字段怎样参与系统运行
| 位 | 名称 | 在本系列中的意义 |
|---|---|---|
| 31 | N | 算术结果符号 |
| 30 | Z | 结果为零 |
| 29 | C | 加法进位;减法通常表示没有借位 |
| 28 | V | 有符号溢出 |
| 27 | Q | 饱和运算的粘滞标志 |
| 26:25、15:10 | IT | Thumb IT 块状态 |
| 24 | J | 与特定指令执行状态有关,当前代码不用 |
| 19:16 | GE | 部分并行算术指令的比较状态 |
| 9 | E | 数据访问端序控制 |
| 8 | A | 异步 Abort 屏蔽控制 |
| 7 | I | IRQ 屏蔽,1 表示屏蔽 |
| 6 | F | FIQ 屏蔽,1 表示屏蔽 |
| 5 | T | Thumb 状态指示 |
| 4:0 | M | 处理器模式 |
保留位与未实现扩展不能随意写固定值。尤其不能把 A 当成“所有 Abort 的总开关”:同步地址转换故障不会因为 A=1 就消失。
前四位很容易在异常代码中被忽略。假设任务执行:
1 | |
IRQ handler 中也会执行比较和算术。如果返回时只恢复 PC,不恢复原来的 N/Z/C/V,beq 就可能使用 handler 留下的标志。这解释了为什么异步返回必须恢复 CPSR,而普通 C 函数调用一般不承诺保留条件标志。
另一个容易读反的是 I:I=1 表示屏蔽 IRQ。调试时看到 CPSR 低字节为 0xd3,可拆成:
1 | |
因此 0xd3 常用于暂时不接受 IRQ 的 SVC 现场;0x53 则允许 IRQ,仍屏蔽 FIQ。这里只解释这些位,不意味着完整 CPSR 一定只有这一字节。
MRS、MSR、CPS 的职责
1 | |
mrs r1, spsr 的含义依赖当前模式。在 IRQ 模式读取的是 SPSR_irq;切到 SVC 后再读,得到的是 SPSR_svc。User/System 没有自己的 SPSR,不要把它当成任意位置都可读取的“上一个状态”。
MSR 还能选择写 CPSR 的哪些字段。例如工程普通 context 恢复使用:
1 | |
这里写控制字段,不是恢复整份算术标志。对一个遵守 ABI 的普通函数调用边界,这与“条件标志不保证跨调用保持”的约定相符。但它不能替代异步中断的完整状态恢复。
也不能通过随意写 T 位,把正在执行的 ARM 指令流原地变成 Thumb。普通代码的 ARM/Thumb 互操作应使用相应分支机制;异常返回则使用架构规定的状态恢复路径。
SPSR 是快照,但只有一份
IRQ 发生时,硬件把异常前 CPSR 保存到 SPSR_irq,把返回相关地址放进 LR_irq,并进入 IRQ 模式。软件因此能取得异常前的执行状态。
每个核只有一份 SPSR_irq 和 LR_irq。若允许下一次 IRQ 在它们尚未转移到内存前到来,新异常会覆盖旧信息。支持嵌套中断的实现,必须先解决这个所有权问题,再重新允许 IRQ。
仅仅执行 cps #0x12 不会自动完成上述异常快照。手动切模式和硬件异常入口不能当成完全相同的操作。
一次 IRQ 的寄存器视图
1 | |
“切到独立的软件中断栈”不一定意味着当前模式再次变成 IRQ。本工程在 SVC 模式下,把 SVC 的 SP 临时设置到一块专用于中断处理的内存。栈区的名字和 CPU 当前模式必须分别观察。
模式切换和任务切换的分界
| 动作 | 改变什么 | 是否足以换线程 |
|---|---|---|
cps #IRQ |
模式与 bank 可见性 | 否 |
| 硬件 IRQ 入口 | 保存有限返回信息,切模式与向量 | 否 |
_CPU_Context_switch |
软件保存旧状态,恢复新任务栈与继续点 | 是,在其约束内 |
| IRQ 尾部选择另一 frame | 从新任务的 PC/CPSR 返回 | 是,在完整现场已保存的前提下 |
两个任务都运行在 SVC 时,也能进行软件切换;它们轮流使用同一个硬件 SVC bank,把暂时不用的状态保存在不同内存对象中。
复查时用三个小实验
第一,在 _start 的两个 CPS 附近单步,分别记录 CPSR & 0x1f 和 SP。切模式会改变当前 SP 视图,但不会自动改变普通 r4 的值。
第二,在 IRQ 入口第一条指令停住,读取 SPSR 与 LR;继续执行到 cps #SVC 后再看 CPSR。不要把此时的 SVC LR 当成刚才的 IRQ LR。
第三,在 RFE 前检查内存里的 return_pc 与 cpsr,再单步返回。此时恢复的不只是地址,也包括中断屏蔽、模式与条件标志。
原图可对照 Programmer’s Guide 第 4 章的 The ARM register set、CPSR bits,以及 ARM ARM B1.3 的 ARM core registers, PSRs, and ELR_hyp, showing register banking。后者画的是更广的架构集合,阅读时要排除 A9 未实现的扩展。
下一篇:AAPCS32 与关键汇编。
重点回看
处理器模式切换改变的是 banked 寄存器的可见窗口,不是把一组寄存器复制到另一组。CPSR 描述当前执行状态,异常模式的 SPSR 保存异常前状态;IRQ 返回必须同时恢复地址与 CPSR。双核又在模式 bank 之外增加了每核独立性。
本章总结
处理器模式切换改变的是 banked 寄存器的可见窗口,不是把一组寄存器复制到另一组。CPSR 描述当前执行状态,异常模式的 SPSR 保存异常前状态;IRQ 返回必须同时恢复地址与 CPSR。双核又在模式 bank 之外增加了每核独立性。
下一章进入 AAPCS32,说明不发生异常时,C 与汇编如何约定寄存器和栈。
ARMv7-A小系统实践(二):处理器模式、寄存器组与CPSR
https://goko-son626.github.io/post/armv7a-01-registers-modes.html

