| 帮同学宣传一下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
















