news 2026/7/22 4:34:02

ARM day5

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM day5

1. 什么是 GIC?

🔸 GIC 全称

GIC(Generic Interrupt Controller,通用中断控制器),是 ARM 公司专门为Cortex-A 系列内核设计的一款集中式中断控制器

🔸 为什么需要 GIC?

随着 SoC(片上系统)复杂度提升,芯片中集成了大量外设(UART、I2C、SPI、GPIO、USB、Ethernet 等),每个外设都可能产生中断。如果让 CPU 直接处理每个外设的中断:

  • 硬件连线复杂:每个外设都需要一根独立的中断线接到 CPU

  • 软件处理困难:中断优先级、屏蔽、使能等操作难以统一管理

  • 多核支持差:在多核 CPU 上难以实现中断路由

GIC 解决了上述问题,它将所有外设的中断集中管理,统一向 CPU 提交中断请求。

🔸 GIC 主要功能

功能说明
中断使能/屏蔽控制每个中断源是否使能
中断优先级管理多个中断同时到达时,按优先级仲裁
中断分发多核系统中将中断路由到指定的 CPU 核
中断状态维护记录中断的 pending、active 等状态
中断通知通过 IRQ 或 FIQ 信号通知 CPU

🔸 GIC 版本演进

版本架构适用 CPU说明
GIC V132 位Cortex-A5早期版本
GIC V232 位Cortex-A7、A9、A15i.MX6ULL 使用此版本
GIC V364 位Cortex-A53、A57支持更大规模系统
GIC V464 位Cortex-A53、A57V3 增强版,支持虚拟中断
GIC V5新架构最新高性能芯片下一代架构

💡i.MX6ULL内置的是GIC-400(GIC V2 架构),最多支持 160 个中断源。


2. 什么是协处理器?ARM Cortex-A7 内核配备了几个协处理器?

🔸 协处理器(Coprocessor)定义

协处理器是一种协助主处理器完成特定功能的专用处理单元。在 ARM 架构中,协处理器通过一组专用的指令(MCR/MRC)和寄存器与主 CPU 交互,用于完成主 CPU 不擅长或效率不高的任务。

🔸 ARM 协处理器编号

ARM 架构最多支持16 个协处理器,编号为cp0 ~ cp15

协处理器用途
cp0 ~ cp13由芯片厂商自行定义(保留)
cp14调试协处理器(Debug)
cp15系统控制协处理器(最常用)

⚠️ Cortex-A 系列实际只实现了cp14cp15,其他协处理器编号保留未用。

🔸 cp15 系统控制协处理器

cp15 是 ARM 架构中最重要的协处理器,通过它可以访问 CPU 内部的关键控制寄存器:

主寄存器 (CRn)主要功能
c0ID 寄存器、Cache 类型、TRM
c1SCTLR(系统控制寄存器,控制 MMU、Cache、异常向量表位置等)
c2TTBR(转换表基址寄存器,配置页表)
c3DACR(域访问控制寄存器)
c5FSR(故障状态寄存器)
c6FAR(故障地址寄存器)
c7Cache 控制、TLB 管理
c8TLB 控制
c12VBAR(异常向量表基址寄存器)
c13FCSE、上下文 ID
c15CBAR(GIC 基址寄存器,Cortex-A7 特有)

🔸 协处理器操作指令

