资讯详情

S3C2440中断代码的深层次分析

在前一段时间分析了ARM异常处理机制的处理方式,分析了在异常产生以后CPU自动完成的相关处理以及程序员应该完成的基本操作。着重分析了异常代码的返回地址分析已经采用通用代码处理各种异常的可能性。

异常处理的基本过程如下:异常产生(在指令的临界中检测 的状态,一般实质在这条指令被执行完成,但是还没有执行下一条指令之前检测)——>保存状态寄存器,切换状态寄存器,保存LR=PC-4,强制PC跳转到对应异常向量(以上的过程都是CPU自动完成)——》调整返回地址,在栈中保存寄存器,便于恢复寄存器的值——》异常处理函数——》退出异常。 中断处理机制的两种形式: 1、 采用在中断向量中存储简单的跳转指令,跳转到异常处理函数中,但是这种方式存在的缺点就是跳转指令的范围是有局限性的。 2、 采用更新PC 值的方法进行,具体的实现形式是在另一个固定地址处(handle_addr )保存对应异常处理函数的地址,然后采用LDR PC [PC, offset], 其中offset = handle_addr – vect – 0x08; 这种机制只要保证选择的地址恰当就能实现不同距离的跳转。 以上的分析和处理在上一次中已经分析,这次分析中断的处理过程,中断只是异常的一种特殊情况,对异常的处理得到了好的理解,那么对中断的处理也就比较方便了。 在 内核中只支持 Q和IFQ两种类型的中断,但是不同的厂商提供不同类型的中断控制器实现对中断的扩展,使得实际的芯片更加适合我们的使用。但是中断控制器的差别也使得不同厂商的中断处理也有差别,但是基本的思想是一致的。 S3C2440的中断控制器一个支持60种中断源,基本的实现如上图所示。基本的寄存器包括SRCPND、 INTPND (有且仅有1bit 会被置位,可以通过这个寄存器判断中断源,找出那个IRQ 源发生中断)、I MOD、INTMSK、 PRIORITY(用来改变中断的优先级顺序,但是其中还是存在一些固有的顺序,具体的参看手册)、 INTOFFSET (用来表示IRQ 中INTPND 的那个bit 被置位,这样每一类的中断源都存在一个固定的偏移量,这个寄存器可以用来用来计算偏移量以及通过这个偏移量找到对应的中断处理函数地址存储位置等),当然也存在一些关于多个中断源构成的子中断寄存器,SUBSRCPND、INTSUBMSK。 在S3C2440的启动代码中描述了关于中断处理过程的基本过程和原理。 首先需要搞清楚下面的一个宏定义: MACRO $HandlerLabel HANDLER $HandleLabel $HandlerLabel subsp,sp,#4;decrement sp(to store jump address) stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address) ldr r0,=$HandleLabel;load the address of HandleXXX to r0 ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) MEND 1、搞清楚ARM中的MACRO伪指令,这个伪指令就是我们在汇编中的宏定义,我们都知道宏的实现能够避免代码的重复型以及代码的可修复性。关于ARM汇编中的宏定义基本的形式如下: MACRO {$label} macroname {$parameter} {$parameter}… Code MEND 其中$label 宏指令被展开时,label可被替换为相应的符号,一般为一个标号 macroname 所定义的宏的名称 $parameter 宏指令的参数,当宏指令被展开时被替换成对应的值。 2、依据上面的定义我们可以知道当前这段代码定义了一个宏指令,HANDLER,其中标号为$HandlerLabel,参数为$HandleLabel 基本的实现代码分析如下: subsp,sp,#4; 在栈中预留一个区域,用来保存PC的值 stmfd sp!,{r0}; 由于r0还需要被使用,因此需要被压栈 ldr r0,=$HandleLabel ;这里的ldr是一个伪指令,主要是将标号$HandleLabel的地址加载到r0中,这也是压栈r0的原因。 ldr r0,[r0] ;这是ARM的ldr指令,主要是将$HandleLabel对应地址中的内容加载到r0中。如果在$HandleLabel中保存的是一个中断处理函数的地址,那么只需要将这个值加载到PC即可实现了中断任务跳转,实际上这个过程就是采用了异常处理的第二种方式: 即加载PC的方式,而不是简单的跳转方式。 str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) 这两句代码正是这段代码的精髓。基本形式如下: str r0,[sp,#4],是指将r0的内容,也就是异常处理函数的地址保存到栈中的 -4位置处,这个位置也恰好是之前sub sp,sp,#4; 用来预留给保存PC值的位置,这时将异常处理函数的地址保存在这个地址处,接下来的ldmfd sp!,{r0,pc}刚好就是将栈中的内容加载到R0和PC中,这样也就实现了将异常处理函数地址加载到PC.实现了跳转过程。
高地址
SP_0/SP_3
SP_1 Handle_addr
SP_2 R0
低地址
从上面的分析可以知道这种中断处理的方式,并不是中断处理中的简单跳转方式(因为跳转范围的局限性)而是采用更新PC值的形式实现的。 接下来分析IRQ,这种在我们实际开发中使用比较多的中断形式进行分析。 首先可以发现存在: 1 、b HandlerIRQ;handler for IRQ interrupt 这种情
锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台