ARM9(以S3C2410为例)中断机制一、ARM异常机制介绍ARM9处理器有7种工作模式。
分别是(除了用户模式其他都是异常模式用户模式(usr:ARM处理器正常的程序执行状态。
快速中断模式(fiq:用于高速数据传输或通道处理。
外部中断模式(irq:用于通用的中断处理。
管理模式(svc:操作系统使用的保护模式。
数据访问终止模式(abt:当数据或指令预取终止时进入该模式。
系统模式(sys:运行具有特权的操作系统任务。
未定义指令中止模式(und:当未定义的指令执行时进入该模式。
每种模式通过5位二进制编码进行标示:用户模式10000快速中断模式10001外部中断模式10010管理模式10011数据访问终止模式10111未定义指令中止模式11011系统模式11111模式编码存放在CPSR(程序当前状态寄存器,记录当前工作模式的编码的值)中的[4:0]。
快速中断模式、外部中断模式、数据访问终止模式、未定义指令中止模式、管理模式称为异常模式。
异常类型具体含义复位当处理器的复位电平有效时,产生复位异常,程序跳转到复位异常处理程序处执行。
未定义指令遇到不能处理的指令时,产生未定义指令异常。
软件中断该异常由执行SWI指令产生,可用于用户模式下的程序调用特权操作指令。
可使用该异常机制实现系统功能调用。
指令预取中止若处理器预取指令的地址不存在,或该地址不允许当前指令访问,存储器会向处理器发出中止信号,但当预取的指令被执行时,才会产生指令预取中止异常。
数据中止若处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常。
IRQ(外部中断请求)当处理器的外部中断请求引脚有效,且CPSR中的I 位为0时,产生IRQ异常。
系统的外设可通过该异常请求中断服务。
FIQ(快速中断请求)当处理器的快速中断请求引脚有效,且CPSR中的F 位为0时,产生FIQ异常。
当多个异常发生时,处理器根据优先级进行处理。
优先级1(最高)复位2 数据中止3 FIQ4 IRQ5 预取指令中止6(最低)未定义指令、SWI异常中断发生时,处理器执行完当前的指令,需要保存当前执行现场,切换到相应的异常模式,并跳转到相应的异常中断处理程序。
中断处理完成后,处理器返回现场执行下条指令。
异常处理一般过程如下:(1)把当前工作模式中的下一条即将运行的指令地址保存到R14中;(2)保存当前CPSR到SPSR(保存程序状态寄存器);(3)改写CPSR中的工作模式为当前产生异常的工作模式;(4)禁止IRQ(如果进入FIQ则禁止FIQ);(5)跳转到相应异常向量表入口;如果是复位异常处理会禁止所有中断,另外由于不用返回,因此不需要作(1)(2)步。
以上的异常处理操作都是由处理器自动完成的。
二、异常向量表Linux内核启动时,向量表的创建过程为:init/main.c->start_kernel(->trap_init(。
trap_init函数位于/arch/$(ARCH/kernel/traps.c中。
trap_init函数中向量表创建的相关代码如下:unsigned long vectors = CONFIG_VECTORS_BASE;……memcpy((void *vectors, __vectors_start, __vectors_end - __vectors_start;memcpy((void *vectors + 0x200, __stubs_start, __stubs_end - __stubs_start;其中CONFIG_VECTORS_BASE是向量表的基地址。
在ARM V4及V4T以后的大部分处理器中,向量表可以有两个位置:一个是0,另一个是0xffff0000。
通过CP15协处理器c1寄存器中V位(bit[13]控制。
V和异常向量表的对应关系如下:V=0 ~ 0x00000000~0x0000001CV=1 ~ 0xffff0000~0xffff001CCONFIG_VECTORS_BASE的值在内核的相关配置文件中定义。
比如在/arch/arm/configs/s3c2410_defconfig中CONFIG_VECTORS_BASE=0xffff0000。
异常向量表存放在__vectors_start至__vectors_end定义的空间内。
异常向量表的逻辑结构(8*4个字节)为:异常类型工作模式地址复位管理模式0x00000000未定义指令未定义指令中止模式0x00000004软件中断管理模式0x00000008数据访问终止模式0x0000000C中止(预取指令)中止(数据)数据访问终止模式0x00000010保留0x00000014IRQ 外部中断模式0x00000018FIQ 快速中断模式0x0000001C异常向量表的物理结构位于arch/arm/kernel/entry-armv.S中,如下所示:.globl __vectors_start__vectors_start:swi SYS_ERROR0:b vector_und + stubs_offset //复位异常:ldr pc, .LCvswi + stubs_offset //未定义指令异常:b vector_pabt + stubs_offset //软件中断异常:b vector_dabt + stubs_offset //数据异常(预取指令中止、数据中止):b vector_addrexcptn + stubs_offset //保留:b vector_irq + stubs_offset //普通中断异常:b vector_fiq + stubs_offset //快速中断异常:.globl __vectors_end:__vectors_end:异常向量表中各种异常对应位置上放置了一条跳转指令,用于指向相应异常的处理程序。
__stubs_start至__stubs_end之间是异常处理程序的位置。
stubs_offset的值为:.equ stubs_offset, __vectors_start + 0x200 - __stubs_start三、中断处理中断发生后,处理器执行向量表中的b vector_irq + stubs_offset指令。
接着根据进入中断前的具体工作模式,执行irq_handler处理过程。
irq_handler处理过程位于/arch/arm/kernel/entry-armv.S中,如下所示:.macro irq_handler……1: get_irqnr_and_base r0, r6, r5, lr @判断中断号,通过R0返回……bne asm_do_IRQ @进入中断处理。
…….endm中断号的值是在和平台相关的文件中决定的。
比如IRQ_EINT0在include/asm/arch-s3c2410/irqs.h中定义。
这说明中断号的具体值和硬件中断挂起寄存器中的中断号是不等的。
asm_do_IRQ位于arch/arm/kernel/irq.c,如下所示:asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs{struct pt_regs *old_regs = set_irq_regs(regs;struct irq_desc *desc = irq_desc + irq; //根据中断号找到对应的irq_desc。
irq_desc 是外部中断描述符表,用于存储中断的相关信息……desc_handle_irq(irq, desc;// 根据中断号和desc进入中断处理irq_finish(irq;irq_exit(;set_irq_regs(old_regs;}static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc{desc->handle_irq(irq, desc; //中断处理}在include/linux/interrupt.h中定义了irqaction结构体,如下所示:struct irqaction {irq_handler_t handler; //中断处理函数,注册时提供unsigned long flags; //中断标志,注册时提供cpumask_t mask; //中断掩码const char *name; //中断名称void *dev_id; //设备id,本文后面部分介绍中断共享时会详细说明这个参数的作用struct irqaction *next; //如果有中断共享,则继续执行,int irq; //中断号,注册时提供struct proc_dir_entry *dir; //指向IRQn相关的/proc/irq/n目录的描述符};在注册中断号为irq的中断服务程序时,系统会根据注册参数封装相应的irqaction 结构体。
并把中断号为irq的irqaction结构体写入irq_desc [irq]->action。
这样就把设备的中断请求号与该设备的中断服务例程irqaction联系在一起了。
当CPU接收到中断请求后,就可以根据中断号通过irq_desc []找到该设备的中断服务程序。