ARMv7-A小系统实践(十):L1、L2、屏障与Cache一致性

本章记录什么

这一章解释 L1 I/D Cache、分支预测、SCU 和 L2C-310 的启用与维护。核心问题不是“把 SCTLR 的某一位置 1”,而是旧缓存状态何时失效、页表何时需要 clean、跨核共享何时依赖 SCU,以及 DMA 或自修改代码出现后软件应怎样维护一致性。

在这一章之前做了什么

上一章已经为普通内存和 MMIO 建立了不同的页表属性。只有在属性正确后,Cache 的行为才有可推导基础。启动阶段还要先无效化旧状态,再按顺序启用 SCU、ACTLR.SMP、L1 和 L2;运行阶段则要区分 clean、invalidate 和 clean+invalidate。

本章从概念、顺序和屏障开始,随后给出完整的 Cache 实现代码。最后会明确当前小系统证明了哪些 CPU 一致性能力,以及尚未通过 DMA 场景证明什么。

ARMv7-A小系统实践(九):用短描述符页表建立MMU与内存保护

本章记录什么

这一章建立 ARMv7-A short-descriptor MMU 的最小可用映射。内容从虚拟地址的拆分开始,解释 16 KiB 一级表、粗页表、Section 与 Small Page 描述符,然后把链接脚本中的代码、只读数据、可写数据、栈、页表和 MMIO 映射成不同权限与内存类型。

在这一章之前做了什么

前面的启动、上下文和中断仍可在 MMU 关闭时运行,但所有地址都只有“数值”,缺少权限与类型约束。打开 Cache 前更不能回避这一层:同一物理区域被标成 Normal Memory 还是 Device Memory,会直接改变访问排序、缓存和推测行为。

本章采用恒等映射降低早期调试难度,但不会把恒等映射误解为“没有地址转换”。文章给出页表构造代码、开启顺序、PAR 验证方式和常见故障定位。

ARMv7-A小系统实践(八):从完整IRQ现场到按需切换,再看RTEMS

本章记录什么

这一章把主动切换与 IRQ 返回路径合并成抢占式调度,并比较两种异常帧方案:基础版在每次 IRQ 保存 64 字节完整现场;精简版常规 IRQ 只保存 32 字节 caller-saved 现场,只有确实切换任务时才保存 r4-r11。最后用 RTEMS 6.2 的 ARM 端口检查这些选择背后的工程条件。

在这一章之前做了什么

此时已经有两个彼此独立但尚未连接的能力:普通函数边界上的协同任务切换,以及能够重复进入的 Timer IRQ。抢占的关键不是在 handler 里直接调用普通 context switch,而是让定时器只提出调度请求,由最外层 IRQ 尾部决定恢复哪一个任务的异常现场。

本章会先用完整 frame 建立容易证明正确的版本,再讨论何时可以优化。性能数字不是重点;重点是每省掉一组保存动作,都必须说明是谁继续承担寄存器保护责任。

ARMv7-A小系统实践(七):GIC、定时器与一次完整的IRQ

本章记录什么

这一章沿着一枚定时器中断走完整条链:Private Timer 产生事件,GIC Distributor 和本核 CPU Interface 判断是否交付,处理器进入 IRQ,软件读取 IAR 完成应答,设备 handler 清除中断源,最后把原始 IAR 写回 EOIR。目标是理解一个状态机,而不是记住几组地址。

在这一章之前做了什么

上一章已经能够安全进入和离开 IRQ,但入口正确并不代表设备中断能够重复工作。还需要解决三层使能、优先级、目标 CPU、Pending/Active 状态以及设备侧状态。任何一层遗漏,现象都可能只是“没有输出”或“只进入一次”。

本章会先解释 SGI、PPI、SPI 和 GIC 两大组成,再用 ID29 建立周期 tick。所有关键寄存器语义、分发器骨架和排错顺序都在文章内展开,不要求读者另开工程才能补齐上下文。

ARMv7-A小系统实践(六):异常入口、返回地址与独立中断栈

本章记录什么

这一章解释异常入口与返回,并建立任务私有的 IRQ 异常帧。它要解决的不是“进入了哪个 C 函数”,而是任意指令执行到一半时,怎样保存足够的信息,使处理结束后能够像中断从未发生一样继续执行;如果调度器选择了另一个任务,又怎样改为恢复另一个任务的现场。

在这一章之前做了什么

上一章的协同切换发生在函数调用边界,因此可借助 AAPCS 只保存 callee-saved 状态。IRQ 没有这个便利:r0-r3r12、条件标志和返回地址都可能正处于有效计算中。硬件只完成模式切换、CPSR 到 SPSR 的保存以及返回地址写入 banked lr,其余现场必须由入口汇编建立。

本章先分清硬件动作和软件动作,再说明为什么返回 PC 需要修正、为什么异常帧应归属于被打断任务、为什么 C handler 可以使用独立中断栈但恢复现场不能长期停留在共享栈上。

ARMv7-A小系统实践(五):两个线程怎样真正切换起来

本章记录什么

这一章实现两个任务之间的协同式切换。重点不在调度算法,而在“暂停的计算怎样被表示”:哪些寄存器属于普通函数边界上的长期状态,任务栈与上下文对象分别保存什么,新任务从未运行过又怎样伪造出第一次恢复所需的现场。

在这一章之前做了什么

