ARM的irq和fiq中断以及中断嵌套

时间:2021-09-03 15:53:41

     ARM有七种模式,我们这里只讨论SVC、IRQ和FIQ模式。

     我们可以假设ARM核心有两根中断引脚(实际上是看不见的),一根叫 irq pin, 一根叫fiq pin.

     在ARM的cpsr中,有一个I位和一个F位,分别用来禁止IRQ和FIQ的。

     先不说中断控制器,只说ARM核心。正常情况下,ARM核都只是机械地随着pc的指示去做事情,当CPSR中的I和F位为1的时候,IRQ和FIQ全部处 于禁止状态。无论你在irq pin和fiq pin上面发什么样的中断信号,ARM是不会理你的,你根本不能打断他,因为他耳聋了,眼也瞎了。

     在I位和F位为0的时候,当irq pin上有中断信号过来的时候,就会打断arm的当前工作,并且切换到IRQ模式下,并且跳到相应的异常向量表(vector)位置去执行代码。这个过程是自动的,但是返回到被中断打断的地方就得您亲自动手了。当你跳到异常向量表,处于IRQ的模式的时候,I位被置1,禁止irq中断。这个时候如果irq pin上面又来中断信号了,ARM不会相应的。而对于已经响应的irq中断,退出中断服务函数时,要清楚中断源,否则会一直触发irq中断,ARM会一直响应。

     但是有一种情况例外,当ARM处在IRQ模式,这个时候fiqpin来了一个中断信号,fiq pin是什么?是快速中断呀,它会直接打断当前的IRQ模式,进入到fiq模式下,并且跳到相应的fiq的异常向量表处去执行代码。那如果当ARM处理FIQ模式,fiq pin又来中断信号,则ARM不响应新的frq中断。那如果这个时候irq pin来了呢?ARM更不会响应了。

    所以得出一个结论: IRQ模式只能被FIQ模式打断,FIQ模式下谁也打不断。

   在打不断的情况下,irq pin 或 fiq pin随便你怎么发中断信号,都是白发。

   所以除了fiq能打断irq以外,根本没有所谓真正意义上的中断嵌套情况。

 

   但是再怎么说irq pin 和fiq pin加起来也就2根引脚,那这么多中断源,怎么办呢?

那么接下来该说谁来给irq pin或者 fiq pin发信号呢?这个事情就是由中断控制器来管理。

 

    拿最简单的2410/2440的中断控制器举例,这个中断控制器加一个子中断控制器,还有一个外部中断控制器管理了50多个中断资源,说穿了有50多个脚。这些脚除了外部中断都是规定了功能的,比如WDT、LCD、DMA等,这个功能不能改,因为2410/2440内部硬件连线决定了。

  当你WDT和DMA的中断都到来的时候,就会被送到SRCPND寄存器中,两个中断都在里面,那到底把哪一个送给ARM呢?这个时候先看INTMOD,也就是模式,哪个是设置成为了快速中断,哪个就被送上去;那如果两个都是设置的快速中断呢?不可能,因为同一时间只能有一个中断可以被设成快速中断。所以,如果有快速中断,这个时候直接就给fiq pin发中断信号,打断ARM。

   那要是没有快速中断呢,这个时候就看INTMSK,看WDT和DMA有没有被屏蔽的,如果DMA在INTMSK被屏蔽了,那就只有WDT继续向上送了,如果都没有屏蔽,那么他们两个同进来到了PRIORITY优先级寄存器,在这里,根据优先级的设置,一定会分出一个高,一个低的优先级出来,高的那个就被送上去,送到了INTPND寄存器,所以INTPND随时随地有且只有一个一个中断在里面。

只要INTPND里面有中断,irq pin就不会一直不断给ARM中断信号,当第一次发的时候,中断了ARM,这个时候ARM进入相应的异常向量,并处于IRQ模式,正在这个时候,INTPND仍然在不断的通过irq pin向ARM发中断信号,但是ARM这个时候已经处于IRQ模式,CPSR的I位被置1了,是不会理睬你的。

当你中断处理完了,要退出IRQ模式了,这个时候小心了,如果你在退 出IRQ模式之前不清除INTPND里面的中断位,当你刚退出IRQ模式,又被中断了,因为INTPND一直在发中断信号。所以在退出IRQ模式之前要清除INTPND里面的中断位。但是光清除INTPND里面的位还不行,因为SRCPND里面WDT和DMA的中断还在,当你刚清除完INTPND,结果 SRCPND里面又选了一个出来又送到了INTPND里面。所以正确的处理方法是退出IRQ模式之前,一定要先清除SRCPND里相应的中断位,再清除 INTPND里相应的位。那么请注意,SRCPND里面可能有多位,所以清除你已处理过的中断就行了,而INTPND里面只可能有一位,直接清掉就可以了。

  附上一张我用+-号画的简图:

 

     再来说说Linux的情况。Linux不用FIQ,只用到了IRQ。但是我们有时候一个中断需要处理很长时间,那我们就需要占用IRQ模式那么长的时间吗?没有,linuxIRQ模式下只是简单的记录是什么中断,马上就切换回了SVC模式,换句话说,Linux的中断处理都是在SVC模式下处理的。那么中断号是怎么来的呢?在ARM上固死了,相应的中断号只有一个办法得到:查询irqs.h 。那我先用一个中断号注册一个中断处理程序,当中断发生的时候,Linux怎么知道是我这个中断号发生的中断呢?在处理中断的时候,先读取INTPND,根据需要再读取EINTPEND或SUBSRCPND计算出一个中断号,相应的处理算法在 get_irq_nr_base这个宏中。而且irqs.h中的中断号就是根据这个算法把每个中断算一下得来的。

 

      还有子中断、外部中断,电平触发、边沿触发等,以后再谈。

 

         在linux的中断处理子模块中,把中断的处理分为两部分,上半部和下半部。上半部运行在IRQ模式,置位I位,禁止IRQ中断,主要处理一些紧急的事情,比如记录中断号、清除中断源等。下半部运行在SVC模式,清零I位,允许IRQ中断,处理一些中断相关的非紧急事情,比如读取设备缓存等。

 

         再说一下软中断嵌套,针对IRQ中断嵌套。

         进入IRQ中断后,CPSR的I位被置1,禁止新的irq中断,但是可以通过清零I位人为打开irq中断,允许新的irq中断被响应。

         不过I位清零的时机要把握好,而且要综合考虑中断的触发频率和优先级。

         一般是在进入中断后,先清除中断源,然后清零CPSR寄存器的I位,执行中断服务代码,再次置位CPSR寄存器的I位,退出中断服务代码。