arm中断系统的疑惑

filmwind   2008-4-22 23:32 楼主
arm中断疑惑:
当中断产生时,通过中断向量表   
    b   ResetHandler  
    b   HandlerUndef    ;handler for Undefined mode
    b   HandlerSWI      ;handler for SWI interrupt
    b   HandlerPabort   ;handler for PAbort
    b   HandlerDabort   ;handler for DAbort
    b   .               ;reserved
    b   HandlerIRQ      ;handler for IRQ interrupt
    b   HandlerFIQ      ;handler for FIQ interrupt
调转到handlerIRQ,handlerIRQ根据INTOFFSET判断具体是哪个中断,然后进入相应的中断服务程序,
我的疑惑是中断服务程序执行完后,它怎么样回到被打断的地方继续运行,这是一个IRQ的主体程序
IsrIRQ  
    sub     sp,sp,#4       ;reserved for PC
    stmfd   sp!,{r8-r9}   
   
    ldr     r9,=INTOFFSET
    ldr     r9,[r9]
    ldr     r8,=HandleEINT0
    add     r8,r8,r9,lsl #2
    ldr     r8,[r8]
    str     r8,[sp,#8]
    ldmfd   sp!,{r8-r9,pc}

注册一个时钟中断程序:
pISR_TIMER0=(unsigned)timer0Handler;
IsrIRQ注册到了0x18的地方
当时钟time0中断产生时,pc先指向IsrIRQ,然后跳转到pISR_TIMER0这个地址执行具体的中断服务程序,运行完了后,该怎么回来?
我查了一下__irq中断程序关键字,但这个只是会保存中断函数的进入现场,和恢复退出现场,它怎么回到user状态下被打断的地方继续运行?

回复评论 (22)

__irq 保存现场,就是保存了PC值以及其他寄存器啊,当你中断服务程序结束时,就自动恢复了,PC值也恢复了啊,也就是回到了你原来的未中断状态,你主程序用C写的吧,那么编译器编译成汇编的时候自动加保存和恢复现场的代码进去的
点赞  2008-4-23 09:36
但是为什么别人都是那样写的啊?肯定有点什么原因吧。。。能不能说一下为什么?


引用: 引用 1 楼 sherlock_lai 的回复:
__irq?保存现场,就是保存了PC值以及其他寄存器啊,当你中断服务程序结束时,就自动恢复了,PC值也恢复了啊,也就是回到了你原来的未中断状态,你主程序用C写的吧,那么编译器编译成汇编的时候自动加保存和恢复现场的代码进去的
点赞  2008-4-23 11:06
哪断你不明白?ARM的中断跳转都是这样的,你这个程序是非向量中断模式,第一次跳转是通过判断寄存器的值,第二次跳转的向量表是pISR_xxxx这些,你的的程序包换的.h文件里肯定有#define pisr_xxxx ... 的语句的。 关于__irq 这个关键字应该是和你的C编译器有关的,如果你用汇编的话,就要用LDR PC,R14 来返回了,记得是这样的,R13,R14记不清楚了,反正是保存PC的那个寄存器
点赞  2008-4-23 11:18
能否留下qq交流一下?
引用: 引用 3 楼 sherlock_lai 的回复:
哪断你不明白?ARM的中断跳转都是这样的,你这个程序是非向量中断模式,第一次跳转是通过判断寄存器的值,第二次跳转的向量表是pISR_xxxx这些,你的的程序包换的.h文件里肯定有#define pisr_xxxx ... 的语句的。 关于__irq 这个关键字应该是和你的C编译器有关的,如果你用汇编的话,就要用LDR PC,R14 来返回了,记得是这样的,R13,R14记不清楚了,反正是保存PC的那个寄存器
点赞  2008-4-23 19:38
当执行中断程序的时候CPU会自动把PC压入堆栈!
执行isrIRQ时候 调用ASM是这个:
  stmfd   sp!,{r8-r9}
你没发现isrIRQ最后一条指令:
ldmfd   sp!,{r8-r9,pc}
上面指令多出个PC
,也就是说把堆栈里面的数据放到PC里面,这个数据正是中断前压入堆栈的数据!
PC就变成了以前的PC,所以回到了以前的断点处执行程序!
点赞  2008-4-23 22:11
不是啊ldmfd   sp!,{r8-r9,pc}这句话是说转向具体的中断处理函数,而不是返回被中断的地方
点赞  2008-4-23 22:57
   在中断处理结束后,返回的时候要用到R14(LR),对IRQ来讲,会用到R14_IRQ来保存返回
地址。
   在整个执行过程进入中断处理之前,要保存上下文环境。具体会将下条指令的地址放到R14_IRQ
中,同时将CPSR的值放到SPSR_IRQ中,修改CPSR中的模式位为IRQ,关掉FIQ、IRQ,把跳转地址放
到PC中。从PC中取地址就可以得到中断的处理入口。
   在处理完中断服务程序后,将上述的过程反过来,恢复到中断处理前的状态。

具体到指令:
IsrIRQ   
    sub     sp,sp,#4       ;
    stmfd   sp!,{r8-r9}    ;将用到的寄存器压栈
     
    ldr     r9,=INTOFFSET  ;下面这个不是很清楚
    ldr     r9,[r9]        
    ldr     r8,=HandleEINT0
    add     r8,r8,r9,lsl #2
    ldr     r8,[r8]
    str     r8,[sp,#8]
    ldmfd   sp!,{r8-r9,pc}^ ;恢复用到的寄存器中断前的状态

   返回的话,就像3楼说的那样,要用到 mov pc,LR   LR中保存的就是中断返回地址。

   
点赞  2008-4-24 18:48
我意思就是具体的中断处理程序(比如我上面说到的timer0Handler中断处理程序)执行完了该返回到哪里?,mov pc,LR 该写在哪里?
点赞  2008-4-24 20:23
sorry上面说错了,7搂说得对!
点赞  2008-4-24 22:05
IsrIRQ   
    sub     sp,sp,#4       ;reserved for PC
    /* 上面这条指令目的是什么?应该是 sub R14,R14,#4 是用来计算offset
     * 调整寄存器LR。如果你用SP(R13)的话,只是移动堆栈栈顶指针,对
      * PC没有任何影响。
      */   
    stmfd   sp!,{r8-r9}/*保护现场*/   
     /*下面是你的中断处理过程*/
    ldr     r9,=INTOFFSET
    ldr     r9,[r9]
    ldr     r8,=HandleEINT0
    add     r8,r8,r9,lsl #2
    ldr     r8,[r8]
    str     r8,[sp,#8]
    /*恢复现场*/
    ldmfd   sp!,{r8-r9,pc} /*出栈的话,PC中放什么?stmfd只保存了r8 & r9*/

   这段代码中,在进行中断响应时,并没有将LR入栈,而在中断返回时,又需要用到LR,
这就是问题的所在。

解决方案又两个:
(1)
IsrIRQ
    sub R14,R14,#4    /* 在进入中断处理之前,R14的地址已经被硬件作了修改。在
                          * 这里用需要计算一下它的偏移地址。*/
    stmfd   sp!,{r8-r9,LR}/*保护现场,这里需要将LR入栈。以备返回时候使用。*/   
     /*下面是你的中断处理过程*/
     ...
     ...
    /*恢复现场*/
    ldmfd   sp!,{r8-r9,pc}^/* ^ 这个符号很重要,除了表示正常的数据传输之外,
                                * 还将SPSR 拷贝到 CPSR中。/
(2)
IsrIRQ
    sub R14,R14,#4
    stmfd   sp!,{r8-r9}/*保护现场,这里需要将LR入栈。以备返回时候使用。*/   
     /*下面是你的中断处理过程*/
     ...
     ...
    /*恢复现场*/
    ldmfd   sp!,{r8-r9}
    MOV PC,LR     /*中断返回*/
   方案(2)有很多隐患。如果有中断嵌套发生,导致R14无效,中断结束后将不能正确返回。
另外CPSR也可能得不到恢复。建议使用方案(1)。
   

点赞  2008-4-25 10:31
能否留下email或qq交流一下,这个代码好多地方都是这样写的,我也不知道为什么
引用: 引用 10 楼 Cop_007 的回复:
IsrIRQ???
????sub?????sp,sp,#4???????;reserved?for?PC?
????/*?上面这条指令目的是什么?应该是?sub?R14,R14,#4?是用来计算offset
?????*?调整寄存器LR。如果你用SP(R13)的话,只是移动堆栈栈顶指针,对
??????*?PC没有任何影响。
??????*/????
????stmfd???sp!,{r8-r9}/*保护现场*/????
?????/*下面是你的中断处理过程*/
????ldr?????r9,=INTOFFSET?
????ldr?????r9,[r9]?
????ldr?????r8,=HandleEINT…
点赞  2008-4-25 12:37
////////////////////////////////////////////////
当中断产生时,通过中断向量表     
    b   ResetHandler   
    b   HandlerUndef    ;handler for Undefined mode
    b   HandlerSWI      ;handler for SWI interrupt
    b   HandlerPabort   ;handler for PAbort
    b   HandlerDabort   ;handler for DAbort
    b   .               ;reserved
    b   HandlerIRQ      ;handler for IRQ interrupt  
    b   HandlerFIQ      ;handler for FIQ interrupt
调转到handlerIRQ,handlerIRQ根据INTOFFSET判断具体是哪个中断,然后进入相应的中断服务程序,
我的疑惑是中断服务程序执行完后,它怎么样回到被打断的地方继续运行,这是一个IRQ的主体程序
////////////////////////////////////////////////

IsrIRQ   
    sub     sp,sp,#4       ;reserved for PC 堆栈是递减的,空一个位置
    stmfd   sp!,{r8-r9}    ;保存下面要用到的r8和r9
     
    ldr     r9,=INTOFFSET  ;获取中断源的偏移量寄存器,三星系列处理器都有32个中断源
    ldr     r9,[r9]     ;得到具体的中断源0--31
    ldr     r8,=HandleEINT0  ;保存第一个中断源的中断处理函数的地址
    add     r8,r8,r9,lsl #2  ;每个函数指针四个字节,HandleEINT0 + r9×4,
    ldr     r8,[r8]   ;r8地址处地址保存了对应中断源的中断入口地址,[r8]取出入口地址放在r8中
    str     r8,[sp,#8] ;将入口地址放在sub     sp,sp,#4空出的那个栈位置中
    ldmfd   sp!,{r8-r9,pc} ;恢复r8和r9,同时将PC指向对应中断源的中断入口地址,也就是下面的timer0Handler


////////////////////////////////////////////////
注册一个时钟中断程序:
pISR_TIMER0=(unsigned)timer0Handler;

当时钟time0中断产生时,pc先指向IsrIRQ,然后跳转到pISR_TIMER0这个地址执行具体的中断服务程序,运行完了后,该怎么回来?
我查了一下__irq中断程序关键字,但这个只是会保存中断函数的进入现场,和恢复退出现场,它怎么回到user状态下被打断的地方继续运行?
////////////////////////////////////////////////

假设timer0Handler函数如下
__irq void timer0Handler(void)
{
   
    C_int_handler();  // process the interrupt
   
    XXXXX// clear the interrupt
}
因为使用了__irq关键字,所以编译器认为其为中断函数,进入时自动将加上保护相关资源的代码
同时退出时将自动恢复相关寄存器

此时所有的寄存器都是IRQ模式下的,且lr保存了user模式下返回地址
timer0Handler
  0x000000:  STMFD    sp!,{r0-r4,r12,lr}  ;自动保存BL需要用到的传递参数寄存器和ip(r12)指针
  0x000004:  BL    C_int_handler    ;调用相关处理
  0x000014:  xxxx    ;清除中断源
  0x00001c:  LDMFD    sp!,{r0-r4,r12,lr}  ;恢复相关寄存器
  0x000020:  SUBS     pc,lr,#4   ;将返回的指令地址减4后,赋值给PC,同时SUBS中断S表示回复CPSR

这样就实现了从中断服务程序返回到user模式下被中断的代码处继续执行
为什么要减4呢?
这和ARM的三级指令流水线相关,当执行到A指令时,此时PC为A指令地址+8,此时A未执行完毕,来了中断,
CPU会自动将PC-4=A+4保存到IRQ模式的LR寄存器中,以便返回,LR=A+4
但目前要返回的地址是A,所以最后pc=lr -4 = A
点赞  2008-4-26 03:23
Cop_007分析的有道理,但那是针对手动保存中断现场的情况
目前lz是利用ARM的关键字生成中断服务程序的,所以那种方法不行

__irq关键字的好处在于从0x18处就可以直接跳转到中断服务函数中,不必写太多汇编代码
但是其不支持可重入中断,即中断不能嵌套

因为在BL    C_int_handler  执行过程中若来了中断,将会破坏LR中保存的C_int_handler的返回地址

因此要实现中断嵌套的话,必须手动编写保存现场的代码
在进行函数调用前切换到系统模式运行,这样再来中断时不会破坏系统模式下的LR
可以保证C_int_handler  正确返回
中断服务程序执行完毕后,再切换至IRQ模式,再恢复现场  
点赞  2008-4-26 03:34
提示: 作者被禁止或删除 内容自动屏蔽
点赞  2008-4-26 17:02
关注 接分
点赞  2008-5-1 07:32
lz下次提这么复杂的问题最好多搞点分啊
大家都在参予这个帖子但没有得分
这样对不起大家的付出啊

呵呵,其实10分我还觉得少了
点赞  2008-5-21 23:30
5,7,10,14楼,你们越说越扯了,不要误人子弟了,人家12楼说的是正确的
14楼,你看清楚点再发言,人家楼主的代码最后一行跟本没有^号
IsrIRQ  
    sub    sp,sp,#4      ;reserved for PC    ;堆栈指针减4,空出一个单元,以便后面放入PC需要的字(看不懂英文吗?)
    stmfd  sp!,{r8-r9}                       ;r8,r9入栈,此时存储r8,r9的两个堆栈单元下面的一个单元是空的
   
    ldr    r9,=INTOFFSET                     ;从interrupt offset寄存器读取中断偏移量
    ldr    r9,[r9]                          
    ldr    r8,=HandleEINT0                   ;以中断向量0为基地址计算中断向量地址
    add    r8,r8,r9,lsl #2
    ldr    r8,[r8]
    str    r8,[sp,#8]                        ;把得到的中断向量的内容(timer0Handler的地址)放入最开始空出的堆栈单元,由于这个单元在r8,r9下面,
                                                    ;所以位置是sp+8
    ldmfd  sp!,{r8-r9,pc}                    ;把堆栈顶部的三个单元分别出栈到r8,r9,和pc,此时pc会跳转到中断向量里存储的地址,也就是timer0Handler

以上的过程中LR根本没有被做任何修改,如果楼主的中断服务程序是用C语言写的,并且使用了__irq关键字,那么在进入时候会自动保存LR以及使用到的寄存器,在退出的时候会恢复寄存器,并将LR的值传递到PC,同时把SPSR的内容传递到CPSR,但是这种方法不支持嵌套中断,如果要支持嵌套,在进入的时候要自己保存寄存器,在退出的时候恢复寄存器,同时判断嵌套是否结束,如果未结束,则不恢复CPSR的值,如果嵌套结束,要恢复CPSR的值
点赞  2008-7-8 21:30
还有一点,就是中断后LR中保存的值由于流水线的关系可能不是要返回的地址,如果要自己恢复现场,则需要在将LR装入PC的时候进行修正,如果是用__irq关键字写的C中断服务程序,则不用担心,编译器自动计算的。
点赞  2008-7-8 21:34
可以看下这篇,希望对你有帮助:
http://blog.eeworld.net/libaizhang/archive/2009/07/02/4317141.aspx
点赞  2009-7-2 22:27
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复