启动代码已经建立 SVC 栈并进入 C 环境,AAPCS32 也给出了 r4-r11splr 的保存责任。于是可以先在没有中断的情况下做最小闭环:任务 A 主动调用切换函数,汇编保存 A 的 callee-saved 状态,恢复 B 的状态;B 再切回 A,A 从原调用点之后继续执行。

这种主动切换故意避开 IRQ。原因是它把问题缩小到普通调用边界,容易观察、容易回退。下一章再处理任意指令点被打断时所需的完整异常现场。

ARMv7-A小系统实践(四):从ELF、模式栈到第一行串口输出

本章记录什么

这一章把前两章的寄存器规则和调用规范放进真正的启动路径:镜像被放到什么地址,向量表为何必须对齐,链接脚本如何给代码、数据、页表和各模式栈分配空间,启动汇编又如何把一个没有 C 运行时的处理器带到 kernel_main。这是后续一切功能的地基。

在这一章之前做了什么

此时已经知道处理器模式会选择不同的 banked sp,也知道调用 C 前必须满足 AAPCS 的栈对齐和寄存器约定。但这些知识仍是静态规则。启动阶段要把它们变成具体地址:先屏蔽异步事件,建立向量基址和每模式栈,清理 BSS,再由 CPU0 进入共享初始化;CPU1 不能重复执行同一套破坏性操作。

本章会从链接合同讲到第一行串口输出,并给出一份可独立阅读的启动汇编。代码不是“最终模板”,而是一条可以逐条断点验证的最小路径。

ARMv7-A小系统实践(三):AAPCS32、栈与上下文汇编

本章记录什么

这一章讨论 C 函数与汇编函数如何彼此信任。处理器不会自动知道哪几个寄存器应该由调用者保存,也不会自动保证栈满足编译器要求;这些规则来自 AAPCS32。只有把参数、返回值、caller-saved、callee-saved、栈对齐和 lr 的含义连起来,才能安全地从启动汇编调用 C、从 IRQ 入口调用分发器,或者实现一个与编译器兼容的上下文切换函数。

在这一章之前做了什么

上一章已经说明模式切换如何改变 splr 和 SPSR 的可见版本。那属于处理器架构。普通函数调用则通常不改变处理器模式,它依赖的是软件合同。两者都在操作寄存器,但保存集合、返回方式和错误症状完全不同。本章先把这条边界画清楚,再进入多寄存器访存和实际函数边界。

文章中的示例会从一个普通五参数函数开始,逐步走到汇编调用 C、C 调用汇编以及结构体偏移校验。读完后,即使没有工程源码,也应能独立判断一段汇编是否破坏了 ABI。

ARMv7-A小系统实践(二):处理器模式、寄存器组与CPSR

本章记录什么

这一章解释 ARMv7-A 程序最先遇到的一组概念:处理器模式、可见寄存器、banked 寄存器、CPSR 和 SPSR。目标不是背诵一张寄存器表,而是能够回答三个工程问题:为什么启动代码要多次切换模式设置 sp,为什么 IRQ 到来后同名的 lrsp 会指向另一份物理寄存器,以及异常返回时为什么必须同时恢复程序地址和处理器状态。

在这一章之前做了什么

上一章确定了整个小系统的能力边界和资料分工,但还没有进入任何一条具体执行路径。现在从 CPU 的“当前身份”开始,因为启动、异常和调度最终都要落到寄存器可见性上。若这一层没有建立,看到 cps #0x12mrs r0, spsrrfeia 时只能记住指令表面,无法解释状态为什么能够恢复。

本章会先建立模式与寄存器组的基础,再把规则放进启动代码和一次 IRQ 中推演,最后列出可以在调试器里验证的观察点。任务切换只在结尾用于对比,不会提前混入。

ARMv7-A小系统实践(一):从Cortex-A9与Zynq-7020开始

本章记录什么

这一章不急着写第一条汇编,而是先回答一个更容易被忽略的问题:要在 ARMv7-A、Cortex-A9 和 Zynq-7020 上做出一个能启动、能切换任务、能响应中断的小系统,究竟需要建立哪些知识和软件部件?如果一开始不把层次分清,后面很容易把 ARMv7-A 的架构规定、Cortex-A9 的实现细节和 Zynq-7020 的片上地址混成一张表,遇到故障时也不知道该查哪一本手册。

本系列的目标不是照抄一份现成工程,也不是把 x86_64 汇编逐行翻译成 ARM 汇编。真正要迁移的是系统能力:处理器从复位入口进入 C 环境,任务拥有独立栈和可恢复现场,定时器能够触发 IRQ,GIC 能够完成中断状态闭环,MMU 与 Cache 按内存属性工作,第二个处理器核能够被安全释放。每一项能力都要给出可观察证据和明确边界。

在这一章之前做了什么

动手前已经具备一个很小的 x86_64 参考系统和两套 ARM 侧参照:一套是按同样功能合同搭建的 Zynq-7020 教学实现,另一套是 RTEMS 6.2 的成熟 ARM 端口。前者适合看最小闭环,后者适合验证设计方向。官方架构手册、Cortex-A9 MPCore 技术参考手册和 Zynq-7000 技术参考手册负责裁决寄存器和硬件语义。

接下来的文章会从处理器模式开始,逐步走到 ABI、启动、任务上下文、异常现场、GIC、抢占、MMU、Cache、SMP 和调试。读者只看博客也能顺着这条链完成推演;文中出现的源码路径只用于说明工程如何组织,不作为理解前提。


:D 一言句子获取中...