ARM汇编嵌套中断处理

参考书籍:《ARM嵌入式系统开发---软件设计与优化》、《一步步写嵌入式操作系统--ARM编程的方法与实践》
嵌套中断考虑了当前调用中断处理执行过程中又发送中断的情形。使用类似函数递归调用的栈帧概念来将上下文压入堆栈。
具体参考《ARM嵌入式系统开发》第九章9.3.2部分和《一步步》的第五章5.4部分,前者比较通用完整。描速的都是在IRQ模式下不能简单打开中断,而是转换到SVC模式下保存上下文。
下面核心源代码是摘抄于《ARM嵌入式系统开发》的配套源码:

; /* ; *____________________________________________________________________ ; * ; *Copyright (c) 2004, Andrew N. Sloss, Chris Wright and Dominic Symes ; *All rights reserved. ; *____________________________________________________________________ ; * ; *NON-COMMERCIAL USE License ; * ; *Redistribution and use in source and binary forms, with or without ; *modification, are permitted provided that the following conditions ; *are met: ; * ; *1. For NON-COMMERCIAL USE only. ; * ; *2. Redistributions of source code must retain the above copyright ; *notice, this list of conditions and the following disclaimer. ; * ; *3. Redistributions in binary form must reproduce the above ; *copyright notice, this list of conditions and the following ; *disclaimer in the documentation and/or other materials provided ; *with the distribution. ; * ; *4. All advertising materials mentioning features or use of this ; *software must display the following acknowledgement: ; * ; *This product includes software developed by Andrew N. Sloss, ; *Chris Wright and Dominic Symes. ; * ; *THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY ; *EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ; *PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE CONTRIBUTORS BE ; *LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, ; *OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ; *PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, ; *OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ; *THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ; *TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ; *OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY ; *OF SUCH DAMAGE. ; * ; *If you have questions about this license or would like a different ; *license please email : ; * ; *andrew@sloss.net ; * ; * ; */; /*********************************************************************** ; * ; *Module: nih9_9.s ; *Descriptions : Nested Interrupt Handler ; *Example: 9.9 ; *OS: generic ; *Platform: generic ; *History: ; * ; *31th December 2003 ; *- added header ; * ; ***********************************************************************/EXPORT nestedInterruptHandlerMaskmdEQU 0x1f; processor mode mask SVC32mdEQU 0x13; SVC mode I_BitEQU 0x80; IRQ bit FRAME_R0EQU 0x00 FRAME_R1EQU FRAME_R0+4 FRAME_R2EQU FRAME_R1+4 FRAME_R3EQU FRAME_R2+4 FRAME_R4EQU FRAME_R3+4 FRAME_R5EQU FRAME_R4+4 FRAME_R6EQU FRAME_R5+4 FRAME_R7EQU FRAME_R6+4 FRAME_R8EQU FRAME_R7+4 FRAME_R9EQU FRAME_R8+4 FRAME_R10EQU FRAME_R9+4 FRAME_R11EQU FRAME_R10+4 FRAME_R12EQU FRAME_R11+4 FRAME_PSREQU FRAME_R12+4 FRAME_LREQU FRAME_PSR+4 FRAME_PCEQU FRAME_LR+4 FRAME_SIZE EQU FRAME_PC+4 AREA nih9_9,CODE,readonly nestedInterruptHandler ; instructionstate : comment SUBr14,r14,#4; 2 : STMDBr13!,{r0-r3,r12,r14}; 2 : save context ; BLread_RescheduleFlag; 3 : more processing CMPr0,#0; 3 : if processing? LDMNEIA r13!,{r0-r3,r12,pc}^; 4 :then return MRSr2,SPSR; 5 : copy SPSR_irq MOVr0,r13; 5 : copy r13_irq ADDr13,r13,#6*4; 5 : reset stack MRSr1,CPSR; 6 : copy CPSR BICr1,r1,#Maskmd; 6 : ORRr1,r1,#SVC32md; 6 : MSRCPSR_c,r1; 6 : change SVC mode SUBr13,r13,#FRAME_SIZE-FRAME_R4 ; 7 : make stack space STMIAr13,{r4-r11}; 7 : save r4-r11 LDMIAr0,{r4-r9}; 7 : r4-r9 IRQ stack BICr1,r1,#I_Bit; 8 : MSRCPSR_c,r1; 8 : enable int STMDBr13!,{r4-r7}; 9 : save r4-r7 SVC STRr2,[r13,#FRAME_PSR]; 9 : save PSR STRr8,[r13,#FRAME_R12]; 9 : save r12 STRr9,[r13,#FRAME_PC]; 9 : save pc STRr14,[r13,#FRAME_LR]; 9 : save lr ; LDMIAr13!,{r0-r12,r14}; 11 : restore context MSRSPSR_cxsf,r14; 11 : restore SPSR LDMIAr13!,{r14,pc}^; 11 : returnread_RescheduleFlag ; MOVr0,#0; more processing MOVpc,r14; returnEND


