IMX6ULL中断之复位中断函数实现
一. 复位中断函数
开发板一上电或者进行一次复位操作,其就会执行复位中断函数。复位中断函数可能需要实现的一些功能,包括进入 SVC(特权)模式,清除 bss段,设置 SP指针等。
二. 复位中断函数实现
1. 复位中断函数工作如下:
(1) 关闭全局中断。
参考正点原子提供的资料【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6,第17章 GPIO中断实验,关闭全局中断需要用到的指令如下:
cpsid i //禁止 IRQ 中断cpsie i //使能 IRQ 中断
(2) 关闭 I Cache,D Cache 和 MMU。
关闭这些需要用到 CP15协处理器的一些指令操作。
(3) 设置中断向量偏移。
ARM 处理器都是从地址 0X00000000 开始运行的,通过中断向量表偏移就可以将中断向量表存放到任意地址。
注意:设置中断向量偏移这个操作,也可以在 C语言中进行设置。只要在中断发生之前设置好就可以。
(4) 设置处理器9种工作模式下对应的SP指针。
要使用中断,那么必须设置 IRQ 模式下的 SP 指针。索性直接设置所有模式下的 SP 指针。(因为对于 Cortex-A系列的芯片而言, 内核 CPU 的所有外部中断都属于这个 IRQ 中断)
(5) 清除 bss段。
(6) 跳到 C函数,也就是 main() 函数。
2. 代码实现分析
下面分析以上内容的实现。分析如下:
(1) 关闭 I Cache,D Cache 和 MMU。
关闭这些涉及 CP15协处理器的一些指令操作。
参考 "参考资料" 目录下Cortex-A7 Technical ReferenceManua.pdf 文档的 55 页,可知如下指令:
MRC 指令: 读取 CP15 协处理器中的寄存器数据到 ARM 寄存器中。
MCR 指令:写 CP15 寄存器
格式如下:
MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
参考 Cortex-A7 Technical ReferenceManua.pdf 文档,可找到 SCTLR寄存器。该寄存器即可关闭/打开 I-Cache,D-Cache,MMU。
例如如下指令操作:
MRC p15, 0, R0, c1, c0, 0 //读取 SCTLR寄存器值到ARM的R0寄存器中
MCR p15, 0, R0, c1, c0, 0 //将ARM的R0寄存器的值写到 SCTLR寄存器中
(2) 设置中断向量偏移
将新的中断向量表首地址写入到 CP15协处理器的 VBAR 寄存器。参考资料Cortex-A7 Technical ReferenceManua.pdf 文档 第 B3.17 章节 。指令使用方法如 MRC一样。
3. 汇编代码实现
汇编文件 start.S 代码实现如下:
.global _start
.global _bss_start
_bss_start:.word _bss_start
.global _bss_end
_bss_end:.word _bss_end_start:ldr pc, =Reset_Handler /* 复位中断 */ ldr pc, =Undefined_Handler /* 未定义中断 */ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */ldr pc, =PrefAbort_Handler /* 预取终止中断 */ldr pc, =DataAbort_Handler /* 数据终止中断 */ldr pc, =NotUsed_Handler /* 未使用中断 */ldr pc, =IRQ_Handler /* IRQ中断 */ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 *//* 复位中断服务函数 */
Reset_Handler:cpsid i //禁止IRQ中断MRC p15, 0, r0, c1, c0, 0 //读取 SCTLR寄存器值到ARM的R0寄存器中bic r0, r0, #(1 << 12) //关闭 I-Cachebic r0, r0, #(1 << 11) //关闭分支预测bic r0, r0, #(1 << 2) //关闭 D-Cachebic r0, r0, #(1 << 1) //关闭对齐bic r0, r0, #(1 << 0) //关闭 MMUMCR p15, 0, R0, c1, c0, 0 //将ARM的R0寄存器的值写到SCTLR寄存器中/*设置中断向量 */ldr r0, =0x87800000dsb //数据同步isb //指令同步MCR p15, 0, r0, c12, c0, 0 //将0x87800000地址写入C15协处理器VBAR寄存器dsbisb/*清除BSS段 */ldr r0, _bss_startldr r1, _bss_endmov r2, #0
bss_loop: /*循环 */stmia r0!, {r2} cmp r0, r1ble bss_loop /* 如果小于等于的话就跳转到bss_loop继续清bss段*//*配置处理器模式为 IRQ模式*/mrs r0, cpsr /*将cpsr寄存器中的值读到r0寄存器*/bic r0, r0, #0x1f /*清零cprs寄存器的 bit[4~0]位*/orr r0, r0, #0x12 /*设置处理器模式为 SVC模式*/msr cpsr, r0 /*写到 cpsr 寄存器中*/ldr sp, =0x80600000 //设置 SVC模式下的 sp 指针/*配置处理器模式为 SYS模式*/mrs r0, cpsr /*将cpsr寄存器中的值读到r0寄存器*/bic r0, r0, #0x1f /*清零cprs寄存器的 bit[4~0]位*/orr r0, r0, #0x1f /*设置处理器模式为 SVC模式*/msr cpsr, r0 /*写到 cpsr 寄存器中*/ldr sp, =0x80400000 //设置 SVC模式下的 sp 指针/*配置处理器模式为 SVC模式*/mrs r0, cpsr /*将cpsr寄存器中的值读到r0寄存器*/bic r0, r0, #0x1f /*清零cprs寄存器的 bit[4~0]位*/orr r0, r0, #0x13 /*设置处理器模式为 SVC模式*/msr cpsr, r0 /*写到 cpsr 寄存器中*/ldr sp, =0x80200000 //设置 SVC模式下的 sp 指针cpsie i //打开IRQ
/*跳转到 C 语言 main函数*/b main /* 未定义中断服务函数 */
Undefined_Handler:ldr r0, =Undefined_Handlerbx r0/* SVC中断服务函数 */
SVC_Handler:ldr r0, =SVC_Handlerbx r0/* 预取终止中断服务函数 */
PrefAbort_Handler:ldr r0, =PrefAbort_Handler bx r0/* 数据终止中断服务函数 */
DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0/* 未使用的中断服务函数 */
NotUsed_Handler:ldr r0, =NotUsed_Handlerbx r0/* IRQ中断服务函数!重点!!!!! */
IRQ_Handler:ldr r0, =IRQ_Handlerbx r0/* FIQ中断服务函数 */
FIQ_Handler:ldr r0, =FIQ_Handler bx r0
通过开发板测试验证,开发板上的复位键按键按下后,蜂鸣器正常响。灯也是正常闪烁。