【ARM自学笔记】ARM Cortex -A中断系统(原理篇)

文章目录

  • 前言
  • 简述
  • 中断向量表
  • GIC
    • 架构
    • 中断ID
    • GIC逻辑分块
    • CP15协处理器
  • 中断使能
  • 中断优先级设置
  • 程序编写
  • 最后

前言

本文所描述的为Cortex-A7中断系统

简述

中断是什么?
中断(Interrupt)是指处理器接收到来自硬件或软件的信号,提示发生了某个事件,应该被注意,这种情况就称为中断。

通常,在接收到来自外围硬件(相对于中央处理器和内存)的异步信号,或来自软件的同步信号之后,处理器将会进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器通过一个运行信息切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的一个指令,以可编程的方式直接指示这种运行信息切换,并将处理导向一段中断处理代码。中断在计算机多任务处理,尤其是即时系统中尤为有用。这样的系统,包括运行于其上的操作系统,也被称为“中断驱动的”)。


中断系统主要包括以下几点:

  1. 中断向量表
  2. GIC(向量中断控制器)
  3. 中断使能
  4. 中断服务函数

中断向量表

中断向量是中断服务程序的入口地址,或中断向量表(它是一个中断处理程序地址的数组)的表项。

系统程序必须维护一份中断向量表,每一个表项纪录一个中断处理程序的地址。当外部事件或异常产生时,由硬件负责产生一个中断标记,CPU根据中断标记获得相应中断的中断向量号,然后由CPU根据中断向量表的地址和中断向量号去查找中断向量表获得相应中断号的中断程序地址,进一步执行对应的中断处理程序。

中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。

因此,中断向量表在整个程序的最前面。


ARM处理器是从0X00000000开始运行的,理论上中断向量表也应该是存放在0x00000000的,但是实际并不是这样,大部分取决于代码烧录的位置。为了解决这个问题,引入了中断向量表偏移。通过偏移让中断向量表存放在任意地址处。


Cortex-A7有8个异常中断,如下表所示:

向量地址 中断类型 中断模式
0X00 复位中断(Rest) 特权模式(SVC)
0X04 未定义指令中断(Undefined Instruction) 未定义指令中止模式(Undef)
0X08 软中断(Software Interrupt,SWI) 特权模式(SVC)
0X0C 指令预取中止中断(Prefetch Abort) 中止模式
0X10 数据访问中止中断(Data Abort) 中止模式
0X14 未使用(Not Used) 未使用
0X18 IRQ 中断(IRQ Interrupt) 外部中断模式(IRQ)
0X1C FIQ 中断(FIQ Interrupt) 快速中断模式(FIQ)

描述:

  1. 复位中断(Rest), CPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化 SP 指针、 DDR 等等。
  2. 未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
  3. 软中断(Software Interrupt,SWI),由SWI指令引起的中断,Linux的系统调用会用SWI指令来引起软中断,通过软中断来陷入到内核空间。
  4. 指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。
  5. 数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。
  6. IRQ 中断(IRQ Interrupt),外部中断,Cortex-A内核CPU的所有外部中断都属于这个IQR中断,当任意一个外部中断发生的时候都会触发 IRQ 中断。在 IRQ 中断服务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断。
  7. FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中。

GIC

英文全称:general interrupt controller。 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器。

现有4个版本:V1 ~ V4。

  • V1最老的版本,现已抛弃。
  • V2主要用于ARMv7-A架构使用,eg:Cortex-A7、 Cortex-A9、 Cortex-A15 等
  • V3和V4主要用于ARMv8-A/R架构使用(64位芯片)

架构

Cortex-A7使用的是GIC V2,GIC V2 最多支持 8 个核。 GIC V2 的中断控制器 IP 核为GIC400。

当 GIC 接收到外部中断信号以后就会报给 ARM 内核,但是ARM 内核只提供了四个信号给 GIC 来汇报中断情况: VFIQ、 VIRQ、 FIQ 和 IRQ,关系图如下:
【ARM自学笔记】ARM Cortex -A中断系统(原理篇)_第1张图片

含义:

  • VFIQ:虚拟快速 FIQ。
  • VIRQ:虚拟快速 IRQ。
  • FIQ:快速中断 IRQ。
  • IRQ:外部中断 IRQ。

GICV2框架图如下:
【ARM自学笔记】ARM Cortex -A中断系统(原理篇)_第2张图片

说明:

左侧部分 中间部分 右侧部分
中断源 GIC控制器 中断控制器向内核处理器发送中断信息

其中GIC控制器是最重要的,GIC将众多的中断源分为三类:

  1. SPI(Shared Peripheral Interrupt),共享中断,所有Core共享的中断(最常见),这些外部中断属于SPI中断,eg:按键中断、串口中断etc。
  2. PPI(Private Peripheral Interrupt),私有中断,GIC支持多核,每个核有自己独有的中断,这些独有的中断肯定是要指定的核心处。
  3. SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。

