ARM7伪中断分析

本文为多年前查找一个LPC2136引发伪中断的问题,特从网上搜索资料,特记录之。

伪中断为ARM7特有,当前更高级的单片级是不存在这种概念了。

1 伪中断产生的原因
  由于异步中断处理,伪中断可能出现在基于ARM7TDMIS 的微控制器LPC2292中。如果不进行正确处理,有可能引起严重的后果。中断处理的异步特性来源于内核和向量中断控制器(VIC)的相互作用。如果在内核中检测到中断和内核真正开始处理中断的过程中VIC 的状态发生改变,则产生中断的异步特性[4]。
  应用中可能经过以下步骤:
  ① VIC 判断是否有IRQ 中断。若有,则向内核发送IRQ 信号。
  ② 内核保存IRQ 状态。
  ③ 执行流水线的多个周期的处理。
  ④ 内核从VIC 中装入IRQ 地址。
  如果在执行到步骤③时向量中断控制器的状态有所改变,那么就要发生伪中断。所以,在以下两种情况下会发生伪中断。
  ◆ 在步骤③时执行了关中断指令。
  ◆ 向向量中断控制器发送IRQ信号的中断的中断标志丢失。当UART0/UART1的RDA/CTI中断允许时就可能发生这种情况[5]。
  进入伪中断时,VIC 不能清楚地识别产生中断请求的中断,最后只能返回到VicDefVectAddr (0xFFFFF034)默认中断进行处理。因此,如果不正确处理伪中断,就可能导致严重的后果。
2 伪中断的处理
  本控制器中,可能出现伪中断的地方是:关中断、喂看门狗、UART0通信和UART1通信。本文的设计思路是:尽量避免产生伪中断;实在避不开的话,则写好相应的处理程序。
2.1 关中断指令的处理
  μC/OS-II中的关中断指令OS_ENTER_CRITICAL()不采用直接关中断,而是先进入管理模式中,设置好寄存器SPSR,退出时关掉IRQ中断。这样就消除了由关中断而引起伪中断的可能性。
2.2 看门狗的处理
  喂看门狗时必须先关闭IRQ和FIQ,否则可能发生意外的复位,导致控制器不能工作。在周期性的时钟节拍中断程序中第一件事就是喂看门狗。如果在进入时钟节拍中断时IRQ已关闭,就可避免伪中断的出现。当然,在喂狗指令前必须先关闭FIQ,喂狗指令后再打开FIQ。关闭FIQ是不会引起伪中断的[1]。
2.3 UART0和UART1的处理
  在UART0中(UART1同理),当UART0 Rx FIFO到达寄存器U0FCR7∶6 所定义的触发点(比如接收4个字符)时,发生RDA 中断。当UART0 Rx FIFO 的深度低于触发点时,RDA 中断标志被清除。
  当UART0 Rx FIFO 包含至少1 个字符,且在接收3.5~4.5 字符的时间内没有发生UART0 Rx FIFO 动作时,产生CTI中断。当UART0 Rx FIFO 的任何动作(读或写UART0 RSR)都将清除CTI中断标志。
  CTI伪中断是这样发生的:比如UART0 Rx FIFO已接收2字符,且超过了3.5~4.5 字符的时间,发生CTI中断;但是这时又有字符进来,于是CTI中断标志被清除。向量中断控制器无法识别是谁产生了中断,伪中断就发生了。
  RDA伪中断是这样发生的:以接收4字符发生RDA中断为例,比如UART0 Rx FIFO已接收3字符,且超过了3.5~4.5 字符的时间,发生CTI中断。在系统正确处理CTI中断时,恰好有一个字符进来,使得UART0 Rx FIFO中的字符数正好为4,于是发生RDA中断。但是由于先处理CTI中断,CTI中断程序先读取了其中的字符,使UART0 Rx FIFO内的字符数小于4,因此RDA中断标志就被清除了。等到系统处理RDA中断时,伪中断就发生了。
  可以看出,CTI中断的存在是产生UART0和UART1伪中断的罪魁祸首。UART1与LVDS接口相连,用于控制器和冗余控制器的在线互相监测。采用每次只发一个字符的方法,使得CTI中断不可能发生,这样就彻底消除了UART1产生伪中断的可能性。
  本控制器只在UART0中断中存在产生伪中断的可能性。发生伪中断时,系统会把默认中断地址寄存器VICDefVectAddr中的地址拷贝到向量地址寄存器VICVectAddr中,系统执行该地址处的程序。所以,要编写相应的处理伪中断程序,把其首地址放入VICDefVectAddr中。在处理伪中断的程序中,要尽快读出UART0 Rx FIFO中的字符,以免丢失[5]。

开关IRQ/FIQ不应该在用户级上进行

在用户级上应该用swi调用.
如;

void __swi(0) Enable_IRQ(void);
void __SWI_0            (void) {
int tmp;
  __asm
  {
    MRS tmp, SPSR
    BIC tmp, tmp, #0x80
    MSR SPSR_c, tmp
  }
}

void __swi(1) Disable_IRQ(void);
void __SWI_1             (void) {
int tmp;
  __asm
  {
    MRS tmp, SPSR
    ORR tmp, tmp, #0x80
    MSR SPSR_c, tmp
  }
}

