以ARM系统为例,物理地址最开始的几个地址,存放的是对应的几个最常见的向量中断,如数据中止异常,软中断,预取指错误异常等,还有一个是其他非向量中断的总入口IRQ。
此部分内容,可以参考Uboot中的ARM的初始化部分start.S中的代码来解释:
其相关代码如下:
_start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undefined_instruction: .word undefined_instruction _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq 。。。 /* * exception handlers */ .align 5 undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction .align 5 software_interrupt: get_bad_stack bad_save_user_regs bl do_software_interrupt .align 5 prefetch_abort: get_bad_stack bad_save_user_regs bl do_prefetch_abort .align 5 data_abort: get_bad_stack bad_save_user_regs bl do_data_abort .align 5 not_used: get_bad_stack bad_save_user_regs bl do_not_used 。。。 .align 5 irq: sub lr, lr, #4 @ the return address ldr sp, IRQ_STACK_START @ the stack for irq stmdb sp!, { r0-r12,lr } @ save registers ldr lr, =int_return @ set the return addr ldr pc, =IRQ_Handle @ call the isr int_return: ldmia sp!, { r0-r12,pc }^ @ return from interrupt .align 5 fiq: get_fiq_stack /* someone ought to write a more effiction fiq_save_user_regs */ irq_save_user_regs bl do_fiq irq_restore_user_regs
可看出,物理内存最开始的存放的内容是:
地址0x0: reset整个系统
地址0x04:放了一个指令,该指令是将_undefined_instruction存入PC,即实现PC跳转到_undefined_instruction的地址中去;
地址0x08:同理,PC跳转到_software_interrupt
地址0x0C:同理,PC跳转到_prefetch_abort
地址0x10:同理,PC跳转到_data_abort
地址0x14:同理,PC跳转到_not_used
地址0x18:同理,PC跳转到_irq
地址0x1C:同理,PC跳转到_fiq
其中,对于_undefined_instruction,很明显,就是我们之前所解释的异常,即指令执行出了对应的问题了,PC会直接跳转到此处的0x04的地址,然后该地址中,就是把PC跳转到对应的_undefined_instructio的位置,去执行对应的异常处理。
其他的_software_interrupt和_data_abort等,都是同样道理,不多解释。
而上述这些异常或_software_interrupt,就都是所谓的中断向量,都是由硬件架构决定的,固定好的了地址,作为软件开发人员,只要把对应的指令写好,到时候发生对应的异常,系统自动会跳转到此处的地址,实现对应的PC的跳转,去做对应的处理。
而对于0x18处的_irq,就是我们所说的所有的非向量中断的总的入口地址,即系统发现有中断了,此处发现是普通的中断,那么就会跳转到0x18的地址这里,然后执行的是:
PC跳转到_irq,而_irq地址所对应的内容是:保存对应的当前的环境,即上下文,然后执行“ldr pc, =IRQ_Handle”,即跳转到IRQ_Handle函数中去。
而以TQ2440的S3C2410为例,其代码为:
interrupts.c (opt\embedsky\u-boot-1.1.6\cpu\arm920t\s3c24x0)
void Isr_Init(void) { int i = 0; intregs = S3C24X0_GetBase_INTERRUPT(); for (i = 0; i < sizeof(isr_handle_array) / sizeof(isr_handle_array[0]); i++ ) { isr_handle_array[i] = Dummy_isr; } intregs->INTMOD=0x0; // All=IRQ mode intregs->INTMSK=BIT_ALLMSK; // All interrupt is masked. //pISR_URXD0=(unsigned)Uart0_RxInt; //rINTMSK=~(BIT_URXD0); //enable UART0 RX Default value=0xffffffff isr_handle_array[ISR_TIMER4_OFT] = IsrTimer4; isr_handle_array[ISR_WDT_OFT] = IsrWatchdog; #ifdef CONFIG_USB_DEVICE isr_handle_array[ISR_USBD_OFT] = IsrUsbd; isr_handle_array[ISR_DMA2_OFT] = IsrDma2; ClearPending(BIT_DMA2); ClearPending(BIT_USBD); #endif } void IRQ_Handle(void) { unsigned long oft = intregs->INTOFFSET; S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); // printk("IRQ_Handle: %d\n", oft); if( oft == 4 ) gpio->EINTPEND = 1<<7; intregs->SRCPND = 1<<oft; intregs->INTPND = intregs->INTPND; /* run the isr */ isr_handle_array[oft](); }
可见,其中IRQ_Handle做的事情,就是去读取对应的寄存器,然后经过计算,找到真正的中断源的偏移量,然后再通过偏移量,在中断函数表中,去获得对应该中断的中断服务程序ISR。
而其中的中断函数表isr_handle_array是在程序最开始初始化时候去调用Isr_Init来初始化好的,已经见每个中断多对应的ISR函数存放了对应的位置了。
向量中断的优点是,反应速度快,有了中断,CPU直接跳转到对应的位置,去执行对应的代码了,属于速度快,但是无法扩展,由硬件设计时候觉得的,固定好了,没法改变。
而非向量中断,由于多了一层调用关系,而且在总的普通中断的入口函数中,要去读取寄存器,再去计算到底是哪个中断,所以,速度上,就相对较慢了,属于速度慢,但是扩展性较好。
简单的说就是:
硬件中断,由硬件提供ISR地址,速度较快;
软件中断,由软件计算出中断源,再去找出对应的ISR,速度相对慢。