imx6ull时钟 中断汇编代码初始化

1.Reset_Handler

先调用指令“cpsid i”关闭 IRQ,

在这里插入图片描述

cpsid i						/* 关闭全局中断 */

关闭 I/D Cache、 MMU、对齐检测和分支预测。CP15 协处理器

MRC: 将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中。
MCR: 将 ARM 寄存器的数据写入到 CP15协处理器寄存器中。
在这里插入图片描述

mrc     p15, 0, r0, c1, c0, 0     /* 读取CP15的C1寄存器到R0中 */ 
 	bic     r0,  r0, #(0x1 << 12)     /* 清除C1寄存器的bit12位(I位),关闭I Cache            	*/
    bic     r0,  r0, #(0x1 <<  2)     /* 清除C1寄存器的bit2(C位),关闭D Cache    				*/
    bic     r0,  r0, #0x2             /* 清除C1寄存器的bit1(A位),关闭对齐						*/
    bic     r0,  r0, #(0x1 << 11)     /* 清除C1寄存器的bit11(Z位),关闭分支预测					*/
    bic     r0,  r0, #0x1             /* 清除C1寄存器的bit0(M位),关闭MMU				       	*/
mcr     p15, 0, r0, c1, c0, 0     /* 将r0寄存器中的值写入到CP15的C1寄存器中	 				*/    

在这里插入图片描述
在这里插入图片描述

汇编版本的中断向量表重映射。

ldr r0, =0X87800000

/*
DSB
数据同步隔离。比 DMB 严格: 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访 问操作——译者注)
ISB
指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令。
*/

	dsb
	isb
	mcr p15, 0, r0, c12, c0, 0
	dsb
	isb

设置不同模式下的 sp 指针,分别设置 IRQ 模式、 SYS 模式和 SVC 模式的栈指针,每种模式的栈大小都是 2MB。

在这里插入图片描述

/* 进入IRQ模式 */

	mrs r0, cpsr

	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/

	orr r0, r0, #0x12 	/* r0或上0x13,表示使用IRQ模式					*/

	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/

	ldr sp, =0x80600000	/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */



	/* 进入SYS模式 */

	mrs r0, cpsr

	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/

	orr r0, r0, #0x1f 	/* r0或上0x13,表示使用SYS模式					*/

	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/

	ldr sp, =0x80400000	/* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */



	/* 进入SVC模式 */

	mrs r0, cpsr

	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/

	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式					*/

	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/

	ldr sp, =0X80200000	/* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */

调用指令“cpsie i”重新打开IRQ 中断

cpsie i				/* 打开全局中断 */

操作 CPSR 寄存器来打开 IRQ 中断。当初始化工作都完成以后就可以进入到 main 函数了,

/* 使能IRQ中断 */

	mrs r0, cpsr		/* 读取cpsr寄存器值到r0中 			*/

	bic r0, r0, #0x80	/* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */

	msr cpsr, r0		/* 将r0重新写入到cpsr中 			*/

跳转到 main 函数

b main				/* 跳转到main函数 			 	*/

2. IRQ_Handler

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, 0 /* 将 CP15 的 C0 内的值到 R1 寄存器中
118 * 参考文档 ARM Cortex-A(armV7)编程手册 V4.0.pdf P49
119 * Cortex-A7 Technical ReferenceManua.pdf P68 P138
120 */
121 add r1, r1, #0X2000 /* GIC 基地址加 0X2000, 得到 CPU 接口端基地址 */
122 ldr r0, [r1, #0XC] /* CPU 接口端基地址加 0X0C 就是 GICC_IAR 寄存器,
123 * GICC_IAR 保存着当前发生中断的中断号,我们要根据原子哥在线教学: www.yuanzige.com 论坛:www.openedv.com
406
I.MX6U 嵌入式 Linux 驱动开发指南
124 * 这个中断号来绝对调用哪个中断服务函数
125 */
126 push {r0, r1} /* 保存 r0,r1 */
127
128 cps #0x13 /* 进入 SVC 模式,允许其他中断再次进去 */
129
130 push {lr} /* 保存 SVC 模式的 lr 寄存器 */
131 ldr r2, =system_irqhandler /* 加载 C 语言中断处理函数到 r2 寄存器中*/
132 blx r2 /* 运行 C 语言中断处理函数,带有一个参数 */
133
134 pop {lr} /* 执行完 C 语言中断服务函数, lr 出栈 */
135 cps #0x12 /* 进入 IRQ 模式 */
136 pop {r0, r1}
137 str r0, [r1, #0X10] /* 中断执行完成,写 EOIR */
138
139 pop {r0}
140 msr spsr_cxsf, r0 /* 恢复 spsr */
141
142 pop {r0-r3, r12} /* r0-r3,r12 出栈 */
143 pop {lr} /* lr 出栈 */
144 subs pc, lr, #4 /* 将 lr-4 赋给 pc */
145
146 /* FIQ 中断 */
147 FIQ_Handler:
148
149 ldr r0, =FIQ_Handler
150 bx r0

第 110 到 144 行是中断服务函数 IRQ_Handler,这个是本章的重点,因为所有的外部中断最终都会触发 IRQ 中断, 所以 IRQ 中断服务函数主要的工作就是区分去当前发生的什么中断(中断 ID)?然后针对不同的外部中断做出不同的处理。
第 111 到 115 行是保存现场,
https://blog.csdn.net/monkea123/article/details/102961206
第 117 到122 行是获取当前中断号,中断号被保存到了 r0 寄存器中。
第 131 和 132 行才是中断处理的重点,这两行相当于调用了函数 system_irqhandler,函数 system_irqhandler 是一个 C 语言函数,此函数有一个参数,这个参数中断号,所以我们需要传递一个参数。汇编中调用 C 函数如何实现参数传递呢?根据 ATPCS(ARM-Thumb Procedure Call Standard)定义的函数参数传递规则,在汇编调用 C 函数的时候建议形参不要超过 4 个, 形参可以由 r0~r3 这四个寄存器来传递,如果形参大于 4 个, 那么大于 4 个的部分要使用堆栈进行传递。 以给 r0 寄存器写入中断号就可以了函数 system_irqhandler 的参数传递
在 136 行已经向 r0 寄存器写入了中断号了。中断的真正处理过程其实是在函数 system_irqhandler 中完成,稍后需要编写函数stem_irqhandler。
第 151 行向 GICC_EOIR 寄存器写入刚刚处理完成的中断号, 当一个中断处理完成以后必须向 GICC_EOIR 寄存器写入其中断号表示中断处理完成。
第 153 到 157 行就是恢复现场。
第 158 行中断处理完成以后就要重新返回到曾经被中断打断的地方运行,这里为什么要将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”开始执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值