中断ID

为了区分不同的中断源,要分配他们分配一个唯一的ID,这些是中断ID。
每个CPU最多支持1020个中断ID,中断ID号为ID0~ID1019。具体分配情况如下:

  • ID0~ID15:这16个ID分配给SGI。
  • ID16~ID31:这16个分配给PPI。
  • ID32~ID1019:这988个ID分配给SPI

具体中断ID需查看相应的数据手册。

GIC逻辑分块

GIC架构分为两个逻辑块:

  • Distributor(分发器)
  • CPU Interface(CPU接口)

**Distributor(分发器端):**负责处理各个中断事件的分发,简单来说,就是中断事件应该发送到哪个CPUInterface上。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到 CPU 接口端。
主要工作如下:

  1. 全局中断使能控制
  2. 控制每一中断的使能或者关机
  3. 使能每个中断的优先级
  4. 设置每个中断的目标处理器列表
  5. 设置每个外部中断的触发模式:电平触发或边沿触发
  6. 设置每个中断属于组0还是组1

**CPU Interface(CPU 接口端):**和CPU Core相链接的,在每个 CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface。简单来说,就是分发器和CPU core之间的桥梁。
主要工作如下:

  1. 使能或者关闭发送到CPU Core的中断请求信号
  2. 应答中断
  3. 通知中断处理完成
  4. 设置优先级掩码,通过掩码来设置那些中断不需要上报给CPU Core
  5. 定义抢占策略
  6. 当多个中断到来的时候,选择优先级最高的中断通知给CPU Core

CP15协处理器

GIC控器的寄存器及地址应放置在哪里?比如文章上边提到过中断向量表偏移,这里就要靠CP15协处理器。

CP15 协处理器一般用于存储系统管理,在中断中也会使用到, CP15 协处理器一共有16 个 32 位寄存器。 CP15 协处理器的访问通过如下另个指令完成:

  • MRC: 将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中。
  • MCR: 将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中。
    (MRC 就是读 CP15 寄存器, MCR 就是写 CP15 寄存器, MCR 指令格式如下:)
MCR{cond} p15, , , , , 
  1. **cond:**指令执行的条件码,如果忽略的话就表示无条件执行。
  2. **opc1:**协处理器要执行的操作码。
  3. Rt: ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
  4. CRn: CP15 协处理器的目标寄存器。
  5. CRm: 协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm设置为C0,否则结果不可预测。
  6. opc2: 可选的协处理器特定操作码,当不需要的时候要设置为 0

MRC的指令格式和MCR一样,MRC中的是目标寄存器,而是源寄存器,读取的数据在该处理器。


CP15 一共有16个32位寄存器,c0~c15。
中断用到主要有c0、c1、c12、c15这四个寄存器。

通过 c0 寄存器可以获取到处理器内核信息;通过c1寄存器可以使能或禁止MMU、I/DCache等;通过c12寄存器可以设置中断向量偏移;通过 c15 寄存器可以获取 GIC 基地址。

在使用 MRC 或者 MCR 指令访问这16个寄存器的时候,指令中的CRn、opc1、CRm和opc2通过不同的搭配,其得到的寄存器含义是不同的。

  1. c0寄存器
    寄存器不同的含义

当 MRC/MCR 指令中的 CRn=c0, opc1=0, CRm=c0, opc2=0 的时候就表示此时的 c0 就是 MIDR 寄存器,也就是主 ID 寄存器,这个也是 c0 的基本作用。
其含义如下图:
在这里插入图片描述

各位含义:

  • bit31:24:厂商编号, 0X41, ARM。
  • bit23:20:内核架构的主版本号, ARM 内核版本一般使用 rnpn来表示。
  • bit19:16:架构代码, 0XF, ARMv7 架构
  • bit15:4:内核版本号, 0XC07, Cortex-A7 MPCore 内核。
  • bit3:0:内核架构的次版本号, rnpn 中的 pn。
  1. c1寄存器
    寄存器不同的含义
    【ARM自学笔记】ARM Cortex -A中断系统(原理篇)_第3张图片

当 MRC/MCR 指令中的 CRn=c1,opc1=0,CRm=c0,opc2=0的时候就表示此时的c1就是SCTLR、寄存器,也就是系统控制寄存器,这个是 c1 的基本作用。 SCTLR 寄存器主要是完成控制功能的。
含义图:
在这里插入图片描述

