1.Bootloader是系统上电或复位后首先运行的一段代码,Bootloader代码(即启动代码)的好坏对整个系统的运行效率有着重要的影响, 而
Bootloader代码与芯片的特性有着紧密的联系。本文根据用LPC2210在ADS(ARM Developer Suite)开发环境下进行嵌入式系统设计的实际经验
,总结出基于ARM7TDMI内核的LPC2210处理器的Bootloader代码设计的详细流程及其中的一些关键技术和代码
2。starUP.s
//一、变(常)量声明
// 首先声明堆栈大小
// ;define the stack size
// ;定义堆栈的大小
SVC_STACK_LEGTH EQU 0
FIQ_STACK_LEGTH EQU 0
IRQ_STACK_LEGTH EQU 256 ;IRQ中断堆栈
ABT_STACK_LEGTH EQU 0
UND_STACK_LEGTH EQU 0
处理器模式声明
NoInt EQU 0x80
USR32Mode EQU 0x10
SVC32Mode EQU 0x13
SYS32Mode EQU 0x1f
IRQ32Mode EQU 0x12
FIQ32Mode EQU 0x11
外部存储器配置声明
PINSEL2 EQU 0xE002C014
BCFG0 EQU 0xFFE00000
BCFG1 EQU 0xFFE00004
BCFG2 EQU 0xFFE00008
BCFG3 EQU 0xFFE0000C
IMPORT __use_no_semihosting_swi
IMPORT __use_two_region_memory
;The imported labels
;引入的外部标号在这声明
IMPORT FIQ_Exception ;Fast interrupt exceptions handler 快速中断异常处理程序
IMPORT __main ;The entry point to the main function C语言主程序入口
IMPORT TargetResetInit ;initialize the target board 目标板基本初始化
;The emported labels
;给外部使用的标号在这声明
EXPORT bottom_of_heap ;heap的底部
EXPORT bottom_of_Stacks ;stack的底部
EXPORT top_of_heap ;heap的顶部
EXPORT StackUsr
EXPORT Reset ;复位
EXPORT __user_initial_stackheap ;用户初始化堆栈和堆
二、建立中断向量表(向量表所有数据32位累加和为0)
Reset
LDR PC, ResetAddr ;复位后开始的执行地址
LDR PC, UndefinedAddr ;未定义指令异常
LDR PC, SWI_Addr ;软件中断
LDR PC, PrefetchAddr ;预取中止
LDR PC, DataAbortAddr ;预取数据中止
DCD 0xb9205f80 ;保留的异常
LDR PC, [PC, #-0xff0] ;IRQ ( 该指令会读取VICVectAddr寄存器的值,然后放入PC指针 )
LDR PC, FIQ_Addr ;FIQ
;给每一个向量分配连续的字存储单元
ResetAddr DCD ResetInit
UndefinedAddr DCD Undefined
SWI_Addr DCD SoftwareInterrupt
PrefetchAddr DCD PrefetchAbort
DataAbortAddr DCD DataAbort
Nouse DCD 0
IRQ_Addr DCD 0
FIQ_Addr DCD FIQ_Handler
;发生以下异常时程序暂停( 除了FIQ )
;未定义指令
Undefined
B Undefined
;软中断
SoftwareInterrupt
B SoftwareInterrupt
;取指令中止
PrefetchAbort
B PrefetchAbort
;取数据中止
DataAbort
B DataAbort
;快速中断
FIQ_Handler
STMFD SP!, {R0-R3, LR} ;把R0-R3,LR的值存入堆栈
BL FIQ_Exception ;跳转到FIQ中断程序
LDMFD SP!, {R0-R3, LR} ;恢复R0-R3,LR的值
SUBS PC, LR, #4 ;PC指针跳转到LR-4
三、复位程序
根据外部存储器控制器的引脚接法,对GPIO进行设置(外部存储器所接引脚与P1,P2,P3口的GPIO功能复用),之后对用到的四组存储器组进行
设置.
ResetInit
;Initial extenal bus controller.
;初始化外部总线控制器,根据目标板决定配置
LDR R0, =PINSEL2
IF :DEF: EN_CRP
LDR R1, =0x0f814910 ;芯片加密,禁止JTAG调试
ELSE
LDR R1, =0x0f814914 ;设置总线的I/O引脚
ENDIF
STR R1, [R0]
;设置四组存储器配置
LDR R0, =BCFG0
LDR R1, =0x1000ffef
STR R1, [R0] ;设置外部第0个存储区
LDR R0, =BCFG1
LDR R1, =0x1000ffef
STR R1, [R0] ;设置外部第1个存储区
; LDR R0, =BCFG2 ;该开发板没有用到后面两组存储器接口
; LDR R1, =0x2000ffef
; STR R1, [R0] ;设置外部第2个存储区
; LDR R0, =BCFG3
; LDR R1, =0x2000ffef
; STR R1, [R0] ;设置外部第3个存储区
BL InitStack ;初始化堆栈 Initialize the stack
BL TargetResetInit ;目标板基本初始化 Initialize the target board
;跳转到c语言入口 Jump to the entry point of C program
B __main
四、堆栈初始化
初始化各模式堆栈
InitStack
MOV R0, LR;因芯片模式切换,故将程序返回地址保存到R0,同时在初始化堆栈完成后使用R0返回
;Build the SVC stack
;设置管理模式堆栈
MSR CPSR_c, #0xd3
LDR SP, StackSvc
;Build the IRQ stack
;设置中断模式堆栈
MSR CPSR_c, #0xd2
LDR SP, StackIrq
;Build the FIQ stack
;设置快速中断模式堆栈
MSR CPSR_c, #0xd1
LDR SP, StackFiq
;Build the DATAABORT stack
;设置中止模式堆栈
MSR CPSR_c, #0xd7
LDR SP, StackAbt
;Build the UDF stack
;设置未定义模式堆栈
MSR CPSR_c, #0xdb
LDR SP, StackUnd
;Build the SYS stack
;设置系统模式堆栈
MSR CPSR_c, #0xdf ;修改为0x5d将打开IRQ中断
LDR SP, =StackUsr
MOV PC, R0 ;子程序返回
五、目标板初始化
void TargetResetInit(void)
{
/***************************************************************************
// 设置存储器映射方式,这必须根据硬件来设置. 这里是ADS1.2中的设置.
***************************************************************************/
#ifdef __DEBUG
MEMMAP = 0x3; //remap
#endif
#ifdef __OUT_CHIP
MEMMAP = 0x3; //remap
#endif
#ifdef __IN_CHIP
MEMMAP = 0x1; //remap,根据预定义的宏,设置存储器映射方式
#endif
/* 设置系统各部分时钟 */
PLLCON = 1; ;使能PLL(锁相环),但不连接PLL
#if (Fpclk / (Fcclk / 4)) == 1
VPBDIV = 0;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
VPBDIV = 2;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
VPBDIV = 1;
#endif /*设置外设时钟(VPB时钟pclk)与系统时钟(cclk)的分频比*/
#if (Fcco / Fcclk) == 2
PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
#endif
#if (Fcco / Fcclk) == 4
PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
#endif
#if (Fcco / Fcclk) == 8
PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
#endif
#if (Fcco / Fcclk) == 16
PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
#endif /*根据PLL的电流控制振荡器和系统时钟的频率比,设置PLL的乘因子和除因子*/
PLLFEED = 0xaa;
PLLFEED = 0x55; /*使用芯片要求的访问序列把数据写入PLL相关寄存器*/
while((PLLSTAT & (1 << 10)) == 0); /*等待PLL跟踪完成*/
PLLCON = 3; // 按照特定的设置方式对PLL控制寄存器进行设置
PLLFEED = 0xaa;
PLLFEED = 0x55; /*使PLL连上系统*/
/* 设置存储器加速模块 */
MAMCR = 0; /*关闭MAM*/
#if Fcclk < 20000000
MAMTIM = 1;
#else
#if Fcclk < 40000000
MAMTIM = 2;
#else
MAMTIM = 3; /*根据Fcclk的大小来设置MAM定时寄存器*/
#endif
#endif
MAMCR = 2; /*使能MAM*/
/* 初始化VIC */
/* initialize VIC*/
VICIntEnClr = 0xffffffff; ;禁止所有中断
VICVectAddr = 0; ;设置向量地址寄存器(VICVectAddr)的值为0
VICIntSelect = 0; ;将所有中断设置为IRQ中断
}
六、其它
;/*********************************************************************************************************
;** unction name 函数名称: __user_initial_stackheap
;** Descriptions 功能描述: Initial the function library stacks and heaps, can not deleted!
;** 库函数初始化堆和栈,不能删除
;** input parameters 输 入: reference by function library 参考库函数手册
;** Returned value 输 出 : reference by function library 参考库函数手册
;** Used global variables 全局变量: None 无
;** Calling modules 调用模块: None 无
;********************************************************************************************************/
__user_initial_stackheap
LDR r0,=bottom_of_heap
; LDR r1,=StackUsr
LDR r2,=top_of_heap
LDR r3,=bottom_of_Stacks
MOV pc,lr
StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4
StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4
StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4
StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4
StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)* 4
;/*********************************************************************************************************
;** unction name 函数名称: CrpData
;** Descriptions 功能描述: encrypt the chip
;** input parameters 输 入: None 无
;** Returned value 输 出 : None 无
;** Used global variables 全局变量: None 无
;** Calling modules 调用模块: None 无
;********************************************************************************************************/
IF :DEF: EN_CRP
IF . >= 0x1fc
INFO 1,"\nThe data at 0x000001fc must be 0x87654321.\nPlease delete some source before this line."
ENDIF
CrpData
WHILE . < 0x1fc
NOP
WEND
CrpData1
DCD 0x87654321 ;/*When the Data is 为0x87654321,user code be protected. 当此数为0x87654321时,用户程序被保护
*/
ENDIF
;/* 分配堆栈空间 */
AREA MyStacks, DATA, NOINIT, ALIGN="2"
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;Stack spaces for Administration Mode 管理模式堆栈空间
IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;Stack spaces for Interrupt ReQuest Mode 中断模式堆栈空间
FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;Stack spaces for Fast Interrupt reQuest Mode 快速中断模式堆栈空间
AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ;Stack spaces for Suspend Mode 中止义模式堆栈空间
UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;Stack spaces for Undefined Mode 未定义模式堆栈
AREA Heap, DATA, NOINIT
bottom_of_heap SPACE 1
AREA StackBottom, DATA, NOINIT
bottom_of_Stacks SPACE 1
AREA HeapTop, DATA, NOINIT
top_of_heap
AREA Stacks, DATA, NOINIT
StackUsr
.3.重点分析如下
void TargetResetInit(void)
{
/***************************************************************************
// 设置存储器映射方式,这必须根据硬件来设置. 这里是ADS1.2中的设置.
***************************************************************************/
#ifdef __DEBUG
MEMMAP = 0x3; //remap
#endif
#ifdef __OUT_CHIP
MEMMAP = 0x3; //remap
#endif
#ifdef __IN_CHIP
MEMMAP = 0x1; //remap,根据预定义的宏,设置存储器映射方式
#endif
中断重新映射通过控制存储器映射控制特性来实现:
[1].在LPC2210/2220/2290是没有内部FLASH,但是这个BOOT BLOCK存在的。控制BOOT【0:1】高低电平来实现引导和初始化,
0:BOOT装载程序模式
1:用户FLASH模式
2:用户RAM模式
3: 用户外部模式PCL2210/2212/2214才能用,当复位时BOOT【0:1】不全为1时会激活这一种方式。
原理如下:
[2].当MEMMAP=0x02时,中断向量从静态RAM映射, 用户RAM模式型
MEMMAP="2":最为主要的设置,即是重映射的关键之所在,当设置MEMMAP=2 时,中断向量表(0x00000000-0x0000003c)映射的是片内SRAM中
的0x40000000-0x4000003c中的值,而因为是SRAM,所以在程序运行的过程中是可以改变的,这样就可以达到重映射的目的啦(中断向量表可以
随时修改)。
[3].MMAP=0x00时, 中断向量从BOOT BLOCK重映射,BOOT装载程序模式
MEMMAP="0":开机默认值,Boot装载模式----中断向量表(0x00000000-0x0000003c)映射的是BootBlock中的0x7FFFE000-0x7FFFF03c中的值;
芯片复位时,启动boot装载程序,boot装载程序检查P0.14口的状态和用户的异常向量,判断是进入ISP状态还是启动用户程序,若启动用户程
序,则自动设置MEMMAP=1(片内flash启动)或3(片外程序存储器启动)。由于没有片内FLASH,所不能用ISP.会自动转向片外执行。
【4】MEMMAP=1:中断向量表就在片内中,地址就是0x00000000-0x0000003c,相当于没有映射;
【5】MEMMAP=3:中断向量表就在片外存储器中,中断向量表(0x00000000-0x0000003c)映射到是片外外存储器中的0x80000000-0x8000003c中的值;
功能上与MEMMAP=1时的差不多,因为一旦程序固化到flash中,即为只读,只是数值映射而已!
KEIL版的STAR.S
; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F
I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled
;// <h> Stack Configuration (Stack Sizes in Bytes)
;// <o0> Undefined Mode <0x0-0xFFFFFFFF:8>
;// <o1> Supervisor Mode <0x0-0xFFFFFFFF:8>
;// <o2> Abort Mode <0x0-0xFFFFFFFF:8>
;// <o3> Fast Interrupt Mode <0x0-0xFFFFFFFF:8>
;// <o4> Interrupt Mode <0x0-0xFFFFFFFF:8>
;// <o5> User/System Mode <0x0-0xFFFFFFFF:8>
;// </h>
UND_Stack_Size EQU 0x00000100
SVC_Stack_Size EQU 0x00000100
ABT_Stack_Size EQU 0x00000100
FIQ_Stack_Size EQU 0x00000100
IRQ_Stack_Size EQU 0x00000100
USR_Stack_Size EQU 0x00000500
ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)
AREA STACK, NOINIT, READWRITE, ALIGN="3"
Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top
;// <h> Heap Configuration
;// <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF>
;// </h>
Heap_Size EQU 0x00000000
AREA HEAP, NOINIT, READWRITE, ALIGN="3"
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.
AREA RESET, CODE, READONLY
ARM
; Exception Vectors
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.
Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr
Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
Undef_Handler B Undef_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
AREA SWI_Handler,CODE,READONLY
CMP R0, #4
LDRLO PC, [PC, R0, LSL #2]
MOVS PC, LR
SwiFunction
DCD IRQDisable ;0
DCD IRQEnable ;1
DCD FIQDisable ;2
DCD FIQEnable ;3
IRQDisable
;关IRQ中断
MRS R0, SPSR
ORR R0, R0, #I_Bit
MSR SPSR_c, R0
MOVS PC, LR
IRQEnable
;开IRQ中断
MRS R0, SPSR
BIC R0, R0, #I_Bit
MSR SPSR_c, R0
MOVS PC, LR
FIQDisable
;关FIQ中断
MSR CPSR_c, #(Mode_SVC|F_Bit)
MRS R0, SPSR
ORR R0, R0, #F_Bit
MSR SPSR_c, R0
MOVS PC, LR
FIQEnable
;开FIQ中断
MSR CPSR_c, #(Mode_SVC|F_Bit)
MRS R0, SPSR
BIC R0, R0, #F_Bit
MSR SPSR_c, R0
MOVS PC, LR
IMPORT FIQ_Exception
;快速中断
FIQ_Handler
STMFD SP!, {R0-R3, LR}
BL FIQ_Exception
LDMFD SP!, {R0-R3, LR}
SUBS PC, LR, #4
; Reset Handler
EXPORT Reset_Handler
Reset_Handler
; Initialise Interrupt System
; ...
; Setup Stack for each mode
LDR R0, =Stack_Top
; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size
; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size
; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size
; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size
; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size
; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB SL, SP, #USR_Stack_Size-32 ;IAP
;ReMap 映射
; MOV R0, #0x40000000 ;RAM区首地址
; LDR R1, =Vectors ;向量表首地址#下面一段程序是把从0x00000000开
始的64个字节(FLASH中的中断向量表和地址表)搬移到以#0x40000000为首地址的RAM区中
; LDMIA R1!, {R2-R9} ;把以[R1]为首地址的32个字节数据装载到R2-R9中
; STMIA R0!, {R2-R9} ;把R2-R9中的数据存入以[R0]为首地址的单元中
; LDMIA R1!, {R2-R9} ;把以[R1]为首地址的32个字节数据装载到R2-R9中
; STMIA R0!, {R2-R9} ;把R2-R9中的数据存入以[R0]为首地址的单元中
IMPORT TargetResetInit
BL TargetResetInit ;目标板基本初始化 Initialize the target board
;跳转到c语言入口 Jump
to the entry point of C program
; Enter the C code
IMPORT __main
LDR R0, =__main
BX R0
; User Initial Stack & Heap
AREA |.text|, CODE, READONLY
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, = (Stack_Mem + USR_Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
END
MAP[1:0]控制
-------
0X00000000 ---------------- ---------
- 00- ---------------- BOOT装载模式0x7ffff03c-0x7fffe000
- 01 -----------------用户FLASH模式0x00000000-0x0000003c
- 10 - ---------------- 用户RAM模式0x40000000-0x40000003c
- 11- ---------------- 用户外部模式0x80000000-0x8000003c
-
-
0X0000003C---------
FLASH 起始地址
H-FLASHER 需要知道FLASH 的起始地址,才能对FLASH 进行正确的操作。所以,用户需要指定
FLASH 的起始地址。H-FLASHER 会根据用户指定的起始地址和FLASH 的容量自动计算FLASH 的
有效地址空间:FLASH START ADDRESS ~ (FLASH START ADDRESS + SIZE – 1)。对FLASH 进行
写操作的时候,如果烧写目的地址不在这个范围内,H-FLASHER 将会提示OUT OF RANGE 的错误。
大部分情况下,FLASH都可以从地址0X0 访问,而不需要特别的初始化。但有些目标系统支持REMAP,
可以把FLASH 配置到不同的地址。对于这样的系统,用户需要保证设置的FLASH 起始地址与提供的
初始化脚本是相一致的,以免出错。总之,如果有需要,请提供初始化脚本,对存储系统进行初始化,
保证H-FLASHER 可以在用户指定的地址访问目标FLASH。
在烧写的时候,FLASH 驱动需要使用4KByte 的RAM 空间,所以需要用户指定一段>=4KByte 的RAM
空间。在用户指定RAM 开始地址后,H-FLASHER 会把FLASH 驱动下载到RAM START ADDRESS ~
(RAM START ADDRESS + 4K – 1)的范围内。FLASH 驱动可以下载到片内SRAM 或是片外SDRAM。
如果目标系统有片内SRAM, 建议用户使用片内SRAM,因为片内SRAM 的速度比外部SDRAM 快。
在设置初始化脚本的时候,请提供必需的初始化脚本,对存储系统进行初始化,保证用户指定的RAM
空间是可以被正确访问的。
文章评论(0条评论)
登录后参与讨论