传统ARM中IRQ是作为一种体系反常呈现的。关于ARM核来说,有且仅有一个称为IRQ的体系反常。而ARM关于IRQ的处理一般经过反常向量找到IRQ的中止处理程序。当进入IRQ中止处理程序之后,ARM主动屏蔽IRQ,也就是说在中止呼应进程中是疏忽之后到来的中止请求的。即便运用了VIC,VIC也仅仅是悬起后来的中止请求。也就是说,传统ARM的中止是不可嵌套、不行抢占的。
不过,ARM给了咱们一种权力,那就是在中止处理程序中能够手动翻开IRQ,这样在前一个IRQ呼应的进程中,就能够被后来的中止所打断。就给咱们供给了一种用软件处理中止嵌套的途径。
中止的进程咱们都非常清楚:维护现场à呼应中止à康复现场。ARM关于每一种反常都有相应的仓库寄存器,且会主动切换,互不影响。所以天然而然地,在嵌套中,咱们能够用SP_irq来维护现场和康复现场。流程如下所示:
1.2.3.4.5.6.7.7.17.27.37.47.57.67.77.87.97.10经过LR_irq跳转回到7
7.11ARM主动从SPSR_irq康复CPSR
8.9.10.11.
这样就完成了嵌套,并且只需仓库够大,能够嵌套很多层。不考虑优先级,或许把优先级教给中止控制器办理,这样现已不错了吧,尽管不愿意这么说,可是问题仍是来了。
在上面的流程中,有一步是依据中止号进行中止服务。关于不同的中止源,咱们一般都会用不同函数来写中止服务,这样不只明晰,也利于将不同功用的模块分割开。这样咱们就需求将这步变为:依据中止号进入服务子程序。这步中,咱们会牵涉到函数调用。在函数调用进程中,一般都会先将PC保存在LR_irq中,在回来时,再将LR_irq康复到PC。这也正是LR的效果地点。
正是这个现实,导致了问题的产生。幻想这种状况:当咱们进入服务子程序后,此刻LR_irq正是咱们程序的回来地址。这时,第二个中止到来了,回想一下中止产生时ARM主动做了什么,ARM将PC保存到了LR_irq中!就这样,LR_irq被篡改了,由于咱们无法预料到中止什么时候到来,咱们也就底子无法保存这个被篡改的LR_irq。程序呼应好第二个中止后,一路回来到这个LR_irq,毫无意外的,就跑飞了。
很败兴吧,不过咱们天然有办法处理这个问题。办法就是在进入服务子程序之前,先将体系转换到SVC状况,这样,子程序被调用时回来地址就会被保存在LR_svc中,也就不会再被第二个中止所篡改。流程如下,和第一次不同的当地都用赤色标示。
1.2.3.4.5.6.7.8.9.9.19.29.39.49.59.69.79.89.99.10封闭IRQ使能位
9.11从SP_svc所指示的仓库中康复R0-R3,LR_svc
9.12更改体系状况为IRQ
9.13从SP_irq仓库中康复通用寄存器、LR_irq、SPSR_irq
9.14经过LR_irq跳转回到9
9.15ARM主动从SPSR_irq康复CPSR
10.11.12.13.14.15.
这样咱们既能够用中止服务子程序,也不怕LR被篡改了。咱们再来看一下嵌套进程中的仓库运用状况。在进入SVC状况之前,运用IRQ的仓库,保存嵌套所需的通用寄存器、LR_irq和SPSR_irq。进入SVC状况之后,运用SVC仓库,需求保存调用函数规则的R0-R3,LR_svc。当然在中止服务例程中,也是运用SVC仓库。可见两个状况的仓库都被运用了。当然,由于中止服务例程运用SVC仓库,咱们也能够考虑将嵌套所需的仓库也放到SVC中,这样就不需求IRQ仓库了。流程上和前面这种办法很类似,只不过要将保存LR_irq和SPSR_irq的时刻放到进入SVC态之后,办法能够是经过通用寄存器复制。最终也不用再回来IRQ态,能够直接经过SPSR_svc和LR_svc来推出中止处理程序。
程序贴在下面,用的是仓库分隔的办法,仅仅示例。
- __asmvoidIRQ_Handler(void){
- PRESERVE8
- IMPORThandler1
- //STORELR_irq&SPSR_irq
- SUBLR,LR,#4
- MRSR0,SPSR
- STMFDSP!,{R0,LR}
- //INTOSVCMODE
- MRSR0,CPSR
- BICR0,#0x1f
- ORRR0,#0x13
- MSRCPSR_C,R0
- //STOREREGISTORSOFSVCMODE
- STMFDSP!,{R0-R3,LR}
- //ENABLEIRQ
- MRSR0,CPSR
- BICR0,#0x80
- MSRCPSR_C,R0
- //GOTOHANDLER
- BLhandler1
- //RESTOREREGISTORSOFSVCMODE
- LDMFDSP!,{R0-R3,LR}
- //DISABLEIRQ
- MRSR0,CPSR
- ORRR0,#0x80
- MSRCPSR_C,R0
- //INTOIRQMODE
- MRSR0,CPSR
- BICR0,#0x1f
- ORRR0,#0x12
- MSRCPSR_C,R0
- //RESTORELR_irq&SPSR_irq
- LDMFDSP!,{R0,LR}
- MSRSPSR_CFX,R0
- //EXITIRQ
- MOVSPC,LR
- }