1 中断系统简介
1.1 中断向量表
中断向量表的定义的就是中断服务程序的跳转指令,因为每个中断向量在向量表中只有一个字节的存储空间,只能存放一条指令,所以通常存放跳转指令,使程序跳转到存储器的其他地方,再执行中断处理。这里cpu就可以找中断服务程序,跳转指令例如:
LDRPC, =ISR_HANDLER
;
Cortex-A7内核有8个异常中断,这8个异常中断的中断向量表如下表所示:
中断向量表里面都是中断服务函数的入口地址,因此一款芯片有什么中断都是可以从中断向量表看出来的。从上表中可以看出,Cortex-A7 一共有 8 个中断,而且还有一个中断向量未使用,实际只有 7 个中断。
下面我们来简单介绍一下这7个中断:
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),外部中断,前面已经说了,芯片内部的外设中断都会引起此中断的发生。
7.FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中断。
在上面的7个中断中,我们常用的就是复位中断和IRQ中断,所以我们需要编写这两个中断的中断服务函数,稍后我们会讲解如何编写对应的中断服务函数。首先我们要根据上表的内容来创建中断向量表,中断向量表处于程序最开始的地方,比如我们8_int例程的 start.S文件最前面,中断向量表如下:
1
2 .global _start /* 全局标号 */
3
4 /*
5 * 描述:_start函数,首先是中断向量表的创建
6 * 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42,
3 ARM Processor Modes and Registers(ARM处理器模型和寄存器)
7 * ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)
8 */
9 _start:
10 ldr pc, =Reset_Handler /* 复位中断 */
11 ldr pc, =Undefined_Handler /* 未定义中断 */
12 ldr pc, =SVC_Handler /* SVC(Supervisor)中断*/
13 ldr pc, =PrefAbort_Handler /* 预取终止中断 */
14 ldr pc, =DataAbort_Handler /* 数据终止中断 */
15 ldr pc, =NotUsed_Handler /* 未使用中断 */
16 ldr pc, =IRQ_Handler /* IRQ中断 */
17 ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
18
19 /* 复位中断 */
20 Reset_Handler:
21
22 cpsid i /* 关闭全局中断 */
23
24 /* 关闭I,DCache和MMU
25 * 采取读-改-写的方式。
26 */
27 mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中*/
28 bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache*/
29 bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */
30 bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */
31 bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */
32 bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */
33 mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入CP15的C1寄存器中*/
34
35
36 #if 0
37 /* 汇编版本设置中断向量表偏移 */
38 ldr r0, =0X87800000
39
40 dsb
41 isb
42 mcr p15, 0, r0, c12, c0, 0
43 dsb
44 isb
45 #endif
46
47 /* 设置各个模式下的栈指针,
48 * 注意:IMX6UL的堆栈是向下增长的!
49 * 堆栈指针地址一定要是4字节地址对齐的!!!
50 * DDR范围:0X80000000~0X9FFFFFFF
51 */
52 /* 进入IRQ模式 */
53 mrs r0, cpsr
54 bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4*/
55 orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */
56 msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
57 ldr sp, =0x80600000 /*设置IRQ模式下栈首地址0X80600000,大小为2M*/
58
59 /* 进入SYS模式 */
60 mrs r0, cpsr
61 bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,cpsr的M0~M4 */
62 orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */
63 msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
64 ldr sp, =0x80400000 /* 设置SYS模式栈首地址为0X80400000,大小2M*/
65
66 /* 进入SVC模式 */
67 mrs r0, cpsr
68 bic r0, r0, #0x1f /* 将r0寄存器中低5位清零,cpsr的M0~M4 */
69 orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
70 msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
71 ldr sp, =0X80200000 /* 设置SVC模式栈首地址为0X80200000,大小2M*/
72
73 cpsie i /* 打开全局中断 */
74 #if 0
75 /* 使能IRQ中断 */
76 mrs r0, cpsr /* 读取cpsr寄存器值到r0中 */
77 bic r0, r0, #0x80 /* 将r0中bit7清零,也就是CPSR的I位清零,允许IRQ*/
78 msr cpsr, r0 /* 将r0重新写入到cpsr中 */
79 #endif
80
81 b main /* 跳转到main函数 */
82
83 /* 未定义中断 */
84 Undefined_Handler:
85 ldr r0, =Undefined_Handler
86 bx r0
87
88 /* SVC中断 */
89 SVC_Handler:
90 ldr r0, =SVC_Handler
91 bx r0
92
93 /* 预取终止中断 */
94 PrefAbort_Handler:
95 ldr r0, =PrefAbort_Handler
96 bx r0
97
98 /* 数据终止中断 */
99 DataAbort_Handler:
100 ldr r0, =DataAbort_Handler
101 bx r0
102
103 /* 未使用的中断 */
104 NotUsed_Handler:
105
106 ldr r0, =NotUsed_Handler
107 bx r0
108
109 /* IRQ中断!重点!!!!! */
110 IRQ_Handler:
111 push {
lr} /* 保存lr地址 */
112 push {
r0-r3, r12} /* 保存r0-r3,r12寄存器 */
113
114 mrs r0, spsr /* 读取spsr寄存器 */
115 push {
r0} /* 保存spsr寄存器 */
116
117 mrc p15, 4, r1, c15, c0