MRC{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2> ; 读取 cp15 寄存器到 ARM 寄存器 MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2> ; 将 ARM 寄存器写入 cp15 寄存器

常用示例

; 读取 SCTLR 寄存器到 r0 mrc p15, 0, r0, c1, c0, 0 ​ ; 将 r0 写回 SCTLR 寄存器 mcr p15, 0, r0, c1, c0, 0 ​ ; 读取 GIC 基址到 r1(CBAR 寄存器) mrc p15, 4, r1, c15, c0, 0 ​ ; 设置异常向量表基址(VBAR 寄存器) mcr p15, 0, r0, c12, c0, 0

🔸 Cortex-A7 协处理器总结

项目数量/内容
理论支持数16 个(cp0 ~ cp15)
实际实现数2 个(cp14、cp15)
cp14调试协处理器
cp15系统控制协处理器(MMU、Cache、异常向量、GIC 基址)

3. 为什么要将异常向量表重映射?如何重映射异常向量表?

🔸 为什么要重映射异常向量表?

原因一:异常向量表位置的固定性

ARM 处理器上电复位后,异常向量表默认位于:

异常向量表基址触发条件
0x00000000SCTLR.V = 0(默认)
0xFFFF0000SCTLR.V = 1(高端向量)

绝大多数 MCU/MPU 的启动代码(Boot ROM)都映射在低地址 0x00000000,所以默认情况下异常向量表位于此处。

原因二:每项向量只有 4 字节

异常向量表中每项只有 4 字节(刚好放下 1 条跳转指令),无法容纳完整的中断服务程序:

; 默认异常向量表 b reset_handler ; 0x00: 占 4 字节 b undefined_handler ; 0x04: 占 4 字节 b swi_handler ; 0x08: 占 4 字节 b prefetch_abort_handler ; 0x0C: 占 4 字节 b data_abort_handler ; 0x10: 占 4 字节 b reserved_handler ; 0x14: 占 4 字节 b irq_handler ; 0x18: 占 4 字节 b fiq_handler ; 0x1C: 占 4 字节

真正的中断服务程序往往需要几十甚至上百条指令,无法直接放在异常向量表中。

原因三:NOR Flash / ROM 中无法直接写代码
  • NOR Flash支持 XIP(片内执行),但写入困难

  • Boot ROM只能读不能写

如果异常向量表位于 NOR Flash 或 ROM 中,运行后无法修改,也难以调试。

重映射的好处

将异常向量表重映射到RAM(高速、可读写)后:

优势说明
可在 RAM 中修改方便调试和动态更新
访问速度快RAM 访问速度远高于 Flash
可容纳完整程序异常向量表每项可以是一个完整函数

🔸 如何重映射异常向量表?

方法一:通过 SCTLR.V 位切换基址

Cortex-A7 通过 cp15 的c1(SCTLR)寄存器的V 位(bit 13)控制异常向量表位置:

SCTLR.V异常向量表基址
00x00000000(默认)
10xFFFF0000

操作示例

mrc p15, 0, r0, c1, c0, 0 ; 读取 SCTLR 到 r0 orr r0, r0, #(1 << 13) ; 设置 V 位(高端向量 0xFFFF0000) mcr p15, 0, r0, c1, c0, 0 ; 写回 SCTLR

⚠️ 这种方法只能切换到 0x00000000 或 0xFFFF0000 两个固定地址,灵活性差

方法二:通过 VBAR 寄存器设置基址(Cortex-A7 推荐)

Cortex-A7 提供VBAR(Vector Base Address Register)寄存器,cp15 的c12,可以任意指定异常向量表的基址

; 将异常向量表基址设置为 0x87800000 ldr r0, =0x87800000 mcr p15, 0, r0, c12, c0, 0 ; 写入 VBAR

这样就可以将异常向量表放到 RAM 的任意位置(如 0x87800000),方便调试和管理。

方法三:完整的异常向量表重映射流程(i.MX6ULL 示例)
; 1. 初始化 DDR bl init_ddr ​ ; 2. 将异常向量表代码拷贝到 DDR 地址 0x87800000 ldr r0, =_start ; 源地址(Flash 中的异常向量表) ldr r1, =0x87800000 ; 目标地址(DDR) ldr r2, =__vector_end ; 结束地址 cpy: ldmia r0!, {r3 - r10} stmia r1!, {r3 - r10} cmp r0, r2 ble cpy ​ ; 3. 通过 VBAR 寄存器重映射异常向量表 ldr r0, =0x87800000 mcr p15, 0, r0, c12, c0, 0 ​ ; 4. 切换 ARM 状态、设置栈指针,然后跳转到 C 语言 main 函数 cps #0x1F ; 切换到 System 模式 msr cpsr_c, #0xD3 ; 切换到 SVC 模式 ldr sp, =0x80800000 ; 设置 SVC 模式栈指针 msr cpsr_c, #0xD7 ; 切换到 Abort 模式 ldr sp, =0x80800000 msr cpsr_c, #0xDB ; 切换到 Undef 模式 ldr sp, =0x80800000 msr cpsr_c, #0xD2 ; 切换到 IRQ 模式 ldr sp, =0x80800000 msr cpsr_c, #0xD1 ; 切换到 FIQ 模式 ldr sp, =0x80800000 msr cpsr_c, #0x13 ; 切换到 SVC 模式 ​ b main ; 跳转到 main 函数

🔸 异常向量表示例(在 DDR 0x87800000 处)

; 异常向量表 b reset_handler ; 0x00: 复位 b undefined_handler ; 0x04: 未定义指令 b swi_handler ; 0x08: 软中断 b prefetch_abort_handler ; 0x0C: 预取中止 b data_abort_handler ; 0x10: 数据中止 b reserved_handler ; 0x14: 保留 b irq_handler ; 0x18: IRQ 中断 b fiq_handler ; 0x1C: FIQ 中断

4. GIC 支持多少个中断,分为哪几类?

🔸 GIC 中断总数

GIC(V2 架构)最多支持1020 个中断源,编号为ID0 ~ ID1019

🔸 中断分类

GIC 将所有中断分为3 大类

类别全称中断 ID 范围数量用途
SGISoftware Generated Interrupts0 ~ 1516软件生成中断,用于 CPU 核间通信
PPIPrivate Peripheral Interrupts16 ~ 3116私有外设中断,每个 CPU 核独享
SPIShared Peripheral Interrupts32 ~ 1019988共享外设中断,所有 CPU 共享

🔸 三类中断详细说明

1. SGI(软件生成中断)
  • 编号:0 ~ 15

  • 触发方式:由软件写 GIC 的SGIR 寄存器主动触发

  • 典型用途

    • 多核 CPU 之间的通信(IPI,Inter-Processor Interrupt)

    • 例如 CPU0 需要 CPU1 做一些工作时,CPU0 触发一个 SGI 通知 CPU1

  • 特点

    • 完全由软件控制,灵活性高

    • 只能路由到其他 CPU 核(不能发给触发它的核自身)

2. PPI(私有外设中断)
  • 编号:16 ~ 31

  • 触发来源:外设,但每个外设只对应一个 CPU 核

  • 典型外设

    • 本地 Timer

    • 看门狗(Watchdog)

    • 性能监控计数器

  • 特点

    • 每个 CPU 核有自己独立的 PPI

    • 多核系统中,每个核看到的 PPI 可能不同

3. SPI(共享外设中断)
  • 编号:32 ~ 1019

  • 触发来源:SoC 中的各种外设

  • 典型外设

    • GPIO中断

    • UARTI2CSPI中断

    • USBEthernetSD 卡中断

    • LCDPWMADC中断

  • 特点

    • 所有 CPU 核共享

    • GIC 负责决定路由到哪个 CPU 核

    • 数量最多,是嵌入式开发中最常接触的一类中断

🔸 GIC 中断分类示意图

GIC(最多 1020 个中断 ID = 0 ~ 1019) │ ├─── SGI(0 ~ 15,共 16 个)软件中断(核间通信) │ └─ 写 SGIR 寄存器触发 │ ├─── PPI(16 ~ 31,共 16 个)私有外设中断 │ └─ 本地 Timer、Watchdog 等 │ └─── SPI(32 ~ 1019,共 988 个)共享外设中断 └─ GPIO、UART、I2C、USB、SD 等

🔸 GIC 与 CPU 交互的关键寄存器

寄存器偏移读写功能
GICD_CTLR0x000R/WGIC 全局控制寄存器,使能/禁用 GIC
GICD_ISENABLER0x100R/W中断使能寄存器
GICD_ICENABLER0x180R/W中断禁用寄存器
GICD_IPRIORITYR0x400R/W中断优先级寄存器
GICD_ITARGETSR0x800R/W中断目标 CPU 寄存器
GICD_SGIR0xF00W软件生成中断寄存器
GICC_CTLR0x2000R/WCPU 接口控制寄存器
GICC_PMR0x2004R/W中断优先级屏蔽寄存器
GICC_IAR0x200CR中断确认寄存器(读取得到中断 ID)
GICC_EOIR0x2010W中断结束寄存器(写入通知 GIC 中断处理完成)

5. i.MX6ULL 中支持多少个中断?

🔸 i.MX6ULL 中断总览

i.MX6ULL 共支持160 个中断源,编号ID0 ~ ID159,其中:

中断类型ID 范围数量说明
SGI0 ~ 1516保留,软件可触发
PPI16 ~ 3116私有外设中断(本地 Timer、Watchdog 等)
SPI32 ~ 159128共享外设中断(GPIO、UART、I2C 等)
合计0 ~ 159160GIC V2 架构支持

💡 i.MX6ULL 的 GIC 实现为GIC-400,属于GIC V2版本,采用 32 位架构,与 Cortex-A7 内核配套。

🔸 i.MX6ULL 常用中断源示例

中断 ID中断名称说明
32 + 32×nGPIO1_Combined_0_15_IRQn ~ GPIO5_Combined_0_15_IRQnGPIO1 ~ GPIO5 中断
32 + 16GPIO1_Combined_16_31_IRQnGPIO1 第 16 ~ 31 引脚中断
.........
58UART1_IRQnUART1 中断
59UART2_IRQnUART2 中断
60UART3_IRQnUART3 中断
61UART4_IRQnUART4 中断
62 + 32nI2C1_IRQn ~ I2C4_IRQnI2C 中断
.........

完整中断号可参考 NXP 官方手册i.MX6ULL Reference Manual中的Chapter 3: Interrupts章节。

🔸 i.MX6ULL 中断处理流程

i.MX6ULL 中断处理流程 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. 中断源发出中断请求(外设产生中断信号) ↓ 2. CPU 响应中断、比较中断优先级 ↓ 3. 保护现场(CPSR → SPSR_<mode>,LR 保存返回地址) ↓ 4. 切换到 IRQ 模式,跳转到异常向量表 ↓ 5. 执行中断服务函数(irq_handler) ↓ 6. 读取 GIC 的 GICC_IAR 寄存器,得到中断 ID ↓ 7. 根据中断 ID 调用对应的中断处理函数 ↓ 8. 处理完成后,写 GIC 的 GICC_EOIR 寄存器通知 GIC ↓ 9. 恢复现场(SPSR → CPSR,返回到被打断的程序继续执行)

🔸 i.MX6ULL 中断初始化示例(伪代码)

/* 1. 初始化 GIC 控制器 */ void GIC_Init(void) { /* 关闭所有 SPI 中断 */ for (i = 32; i < 160; i += 32) { GICD_ICENABLER[i / 32] = 0xFFFFFFFF; } ​ /* 设置所有 SPI 中断优先级为最低 */ for (i = 32; i < 160; i += 4) { GICD_IPRIORITYR[i / 4] = 0xFFFFFFFF; } ​ /* 设置所有 SPI 中断目标为 CPU0 */ for (i = 32; i < 160; i += 4) { GICD_ITARGETSR[i / 4] = 0x01010101; } ​ /* 使能 GIC */ GICD_CTLR = 3; GICC_CTLR = 1; } ​ /* 2. 使能某个外设的中断(如 GPIO1_18) */ void GIC_EnableIRQ(IRQn_Type IRQn) { GICD_ISENABLER[IRQn / 32] |= (1 << (IRQn % 32)); } ​ /* 3. 设置中断优先级 */ void GIC_SetPriority(IRQn_Type IRQn, uint8_t priority) { GICD_IPRIORITYR[IRQn / 4] |= (priority << ((IRQn % 4) * 8)); } ​ /* 4. 中断服务函数(Cortex-A7 通用入口) */ void irq_handler(void) { uint32_t intID = GICC_IAR; /* 读取中断 ID */ /* 根据中断 ID 调用对应处理函数 */ system_irq_handler(intID); GICC_EOIR = intID; /* 通知 GIC 中断处理完成 */ }

🔸 CPSR 中断相关控制位

名称功能
IIRQ 中断禁止位0 = 使能 IRQ,1 = 禁止 IRQ
FFIQ 中断禁止位0 = 使能 FIQ,1 = 禁止 FIQ

相关汇编指令

cpsie i ; 使能 IRQ 中断(CPSR.I = 0) cpsid i ; 禁止 IRQ 中断(CPSR.I = 1) cpsie f ; 使能 FIQ 中断 cpsid f ; 禁止 FIQ 中断 msr cpsr_c, r0 ; 修改 CPSR 低 8 位

📝 总结

序号题目核心要点
1什么是 GICARM 设计的通用中断控制器,集中管理 SoC 中所有中断
2协处理器ARM 支持 16 个(cp0~cp15),Cortex-A7 实现 2 个(cp14 调试、cp15 系统控制)
3重映射异常向量表原因:默认 0x00 每项只有 4 字节,且在 Flash/ROM 中不便修改;方法:通过 VBAR 寄存器(cp15.c12)任意指定基址
4GIC 中断分类共 1020 个:SGI(0~15,软件中断)、PPI(16~31,私有外设)、SPI(32~1019,共享外设)
5i.MX6ULL 中断数160 个(0~159),GIC V2 架构,配套 Cortex-A7 处理器

🔗 中断相关关键寄存器速查

寄存器含义操作
SCTLR.V(cp15.c1.bit13)异常向量表位置0: 0x00000000;1: 0xFFFF0000
VBAR(cp15.c12)异常向量表基址(推荐使用)可设置为任意地址
CBAR(cp15.c15)GIC 基址寄存器用于获取 GIC 控制器基地址
GICC_IAR(GIC 偏移 0x200C)中断确认寄存器读取得到中断 ID
GICC_EOIR(GIC 偏移 0x2010)中断结束寄存器通知 GIC 中断处理完成
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/22 4:33:23

C++17 std::clamp未定义问题:从编译器支持到跨版本兼容的完整解决方案

1. 项目概述&#xff1a;当std::clamp突然“消失”在C项目里&#xff0c;尤其是那些需要处理数值范围、做数据清洗或者UI组件值绑定的场景&#xff0c;std::clamp函数简直是救星。它用一行代码std::clamp(value, min, max)就能优雅地把一个值限制在指定的最小值和最大值之间&am…

作者头像 李华
网站建设 2026/7/22 4:33:14

C++脚本解释器框架实战:从词法分析到C++绑定

1. 项目概述&#xff1a;为什么我们需要一个C脚本解释器框架&#xff1f;在桌面应用、游戏引擎、工业仿真软件这些大型C项目的开发中&#xff0c;我们经常遇到一个经典矛盾&#xff1a;底层核心系统需要C的高性能与精细控制&#xff0c;而上层的业务逻辑、配置规则甚至用户自定…

作者头像 李华
网站建设 2026/7/22 4:32:56

C++11基于范围的for循环:原理、使用与性能优化指南

1. 项目概述&#xff1a;为什么我们需要“基于范围的for循环”&#xff1f;如果你写过C&#xff0c;尤其是处理过容器&#xff08;比如std::vector、std::map&#xff09;或者数组&#xff0c;那么下面这种“古典”的for循环写法你一定不陌生&#xff1a;std::vector<int>…

作者头像 李华
网站建设 2026/7/22 4:32:49

AI大模型面试核心考点与实战解析

1. 大厂AI大模型面试全景解析 最近两年&#xff0c;AI大模型技术岗位的面试难度直线上升。作为经历过BAT、TMD等多家大厂技术面试的从业者&#xff0c;我整理出这份覆盖90%高频考点的实战指南。不同于网上零散的面试题集合&#xff0c;本文将带你深入理解每个问题背后的技术逻辑…

作者头像 李华
网站建设 2026/7/22 4:31:09

意图共鸣科技 · 时间胶囊(二)|第三个方向启动

从记忆主权、认知安全&#xff0c;到智能体的构造标准日期&#xff1a;2026年7月21日近两个月&#xff0c;我们陆续发布了一系列白皮书。6月的第一期时间胶囊&#xff0c;我们做了一个基础界定&#xff1a;记忆链和认知智能是两条平行的系列&#xff0c;它们从第一天起就是同时…

作者头像 李华
网站建设 2026/7/22 4:29:53

Git 上传代码从零开始:一条命令一条命令带你推上远程仓库

别怕命令行&#xff0c;跟着我一步步敲&#xff0c;你也能把代码安全送上云端。 如果你是编程新手&#xff0c;或者习惯了用 GitHub 网页端那个“Upload files”按钮&#xff0c;那今天这篇文章就是为你准备的。我会带你在终端里&#xff0c;从零开始&#xff0c;一步一步把一个…

作者头像 李华