免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 805 | 回复: 0
打印 上一主题 下一主题

MINI2440开发笔记---中断处理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-19 22:27 |只看该作者 |倒序浏览
由于没有使用内存映射,所以中断向量必须安装在地址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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP