buf

BE something YOU love and understand
posts - 94, comments - 35, trackbacks - 0, articles - 2
   :: 首页 :: 新随笔 :: 联系 ::  :: 管理

S3C44B0 + U-Boot中断控制小结

Posted on 2007-11-23 12:45 buf 阅读(2669) 评论(0)  编辑 收藏 引用 所属分类: Embedded
 

开发板

购于00IC,与北京恒丰瑞科开发的S3C44B0开发板类似,外观上只少一个音频输入口。基本配置如下:
CPU      Samsung S3C44B0X
ROM     AM29LV160DB, 2M * 8-Bit Flash, BANK0
RAM     HY57V641620ETP-7, 4Banks * 1M * 16-Bit SDRAM, BANK6

编译环境

Windows XP SP2
Cygwin, version 2.194.2.24
Arm-elf-gcc, version 2.95.3 20010315
U-Boot
版本1.1.1

S3C44B0的异常类型

S3C44B0采用了ARM7TDMI核心,支持共7种类型的异常。发生异常时,CPU保存异常处理的返回地址到link register,进入与异常类型对应的处理模式,然后从对应的异常向量取指。

Address

Exception

Mode in Entry

0x0000 0000

Reset

Supervisor

0x0000 0004

Undef Instruction

Undefined

0x0000 0008

Software Interrupt

Supervisor

0x0000 000c

Abort (prefetch)

Abort

0x0000 0010

Abort (data)

Abort

0x0000 0014

Reserved

Reserved

0x0000 0018

IRQ

IRQ

0x0000 001c

FIQ

FIQ

 

U-Boot提供的默认异常处理

U-Boot为异常提供了一种默认处理,从启动代码(入口地址0x00000000)开始看:

b   reset            

add pc, pc, #0x0c000000     /* will jump to 0x0c00000c */

add pc, pc, #0x0c000000     /* will jump to 0x0c000010 */

add pc, pc, #0x0c000000     /* will jump to 0x0c000014 */

add pc, pc, #0x0c000000   /* will jump to 0x0c000018 */

b   .                        /* jump here, system halt */

add pc, pc, #0x0c000000     /* will jump to 0x0c000020 */

add pc, pc, #0x0c000000     /* will jump to 0x0c000024 */ 

对于复位异常,CPU通过b reset进入复位代码,重新启动系统。发生复位异常通常意味着出现了较严重的错误,必须对系统重新引导。复位代码总在ROM中执行,而b指令可寻址32MB,应付本例中的2MB Flash绰绰有余。

其它类型异常则比较自由,如何处理完全由用户决定。因此它们的处理常常在RAM中进行,而本例中RAM挂接在BANK6,地址范围为0x0c000000 ~ 0x0c7fffff,已经超出了b指令的寻址范围。于是通过add指令重置pc。目标地址已经在代码注释中说明,注意这里pc读出来的值等于当前指令的地址加0x08

好了,发生异常(复位异常除外)后会从ROM的异常向量跳转到RAM,接下来会发生什么呢?有理由猜测RAM中也存放了一系列的跳转指令,进入到异常处理服务例程中去。来看U-Boot的这段代码:

/* now copy to sram the interrupt vector */

    adr r0, real_vectors

    add r2, r0, #1024

    ldr r1, =0x0c000000

    add r1, r1, #0x08

vector_copy_loop:

    ldmia r0!, {r3-r10}

    stmia r1!, {r3-r10}

    cmp r0, r2

    ble vector_copy_loop

这段代码将把ROM中从real_vectors开始的1024字节内容,复制0x0c000008开始的目标区域中去,相当于在RAM中建立一个二级跳转表。看看这张表的内容:

/* interrupt vectors */

real_vectors:

    b   reset

    b   undefined_instruction

    b   software_interrupt

    b   prefetch_abort

    b   data_abort

    b   not_used

    b   irq

    b   fiq

undefined_instruction:

    mov     r6, #3

    b       reset

software_interrupt:

    mov     r6, #4

    b       reset

...

于是,异常处理从RAM的二次跳转表进入服务例程,这样就和前面ROM中的首次跳转连接起来了。以SWI为例,从0x00000008跳转到0x0c000010,然后跳转到software_interrupt开始执行,最终reset

 

异常处理的定制

这些异常服务例程是U-Boot所提供的一种默认处理,有时候这可能不是你想要的。比如,发生IRQ显然用不着系统复位。如何自定义异常处理呢?直接修改U-Boot的异常处理例程是显然是可以的,但是这里有一个更灵活的处理方法:修改RAM中的二次跳转表,从这里跳转到你自己编写的异常服务例程中去。

