RealView MDK 与 AT91SAM7S64:理解软件中断

 
    在特权模式运行的程序可通过改变CPSR寄存器轻松的实现CPU的工作模式切换,那么用户模式如果有改变CPU工作模式的需求怎么办呢?

   在用户模式下只有中断才能将CPU的工作模式转换到特权模式,而软件中断同样有此特性,它似乎就是为此目的而设计。

      软件中断在汇编中只需要一条指令,在C语言中需要关键字__swi(x),在C语言中调用软件中断需要声明一个软件中断调用函数,如:

     void __swi(0)  EnterSystem(void); 表示此函数的调用将触发软件中断0.而这个函数只需要声明其原型,而不需要实现代码,中断的执行

   代码由启动程序中的软件中断向量分发处理。为此如果软件中断的例程仍是由C编写的那么必须在软件中断向量中调用此C程序。另外,软件中断

   函数的原型格式是任意的,可以有返回值及一个或更多的参数。如:  int __swi(1) Add(int x,int y);

      下面让我们看一下RealView MDK-ARM中软件中断示例:

      首先是启动代码(sam7.s)中的相关部分:

Vectors LDR PC,Reset_Addr
      LDR PC,Undef_Addr
      LDR PC,SWI_Addr
      LDR PC,PAbt_Addr
      LDR PC,DAbt_Addr
      NOP ; Reserved Vector
      ; LDR PC,IRQ_Addr
      LDR PC,[PC,#-0xF20] ; Vector From AIC_IVR
      ; LDR PC,FIQ_Addr
      LDR PC,[PC,#-0xF20] ; Vector From AIC_FVR

            IMPORT SWI_Handler

      Reset_Addr DCD Reset_Handler
      Undef_Addr DCD Undef_Handler
      SWI_Addr DCD SWI_Handler
      PAbt_Addr DCD PAbt_Handler
      DAbt_Addr DCD DAbt_Handler
      DCD 0 ; Reserved Address
      IRQ_Addr DCD IRQ_Handler
      FIQ_Addr DCD FIQ_Handler

     软件中断向量转到了SWI_Handler,这个SWI_Handler在另一个汇编程序文件swi.s中实现:

      T_Bit EQU 0x20

      PRESERVE8 ; 8-Byte aligned Stack
      AREA SWI_Area, CODE, READONLY
      ARM

     
EXPORT SWI_Handler

 SWI_Handler
      STMFD SP!, {R12, LR} ; Store R12, LR
      MRS R12, SPSR ; Get SPSR
      STMFD SP!, {R8, R12} ; Store R8, SPSR
      TST R12, #T_Bit ; Check Thumb Bit
      LDRNEH R12, [LR,#-2] ; Thumb: Load Halfword
      BICNE R12, R12, #0xFF00 ; Extract SWI Number
      LDREQ R12, [LR,#-4] ; ARM: Load Word
      BICEQ R12, R12, #0xFF000000 ; Extract SWI Number

      LDR R8, SWI_Count
      CMP R12, R8
      BHS SWI_Dead ; Overflow
      ADR R8, SWI_Table
      LDR R12, [R8,R12,LSL #2] ; Load SWI Function Address
      MOV LR, PC ; Return Address
     
BX R12 ; Call SWI Function

      LDMFD SP!, {R8, R12} ; Load R8, SPSR
      MSR SPSR_cxsf, R12 ; Set SPSR
      LDMFD SP!, {R12, PC}^ ; Restore R12 and Return

 SWI_Dead B SWI_Dead ; None Existing SWI

 SWI_Cnt EQU (SWI_End-SWI_Table)/4
 SWI_Count DCD SWI_Cnt

            IMPORT __SWI_0
            IMPORT __SWI_1
            IMPORT __SWI_2
           
IMPORT __SWI_3
 SWI_Table
      DCD __SWI_0 ; SWI 0 Function Entry
      DCD __SWI_1 ; SWI 1 Function Entry
      DCD __SWI_2 ; SWI 2 Function Entry
      DCD __SWI_3 ; SWI 3 Function Entry
      ; ...
 SWI_End

END

       在这段代码中调用了C程序中实现的四个处理函数__SWI_0 ~ __SWI_3,分别用于处理0,1,2,3号软件中断,C程序实现如下:


      int __swi(0) add (int i1, int i2);//声明原型
      int __SWI_0 (int i1, int i2) {//中断中回调的实现函数
            return (i1 + i2);
      }

      int __swi(1) mul4(int i);//声明原型
      int __SWI_1 (int i) {//中断中回调的实现函数
            return (i << 2);
      }

      int __swi(2) div (int i1, int i2); //声明原型
      int __SWI_2 (int i1, int i2) {     //中断中回调的实现函数
            return (i1 / i2);
      }

      int __swi(3) mod (int i1, int i2);//声明原型
      int __SWI_3 (int i1, int i2) {//中断中回调的实现函数
            return (i1 % i2);
      }

      RealView这个示例除了示意没有任何意义,因为简单的加减乘除操作不需要在管理模式下运算,下面我们来实现一个非常实际的功能,就是

    通过软件中断改变全局中断标记,此功能在实际中经常用到尤其在基于操作系统的应用当中。

     在RealView内部函数中有__enable_irq,__disable_irq等相应的函数,但是在用户模式下运行的C程序调用它是没什么意义的,因为在

    用户模式下不能改变CPSR寄存器,所以这些函数也不可能让我们达到目的,唯一的办法就得用软件中断,在中断处理程序中改变CPSR寄存器使

    全局中断功能禁止或允许。以下是上面示例基础上实现的此功能:

      T_Bit EQU 0x20

      PRESERVE8 ; 8-Byte aligned Stack
      AREA SWI_Area, CODE, READONLY
      ARM

     
EXPORT SWI_Handler

 SWI_Handler
      STMFD SP!, {R12, LR} ; Store R12, LR
      MRS R12, SPSR ; Get SPSR
      STMFD SP!, {R8, R12} ; Store R8, SPSR
      TST R12, #T_Bit ; Check Thumb Bit
      LDRNEH R12, [LR,#-2] ; Thumb: Load Halfword
      BICNE R12, R12, #0xFF00 ; Extract SWI Number
      LDREQ R12, [LR,#-4] ; ARM: Load Word
      BICEQ R12, R12, #0xFF000000 ; Extract SWI Number

      ; add code to enable/disable the global IRQ flag
      CMP R12,#0xFE ; disable IRQ implemented as __SWI 0xFE
      BEQ disable_IRQ
      CMP R12,#0xFF ; enable IRQ implemented as __SWI 0xFF
      BEQ enable_IRQ


      LDR R8, SWI_Count
      CMP R12, R8
      BHS SWI_Dead ; Overflow
      ADR R8, SWI_Table
      LDR R12, [R8,R12,LSL #2] ; Load SWI Function Address
      MOV LR, PC ; Return Address
     
BX R12 ; Call SWI Function

      LDMFD SP!, {R8, R12} ; Load R8, SPSR
      MSR SPSR_cxsf, R12 ; Set SPSR
      LDMFD SP!, {R12, PC}^ ; Restore R12 and Return

 SWI_Dead B SWI_Dead ; None Existing SWI

 SWI_Cnt EQU (SWI_End-SWI_Table)/4
 SWI_Count DCD SWI_Cnt

            IMPORT __SWI_0
            IMPORT __SWI_1
            IMPORT __SWI_2
           
IMPORT __SWI_3
 SWI_Table
      DCD __SWI_0 ; SWI 0 Function Entry
      DCD __SWI_1 ; SWI 1 Function Entry
      DCD __SWI_2 ; SWI 2 Function Entry
      DCD __SWI_3 ; SWI 3 Function Entry
      ; ...
 SWI_End

disable_IRQ
      LDMFD SP!, {R8, R12} ; Load R8, SPSR
      ORR R12, R12, #0x80 ; Set IRQ flag to disable it
      MSR SPSR_cxsf, R12 ; Set SPSR
      LDMFD SP!, {R12, PC}^ ; Restore R12 and Return

enable_IRQ
      LDMFD SP!, {R8, R12} ; Load R8, SPSR
      BIC R12, R12, #0x80 ; Set IRQ flag to disable it
      MSR SPSR_cxsf, R12 ; Set SPSR
      LDMFD SP!, {R12, PC}^ ; Restore R12 and Return


END
  

     棕色字体为增加部分,在C程序中软件中断格式如下:

      void __swi(0xFE) disable_irq (void);
      void __swi(0xFF) enable_irq (void);

    如果我们的系统没有用到其它SWI中断功能,完全可以不这样啰嗦,直接将简单的处理程序添加到启动代码即可:

  在KEIL提供的标准启动代码中添加:

SWI_Handler
      CMP R0, #4
      LDRLO PC, [PC, R0, LSL #2]
      MOVS PC, LR

SwiFunction
      DCD IRQDisable ;0
      DCD IRQEnable ;1
      DCD FIQDisable ;2
      DCD FIQEnable ;3

IRQDisable
      ;关IRQ中断
      MRS R0, SPSR
      ORR R0, R0, #I_Bit
      MSR SPSR_c, R0
      MOVS PC, LR

IRQEnable
      ;开IRQ中断
      MRS R0, SPSR
      BIC R0, R0, #I_Bit
      MSR SPSR_c, R0
      MOVS PC, LR

FIQDisable
      ;关FIQ中断
      MRS R0, SPSR
      ORR R0, R0, #F_Bit
      MSR SPSR_c, R0
      MOVS PC, LR

FIQEnable
      ;开FIQ中断
      MRS R0, SPSR
      BIC R0, R0, #F_Bit
      MSR SPSR_c, R0
     
MOVS PC, LR
 

      C程序中的声明及调用方法如下:

      __swi(0) void SwiHandle1(int Handle);

      #define IRQDisable() SwiHandle1(0)
      #define IRQEnable() SwiHandle1(1)
      #define FIQDisable() SwiHandle1(2)
      #define FIQEnable() SwiHandle1(3)
      //注:此方法参考了周立功公司启动代码

      以下是我在示例2的基础上做的一次实现,因为示例2是靠定时器中断实现的LED闪烁,现在我要得用软件中断将中断屏蔽,如果LED停止闪烁

    说明软件中断功能正确,请点击这里下载示我的测试代码。
 

芯艺设计室(http://www.chipart.cn) 2010.08.24  转载请注明出处!
 

芯艺设计室    蒙ICP备06005492号

Copyright© 2004-2011 ChipArt Design House All Rights Reserved