将irq和svc两种模式的堆栈图画出来观看的话,理解起来比较直观。因为我是初学,刚开始没能理解其中堆栈操作,原因是:
1.忘记了r0-r12,cpsr是irq和svc共用的堆栈,r13(sp),r14(lr)和spsr是不相同的。
2.对堆栈向下生长不够深刻,刚开始还记得后来有弄反了,导致理解不了,还一度认为书上有错。
3.对ARM的堆栈操作指令和多寄存器传送指令比较生疏,需要查表对照,一步一步得走
另外我觉得书上描速的完成一个栈帧从高地址往低地址的情况好像反了,感觉应该是lr_irq,r14_svc,spsr_irq,r12-r0的顺序。
下面在mini2440板子上實現嵌套中斷:使用了LED PB8(LED4)和PB7(LED3)低電平亮,外部中斷8和19,對應按鍵是K1和K6.


.equNOINT,0xc0 .equI_Bit,0x80 .equSVC_MOD,0x13 .equIRQ_MOD,0x12.equWTCON,0x53000000.equGPBCON,0x56000010 .equGPBDAT,0x56000014 .equGPBUP,0x56000018.equGPGCON,0x56000060.equEINTMASK, 0x560000a4 .equEXTINT1,0x5600008c .equEXTINT2,0x56000090 .equEINTPEND, 0x560000a8.equINTMSK,0x4A000008.equSRCPND,0X4A000000 .equINTPND,0X4A000010.equSVC_Stack, 4096 .equIRQ_Stack, 2048@栈帧定义 .equFRAME_R0,0x00 .equFRAME_R1,FRAME_R0+4 .equFRAME_R2,FRAME_R1+4 .equFRAME_R3,FRAME_R2+4 .equFRAME_R4,FRAME_R3+4 .equFRAME_R5,FRAME_R4+4 .equFRAME_R6,FRAME_R5+4 .equFRAME_R7,FRAME_R6+4 .equFRAME_R8,FRAME_R7+4 .equFRAME_R9,FRAME_R8+4 .equFRAME_R10,FRAME_R9+4 .equFRAME_R11,FRAME_R10+4 .equFRAME_R12,FRAME_R11+4 .equFRAME_PSR,FRAME_R12+4 .equFRAME_LR,FRAME_PSR+4 .equFRAME_PC,FRAME_LR+4 .equFRAME_SIZE,FRAME_PC+4.text .code 32 .global _start _start: b reset ldrpc, _undefined_instruction ldrpc, _software_interrupt ldrpc, _prefetch_abort ldr pc, _data_abort ldrpc, _not_used ldrpc, _irq ldrpc, _fiq_undefined_instruction:.word undefined_instruction _software_interrupt:.word software_interrupt _prefetch_abort:.word prefetch_abort _data_abort:.word data_abort _not_used:.word not_used _irq:.word irq _fiq:.word fiq.balignl 16,0xdeadbeef reset: ldr r0,=WTCON mov r1,#0x0 str r1,[r0] @关看门狗 msr cpsr_c,#(IRQ_MOD|NOINT) ldr sp,=IRQ_Stackmsr cpsr_c,#(SVC_MOD|NOINT) ldr sp,=SVC_Stack ldr r0,=GPBCON ldr r1,=0x14000 str r1,[r0] @PB8,7输出 ldr r0,=GPBDAT mov r1,#0x180 str r1,[r0] @8、7高电平ldr r0,=GPGCON ldr r1,=0x800002 str r1,[r0] @使能外部中断8,外部中断19 ldr r0,=EXTINT1 mov r1,#0x03 str r1,[r0] @外部8下降沿触发 ldr r0,=EXTINT2 ldr r1,=0x3000 str r1,[r0] @外部19下降沿触发 ldr r0, =EINTPEND ldr r1,=0x00fffff0 str r1,[r0]@ 清EINTPEND,写1清零 ldr r0,=EINTMASK ldr r1,=0xfff7feff str r1,[r0] @使能外部中断8、19,0为使能 ldr r0, =SRCPND ldr r1,=0xffffffff str r1,[r0]@ 清SRCPND,写1清零 ldr r0, =INTPND ldr r1,=0xffffffff str r1,[r0]@ 清INTPND,写1清零 ldr r0,=INTMSK ldr r1,=0xffffffdf str r1,[r0] @使能外部中断EINT8-23 mrs r1, cpsr bic r1, r1, #I_Bit msr cpsr_c, r1 @开全局IRQ中断loop: b loopirq_server1: stmfdsp!,{lr}ldr r0,=GPBDAT ldr r1,[r0] bic r2,r1,#0x100 str r2,[r0] bl delay1 orr r2,r1,#0x100 str r2,[r0] @bl delay1 ldmfdsp!,{lr} mov pc,lrirq_server2: stmfdsp!,{lr}ldr r0,=GPBDAT ldr r1,[r0] bic r2,r1,#0x80 str r2,[r0] bl delay2 orr r2,r1,#0x80 str r2,[r0] @bl delay2 ldmfdsp!,{lr} mov pc,lrdelay1: ldr r12,=0x5ffffff delay1loop: sub r12,r12,#1 cmp r12,#0x0 bne delay1loop mov pc,lrdelay2: ldr r3,=0x5ffffff delay2loop: sub r3,r3,#1 cmp r3,#0x0 bne delay2loop mov pc,lrundefined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop irq: subr14,r14,#4 stmdbr13!,{r0-r3,r12,r14}@ save context ldr r0, =EINTPEND ldr r10,[r0] ldr r11,=0x80100 tst r10,r11 @判断是否为EINT8、19,r10记录哪个外部中断 ldr r0, =EINTPEND ldr r1,[r0] @ldr r1,=0x100 str r1,[r0] @清EINTPEND ldr r0, =SRCPND ldr r1,[r0] @ldr r1,=0x20 str r1,[r0] @清SRCPND ldr r0, =INTPND ldr r1,[r0] @ldr r1,=0x20 str r1,[r0] @清INTPND ldmeqfdr13!,{r0-r3,r12,pc}^ mrsr2,SPSR@ copy SPSR_irq movr0,r13@ copy r13_irq addr13,r13,#6*4@ reset stack msrcpsr_c,#(SVC_MOD|NOINT)@ change SVC mode subr13,r13,#FRAME_SIZE-FRAME_R4 @ make stack space stmiar13,{r4-r11}@ save r4-r11 ldmiar0,{r4-r9}@ r4-r9 IRQ stack mrs r1, cpsr bic r1, r1, #I_Bit msr cpsr_c, r1 @开全局IRQ中断 stmdbr13!,{r4-r7}@ save r4-r7 SVC strr2,[r13,#FRAME_PSR]@ save PSR strr8,[r13,#FRAME_R12]@ save r12 strr9,[r13,#FRAME_PC]@ save pc strr14,[r13,#FRAME_LR]@ save lr ldr r11,=0x80000 cmp r10,r11 bleq irq_server1 blne irq_server2 ldmiar13!,{r0-r12,r14}@ restore context msrSPSR_cxsf,r14@ restore SPSR ldmiar13!,{r14,pc}^@ return fiq: nop


可以看到中斷的嵌套,但是按下K6多次後會有問題,譬如LED4一直暗,或一直亮,再按那兩個中斷按鍵也沒反應。JLink調試發現按一次按鍵,中斷重入好幾次,大概是沒有去抖動導致,連續按久了,堆棧溢出?同時還會跑到“ldrpc, _undefined_instruction “這一句上,跑飛了?問題的原因還有待確認!
【ARM汇编嵌套中断处理】

    推荐阅读