主题 : ARM(S3C2440)中断深层理解 复制链接 | 浏览器收藏 | 打印
此生无意行高远,浪迹行书弹指间!
级别: 新手上路
UID: 32708
精华: 2
发帖: 4
金钱: 120 两
威望: 24 点
贡献值: 2 点
综合积分: 48 分
注册时间: 2010-11-19
最后登录: 2011-11-09
楼主  发表于: 2011-04-22 15:03

 ARM(S3C2440)中断深层理解

管理提醒: 本帖被 kasim 执行加亮操作(2011-12-11)
                                                                                                       /*******中国计量学院--郑星--QQ:408958731*********/
     最近准备自己写一个S3C2440的启动代码。参阅了一下2440init.s这一启动代码。发现有很多人对于这个启动代码中的异常,特别是像外部中断这样的异常,到底在2440init.s中是如何实现跳转的这一问题有很大的疑惑。现在就我的理解对这个过程做一下解说。说的不一定很好,呵呵,权当消遣吧。另外,这里面的错误肯定会有,大家尽管给我提吧。
      首先要大家要明白,特别是初学者,当S3C2440发生异常时,其程序指针是会被强制转换的,这个是硬件自己完成的,不需要我们的干涉。下面这个表格就是具体的强制指针转化表。

      地址           异常名称           2440init.s 中的指令    
0x00     复位异常    b   ResetHandler    
0x04    未定义指令异常    b   HandlerUndef    
0x08    软件中断异常    b    HandlerSWI    
0x0c    指令预取异常    b    HandlerPabort    
0x10    数据预取异常    b    HandlerDabort    
0x14    保留    b    .        
0x18    IRQ中断异常    b    HandlerIRQ    
0x1c    FIQ中断异常    b    HandlerFIQ    
     现在可以很清楚的知道了。如果S3C2440刚上电或者是复位,那么pc指针被硬件强制转换到0x00地址处,那么按照2440init.s中的指令“b   ResetHandler”,pc会跳转到ResetHandler处继续执行程序。同样,当发生外部中断时,pc指针会被强制转换到0x18处,执行“b    HandlerIRQ”这条指令。
    接下去,便是讨论程序跳转之后如何运行的了。首先,我们做一个设想,假设S3C    2440只有一个中断(当然这是不可能的了,就连51也有5个中断源。),那么可想而知,程序跳转到HandlerIRQ处就应该执行中断处理函数了。也就是说,我们在C语言中写的中断处理函数的地址应该就是HandlerIRQ的内容了。虽然这个假设不可能,但它可以帮助我们理解。本质上其实就是类似于这样的一个过程,只不过因为S3C2440有那么多的中断源,所以不可能把中断函数的地址直接赋给HandlerIRQ就行了。这中间应该还有一个转换。就是根据不同的、具体的中断源,让HandlerIRQ对应于不同的中断处理函数的地址。
   那么接下来看看HandlerIRQ标号后面的内容吧。
   HandlerIRQ      HANDLER HandleIRQ
   很明显,这里还有一个宏定义在里面,要知道HANDLER的内容,我们可以在2440init.s中查到:
   MACRO  
$HandlerLabel HANDLER $HandleLabel

