Cortex-A7中断向量表是在代码的最前面,Cortex-A7 内核有 8 个异常中断,这 8 个异常中断的中断向量表如表所示:
中断向量表里面都是中断服务函数的入口地址,因此一款芯片有什么中断都是可以从中断向量表看出来的。从表中可以看出,Cortex-A7 一共有 8 个中断,而且还有一个中断向量未使用,实际只有 7 个中断。和“示例代码 17.1.1.1”中的 STM32F103 中断向量表比起来少了很多!难道一个能跑 Linux 的芯片只有这 7 个中断?明显不可能的!那类似 STM32 中的EXTI9_5_IRQHandler、TIM2_IRQHandler 这样的中断向量在哪里?I2C、SPI、定时器等等的中断怎么处理呢?这个就是 Cortex-A 和 Cotex-M 在中断向量表这一块的区别,对于 Cortex-M 内核来说,中断向量表列举出了一款芯片所有的中断向量,包括芯片外设的所有中断。对于 Cotex-A 内核来说并没有这么做,在表中有个 IRQ 中断, Cortex-A 内核 CPU 的所有外部中断都属于这个 IQR 中断,当任意一个外部中断发生的时候都会触发 IRQ 中断。在 IRQ 中断服务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断,进而根据具体的中断做出相应的处理。这些外部中断和 IQR 中断的关系如图所示:
在图中,左侧的 Software0_IRQn~PMU_IRQ2_IRQ 这些都是 I.MX6U 的中断,他们都属于 IRQ 中断。当图 左侧这些中断中任意一个发生的时候 IRQ 中断都会被触发,所以我们需要在IRQ 中断服务函数中判断究竟是左侧的哪个中断发生了,然后再做出具体的处理。
在表中一共有 7 个中断,简单介绍一下这 7 个中断:
①、复位中断(Rest),CPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化 SP 指针、DDR 等等。
②、未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
③、软中断(Software Interrupt,SWI),由 SWI 指令引起的中断,Linux 的系统调用会用 SWI
指令来引起软中断,通过软中断来陷入到内核空间。
④、指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。
⑤、数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。
⑥、IRQ 中断(IRQ Interrupt),外部中断,前面已经说了,芯片内部的外设中断都会引起此中断的发生。
⑦、FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中。 在上面的 7 个中断中,我们常用的就是复位中断和 IRQ 中断,所以我们需要编写这两个中断的中断服务函数,稍后我们会讲解如何编写对应的中断服务函数。首先我们要根据表中的内容来创建中断向量表,中断向量表处于程序最开始的地方,比如我们前面例程的 start.S 文件最前面,中断向量表如下:
1 .global _start /* 全局标号 */
2
3 _start:
4 ldr pc, =Reset_Handler /* 复位中断 */
5 ldr pc, =Undefined_Handler /* 未定义指令中断 */
6 ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */
7 ldr pc, =PrefAbort_Handler /* 预取终止中断 */
8 ldr pc, =DataAbort_Handler /* 数据终止中断 */
9 ldr pc, =NotUsed_Handler /* 未使用中断 */
10 ldr pc, =IRQ_Handler /* IRQ 中断 */
11 ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
12
13 /* 复位中断 */
14 Reset_Handler:
15 /* 复位中断具体处理过程 */
16
17 /* 未定义中断 */
18 Undefined_Handler:
19 ldr r0, =Undefined_Handler
20 bx r0
21
22 /* SVC 中断 */
23 SVC_Handler:
24 ldr r0, =SVC_Handler
25 bx r0
26
27 /* 预取终止中断 */
28 PrefAbort_Handler:
29 ldr r0, =PrefAbort_Handler
30 bx r0 31
32 /* 数据终止中断 */
33 DataAbort_Handler:
34 ldr r0, =DataAbort_Handler
35 bx r0
36
37 /* 未使用的中断 */
38 NotUsed_Handler:
39
40 ldr r0, =NotUsed_Handler
41 bx r0 42
43 /* IRQ 中断!重点!!!!! */
44 IRQ_Handler:
45 /* 复位中断具体处理过程 */
46
47 /* FIQ 中断 */
48 FIQ_Handler:
49 ldr r0, =FIQ_Handler
50 bx r0
第 4 到 11 行是中断向量表,当指定的中断发生以后就会调用对应的中断复位函数,比如复位中断发生以后就会执行第 4 行代码,也就是调用函数 Reset_Handler,函数 Reset_Handler就是复位中断的中断复位函数,其它的中断同理。
第 14 到 50 行就是对应的中断服务函数,中断服务函数都是用汇编编写的,我们实际需要编写的只有复位中断服务函数Reset_Handler 和 IRQ 中断服务函数 IRQ_Handler,其它的中断本教程没有用到,所以都是死循环。在编写复位中断复位函数和 IRQ 中断服务函数之前我们还需要了解一些其它的知识,否则的话就没法编写。
I.MX6U(Cortex-A)的中断控制器叫做 GIC,关于GIC 的详细内容请参考文档《ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf》。
GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的NVIC。目前 GIC 有 4 个版本:V1~V4,V1 是最老的版本,已经被废弃了。V2~V4 目前正在大量的使用。GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A7、Cortex-A9、Cortex-A15 等,V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。I.MX6U 是 Cortex-A 内核的,因此我们主要讲解 GIC V2。GIC V2 最多支持 8 个核。ARM 会根据 GIC 版本的不同研发出不同的 IP 核,那些半导体厂商直接购买对应的 IP 核即可,比如ARM 针对GIC V2 就开发出了 GIC400 这个中断控制器 IP 核。当GIC 接收到外部中断信号以后就会报给ARM 内核,但是ARM 内核只提供了四个信号给GIC 来汇报