本文所描述的为Cortex-A7中断系统
中断是什么?
中断(Interrupt)是指处理器接收到来自硬件或软件的信号,提示发生了某个事件,应该被注意,这种情况就称为中断。
通常,在接收到来自外围硬件(相对于中央处理器和内存)的异步信号,或来自软件的同步信号之后,处理器将会进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器通过一个运行信息切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的一个指令,以可编程的方式直接指示这种运行信息切换,并将处理导向一段中断处理代码。中断在计算机多任务处理,尤其是即时系统中尤为有用。这样的系统,包括运行于其上的操作系统,也被称为“中断驱动的”)。
中断系统主要包括以下几点:
中断向量是中断服务程序的入口地址,或中断向量表(它是一个中断处理程序地址的数组)的表项。
系统程序必须维护一份中断向量表,每一个表项纪录一个中断处理程序的地址。当外部事件或异常产生时,由硬件负责产生一个中断标记,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) |
描述:
英文全称:general interrupt controller。 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器。
现有4个版本:V1 ~ V4。
Cortex-A7使用的是GIC V2,GIC V2 最多支持 8 个核。 GIC V2 的中断控制器 IP 核为GIC400。
当 GIC 接收到外部中断信号以后就会报给 ARM 内核,但是ARM 内核只提供了四个信号给 GIC 来汇报中断情况: VFIQ、 VIRQ、 FIQ 和 IRQ,关系图如下:
含义:
说明:
左侧部分 | 中间部分 | 右侧部分 |
---|---|---|
中断源 | GIC控制器 | 中断控制器向内核处理器发送中断信息 |
其中GIC控制器是最重要的,GIC将众多的中断源分为三类:
为了区分不同的中断源,要分配他们分配一个唯一的ID,这些是中断ID。
每个CPU最多支持1020个中断ID,中断ID号为ID0~ID1019。具体分配情况如下:
具体中断ID需查看相应的数据手册。
GIC架构分为两个逻辑块:
**Distributor(分发器端):**负责处理各个中断事件的分发,简单来说,就是中断事件应该发送到哪个CPUInterface上。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到 CPU 接口端。
主要工作如下:
**CPU Interface(CPU 接口端):**和CPU Core相链接的,在每个 CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface。简单来说,就是分发器和CPU core之间的桥梁。
主要工作如下:
GIC控器的寄存器及地址应放置在哪里?比如文章上边提到过中断向量表偏移,这里就要靠CP15协处理器。
CP15 协处理器一般用于存储系统管理,在中断中也会使用到, CP15 协处理器一共有16 个 32 位寄存器。 CP15 协处理器的访问通过如下另个指令完成:
MCR{cond} p15, ,
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通过不同的搭配,其得到的寄存器含义是不同的。
当 MRC/MCR 指令中的 CRn=c0, opc1=0, CRm=c0, opc2=0 的时候就表示此时的 c0 就是 MIDR 寄存器,也就是主 ID 寄存器,这个也是 c0 的基本作用。
其含义如下图:
各位含义:
当 MRC/MCR 指令中的 CRn=c1,opc1=0,CRm=c0,opc2=0的时候就表示此时的c1就是SCTLR、寄存器,也就是系统控制寄存器,这个是 c1 的基本作用。 SCTLR 寄存器主要是完成控制功能的。
含义图:
部分位含义:
当 MRC/MCR 指令中的 CRn=c12,opc1=0,CRm=c0,opc2=0的时候就表示此时c12为VBAR寄存器,也就是向量表基地址寄存器。设置中断向量表偏移的时候就需要将新的中断向量表基地址写入 VBAR 中。
需要 c15 作为 CBAR 寄存器,因为 GIC 的基地址就保存在 CBAR中。
包括两方面:
指令 | 描述 |
---|---|
cpsid i | 禁止IRQ中断 |
cpsie i | 使能 IRQ 中断 |
cpsid f | 禁止FIQ中断 |
cpsie f | 使能FIQ中断 |
IRQ or FIQ 总中断就像总开关一样,先打开总开关才能中断ID使能才有效。
其中,bit[15:0]对应ID150的SGI中断,bit[31:16]对应ID3116的PPI中断。剩下的都是控制SPI中断。
需要初识化GICC_PMR寄存器,该寄存器低8位有效,如下表:
bit7:0 | 优先级数 |
---|---|
11111111 | 256个优先级 |
11111110 | 128 |
11111100 | 64 |
11111000 | 32 |
11110000 | 16 |
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 级子优先级。 |
GICD_IPRIORITYR[40] = 5 << 3;
点击此文章:ARM Cortex -A中断系统(程序篇)
首先,感谢正点原子的课程以及资料,让我能深入学习ARM+Linux。裸机编程很复杂(相对于学习stm32来说是真很难),ARM7这块底层东西比较多,而且不再像以前学习一样有着成堆好用又方便的工具。这段时间里,我甚至认为裸机操作复杂且毫无意义。但是回过头来想一想,之前学习STM32的过程存在者一些漏洞,屏蔽掉了底层的原理(ARM-M内核掌握不深),注重应用,导致我经常遇到瓶颈,然后去补基础知识,然后再拐回来,反反复复,学习过程就很不流畅,比如RTOS系统,我理解的就不是很透彻(以后还需要进一步学习)。我想,这些裸机操作存在必有它的道理,是为了嵌入式Linux系统打下扎实的基础。不能再像以前,着急出结果,却把最重要最基础的东西没掌握透彻。道阻且长,继续努力吧!