帮同学宣传一下http://shop57644665.taobao.com/

一般一个ARM系统的boot过程是这样的。首先ARM在上电后直接跳转到0地址,在这个地址上存放的是系统的启动代码。系统开始执行时一般会先设置DPLL和clock,然后进行片选,接着将需要放到ram中代码手动copy到ram中,设置EMI等系统必要参数,设置ARM各个模式的系统堆栈,切回SVC模式进行操作系统初始化。
以下是一个实际的基于ARM平台的一个bootloader示例。
编译环境:ADSv1.2

Boot.s

LOCKOUT         EQU     &00C0
LOCK_MSK        EQU     &00C0              ; Interrupt lockout mask value
MODE_MASK       EQU     &001F              ; Processor Mode Mask
SUP_MODE        EQU     &0013              ; Supervisor Mode (SVC)
IRQ_MODE        EQU     &0012              ; Interrupt Mode (IRQ)
FIQ_MODE        EQU     &0011              ; Fast Interrupt Mode (FIQ)
ABORT_MODE      EQU     &0017              ; Abort Interrupt Mode
UNDEF_MODE      EQU     &001B              ; Undefined Interrupt Mode (should not happen)
SYS_MODE        EQU     &001F              ; Undefined Interrupt Mode (should not happen)
;以上是一些需要使用的变量的值,这些值需要参考ARM文档来设定
IRQ_STACK_SIZE  EQU     128                ; Number of bytes in IRQ stack (must be align(8))
FIQ_STACK_SIZE  EQU     512                ; Number of bytes in FIQ stack. (must be align(8))
SYSTEM_SIZE     EQU     1024               ; Define the system stack size (must be align(8))

AREA |STACK_POOL_EXTSRAM|, DATA, READWRITE, ALIGN=3
;这行语句是ADS编译器对应汇编代码在链接是使用的预编译指令,同C语言中的program一样,用于指定对应的代码或数据的特定名称,以在链接是进行对应的处理,比如将指定代码放入指定的区域。
SVC_Stack
DCD     0x80010000
IRQ_Stack
DCD     0x80020000
FIQ_Stack
DCD     0x80030000
Exception_Stack
DCD     0x80040000
Undef_Stack
DCD     0x80050000
SYS_Stack
DCD     0x80060000
;上面是给ARM在各个模式下的堆栈分配的空间。
;        IMPORT |Load$$INTSRAM_CODE$$Base|
;        IMPORT |Image$$INTSRAM_DATA$$Base|
;        IMPORT |Image$$INTSRAM_CODE$$Length|
;这三个变量是链接器生成的全局变量,具体就是指在scattered file 中的INTSRAM_CODE段在系统中的load地址,运行时的地址,以及长度。我们要实现一些代码放到ram中去执行就必须通过对这些变量的访问。
AREA |C$$code|, CODE, READONLY
;指定以下代码的名称为C$$code,以便在链接时指定存放地址。
B    INT_Initialize
B    INT_Undef_Inst
B    INT_Swi
B    INT_Abort_Prefetch
B    INT_Abort_Data
B    INT_Reserved
B    INT_IRQ
B    INT_FIQ
;以上是ARM的异常处理接口,其地址是固定,因为ARM发生异常时的跳转地址是固定的。
ENTRY
;入口函数。
IMPORT  main_loop
EXPORT  INT_Initialize

INT_Initialize
;First we set the DPLL
;       ldr     r1,DPLL_CNTRL_REG     ; Load address of DPLL register in R1
;       ldrh    r2,DPLL_CONTROL_RST   ; Load DPLL reset value in R2
;       strh    r2,[r1]               ; Store DPLL reset value in DPLL register
;Wait_DPLL_Bypass
;        ldr     r2,[r1]               ; Load DPLL register
;        and     r2,r2,#1              ; Perform a mask on bit 0
;        cmp     r2,#1                 ; Compare DPLL lock bit
;        beq     Wait_DPLL_Bypass      ; Wait Bypass mode (i.e. bit[0]='0')

