衡阳到长沙多少公里:【2011.03.21】ARM中断处理过程

来源:百度文库 编辑:九乡新闻网 时间:2024/04/27 23:47:56

1.   首先就是知道ARM状态37个寄存器包括通用寄存器、程序计数器、状态寄存器。绿颜色的就是相应模式下的私有寄存器。就是说程序一般运行在系统和用户模式下,使用的是系统和用户模式下的通用寄存器,当有异常发生时,比如FIQ,那么系统将切换到FIQ模式下,相应的就会采用FIQ模式下的寄存器,其中绿颜色的就是只在FIQ模式下才会用到的寄存器。

2.   在模式切换的过程中,要保护系统和用户模式下的通用寄存器状态,以便在异常处理完成之后程序能正常返回。因为FIQ模式下R8-R14为其私有寄存器,所以切换到FIQ模式的过程中,系统和用户模式下的通用寄存器的R8-R14就不用保护了,所以减少了对寄存器存取的需要,从而可以快速的进行FIQ处理,故称为FIQ。这就是FIQ的私有寄存器比其它模式多的原因。切到FIQ模式需要保护的寄存器为R0~R7,切到SVC,IRQ,ABORT.和未定义模式需要保护的寄存器为R0~R12

3. 异常处理的动作。当然这都是CPU内核自己干的。以FIQ为例。

当CPU切入FIQ模式时,

第一,将原来执行程序的下一条指令地址保存到LR中,就是将R14保存到R14_fiq里面。

第二,拷贝CPSR到SPSR_fiq。

第三,改变CPSR模式位的值,改到FIQ模式。

第四,改变PC值,将其指向异常处理向量所指的下一条指令

离开异常处理的时候,

第一,将LR(R14_fiq)赋给PC。

第二,将SPSR(SPSR_fiq)拷贝到CPSR。

第三,清除中断禁止标志(如果开始时置位了)。

4.异常中断向量

异常中断的向量地址

地址                 异常中断类型             入口时处理器的操作模式

0x00000000        复位               超级用户

0x00000004        未定义指令         未定义

0x00000008        软件中断           超级用户

0x0000000c        中止(预取指)     中止

0x00000010        中止(数据)       中止

0x00000014        保留                              保留

0x00000018        IRQ                                IRQ

0x0000001c        FIQ                                FIQ

异常中断优先级

中断 优先级

复位                                     最高

数据异常   

FIQ

IRQ

预取指异常中断

未定义指令和软件中断        最低

5.当发生IRQ中断时

第一,模式进入到IRQ里面。

第二,PC跳到0x00000018处运行。因为这是IRQ的中断入口。

第三, 通过0x00000018:LDR PC, IRQ_ADDR。跳转到相应的中断服务程序。这个里面就有个确定哪个中断源的问题了。那就有优先级的问题了。每个中断源会有自己的中断服务程序

