IMX6ULL中断之IRQ中断函数实现

一.    IRQ中断函数

本文介绍 IMX6ULL的IRQ中断函数实现,具体是汇编实现。对汇编实现过程进行一下初步的了解。

二.   IRQ中断函数代码实现

IRQ 中断函数涉及 CP15协处理器 与  GIC中断控制器。

关于 CP15 协处理 器和其 相关寄存 器的详细 内容 请参考下 面两份文 档: 《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》 1469 页“ B3.17 Oranization of the CP15 registers in a VMSA implementation ”文档 与
文档 《Cortex-A7 Technical ReferenceManua.pdf》 55 页“ Capter 4 System Control ”。
GIC中断控制器可参考 ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf 文档。
GIC 架构分为了两个逻辑块:Distributor CPU Interface ,也就是分发器端和 CPU 接口端。

start.S 汇编文件中 IMX6ULL IRQ中断函数,代码实现如下:

/* IRQ中断!重点!!!!! */
IRQ_Handler:
	push {lr}					/* 保存lr地址 */
	push {r0-r3, r12}			/* 保存r0-r3,r12寄存器 */

	mrs r0, spsr				/* 读取spsr寄存器 */
	push {r0}					/* 保存spsr寄存器 */

	mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
								* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
								* Cortex-A7 Technical ReferenceManua.pdf P68 P138
								*/							
	add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
	ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
								 * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
								 * 这个中断号来绝对调用哪个中断服务函数
								 */
	push {r0, r1}				/* 保存r0,r1 */
	
	cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */
	
	push {lr}					/* 保存SVC模式的lr寄存器 */
	ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
	blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */

	pop {lr}					/* 执行完C语言中断服务函数,lr出栈 */
	cps #0x12					/* 进入IRQ模式 */
	pop {r0, r1}				
	str r0, [r1, #0X10]			/* 中断执行完成,写EOIR */

	pop {r0}						
	msr spsr_cxsf, r0			/* 恢复spsr */

	pop {r0-r3, r12}			/* r0-r3,r12出栈 */
	pop {lr}					/* lr出栈 */
	subs pc, lr, #4				/* 将lr-4赋给pc */

以上代码分析如下:

mrc p15, 4, r1, c15, c0, 0   //读取CP15的CBAR寄存器。

CBAR寄存器保存了GIC控制器的寄存器组首地址。GIC寄存器组偏移0x1000~0x1fff为GIC的分发器。0x2000~0x3fff为CPU接口端。意味我们可以访问GIC控制器了!

代码中,R1寄存器将保存着GIC控制器的CPU接口端基地址。

读取CPU接口端的 GICC_IAR寄存器的值保存到 R0寄存器里面。

可以从GICC_IAR的bit9~0读取中断ID,我们读取中断ID的目的就是为了得到对应的中断处理函数。

system_irqhandler 就是具体的中断处理函数,此函数有一个参数,为 GICC_IAR寄存器的值。

system_irqhandler 处理完具体的中断以后,需要将对应的中断ID 值写入到 GICC_EOIR寄存器里面。

关于如下代码:

subs pc, lr, #4				/* 将lr-4赋给pc */
LR寄存器(lr): 即链接寄存器。
例如,当触发了中断或者调用了其他函数后执行完以后,就要返回到以前的中断点接着执行,LR寄存器保存的是返回的指令的地址。
PC寄存器(pc):保存当前正在取指的地址。
这里为什么要将 lr-4 然后赋给 pc 呢?而不是直接将 lr 赋值给 pc
ARM 的指令是三级流水线:取指、译指、执 pc 指向的是正在取值的地址,这就是很多书上说的 pc= 当前执行指令地址 +8
例如下面代码示例:
0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC
左侧一列是地址,中间是指令,最右边是流水线。
当前正在执行 0X2000 地址处的指令“MOV R1, R0”,但是 PC 里面已经保存了 0X2008 地址处的指令“MOV R4, R5”。
假设此时发生了中断,中断发生的时候保存在 lr 中的是 pc 的值,也就是地址 0X2008。
当中断处理完成以后肯定需要回到被中断点接着执行,如果直接跳转到 lr 里面保存的地址处(0X2008) 开始运行,那么就有一个指令没有执行,那就是地址 0X2004 处的指令“MOV R2, R3”,显然这 是一个很严重的错误!
所以,就需要将 lr-4 赋值给 pc,也就是 pc=0X2004,从指令“MOV R2,R3”开始执行。

你可能感兴趣的:(嵌入式C开发,arm开发,linux)