;  Configure CNTL_ARM_CLK register with reset value: DPLL is used to
;  generate ARM clock with division factor of 1.
;首先一般我们都需要设置系统的DPLL和clock。
;        ldr     r1,CNTL_ARM_CLK_REG  ; Load address of CNTL_ARM_CLK register in R1
;        ldrh    r2,CNTL_ARM_CLK_RST  ; Load CNTL_ARM_CLK reset value in R2
;        strh    r2,[r1]              ; Store CNTL_ARM_CLK reset value in CNTL_ARM_CLK register
;  ldrh  r2,CS0_MEM_REG  ; FLASH Initialization
;  strh  r2,[r1,#0x0]    ; CS0

;  ldrh  r2,CS5_MEM_REG  ; FLASH Initialization
;  strh  r2,[r1,#0xA]    ; CS5

; ldrh  r2,CS4_MEM_REG  ; RAM Initialization
;  strh  r2,[r1,#0x8]    ; CS4
将片选设置好

MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear the mode bits
ORR   a1,a1,#SUP_MODE                    ; Set the supervisor mode bits
ORR   a1,a1,#LOCKOUT                     ; Insure IRQ/FIQ interrupts are
; locked out
MSR   CPSR_cxsf,a1                       ; Setup the new CPSR
;切换到ARM的svc模式。
LDR     a1,SVC_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
;设置svc模式下的堆栈地址。
;        LDR     r0, =|Load$$INTSRAM_CODE$$Base|       ; load address of region
;        LDR     r1, =|Image$$INTSRAM_CODE$$Base|       ; execution address of region
;        MOV     r2, r1                  ; copy execution address into r2
;        LDR     r4, =|Image$$INTSRAM_CODE$$Length|
;        ADD     r2, r2, r4              ; add region length to execution address to...
;        BL      copy
;以上代码就是通过访问链接器的变量实现将需在ram中运行的代码由flash拷贝到对应的ram中。这步是必须的,因为在链接时,其他函数调用这部分代码的地址已经链接成了相对应的ram的地址!!
;相应的一些全局变量清零的代码也是需要类似实现的。!!

LDR   a3,IRQ_Stack
MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear the mode bits
ORR   a1,a1,#IRQ_MODE                    ; Set the IRQ mode bits
MSR   CPSR_cxsf,a1                       ; Move to IRQ mode
MOV   sp,a3                              ; Setup IRQ stack pointer
;设置ARM irq模式时的堆栈,在设置该模式堆栈时必须将ARM切换到对应的模式。
LDR   a3,FIQ_Stack
MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear the mode bits
ORR   a1,a1,#FIQ_MODE                    ; Set the FIQ mode bits
MSR   CPSR_cxsf,a1                       ; Move to the FIQ mode
MOV   sp,a3                              ; Setup FIQ stack pointer
;设置ARM fiq模式时的堆栈
LDR   a3,Exception_Stack
MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear the mode bits
ORR   a1,a1,#ABORT_MODE                  ; Set the Abort mode bits
MSR   CPSR_cxsf,a1                       ; Move to the Abort mode
MOV   sp,a3                              ; Setup Abort stack pointer
;设置ARM abort模式时的堆栈
LDR   a3,Undef_Stack
MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear the mode bits
ORR   a1,a1,#UNDEF_MODE                    ; Set the Undefine mode bits
MSR   CPSR_cxsf,a1                       ; Move to the Undefine mode
MOV   sp,a3                              ; Setup Undefine stack pointer
;设置ARM Undefine模式时的堆栈
LDR   a3,SYS_Stack
MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear the mode bits
ORR   a1,a1,#SYS_MODE                    ; Set the Undefine mode bits
MSR   CPSR_cxsf,a1                       ; Move to the Undefine mode
MOV   sp,a3                              ; Setup Undefine stack pointer
;设置ARM 系统模式时的堆栈

MRS   a1,CPSR                            ; Pickup current CPSR
BIC   a1,a1,#MODE_MASK                   ; Clear mode bits
ORR   a1,a1,#SUP_MODE                    ; Set the supervisor mode bits
MSR   CPSR_cxsf,a1                       ; All interrupt stacks are setup,
;最后还是要切换到svc模式,因为我们的系统是在svc模式下运行的。
B     main_loop
;当系统的一切设置好后就可以真正运行我们的主要程序了。
INT_Undef_Inst
;First we set the DPLL
;       ldr     r1,DPLL_CNTRL_REG     ; Load address of DPLL register in R1
;       ldrh    r2,DPLL_CONTROL_RST   ; Load DPLL reset value in R2
;       strh    r2,[r1]               ; Store DPLL reset value in DPLL register
LDR     a1,Exception_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
INT_Swi
LDR     a1,Exception_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
INT_Abort_Prefetch
LDR     a1,Exception_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
INT_Abort_Data
LDR     a1,Exception_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
;一般在发生abort时我们可以将ARM切回到SVC模式然后将各个寄存器的值保存下来以方便分析代码执行到何处时发生了什么异常。在TI系统中有一个D-unit,这个模块并行连接到ARM的地址线上可用于记录ARM的具体操作,对于异常分析非常有用。
INT_Reserved
LDR     a1,Exception_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
;一般这个异常不会发生,因为是ARM保留的异常。
INT_IRQ
LDR     a1,IRQ_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
INT_FIQ
LDR     a1,Exception_Stack             ; Pickup the begining address from .cmd file
MOV     sp,a1                       ; Setup initial stack pointer
;对于IRQ和FIQ的处理过程一般都是先进行上下文保护,然后执行对应irq或fiq中断号的对应处理函数,然后进行上下文恢复。

;以上应该是ARM在异常时跳转到对应处理函数的操作代码。
END

对应的链接文件Scatter file
ROM 0×00000000 0×00e00000
{
ROM 0×00000000 0×00400000
{
boot.o (C$$code,+First)
*.o (+RO)

}
EXTSRAM +0×00
{
* (+RW, +ZI)
}

DUMMY_END 0×08400000 0×04
{
boot.o (DUMMY_POOL)
}
INTSRAM_CODE 0xA0000000 0×7600
{
boot.o (INTERNCODE)
}
INTSRAM_DATA 0xA0007600 0×5A00
{
boot.o (STACK_POOL_INTSRAM)
}

}

生成Elf转换出来的一段数据,可以看到我们将ARM异常处理代码放到了ARM要求的地址上。
C$$code
0×00000000:    ea000006    ….    B        INT_Initialize  ; 0×20
0×00000004:    ea00002f    /…    B        INT_Undef_Inst  ; 0xc8
0×00000008:    ea000030    0…    B        INT_Swi  ; 0xd0
0×0000000c:    ea000031    1…    B        INT_Abort_Prefetch  ; 0xd8
0×00000010:    ea000032    2…    B        INT_Abort_Data  ; 0xe0
0×00000014:    ea000033    3…    B        INT_Reserved  ; 0xe8
0×00000018:    ea000034    4…    B        INT_IRQ  ; 0xf0
0×0000001c:    ea000035    5…    B        INT_FIQ  ; 0xf8
INT_Initialize
0×00000020:    e10f0000    ….    MRS      r0,CPSR
0×00000024:    e3c0001f    ….    BIC      r0,r0,#0×1f