第四,得到中断源有硬件实现和软件处理两种方式。比如LPC21XX的就是利用硬件方式,为了利用向量中断控制器的优点,IRQ中断向量入口处代码做了修改,变成

        0x00000018:LDR PC, [PC, #-0xff0]。

    这条指令从内存映射地址0xfffff030处获得数据装载到PC,这样就能够直接从硬件中获得中断源。这样就减少了中断延迟。记得,三星的S3C44B0好象采用的是用软件确定中断源,因此要建立中断向量表。好久不用了,记不清了。

第五,得到中断源,就知道要跳到哪个中断服务程序去了。

    一般都是这么定义的。Timer0_Handler HANDLER Timer0 。这种格式是调用一种宏定义,目的是保护现场,跳到中断服务程序。

 

 

 

 

ARM中断实现过程的个人笔记

作者: 逛逛   发布日期:2006-3-19     

欢迎大家加入我的小圈子共同学习讨论:www.uuzone.com/club/embbed/

决定开始学习嵌入式后,最先做的事情就是要熟悉ARM指令及其伪指令伪操作。ARM指令的助记符其实都是其具体功能的单次缩写,所以学习的过程中最好利用网络,从一些文献或书籍中找到ARM指令助记符的全称,这样方便记忆。学完之后,我做了整理了一个有关这方面的笔记,有需要的朋友请邮件联系:gmman@163.com

    接下来的学习过程中,比较难以理解的是ARM的中断过程和存储系统。ARM中断的实现有些书上看一两遍也不见得能够完全理解,当然可能只对于像我一样跨专业的朋友来说存在这个问题。这次只谈中断。由于是初学者,难免会出错,敬请各位指正。

    当一个程序正常执行过程中,CPU可能检测到有某个中断源发出中断请求,这时ARM硬件实现了程序强制跳转,在这之前保存了相关信息,以便程序正常返回。如果是发生了Reset中断,程序实现系统初始化设置。

    开始比较难以理解的是中断产生后,程序都进行了哪些操作。我就从跟踪PC作为分析的主线。以发生FIQ中断为例。(只以ROM起始地址为0为例,不为0的情况参照存储地址映射)

    最简单的是中断发生后,PC=0x08,在此地址处存放一个跳转指令,跳转到相关处理程序。当然多数情况中断处理程序可能比较复杂,并且要处理多种中断的情况下,采用一步映射两步跳转(我自己起的名字,不一定妥当)。如下图所示:

   

一步映射指,在RAM地址中建立一个中断向量表,图中该表起始地址为0x400000,在该表中存放的是中断处理函数的入口地址。两步跳转是指,当中断发生时,由于系统硬件强制程序跳转到了0x08处,在该地址处是一个跳转指令,跳转到中断函数地址解析程序IRQ_Handler,完成一步跳转。解析程序(IRQ_Handler)的作用无非是把中断向量表内中断处理函数(SystemIrqHandler)的入口地址赋值给 PC,如图所示PC=0x003000280,完成第二步跳转,开始处理中断。在中断处理函数的最后,恢复中断开始时保存的相关寄存器的值,完成中断。

下面以一个实例来具体说明中断建立及实现的过程。

首先通过伪指令建立一个中断向量表,用于存放中断程序的入口地址(如上图中的中断向量表,注意,此时表中还未赋值):

;/* EXCEPTION HANDLER VECTOR TABLE */

^ DRAM_BASE

HandleReset # 4

HandleUndef # 4

HandleSwi # 4

HandlePrefetch # 4

HandleAbort # 4

HandleReserv # 4

HandleIrq # 4

HandleFiq # 4

然后定义一个连续的数据段,并把中断处理函数的入口地址值赋给各字单元

ExceptionHandlerTable

DCD UserCodeArea

DCD SystemUndefinedHandler

DCD SystemSwiHandler

DCD SystemPrefetchHandler

DCD SystemAbortHandler

DCD SystemReserv

DCD SystemIrqHandler

DCD SystemFiqHandler

下面从程序的开始处分析:

AREA Init, CODE, READONLY

ENTRY

/* ROM起始地址向量表 */

B Reset_Handler

B Undefined_Handler

B SWI_Handler

B Prefetch_Handler

B Abort_Handler

NOP Reserved vector

B IRQ_Handler

B FIQ_Handler

/* B跳转范围限于+ -32M内*/

/* 以下是地址解析程序 */

IRQ_Handler

SUB sp, sp, #4

STMFD sp!, {r0} FD满递减堆栈 执行寄存器压栈操作.

LDR r0, =HandleIrq  //对应程序开始处以伪指令定义的向量表

LDR r0, [r0]  //中断处理函数的地址赋给R0.

STR r0, [sp, #4]  //中断处理函数的地址入栈

LDMFD sp!, {r0, pc} //实现程序跳转,目前没明白为什么又给r0赋值?

上面提到了还没有给中断向量表赋值,下面代码把中断处理函数的地址放到DRAM中断向量表里

EXCEPTION_VECTOR_TABLE_SETUP

LDR r0, =HandleReset

LDR r1, =ExceptionHandlerTable

MOV r2, #8

ExceptLoop

LDR r3, [r1], #4

STR r3, [r0], #4

SUBS r2, r2, #1 Down Count

BNE ExceptLoop ;;

下面是中断处理函数

SystemIrqHandler

IMPORT ISR_IrqHandler

STMFD sp!, {r0-r7, lr}

BL ISR_IrqHandler

LDMFD sp!, {r0-r7, lr}

SUBS pc, lr, #4

它实际上只调用了下面的C语言的中断处理函数,其他什么也没做。

void ISR_IrqHandler(void)

{

IntOffSet = (U32)INTOFFSET;

(IntOffSet>>2)

(*InterruptHandlers[IntOffSet>>2])(); // Call interrupt service routine

}

以上编程思路是,先在系统初始化时重新建立一个中断向量表,并把相关的中断处理函数的地址放到中断向量表中。当系统监测到有中断源请求服务后,硬件实现pc跳转到地址0x08处,执行一个跳转指令B IRQ_Handler  , 然后执行地址解析程序,把中断向量表中的中断处理函数的入口地址赋给pc,开始响应中断。在中断处理函数的最后,执行

LDMFD sp!, {r0-r7, lr}

SUBS pc, lr, #4

实现中断的返回

 

 

 

 

 

硬件平台为ARM7内核。当有软中断发生(即调用2.2中某一个函数时)时,系统首先自动调转到0x0008处执行。

1、第一级中断向量

    AREA    Init,CODE,READONLY

 

    ENTRY

    b ResetHandler  ;for debug

    b HandlerUndef  ;handlerUndef

    b HandlerSWI    ;SWI interrupt handler

    b HandlerPabort ;handlerPAbort

    b HandlerDabort ;handlerDAbort

    b .             ;handlerReserved

    b HandlerIRQ

    b HandlerFIQ

2、宏展开

继续找HandlerSWI。

HandlerSWI  HANDLER HandleSWI

3、内存第二级中断向量

再找HandleSWI。

^   _ISR_STARTADDRESS

HandleReset     #   4

HandleUndef     #   4

HandleSWI       #   4

HandlePabort    #   4

HandleDabort    #   4

HandleReserved  #   4

HandleIRQ       #   4

HandleFIQ       #   4

现在我们知道软中断的服务程序跑到了内存中(执行速度较快)去了,我们可以编写相应的代码来实现不同功能号下的不同功能。

 

 

 

 

 

ARM的中断分析

      对于学习ARM的朋友来说,中断处理是一块硬骨头,尤其是中断向量表以及中断的跳转。下面主要对中断向量表的建立问题及重映射问题进行探讨。近来做一些东西用到中断时,总会出现一些问题。

      各种Bootloader的初始化相关代码摘要,首先来看下面的程序:

         ^ DRAM_BASE                     //DRAM的基地址

HandleReset # 4                              // 空留4个单元

HandleUndef # 4

HandleSwi    # 4

HandlePrefetch # 4                       //用于填充地址的

HandleAbort # 4

HandleReserv # 4

HandleIrq    # 4

HandleFiq # 4

  注: 这里的^是RMAP,#是FIELD,分配的意思

  就是在SDARM的BANK0开始的地方定义了一个中断向量表,相当于0地址的FLASH。空留单元用于存放中断程序的入口地址。

ExceptionHandlerTable                             //实际的映射地址

     DCD UserCodeArea

     DCD SystemUndefinedHandler

     DCD SystemSwiHandler

     DCD SystemPrefetchHandler

     DCD SystemAbortHandler

     DCD SystemReserv

     DCD SystemIrqHandler

     DCD SystemFiqHandler

        这个表中存放的是汇编程序中中断处理函数的入口地址,每一项对应一个中断函数。这次的跳转后就进入了C服务程序。 

        从初始化程序的开始处来看:(各种Bootloader的初始化代码)

  AREA Init, CODE, READONLY     //相当于init进程

  ENTRY                                               //入口

     B Reset_Handler

     B Undefined_Handler                      //无条件的跳转

     B SWI_Handler

     B Prefetch_Handler

     B Abort_Handler

     NOP  Reserved vector

     B IRQ_Handler

     B FIQ_Handler

    

FIQ_Handler

     SUB sp, sp, #4          

     STMFD sp!, {r0}       FD满递减堆栈  执行寄存器压栈操作.

LDR r0, =HandleFiq    汇编里的处理函数地址,然后跳到C中,在DRAM。

LDR r0, [r0]          中断向量地址给R0.

     STR r0, [sp, #4]         中断向量地址给PC

     LDMFD sp!, {r0, pc}

   稍微解释一下:

  首先执行了压栈,然后给出了中断入口地址.这个HandleFiq就是我们前面的在DRAM中建立的中断向量其中一个的地址。

在HandleFiq开始的四个字节中,放着汇编中断处理函数的入口地址。

       那么汇编中断处理函数的地址是如何放到DRAM中断向量表里的呢?

  上面的第一个表就起作用了。看下面这段程序:

EXCEPTION_VECTOR_TABLE_SETUP

     LDR r0, =HandleReset 

     LDR r1, =ExceptionHandlerTable

     MOV r2, #8 

    

ExceptLoop

     LDR r3, [r1], #4

     STR r3, [r0], #4

     SUBS r2, r2, #1       //填充8个地址        

     BNE ExceptLoop   //从表里取出来给了HandleReset后面的空间

       这一段把ExceptionHandlerTable里的中断处理函数的地址拷给了SDRAM里的中断向量表。这样两者就联系起来了。

     在执行程序开始的跳转之后就自然跳到了*****Handler.真正的处理函数如下:

       它实际上只调用了C语言的中断处理函数,其他什么也没做。

SystemFiqHandler

     IMPORT ISR_FiqHandler

     STMFD sp!, {r0-r7, lr}

     BL ISR_FiqHandler                    //真正的中断处理服务函数

     LDMFD sp!, {r0-r7, lr}

     SUBS pc, lr, #4

        它实际上只调用了C语言的中断处理函数,其他什么也没做。写中断处理服务程序其实就只写C中相应的处理部分就好了。

void ISR_FiqHandler(void)

{

       IntOffSet = (U32)INTOFFSET;

       (IntOffSet>>2)

       (*InterruptHandlers[IntOffSet>>2])(); // Call interrupt service routine

}

      其实就是将中断向量表重映射,以提高中断的响应速度。代码在SDRAM的运行速度要比在FLASH中运行速度快。