为了让这种方法实施起来更方便,在RAM的高端开辟了一块区域,用于存储每个异常服务例程的入口地址,我把这块区域称为“异常服务例程地址表”。进行异常处理的时候,通过一小段“加载程序”,把服务例程的入口地址写入pc。这看起来有些复杂,但是一旦理解这个过程,编写或是自己的异常服务程序就变得很简单了。

假设通过U-Boot把用户程序下载到RAM0x0c008000开始的地方,用户程序首先执行必要的初始化步骤,在44binit.s:

ENTRY

b ResetHandler    ;for debug

b HandlerUndef    ;handlerUndef

b HandlerSWI      ;SWI interrupt handler

b HandlerPabort  ;handlerPAbort

b HandlerDabort  ;handlerDAbort

b .               ;handlerReserved

b HandlerIRQ

b HandlerFIQ

看起来这些HandlerXXX有点像是异常服务例程的入口了,其实不是,HandlerXXX只是上面说的那个“加载程序”。它是带有一个参数的宏,参数即存放着相应服务例程入口地址的地址,这个宏的作用就是把这个入口地址加载到pc

MACRO

$HandlerLabel HANDLER $HandleLabel

sub    sp,sp,#4

stmfd sp!,{r0}

ldr    r0,=$HandleLabel

ldr    r0,[r0]

str    r0,[sp,#4]

ldmfd sp!,{r0,pc}

MEND

...

HandlerFIQ HANDLER HandleFIQ

HandlerIRQ HANDLER HandleIRQ

HandlerUndef HANDLER HandleUndef

HandlerSWI HANDLER HandleSWI

...

这里的HandleXXX标记了存放各个服务例程地址的地址,它们从_ISR_STARTADDRESS开始,以word为单位,足以存放64项。事实上,如果仅使用non-vectored irq模式的话,只需要8项就足够了:

^ _ISR_STARTADDRESS      /* 0x0c7fff00 in option.s */

HandleReset       # 4

HandleUndef       # 4

HandleSWI         # 4

HandlePabort      # 4

HandleDabort      # 4

HandleReserved    # 4

HandleIRQ         # 4

HandleFIQ         # 4

...

 

至此,用户程序需要完成的工作已经很清楚了:
1
、编写异常处理例程

Void SWI_ISR(void)

{

    Uart_Printf(“SWI Exception Captured!”);

    ...

}

2、将异常处理例程的入口地址写入到RAM高端相应的地址处

#define pISR_SWI (*(unsigned *)(_ISR_STARTADDRESS+0x8))/*44b.h*/

...

pISR_SWI =(unsigned) SWI_ISR;

注意IRQ处理例程已在44binit.s中定义并写入RAM高端相应位置了:

/* 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]

3、修改RAM 0x0c000008开始的二级跳转表,使其跳转到44binit.s开始处建立的“加载程序”。这一步最简单的方法就是向二级跳转表处直接写b指令的机器码0xeaXXXXXX。比如对SWI,起跳地址为0x0c000010,目标地址为0x0c008008,则:

XXXXXX = (0x0c008008 – 0x0c000010 – 0x08) >> 2

        = 7ff0 / 4

= 001ffc

写机器码的方法:

*((volatile unsigned*)0x0c000008) = 0xea001ffc

用下表总结整个跳转过程:

开始地址

目标地址

完成者

当前程序流

0x0000 0008

CPU硬件逻辑

0x0000 0008

0x0c00 0010

U-Boot的一级跳转表

0x0c00 0010

0x0c008008

用户程序的二级跳转表

0x0c00 8008

异常服务例程

44binit.s中的加载程序

 

另一种处理方法

不过我想,既然有了异常服务例程地址表,也可以选择从U-Boot的一级跳转表直接加载服务历程入口地址,只要按照GNU Assembler的语法重写“加载程序”就可以了,修改start.S的开始部分:

.macro Handler Addr

    sub     sp,sp,#4      

    stmfd   sp!,{r0}    

    ldr     r0,=\Addr    

    ldr     r0,[r0]           

    str     r0,[sp,#4]    

    ldmfd   sp!,{r0,pc}    

.endm

_start:   

    b reset

    b HandlerUndef

    b HandlerSWI  

...

/* 加载程序 */

HandlerUndef:

    Handler HandleUndef

HandlerSWI:

    Handler HandleSWI

...

/* 地址表 */

.equ HandleReset, 0xc7fff00

.equ HandleUndef,0xc7fff04

.equ HandleSWI, 0xc7fff08

并注释掉now copy to sram the interrupt vector那段代码。

参考资料

1.         S3C44B0X Datasheet, Samsung

2.         基于S3C44B0X嵌入式uCLinux系统原理及应用,李岩,荣盘祥编著

3.         关于S3C44b启动代码中中断初始化部分的讨论和问题,hufeng

只有注册用户登录后才能发表评论。