ARM7/9 的中断与 RTOS 系统

来源:互联网 发布:catiav5r22软件下载 编辑:程序博客网 时间:2024/05/22 02:28

最近比较忙,博客没有更新。今天特别想起来朋友问我的一个问题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体系结构与编程》一文中给出的办法是:

  1. 将返回地址保存到IRQ的数据栈中;
  2. 保存工作寄存器和SPSR_irq;
  3. 清除中断标志位;
  4. 将处理器切换到系统模式,重新使能中断(IRQ/FIQ);
  5. 保存用户模式的LR寄存器和被调用者不保存的寄存器;
  6. 调用C语言的IRQ/FIQ异常中断处理程序;
  7. 当C语言的IRQ/FIQ异常中断处理程序返回后,恢复用户模式的寄存器,并禁止中断(IRQ/FIQ);
  8. 切换到IRQ模式,禁止中断;
  9. 恢复工组寄存器和寄存器LR_irq
  10. 从IRQ异常中断处理程序中返回。

书中也给了现实的代码例子,这个处理过程是通用处理过程。一般的RTOS系统,比如说uC/OS-II和RTEMS,从bootloader接手CPU的控制权后,就停留在SVC模式下执行。用户态和系统态根本就没有用到。FIQ模式也没有用到。只用到:SVC模式、IRQ模式、UND和ABT模式。

 

我从uC/OS-II的官网上下载了uC/OS-II 2.86 在 AT91SAM9260 上的移植代码。相关代码罗列如下:

[cpp] view plaincopy
  1. ;********************************************************************************************************  
  2. ;                                  INTERRUPT REQUEST EXCEPTION HANDLER  
  3. ;  
  4. ; Register Usage:  R0     Exception Type  
  5. ;                  R1  
  6. ;                  R2  
  7. ;                  R3     Return PC  
  8. ;********************************************************************************************************  
  9. OS_CPU_ARM_ExceptIrqHndlr  
  10.     SUB     LR, LR, #4                                          ; LR offset to return from this exception: -4.  
  11.     STMFD   SP!, {R0-R12, LR}                                   ; Push working registers.  
  12.     MOV     R3, LR                                              ; Save link register.  
  13.     MOV     R0, #OS_CPU_ARM_EXCEPT_IRQ                          ; Set exception ID to OS_CPU_ARM_EXCEPT_IRQ.  
  14.     B            OS_CPU_ARM_ExceptHndlr                         ; Branch to global exception handler.  

 

进入中断的向量的代码从384行的,OS_CPU_ARM_ExceptIrqHndlr开始。

 