$HandlerLabel
    sub    sp,sp,#4    
    stmfd    sp!,{r0}    
    ldr     r0,=$HandleLabel
    ldr     r0,[r0]    
    str     r0,[sp,#4]  
    ldmfd   sp!,{r0,pc}  
    MEND
下面对这个宏定义做一下说明。首先是MACRO表明了宏定义的开始,MEND则表示了宏定义的结束。这个宏的作用其实就是在不改变任何寄存器的前提下,把pc指针指向$HandleLabel。
     明白了这个以后,我们就可以把这个宏去掉再来理解一遍:
     首先是当S3C2440发生外部中断时,pc指针被强制指向了0x18处:                      
  b    HandlerIRQ
  ……
  HandlerIRQ
      sub    sp,sp,#4
      stmfd    sp!,{r0}    
      ldr     r0,=HandleIRQ
      ldr     r0,[r0]    
      str     r0,[sp,#4]  
      ldmfd   sp!,{r0,pc}
经过上面的程序后,此时的pc指针已经指向了HandleIRQ处。呵呵,挺简单的吧。然后
呢。我们就得关注一下HandleIRQ这个标号了。它在2440init.s中设这样定义的:
^   _ISR_STARTADDRESS        ; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset     #   4
HandleUndef     #   4
HandleSWI        #   4
HandlePabort    #   4
HandleDabort    #   4
HandleReserved  #   4
HandleIRQ        #   4
HandleFIQ        #   4

HandleEINT0        #   4
HandleEINT1        #   4
HandleEINT2        #   4
HandleEINT3        #   4
……
很简单,这里定义了一个文字池,首地址:_ISR_STARTADDRESS代表了地址为0x33FF_FF00的内存区域,每隔4个字节,定义一个标号。很很容易就找到了 HandleIRQ这个我们需要找的标号。那么它所代表的内存区域自然就是0x33FF_FF00+0x04*6的内存地址了。那么接下来的工作就是要把真正的、具体的中断处理函数的地址赋给HandleIRQ了。这里大家先看一下下面的两端代码:
(1):
   ldr    r0,=HandleIRQ       ;This routine is needed
   ldr    r1,=IsrIRQ      ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
   str    r1,[r0]

(2):
   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}
首先我们来看一下第(1)段程序,前面已经提到,此时的的pc已经指向了HandeIRQ所表示的内存了,但是现在该内存还是空的,pc跳转到这里也不能接着往下运行了。所以才有了第1段代码,它的作用就是给HandleIRQ安装句柄了,把IsrIRQ的入口地址填充到了HandeIRQ里面了。所以程序接着就会跳转到IsrIRQ处执行。也就是上面的第(2)段程序了。这段程序具体讲解我就不说了,跟最上面的宏定义很类似,其实就是让PC跳转到另外一个地方(pc=HandleEINT0+INTOFFSET*4)。而那个地方正是真正的中断函数。那我们再来看看这个地址是怎么算出来的。首先HandleEINT0就是上面那个MAP定义里面的一个内存区域,有没有发现它是第一个中断源,紧接着它,就是其它各种类型的中断源了。而INTOFFSET则是S3C2440的一个特殊功能寄存器了,某个类型的中断发生了,它的值就会发生变化。而后面为什么要乘以4呢,因为文字池中定义的标号都是4字节的,其实是因为S3C2440中指针变量就是占据4个字节的,这个可以用sizeof(*p)来验证。所以此时pc指针就指向了另外一个地方,那就是刚才说的中断表了。而在c语言中,我们通常会有这样的中断函数句柄安装语句:pISR_EINTn = (unsigned int )key_interrupt;
这里的pISR_EINTn其实是有定义的,我们以pISR_EINT0为例,宏定义如下:
  #define pISR_EINT0    _ISR_STARTADDRESS+0X20
看这个地址,其实就是上面那个中断表里面的:
^   _ISR_STARTADDRESS        ; _ISR_STARTADDRESS=0x33FF_FF00
……

HandleEINT0        #   4
HandleEINT1        #   4
HandleEINT2        #   4
HandleEINT3        #   4
……
大家发现了吧,HandleEINT0的地址是不是就是 _ISR_STARTADDRESS+0X20,所以说c语言中我们写的中断函数安装句柄,就是这个作用。
   好的,现在让我们来总结一下吧。(以外部中断0为例)
首先是在c语言的函数中,我们已经执行了这样一条语句pISR_EINTn = (unsigned int )key_interrupt;它的作用是把中断处理函数key_interrupt的地址赋给了中断表中的HandleEINT0。
然后当某个时刻,发生了外部中断0,那么pc指针被强制指向了0x18处,执行指令:  
  b     HandlerIRQ
跳转到HandlerIRQ之后,执行如下代码:(已经把宏定义屏蔽)
HandlerIRQ
      sub    sp,sp,#4
      stmfd    sp!,{r0}    
      ldr     r0,=HandleIRQ
      ldr     r0,[r0]    
      str     r0,[sp,#4]  
      ldmfd   sp!,{r0,pc}
然后pc之争有指向了HandleIRQ这个内存区域。而又由于HandlerIRQ已经被安装了IsrIRQ的句柄,所以紧接着pc又跳转到IsrIRQ处执行如下程序:
     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}
它的作用是让pc指针根据INTOFFSET的值跳转到中断向量表中的HandleEINT0处。而在此处已经被安装了c语言中的中断处理函数的句柄,所以pc又跳到了中断处理函数中区执行中断函数了。
    以上就是鄙人所理解的ARM中断处理过程。







      
    
    

  
  
  
    :