- 论坛徽章:
- 0
|
由于没有使用内存映射,所以中断向量必须安装在地址0x00 ~ 0x20处,也就是ROM中.所以为了实现中断向量跳转到RAM中执行,ROM中必须有相应的跳转代码。之前看到一个大侠直接使用 add pc, pc ,#0x30000000来实现跳转,但在实际测试环境中发现这个方法不工作,会导致系统重启。故还是照搬现有的bootloader的启动代码,在ROM中实现各种异常模式的处理函数,而其功能则十分简单,就是跳转到从0x30000000地址开始的中断向量表处执行。代码如下:
.global mem_cfg_val
.text
_start:
b __rst_handler @@reset
b __udf_handler @@undefined instruction
b __swi_handler @@swi
b __ins_handler @@instruction abort
b __dat_handler @@data abort
b __rsv_handler @@reserved
b __irq_handler @@irq interrupt
b __fiq_handler @@fip interrupt
@@8 vectors --- nop.
nop
nop
nop
nop
nop
nop
nop
nop
.func __rst_handler
__rst_handler:
@@setup cpsr flag --- manage mode, disable irq & fiq.
msr cpsr_c, #0xd3
@@disable watch dog
mov r1, #0x53000000
mov r2, #0
str r2, [r1]
@@disable interrupt
ldr r1, =0x4A000008
mov r2, #0xFFFFFFFF
str r2, [r1]
@@disable all sub interrupt.
ldr r1, =0x4A00001C @#INTR_SUB_MSK
ldr r2, =0x7FFF @#INTR_SUB_MSK_VALUE
str r2, [r1]
@@initialise system clocks
@@clock pll
ldr r1, =0x4C000000 @#CLK_LOCK_TIME
ldr r2, =0xffffff
str r2, [r1]
@@clock div --- FCLK:HCLK:PCLK
ldr r1, =0x4C000014
ldr r2, =0x5 @@1:4:8
str r2, [r1]
@@for what? --- asynchronous mode when FCLK != HCLK.
mrc p15, 0, r1, c1, c0, 0 @@read ctrl register
orr r1, r1, #0xc0000000 @@Asynchronous
mcr p15, 0, r1, c1, c0, 0 @@write ctrl register
@@UPLL --- USB PLL
ldr r1, =0x4C000008
ldr r2, =0x38022 @@48MHz
str r2, [r1]
@@wait for 8 clocks.
nop
nop
nop
nop
nop
nop
nop
nop
@@MPLL
ldr r1, =0x4C000004
ldr r2, =0x7d011 @@533MHz --> 532MHz,266MHz,133MHz
str r2, [r1]
@@wait for 8 clocks.
nop
nop
nop
nop
nop
nop
nop
nop
@@memory setup
ldr r1, =0x48000000
ldr r2, =mem_cfg_val
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
@@jump
mov r0, #0x1 @@boot from rom.
mrs r1, cpsr @@clear regs
mov r3, #0x0
mov r4, #0x0
mov r5, #0x0
mov r6, #0x0
mov r7, #0x0
mov r8, #0x0
mov r9, #0x0
mov r10,#0x0
mov r11,#0x0
mov r12,#0x0
mov r13,#0x0 @@sp
ldr r2, =0x30000000 @@jump to 0x30000000
mov pc, r2 @@jump to 0x30000000
.endfunc
.size __rst_handler, . - __rst_handler
.func __udf_handler
__udf_handler:
@@---------------------- undef ins interrupt -------------------@@
sub sp, 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,=0x30000004 @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __udf_handler, . - __udf_handler
@@---------------------- swi interrupt -------------------@@
.func __swi_handler
__swi_handler:
sub sp, 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,=0x30000008 @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __swi_handler, . - __swi_handler
@@---------------------- ins abort interrupt -------------------@@
.func __ins_handler
__ins_handler:
sub sp, 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,=0x3000000C @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __ins_handler, . - __ins_handler
@@---------------------- data abort interrupt -------------------@@
.func __dat_handler
__dat_handler:
sub sp, 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,=0x30000010 @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __dat_handler, . - __dat_handler
@@---------------------- reserved interrupt -------------------@@
.func __rsv_handler
__rsv_handler:
sub sp, 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,=0x30000014 @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __rsv_handler, . - __rsv_handler
@@---------------------- reserved interrupt -------------------@@
.func __irq_handler
__irq_handler:
sub sp, 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,=0x30000018 @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __irq_handler, . - __irq_handler
@@---------------------- reserved interrupt -------------------@@
.func __fiq_handler
__fiq_handler:
sub sp, 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,=0x3000001C @load the address of HandleXXX to r0
str r0,[sp,#4] @store the HandleXXX to stack
ldmfd sp!,{r0,pc} @POP the work register and pc(jump to ISR)
.endfunc
.size __fiq_handler, . - __fiq_handler
.data
.align 4
mem_cfg_val:
.long 0x22111110 @@vBWSCON
.long 0x00000700 @@vBANKCON0
.long 0x00000700 @@vBANKCON1
.long 0x00000700 @@vBANKCON2
.long 0x00000700 @@vBANKCON3
.long 0x00000700 @@vBANKCON4
.long 0x00000700 @@vBANKCON5
.long 0x00018009 @@vBANKCON6
.long 0x00018009 @@vBANKCON7
.long 0x008e04eb @@vREFRESH
.long 0x32 @@vBANKSIZE
.long 0x20 @@vMRSRB6
.long 0x20 @@vMRSRB7
.end
其实这份代码就是最简单的BootLoader,只是缺少将应用拷贝到0x30000000处的代码,但是对于在RAM中运行的程序,这已经足够,在以后实现BootLoader的过程中,将对此代码进行完善。
ROM代码已经做好,现在的任务是实现RAM代码中的向量处理,由于此时我已经实现了进程调度,故最新的IRQ和SWI向量处理函数已经支持进程切换过程中的现场保护及恢复。
其中,IRQ中断向量代码如下:
@@---------------------- reserved interrupt -------------------@@
.func __irq_handler
__irq_handler:
sub lr, lr, #4 @calc return address.
stmfd sp!, {lr} @save return address firstly (pc).
stmfd sp, {r0-r14}^ @save user regisers.
sub sp, sp, #60 @r0-r15 offset.
mrs r5, spsr @save cpsr
stmfd sp!, {r5} @save cpsr
@@-----------call task save ----------@@
@@load dst address;
ldr r0, =g_curr_tcb_p @@load g_curr_task_p address
ldr r0, [r0] @@load TCB address
@@load src address;
mov r1, sp @@base address;
bl tcb_save @@save task.
@@-----------call system routines ----------@@
bl main_irq_handler
@@-----------call task load ----------@@
@@load dst address;
mov r0, sp @@base address;
@@load src address;
ldr r1, =g_curr_tcb_p @@load g_curr_task_p address
ldr r1, [r1] @@load TCB address
bl tcb_save @load task.
@@-----------restore TCB ----------@@
ldmfd sp!, {r0} @pop spsr
msr spsr, r0 @restore spsr
ldmfd sp, {r0-r14}^ @restore r0-r12
add sp, sp, #60 @skip user sp.
ldmfd sp!, {lr} @restore user pc.
movs pc, lr @return;
.endfunc
.size __irq_handler, . - __irq_handler
其中 tcb_save 的功能就是内存拷贝,也就是将R0-R15,cpsr拷贝到进程的TCB结构中。
进程的TCB结构定义如下:
typedef struct
{
/*context*/
u_int32 cpsr;
u_int32 r0;
u_int32 r1;
u_int32 r2;
u_int32 r3;
u_int32 r4;
u_int32 r5;
u_int32 r6;
u_int32 r7;
u_int32 r8;
u_int32 r9;
u_int32 r10;
u_int32 r11;
u_int32 r12;
u_int32 sp;
u_int32 lr;
u_int32 pc;
#if 0
u_int32 rsvd; /* u_int32 spsr; --- not needed in user/sys mode! */
#endif
}TCB_t;
这里将cpsr作为结构的第一个成员的原因是,读取和恢复cprs都需要r0等通用寄存器的参与,而r0等通用寄存器的内容也必须被保存,故cpsr将最后被保存,而这对应于栈空间来说,就是位于栈顶,对于TCB结构来说,就是第一个成员。
在IRQ处理例程中,main_irq_handler是系统的中断处理入口,它将读取中断控制器中的值,判断中断源,调用相应的中断处理程序(由用户注册),ACK系统中断,最后返回。
在刚进入IRQ例程时,我们在栈中构造了user/system模式下被中断的进程的TCB结构,在从IRQ例程中返回时,我们需要恢复被选择运行的进程的TCB结构,这里就有一个问题了,恢复TCB的过程包含了对SP指针的恢复,因此必须十分小心,而最后的返回操作则是对PC指针的赋值过程(包括CPSR寄存器的自动恢复),因此,我们使用IRQ模式下的LR寄存器保存从IRQ栈中弹出的进程PC值,最后利用指令 movs pc, lr 返回,这样就确保了返回地址的正确性以及TCB恢复过程的高效性。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94507/showart_1933238.html |
|