(本文原创,转载注明出处,谢谢)
最近比较忙,博客没有更新。今天特别想起来朋友问我的一个问题ARM7、ARM9支持嵌套中断吗?这个问题当时我不假思索的回答支持。
实际上,这个问题并不像我想象的那么简单,是非常复杂的。在RTOS系统里,如果想支持ARM的嵌套中断,也需要对RTOS针对ARM做特殊的处理。
首先我们来看一个问题,ARM的中断过程。ARM 有两种中断,一个是FIQ;一个是IRQ。FIQ异常中断为快速异常中断,它比IRQ异常中断优先级高。体现在:
1.当FIQ和IRQ异常中断同时产生时,CPU先处理FIQ异常中断;
2.在FIQ异常中断处理程序中,IRQ异常中断被禁止。
同时,与其他的异常模式相比,FIQ异常向量还有额外的5个物理寄存器,在进入FIQ处理程序时,可以不用保存这5个寄存器,从而也提高了FIQ异常中断的执行速度。
当中断来临时,ARM硬件响应IRQ异常中断的伪代码如下所示:
R14_irq = address of next instruction to be executed + 4
SPSR_irq = CPSR
/*进入IRQ异常中断*/
CPSR[4:0]=0b10010
/*切换到ARM状态*/
CPSR[5]=0
/*CPSR[6] is unchanged*/
/*禁止IRQ异常中断*/
CPSR[7] =1
if high vectors configured then
PC = 0xFFFF0018
else
PC = 0x00000018
硬件响应FIQ异常中断的伪代码如下:
R14_fiq = address of next instruction to be executed + 4
SPSR_fiq = CPSR
/*进入FIQ异常中断模式*/
CPSR[4:0]=0b10001
/*切换到ARM状态*/
CPSR[5]=0
/*禁止FIQ异常中断*/
CPSR[6]=1
/*禁止IRQ异常中断*/
CPSR[5]=1
if high vectors configured then
PC=0xFFFF001C
else
PC=0x0000001C
看到这里我们可能已经明白了,ARM在IRQ状态下,是不允许IRQ中断的,允许FIQ中断嵌套;而在FIQ中断下,既不允许IRQ中断,也不允许FIQ中断。这是不是意味着ARM不支持中断嵌套呢?在RTOS中又如何设计呢?
先看看ARM支持不支持嵌套中断。进入IRQ时,CPSR[5]=1;那么也意味着,IRQ产生中断后,就不能再中断了。如果我们手动的将CPSR[5]=0,这样,在IRQ状态下即可允许产生第二个IRQ中断。但第二中断来了,根据上面的伪代码很显然会破坏 R14_irq、SPSR_irq。
杜春雷《ARM体系结构与编程》一文中给出的办法是:
- 将返回地址保存到IRQ的数据栈中;
- 保存工作寄存器和SPSR_irq;
- 清除中断标志位;
- 将处理器切换到系统模式,重新使能中断(IRQ/FIQ);
- 保存用户模式的LR寄存器和被调用者不保存的寄存器;
- 调用C语言的IRQ/FIQ异常中断处理程序;
- 当C语言的IRQ/FIQ异常中断处理程序返回后,恢复用户模式的寄存器,并禁止中断(IRQ/FIQ);
- 切换到IRQ模式,禁止中断;
- 恢复工组寄存器和寄存器LR_irq
- 从IRQ异常中断处理程序中返回。
书中也给了现实的代码例子,这个处理过程是通用处理过程。一般的RTOS系统,比如说uC/OS-II和RTEMS,从bootloader接手CPU的控制权后,就停留在SVC模式下执行。用户态和系统态根本就没有用到。FIQ模式也没有用到。只用到:SVC模式、IRQ模式、UND和ABT模式。
我从uC/OS-II的官网上下载了uC/OS-II 2.86 在 AT91SAM9260 上的移植代码。相关代码罗列如下:
进入中断的向量的代码从384行的,OS_CPU_ARM_ExceptIrqHndlr开始。
将相关寄存器保护后进入OS_CPU_ARM_ExceptHndlr处执行,分为两种情况,一种是从SVC模式中断;另外是从其它模式中断而来。我们从上面的IRQ进入的伪代码可知,在不支持嵌套的情况下,必然是从SVC模式而来。所以,必然从标号OS_CPU_ARM_ExceptHndlr_BreakTask 处执行。贴出的两段代码英文注释写得非常清楚了,在这里不做细细的介绍了。我只大致说一下这段代码的问题:
我们上面贴出的ARM进入IRQ模式的伪代码是硬件过程,硬件自动完成的。这段代码并未考虑这一点,441行和445行还禁止了IRQ中断。471和472行又恢复了中断。 这是多余的一个工作。 中断本身就是禁止的,恢复后依然是禁止的。从整段代码来看,这个代码是支持嵌套中断的代码,但却没有考虑到ARM中断的硬件动作。致使嵌套中断的代码变成了不支持嵌套中断的代码。
仔细看一下这段代码,uC/OS-II系统中断栈的处理,基本上是中断栈使用中断硬件独立的栈,而任务使用任务的栈。这样可以避免任务栈的浪费,有些CPU不支持中断的独立栈,只能是浪费被中断了的任务的栈。uC/OS-II这个移植做得还不错。充分考虑了栈这里的特点,使用了ARM的硬件栈,避免了任务栈的浪费。
如果将uC/OS-II的代码改为支持嵌套中断的代码也非常简单,在第461和462行插入伪代码
R2 &= ~(OS_CPU_ARM_CONTROL_IRQ_DIS);
在第499和500行也插入同样的伪代码。这样,就将屏蔽的中断位打开了。即可允许嵌套了。RTEMS的移植包中,其实也有同样的问题,但我还没有细细的阅读相关的代码,回头读完一并将代码分析贴出来。
写得不好,希望大家宽宥,如有错误,希望多多指教。
(本文原创,转载注明出处,谢谢)