void __swi(2) Enable_FIQ(void);
void __SWI_2            (void) {
int tmp;
  __asm
  {
    MRS tmp, SPSR
    BIC tmp, tmp, #0x40
    MSR SPSR_c, tmp
  }
}

void __swi(3) Disable_FIQ(void);
void __SWI_3             (void) {
int tmp;
  __asm
  {
    MRS tmp, SPSR
    ORR tmp, tmp, #0x40
    MSR SPSR_c, tmp
  }
}


int main(void)
{
  Disable_IRQ();
  Disable_FIQ();
//.................
  Enable_FIQ();
  Enable_IRQ();
  while(i == 0) {
  }
}

评论回复

vicControl.h 有这样一句函数声明__swi(0x00) void SwiHandle1(int Handle);他的函数体在哪里?

__swi(0x00) void SwiHandle1(int Handle);其实没有函数体,执行这个语句后就自动把Handle的值赋给了R0,接着执行下面的代码。__swi(0x00)是软件中断,0为软中断指令中的24位立即数,但是通过R0寄存器来传递参数具体的函数体,当然是要在swi的中断处理程序中去找了可以在复位时的异常向量表里面找到swi中断服务程序的入口地址。

SWI 执行的流程是,先进入异常中断向量表,然后跳到向量地址处,接着一小段汇编操作,把功能号读入到一个寄存器中,然后 switch 判断这个功能号是多少,接着跳转到对应的终端服务程序,如果函数有参数,则根据ATPCS规则进行参数的传递;如只有一个参数,则用 R0 来传递,超过4个参数,超出的部分用堆栈来传递。

__swi是ADS编译器的关键字,用它做前缀可以声明一个软中断调用,格式为:

__swi(功能号)   返回值  名称 (参数列表)

功能号:即软中断指令中的24位立即数,软中断号

名  称:即调用软中断时用于描述软中断的函数名称

参  数:软中断函数的参数,根据ATPCS规则,如果软中断函数有不超过4个参数时,通过R0~R3传递,超过4个参数时用堆栈来传递。

__swi(0x00) void SwiHandle1(int Handle)。其中0x00为软中断功能号(软中断号);软中断函数名称为SwiHandle1;只有一个参数,则使用R0来传递;函数没有返回值。紧接着这句代码的是定义了4个宏,分别表示禁能IRQ函数、使能IRQ函数、禁能FIQ函数、使能IFQ函数,其实调用的软中断函数是一样的,只是参数不同而已。例如在用户程序中调用“IRQEnable( );”时,处理器会产生软中断。位于启动代码中的那些是软中断处理函数,当发生软中断时,PC被强制指向0x00000008,这个地址中存放的是软中断异常的处理函数的地址,所以程序会跳转至标号“SoftwareInterrupt ”处执行。SoftwareInterrupt 函数的功能是判断R0的值(R0的值为软中断函数传递过来的参数)是否小于4,如果小于4则跳转至标号“SwiFunction”执行,如果不是则函数返回。SwiFunction函数是一个散转函数,它的功能是根据R0的值跳转至对应的函数处执行,即如果参数为1,则函数会跳转至IRQEnable处执行,将IRQ中断使能。

本文件SWI.s位于ARM Executable Image for LPC2294工程模板中,故不考虑SWI触发前为Thumb态;SWI异常一旦触发,内核硬件完成:

   ♂ 进入Supervisor模式;

   ♂ 拷贝CPSR至SPSR_svc

   ♂ 拷贝异常返回地址至LR_svc

   ♂ 将0x00000008装入PC

    因此,当触发SWI软中断前内核处于Supervisor模式,SPSR_svc、LR_svc中的值将被破坏;

3、SWI指令编码中自带24bit数据作为软中断号(swi_num),因此可通过取SWI指令编码获取软中断号;LDR r0,[lr,#-4]就是这样;

4、SWI_Exception_Function函数一般采用C编码(也可汇编),采用C编码可直接套用switch根据swi_nun软中断号切换,SWI_Exception_Function函数的编制是灵活的,比如可以为带参或不带参函数;

5、一个SWI调用允许带1~4个字型参数和1~4个字型返回值,触发SWI调用时四个参数依次保存在R0~R3中,返回值也存于R0~R3内,这和ATPCS函数调用一致;

6、在C中声明一个典型的无参无返回值的SWI调用为:”__swi(0x00) void IRQEnable();“这样随时都可以使用”IRQEnable();“触发一个软中断(中断号0),其允许IRQ中断的功能必须在SWI_Exception_Function软中断处理函数中实现;

7、以下为带参数的SWI调用,SWI调用和普通函数调用一样遵循ATPCS标准,Handle参数存放在R0中:

__swi(0x01) void SwiHandle(int Handle);

#define IRQDisable()    SwiHandle(0)

#define IRQEnable()     SwiHandle(1)

#define FIQDisable()    SwiHandle(2)

#define FIQEnable()     SwiHandle(3)

————————————————

版权声明:本文为CSDN博主「lsnail」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:__swi(0x00) void SwiHandle1(int …-CSDN博客

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值