中断服务程序小结
---------------by nasiry
转载请说明出处,并通知我
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does't 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
b ResetHandler ;0x0
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt
LTORG
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
IsrIRQ
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc}
; Setup IRQ handler
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
与中断相关的内容展开来看:
0x1c: b HandlerIRQ
HandlerIRQ:
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does't push because it return to original address)
ldr r0,=HandleIRQ ;load the address of HandleXXX to r0 //注意是HandleIRQ不是HandlerIRQ
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)
IsrIRQ
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc}
; Setup IRQ handler
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
看这个具体的例子说明了中断的安装过程其实也就是设置变量HandleIRQ的过程,这样一来有很多好处,使得原本静态安装的中断服务程序具备了动态特性。也同时约束了中断程序所要求的一些特性:1.中断使用前必须初始化。2.初始化的动作必须在使用前完成 3.中断服务程序的初始化仅仅需要挂接相应的函数指针就可以完成。
针对smdk2440的eboot该如何添加中断代码?
smdk2440的eboot工作在所谓的catfish模式:这个eboot以在RAM中运行为特点,也就说不具备xip的特性,由于我们使用的eboot是从0x0启动的,也就是说我们需要搬运相应的中断向量到影射后的0x0区域,也就是0x30000000区域,代码如下
BringUpWinCE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Modify for ROM Bootloader NASIRY 2004.6.28
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MoveLoaderToRAM
; ands r9, pc, #0xFF000000 ; see if we are in flash or in ram
; bne %F15 ; go ahead if we are already in ram
ldr r2,=0x0
ldr r3,=0x30038000
1
ldr r0, [r2], #4
str r0, [r3], #4
cmp r2,#0x60000
bne %b1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;Modify by nasiry for interrupt handle
;; Setup IRQ handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
InitVector
ldr r2,=0x0
ldr r3,=0x30000000
1
ldr r0, [r2], #4
str r0, [r3], #4
cmp r2,#0x1000
bne %b1
InstallIrqIsr
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
就是把eboot搬运到ram以后再搬运最前端的1K代码到0x3000 0000,然后将IsrIRQ的地址装入HandleIRQ地址所代表的内存单元。
中断部分的代码如下:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;Modify by nasiry for ISR
;;2004.9.20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
HandleIRQ EQU 0x33ffff18
HandleFIQ EQU 0x33ffff1c
STARTUPTEXT
LEAF_ENTRY StartUp
1 b ResetHandler
b %B1 ;handler for Undefined mode
b %B1 ;handler for SWI interrupt
b %B1 ;handler for PAbort
b %B1 ;handler for DAbort
b %B1 ;reserved
;b %B1 ;handler for IRQ interrupt
b IRQHandler ;modify by nasiry for interrupt
;b HandlerFIQ ;handler for FIQ interrupt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;modify by nasiry for add interrupt route for USB Fucntion
;;2004.9.20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IRQHandler
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does't push because it return to original address)
ldr r0,=HandleIRQ ;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)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
中断的安装就就此完成。
由于C语言不能直接操作CPSR故而使用如下汇编代码构建开启中断的函数。
MACRO
RETURN
bx lr
MEND
;-------------------------------------------------------------------------------
; INTERRUPTS_ON - enable interrupts
;-------------------------------------------------------------------------------
LEAF_ENTRY INTERRUPTS_ON
mrs r0, cpsr ; (r0) = current status
bic r1, r0, #0x80 ; clear interrupt disable bit
msr cpsr, r1 ; update status register
RETURN ; return to caller
;-------------------------------------------------------------------------------
; INTERRUPTS_OFF - disable interrupts
;-------------------------------------------------------------------------------
LEAF_ENTRY INTERRUPTS_OFF
mrs r0, cpsr ; (r0) = current status
orr r1, r0, #0x80 ; set interrupt disable bit
msr cpsr, r1 ; update status register
RETURN ; return to caller
该代码完全遵循ATPCS,可以在armcc下直接使用。
由于windowsCE编译器会在最前端的0x1000区域保留一个空区域所以上面所修改的中断向量都不会得到执行。如果使用偏移代码/数据副本的办法来填补这个区域的话,又必须保持一份原数据/代码,两份代码/数据之间的空间仅仅有4K所以偏移的办法基本上没有什么实际的用途。
因此上面的代码不能用。
另外就只有参考ATPCS动态通过C/Cpp装载中断向量的办法,通过 MMU构建一个位于ram中的高向量/低向量,然后通过构造的办法来实现中断向量。
所以这样一来,ISR就没有必要放在fw.s中单独拿出来就行了。
然后通过下列语句装载
VectorIRQ =(unsigned)(0xEA000000)+(((unsigned)IRQHandler - (0x8c000000 + 0x18 + 0x8) )>>2);