[cpp] view plaincopy
  1. ;********************************************************************************************************  
  2. ;                                       GLOBAL EXCEPTION HANDLER  
  3. ;  
  4. ; Register Usage:  R0     Exception Type  
  5. ;                  R1     Exception's SPSR  
  6. ;                  R2     Old CPU mode  
  7. ;                  R3     Return PC  
  8. ;********************************************************************************************************  
  9. OS_CPU_ARM_ExceptHndlr  
  10.     MRS     R1, SPSR                                            ; Save CPSR (i.e. exception's SPSR).  
  11.                                                                 ; DETERMINE IF WE INTERRUPTED A TASK OR ANOTHER LOWER PRIORITY EXCEPTION:  
  12.                                                                 ;   SPSR.Mode = SVC                :  task,  
  13.                                                                 ;   SPSR.Mode = FIQ, IRQ, ABT, UND :  other exceptions,  
  14.                                                                 ;   SPSR.Mode = USR                : *unsupported state*.  
  15.     AND     R2, R1, #OS_CPU_ARM_MODE_MASK  
  16.     CMP     R2,     #OS_CPU_ARM_MODE_SVC  
  17.     BNE     OS_CPU_ARM_ExceptHndlr_BreakExcept  
  18. ;********************************************************************************************************  
  19. ;                                  EXCEPTION HANDLER: TASK INTERRUPTED  
  20. ;  
  21. ; Register Usage:  R0     Exception Type  
  22. ;                  R1     Exception's SPSR  
  23. ;                  R2     Exception's CPSR  
  24. ;                  R3     Return PC  
  25. ;                  R4     Exception's SP  
  26. ;********************************************************************************************************  
  27. OS_CPU_ARM_ExceptHndlr_BreakTask  
  28.     MRS     R2, CPSR                                            ; Save exception's CPSR.  
  29.     MOV     R4, SP                                              ; Save exception's stack pointer.  
  30.                                                                 ; Change to SVC mode & disable interruptions.  
  31.     MSR     CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)  
  32.                                                                 ; SAVE TASK'S CONTEXT ONTO TASK'S STACK:  
  33.     STMFD   SP!, {R3}                                           ;   Push task's PC,  
  34.     STMFD   SP!, {LR}                                           ;   Push task's LR,  
  35.     STMFD   SP!, {R5-R12}                                       ;   Push task's R12-R5,  
  36.     LDMFD   R4!, {R5-R9}                                        ;   Move task's R4-R0 from exception stack to task's stack.  
  37.     STMFD   SP!, {R5-R9}  
  38.     STMFD   SP!, {R1}                                           ;   Push task's CPSR (i.e. exception SPSR).  
  39.                                                                 ; if (OSRunning == 1)  
  40.     LDR     R1, ?OS_Running  
  41.     LDRB    R1, [R1]  
  42.     CMP     R1, #1  
  43.     BNE     OS_CPU_ARM_ExceptHndlr_BreakTask_1  
  44.                                                                 ; HANDLE NESTING COUNTER:  
  45.     LDR     R3, ?OS_IntNesting                                  ;   OSIntNesting++;  
  46.     LDRB    R4, [R3]  
  47.     ADD     R4, R4, #1  
  48.     STRB    R4, [R3]  
  49.     LDR     R3, ?OS_TCBCur                                      ;   OSTCBCur->OSTCBStkPtr = SP;  
  50.     LDR     R4, [R3]  
  51.     STR     SP, [R4]  
  52. OS_CPU_ARM_ExceptHndlr_BreakTask_1  
  53.     MSR     CPSR_cxsf, R2                                       ; RESTORE INTERRUPTED MODE.  
  54.                                                                 ; EXECUTE EXCEPTION HANDLER:  
  55.     LDR     R1, ?OS_CPU_ExceptHndlr                             ; OS_CPU_ExceptHndlr(except_type = R0);  
  56.     MOV     LR, PC  
  57.     BX      R1  
  58.                                                                 ; Adjust exception stack pointer.  This is needed because  
  59.                                                                 ; exception stack is not used when restoring task context.  
  60.     ADD     SP, SP, #(14 * 4)  
  61.                                                                 ; Change to SVC mode & disable interruptions.  
  62.     MSR     CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)  
  63.                                                                 ; Call OSIntExit().  This call MAY never return if a ready  
  64.                                                                 ;  task with higher priority than the interrupted one is  
  65.                                                                 ;  found.  
  66.     LDR     R0, ?OS_IntExit  
  67.     MOV     LR, PC  
  68.     BX      R0  
  69.                                                                 ; RESTORE NEW TASK'S CONTEXT:  
  70.     LDMFD   SP!, {R0}                                           ;    Pop new task's CPSR,  
  71.     MSR     SPSR_cxsf, R0  
  72.     LDMFD   SP!, {R0-R12, LR, PC}^                              ;    Pop new task's context.  
  73. ;********************************************************************************************************  
  74. ;                               EXCEPTION HANDLER: EXCEPTION INTERRUPTED  
  75. ;  
  76. ; Register Usage:  R0     Exception Type  
  77. ;                  R1  
  78. ;                  R2  
  79. ;                  R3  
  80. ;********************************************************************************************************  
  81. OS_CPU_ARM_ExceptHndlr_BreakExcept  
  82.     MRS     R2, CPSR                                            ; Save exception's CPSR.  
  83.                                                                 ; Change to SVC mode & disable interruptions.  
  84.     MSR     CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)  
  85.                                                                 ; HANDLE NESTING COUNTER:  
  86.     LDR     R3, ?OS_IntNesting                                  ;   OSIntNesting++;  
  87.     LDRB    R4, [R3]  
  88.     ADD     R4, R4, #1  
  89.     STRB    R4, [R3]  
  90.     MSR     CPSR_cxsf, R2                                       ; RESTORE INTERRUPTED MODE.  
  91.                                                                 ; EXECUTE EXCEPTION HANDLER:  
  92.     LDR     R3, ?OS_CPU_ExceptHndlr                             ; OS_CPU_ExceptHndlr(except_type = R0);  
  93.     MOV     LR, PC  
  94.     BX      R3  
  95.                                                                 ; Change to SVC mode & disable interruptions.  
  96.     MSR     CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)  
  97.                                                                 ; HANDLE NESTING COUNTER:  
  98.     LDR     R3, ?OS_IntNesting                                  ;   OSIntNesting--;  
  99.     LDRB    R4, [R3]  
  100.     SUB     R4, R4, #1  
  101.     STRB    R4, [R3]  
  102.     MSR     CPSR_cxsf, R2                                       ; RESTORE INTERRUPTED MODE.  
  103.                                                                 ; RESTORE OLD CONTEXT:  
  104.     LDMFD   SP!, {R0-R12, PC}^                                  ; Pull working registers and return from exception.  

 

将相关寄存器保护后进入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的移植包中,其实也有同样的问题,但我还没有细细的阅读相关的代码,回头读完一并将代码分析贴出来。

 

写得不好,希望大家宽宥,如有错误,希望多多指教。


0 0
原创粉丝点击