Realview MDK 中编译器对中断处理的过程详解
在 ARM 程序的开发过程中,对中断的处理是非常普遍且重要的。Realview MDK 使用的 RVCT 编译器提供了 __irq
关键字,使用此关键字修饰的函数被编译为中断处理函数。在编译过程中,编译器会自动添加中断处理过程中现场保护和恢复的代码,减小程序的开发难度,加快软件的开发过程。
ARM 核对异常的处理过程
在理解 __irq
关键字的作用之前,先看一下 ARM 核对异常的处理过程。当产生异常时,ARM 核会执行以下步骤:
- 拷贝 CPSR 寄存器的内容到 SPSR_ 寄存器中。
- 设置适当的 CPSR 位,改变处理器状态进入 ARM 态和处理器模式,从而进入相应的异常模式。
- 在需要的情况下,设置中断禁止位,禁止相应中断。
- 保存返回地址到 LR_,同时设置 PC 为相应的异常向量。
异常返回时,处理器需要从 SPSR_ 寄存器中恢复 CPSR 的值,同时从 LR_ 恢复 PC。具体的异常返回指令如下:
-
从 SWI 和 Undef 异常返回时使用:
movs pc, LR;
-
从 FIQ、IRQ 和预取终止返回时使用:
SUBS PC, LR, #4;
-
从数据异常返回时使用:
SUBS PC, LR, #8;
在使用上述指令异常返回时,如果 LR 之前被压栈,可以使用以下指令:
LDMFD SP!, {PC};
Realview MDK 中 __irq
关键字的作用
理解了 ARM 异常处理的过程后,Realview MDK 中 __irq
关键字的作用就容易理解了。以下是一个中断处理函数的示例,前面加了 __irq
关键字:
__irq void pwm0_irq_handler(void)
{
// Deassert PWM0 interrupt signal
unsigned int i = AT91F_PWMC_GetInterruptStatus(AT91C_BASE_PWMC);
// Clear the LED's. On the Board we must apply a "1" to turn off LEDs
AT91F_PIO_SetOutput(AT91C_BASE_PIOA, led_mask[0]);
AT91F_PWMC_StopChannel(AT91C_BASE_PWMC, AT91C_PWMC_CHID1);
AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_PWMC);
AT91F_AIC_AcknowledgeIt(AT91C_BASE_AIC);
}
当编译器编译这个函数时,除了保存 ATPCS 规则规定的寄存器以外,还会保存 CPSR 和 PC 的值。在函数返回时,编译器会自动添加 SUBS PC, LR, #4
和从 SPSR 寄存器恢复 CPSR 寄存器值的指令。这样处理后,中断处理函数可以像普通函数一样使用。
注意事项
- 中断处理都是在 ARM 模式下进行的。当源程序编译成 Thumb 指令时,使用
__irq
关键字修饰的函数仍然会被编译成 ARM 指令。 - 如果源程序编译成在 CORTEX M3 上运行的指令,关键字
__irq
对函数的编译没有任何影响。此时,编译器不会自动保存 CPSR 和 PC 的值,也不会添加SUBS PC, LR, #4
和从 SPSR 寄存器恢复 CPSR 寄存器值的指令,因为 CORTEX M3 处理器硬件会自动处理这些问题,无需软件开发人员关心。