部分位含义:

  • bit13: V , 中断向量表基地址选择位,为0的话中断向量表基地址为0X00000000,软件可以使用VBAR来重映射此基地址,也就是中断向量表重定位。为 1 的话中断向量表基地址为0XFFFF0000,此基地址不能被重映射。
  • bit12: I, I Cache 使能位,为 0 的话关闭 I Cache,为 1 的话使能 I Cache。
  • bit11: Z,分支预测使能位,如果开启 MMU 的话,此位也会使能
  • bit10: SW, SWP 和 SWPB 使能位,当为 0 的话关闭 SWP 和 SWPB 指令,当为 1 的时候就使能 SWP 和 SWPB 指令
  • bit9:3:未使用,保留。
  • bit2: C, D Cache 和缓存一致性使能位,为 0 的时候禁止 D Cache 和缓存一致性,为 1 时使能。
  • bit1: A,内存对齐检查使能位,为 0 的时候关闭内存对齐检查,为 1 的时候使能内存对齐检查
  • bit0: M, MMU 使能位,为 0 的时候禁止 MMU,为 1 的时候使能 MMU。
  1. c12寄存器
    寄存器不同的含义
    【ARM自学笔记】ARM Cortex -A中断系统(原理篇)_第4张图片

当 MRC/MCR 指令中的 CRn=c12,opc1=0,CRm=c0,opc2=0的时候就表示此时c12为VBAR寄存器,也就是向量表基地址寄存器。设置中断向量表偏移的时候就需要将新的中断向量表基地址写入 VBAR 中。

  1. c15寄存器
    寄存器不同的含义
    【ARM自学笔记】ARM Cortex -A中断系统(原理篇)_第5张图片

需要 c15 作为 CBAR 寄存器,因为 GIC 的基地址就保存在 CBAR中。

中断使能

包括两方面:

  • IRQ or FIQ 总中断使能
  • ID0~ID1019中断源的使能
  1. 我们可以通过寄存器CPSR进行使能总中断,也可以以通过如下指令使能:
指令 描述
cpsid i 禁止IRQ中断
cpsie i 使能 IRQ 中断
cpsid f 禁止FIQ中断
cpsie f 使能FIQ中断

IRQ or FIQ 总中断就像总开关一样,先打开总开关才能中断ID使能才有效。

  1. GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用来完成外部中断的使能和禁止,对于 Cortex-A7 内核来说中断 ID 只使用了 512 个。一个 bit 控制一个中断 ID 的使能,需要16 个GICD_ISENABLER寄存器来完成中断的使能,需要16 个GICD_ICENABLER 寄存器来完成中断的禁止。

其中,bit[15:0]对应ID150的SGI中断,bit[31:16]对应ID3116的PPI中断。剩下的都是控制SPI中断。

中断优先级设置

  1. 优先级数配置
    Cortex-A7 的中断优先级分为抢占优先级和子优先级,最多支持256个优先级,数字越小优先级越高(由半导体厂商决定优先级个数)。

需要初识化GICC_PMR寄存器,该寄存器低8位有效,如下表:

bit7:0 优先级数
11111111 256个优先级
11111110 128
11111100 64
11111000 32
11110000 16
  1. 抢占优先级和子优先级位数设置
    抢占优先级和子优先级各占多少位是由寄存器 GICC_BPR 来决定的,只有低3位有效,配置如下:
Binary Point 抢占优先级域 子优先级域 描述
000 [7:1] [0] 7 级抢占优先级, 1 级子优先级。
001 [7:2] [1:0] 6 级抢占优先级, 2 级子优先级。
010 [7:3] [2:0] 5 级抢占优先级, 3 级子优先级
011 [7:4] [3:0] 4 级抢占优先级, 4 级子优先级。
100 [7:5] [4:0] 3 级抢占优先级, 5 级子优先级。
101 [7:6] [5:0] 2 级抢占优先级, 6 级子优先级。
110 [7:7] [6:0] 1 级抢占优先级, 7 级子优先级。
111 [0] [7:0] 0 级抢占优先级, 8 级子优先级。
  1. 优先级设置
    某个中断 ID 的中断优先级设置由寄存器D_IPRIORITYR 来完成。如果优先级个数为 32 的话,使用寄存器 D_IPRIORITYR 的 bit7:4 来设置优先级,也就是说实际的优先级要左移 3 位。比如要设置ID40 中断的优先级为 5,示例代码如下:
GICD_IPRIORITYR[40] = 5 << 3;

程序编写

点击此文章:ARM Cortex -A中断系统(程序篇)

最后

首先,感谢正点原子的课程以及资料,让我能深入学习ARM+Linux。裸机编程很复杂(相对于学习stm32来说是真很难),ARM7这块底层东西比较多,而且不再像以前学习一样有着成堆好用又方便的工具。这段时间里,我甚至认为裸机操作复杂且毫无意义。但是回过头来想一想,之前学习STM32的过程存在者一些漏洞,屏蔽掉了底层的原理(ARM-M内核掌握不深),注重应用,导致我经常遇到瓶颈,然后去补基础知识,然后再拐回来,反反复复,学习过程就很不流畅,比如RTOS系统,我理解的就不是很透彻(以后还需要进一步学习)。我想,这些裸机操作存在必有它的道理,是为了嵌入式Linux系统打下扎实的基础。不能再像以前,着急出结果,却把最重要最基础的东西没掌握透彻。道阻且长,继续努力吧!

你可能感兴趣的:(ARM,嵌入式,arma,单片机)