||
使用的是S2C2410A.S启动代码,配置过程中可以通过MDK软件的界面进行配置,相应选项打勾,打勾的区别体现在代码模块的开关量上,比如内存控制模块要使用,当打上勾时,相应代码的MC_SETUP为1,启动代码执行相应的配置。否则,不执行。下面把有钩的代码粘贴如下:
; *** Startup Code (executed after Reset) ***
; 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
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400
Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size + USR_Stack_Size)
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
Stack_Top EQU Stack_Mem + Stack_Size
Heap_Size EQU 0x00000000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem SPACE Heap_Size
; Clock Management definitions
CLK_BASE EQU 0x4C000000 ; Clock Base Address
LOCKTIME_OFS EQU 0x00 ; LOCKTIME Offset
MPLLCON_OFS EQU 0x04 ; MPLLCON Offset
UPLLCON_OFS EQU 0X08 ; UPLLCON Offset
CLKCON_OFS EQU 0x0C ; CLKCON Offset
CLKSLOW_OFS EQU 0x10 ; CLKSLOW Offset
CLKDIVN_OFS EQU 0X14 ; CLDKIVN Offset
CAMDIVN_OFS EQU 0X18 ; CAMDIVN Offset
CLK_SETUP EQU 1//同下
MPLLCON_Val EQU 0x0005C080
UPLLCON_Val EQU 0x00028080
CLKCON_Val EQU 0x0007FFF0
CLKSLOW_Val EQU 0x00000004
LOCKTIME_Val EQU 0x00FFFFFF
CLKDIVN_Val EQU 0X00000000
;Interrupt definitions
INTOFFSET EQU 0X4A000014 ;Address of Interrupt offset Register
IntVT_SETUP EQU 1 //同下
IntVTAddress EQU 0x33FFFF20
; Watchdog Timer definitions
WT_BASE EQU 0x53000000 ; WT Base Address
WTCON_OFS EQU 0x00 ; WTCON Offset
WTDAT_OFS EQU 0x04 ; WTDAT Offset
WTCNT_OFS EQU 0x08 ; WTCNT Offset
WT_SETUP EQU 1 //同下
WTCON_Val EQU 0x00008021
WTDAT_Val EQU 0x00008000
; Memory Controller definitions
MC_BASE EQU 0x48000000 ; Memory Controller Base Address
;// <e> Memory Controller
MC_SETUP EQU 1 //值都是相同的,就这个开关量不同。
BANKCON0_Val EQU 0x00000700
BANKCON1_Val EQU 0x00000700
BANKCON2_Val EQU 0x00000700
BANKCON3_Val EQU 0x00000700
BANKCON4_Val EQU 0x00000700
BANKCON5_Val EQU 0x00000700
BANKCON6_Val EQU 0x00018008
BANKCON7_Val EQU 0x00018008
BWSCON_Val EQU 0x00000000
REFRESH_Val EQU 0x00ac0000
BANKSIZE_Val EQU 0x00000000
MRSRB6_Val EQU 0x00000020
MRSRB7_Val EQU 0x00000000
;// </e> End of MC
; I/O Ports definitions
PIO_BASE EQU 0x56000000 ; PIO Base Address
PCONA_OFS EQU 0x00 ; PCONA Offset
PCONB_OFS EQU 0x10 ; PCONB Offset
PCONC_OFS EQU 0x20 ; PCONC Offset
PCOND_OFS EQU 0x30 ; PCOND Offset
PCONE_OFS EQU 0x40 ; PCONE Offset
PCONF_OFS EQU 0x50 ; PCONF Offset
PCONG_OFS EQU 0x60 ; PCONG Offset
PCONH_OFS EQU 0x70 ; PCONH Offset
PCONJ_OFS EQU 0xD0 ; PCONJ Offset
PUPB_OFS EQU 0x18 ; PUPB Offset
PUPC_OFS EQU 0x28 ; PUPC Offset
PUPD_OFS EQU 0x38 ; PUPD Offset
PUPE_OFS EQU 0x48 ; PUPE Offset
PUPF_OFS EQU 0x58 ; PUPF Offset
PUPG_OFS EQU 0x68 ; PUPG Offset
PUPH_OFS EQU 0x78 ; PUPH Offset
PUPJ_OFS EQU 0xD8 ; PUPJ Offset
;// <e> I/O Configuration
PIO_SETUP EQU 1 //与没有勾的区别。
PIOA_SETUP EQU 0
PCONA_Val EQU 0x000003FF
PIOB_SETUP EQU 0
PCONB_Val EQU 0x000007FF
PUPB_Val EQU 0x00000000
PIOC_SETUP EQU 0
PCONC_Val EQU 0xAAAAAAAA
PUPC_Val EQU 0x00000000
PIOD_SETUP EQU 0
PCOND_Val EQU 0x00000000
PUPD_Val EQU 0x00000000
PIOE_SETUP EQU 0
PCONE_Val EQU 0x00000000
PUPE_Val EQU 0x00000000
PIOF_SETUP EQU 1//这个竟然是相同的。
PCONF_Val EQU 0x0000511A
PUPF_Val EQU 0x00000000
PIOG_SETUP EQU 0
PCONG_Val EQU 0x00000000
PUPG_Val EQU 0x00000000
PIOH_SETUP EQU 0
PCONH_Val EQU 0x000007FF
PUPH_Val EQU 0x00000000
PRESERVE8
AREA RESET, CODE, READONLY
ARM
Vector 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, FIQ_Addr
IF IntVT_SETUP <> 0
;Interrupt Vector Table Address
HandleEINT0 EQU IntVTAddress
HandleEINT1 EQU IntVTAddress +4
HandleEINT2 EQU IntVTAddress +4*2
HandleEINT3 EQU IntVTAddress +4*3
HandleEINT4_7 EQU IntVTAddress +4*4
HandleEINT8_23 EQU IntVTAddress +4*5
HandleReserved EQU IntVTAddress +4*6
HandleBATFLT EQU IntVTAddress +4*7
HandleTICK EQU IntVTAddress +4*8
HandleWDT EQU IntVTAddress +4*9
HandleTIMER0 EQU IntVTAddress +4*10
HandleTIMER1 EQU IntVTAddress +4*11
HandleTIMER2 EQU IntVTAddress +4*12
HandleTIMER3 EQU IntVTAddress +4*13
HandleTIMER4 EQU IntVTAddress +4*14
HandleUART2 EQU IntVTAddress +4*15
HandleLCD EQU IntVTAddress +4*16
HandleDMA0 EQU IntVTAddress +4*17
HandleDMA1 EQU IntVTAddress +4*18
HandleDMA2 EQU IntVTAddress +4*19
HandleDMA3 EQU IntVTAddress +4*20
HandleMMC EQU IntVTAddress +4*21
HandleSPI0 EQU IntVTAddress +4*22
HandleUART1 EQU IntVTAddress +4*23
;HandleReserved EQU IntVTAddress +4*24
HandleUSBD EQU IntVTAddress +4*25
HandleUSBH EQU IntVTAddress +4*26
HandleIIC EQU IntVTAddress +4*27
HandleUART0 EQU IntVTAddress +4*28
HandleSPI1 EQU IntVTAddress +4*39
HandleRTC EQU IntVTAddress +4*30
HandleADC EQU IntVTAddress +4*31
IRQ_Entry
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc}
ENDIF
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
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IF IntVT_SETUP <> 1
IRQ_Handler B IRQ_Handler
ENDIF
IF IntVT_SETUP <> 0 ;“<>”是判断不等于的意思。
IRQ_Handler B IRQ_Entry
ENDIF
FIQ_Handler B FIQ_Handler
; Memory Controller Configuration
IF MC_SETUP <> 0
MC_CFG
DCD BWSCON_Val
DCD BANKCON0_Val
DCD BANKCON1_Val
DCD BANKCON2_Val
DCD BANKCON3_Val
DCD BANKCON4_Val
DCD BANKCON5_Val
DCD BANKCON6_Val
DCD BANKCON7_Val
DCD REFRESH_Val
DCD BANKSIZE_Val
DCD MRSRB6_Val
DCD MRSRB7_Val
ENDIF
; Clock Management Configuration
IF CLK_SETUP <> 0
CLK_CFG
DCD LOCKTIME_Val
DCD CLKDIVN_Val
DCD MPLLCON_Val
DCD UPLLCON_Val
DCD CLKSLOW_Val
DCD CLKCON_Val
ENDIF
; Reset Handler
EXPORT Reset_Handler
Reset_Handler
IF WT_SETUP <> 0
LDR R0, =WT_BASE
LDR R1, =WTCON_Val
LDR R2, =WTDAT_Val
STR R2, [R0, #WTCNT_OFS]
STR R2, [R0, #WTDAT_OFS]
STR R1, [R0, #WTCON_OFS]
ENDIF
IF CLK_SETUP <> 0
LDR R0, =CLK_BASE
ADR R8, CLK_CFG
LDMIA R8, {R1-R6}
STR R1, [R0, #LOCKTIME_OFS]
STR R2, [R0, #CLKDIVN_OFS]
STR R3, [R0, #MPLLCON_OFS]
STR R4, [R0, #UPLLCON_OFS]
STR R5, [R0, #CLKSLOW_OFS]
STR R6, [R0, #CLKCON_OFS]
ENDIF
IF MC_SETUP <> 0
ADR R14, MC_CFG
LDMIA R14, {R0-R12}
LDR R14, =MC_BASE
STMIA R14, {R0-R12}
ENDIF
IF PIO_SETUP <> 0
LDR R14, =PIO_BASE
IF PIOA_SETUP <> 0
LDR R0, =PCONA_Val
STR R0, [R14, #PCONA_OFS]
ENDIF
IF PIOB_SETUP <> 0
LDR R0, =PCONB_Val
LDR R1, =PUPB_Val
STR R0, [R14, #PCONB_OFS]
STR R1, [R14, #PUPB_OFS]
ENDIF
IF PIOC_SETUP <> 0
LDR R0, =PCONC_Val
LDR R1, =PUPC_Val
STR R0, [R14, #PCONC_OFS]
STR R1, [R14, #PUPC_OFS]
ENDIF
IF PIOD_SETUP <> 0
LDR R0, =PCOND_Val
LDR R1, =PUPD_Val
STR R0, [R14, #PCOND_OFS]
STR R1, [R14, #PUPD_OFS]
ENDIF
IF PIOE_SETUP <> 0
LDR R0, =PCONE_Val
LDR R1, =PUPE_Val
STR R0, [R14, #PCONE_OFS]
STR R1, [R14, #PUPE_OFS]
ENDIF
IF PIOF_SETUP <> 0
LDR R0, =PCONF_Val
LDR R1, =PUPF_Val
STR R0, [R14, #PCONF_OFS]
STR R1, [R14, #PUPF_OFS]
ENDIF
IF PIOG_SETUP <> 0
LDR R0, =PCONG_Val
LDR R1, =PUPG_Val
STR R0, [R14, #PCONG_OFS]
STR R1, [R14, #PUPG_OFS]
ENDIF
IF PIOH_SETUP <> 0
LDR R0, =PCONH_Val
LDR R1, =PUPH_Val
STR R0, [R14, #PCONH_OFS]
STR R1, [R14, #PUPH_OFS]
ENDIF
ENDIF
; 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
MOV SP, R0
SUB SL, SP, #USR_Stack_Size
; 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
按照程序的分布,前面为各种变量的定义,后边为正式启动程序的开始。
前面各种变量的定义,根据程序的顺序主要包括几个部分:处理器的工作模式==>堆栈地址、尺寸的定义==>时钟寄存器及初始值的定义==>看门狗寄存器及其初始值定义==>内存控制器寄存器及其值定义==>端口寄存器及初始化值。
下面进行启动代码分析,一段段来。
PRESERVE8
AREA RESET, CODE, READONLY
ARM
第一句的意思是指示当前数据栈是8字节对齐的。
另外还有个REQUIRE8伪操作指示当前代码中要求数据栈8字节对齐。
AREA用来定义一个数据段或者代码段,可读写,按8字节对齐,不初始化为0。
另外还有SPACE也用于分配一块内存单元,但是是用0初始化。
第二句也就不用说了,是定义一个RESET段,其属性就是只读了。
第三句:ARM,在书上着实没找到,网上的一篇说到说是:以ARM模式运行程序。暂且这样认为!!
1,接下来是Vector这个标号下边的内容。
首先是LDR PC,Reset_Addr;…………
看到此时的LDR PC,Reset_Addr…………,查了一下书,看到了LDR的两个功能:
A,从内存中读取32位字数据到通用寄存器当中,并且对数据进行一定的操作。
B, 当PC作为目标寄存器的时候,指令可以实现程序的跳转的功能。以取得的地址作为目标地址,从那开始执行。注意,在ARM v5及以上的版本中,地址值的bit[0]用来确定程序状态,为1的话,为Thumb指令,为0时为ARM指令。在之前的版本中,bits[1:0]常被忽略,执行ARM状态。所以执行第一句话的时候就跳到Reset_Addr处执行程序了,后面依次列推。
各个中断地址的定义了。比如:HandleEINT1 EQU IntVTAddress +4
中断向量表的定义,而那个IntVTAddress EQU 0x33FFFF20
另外还有个地址也注意下:INTOFFSET EQU 0X4A000014
IntVTAddress的地址为什么是这样了?!不是很明白??估计是硬件设计时就已经决定好了。为什么是这个地址了??
2,然后是进入IRQ_Entry标号下的代码。在这个里边干的事为:
IRQ_Entry
sub sp,sp,#4 ;reserved for PC,sp - 4—>sp。
stmfd sp!,{r8-r9}
ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc}
分析如下图:
说明:首先假设SP指向12,SP-4后往上走到8,然后执行指令stmfd sp!,{r8-r9},该指令的在8处存入r9的值,然后在4处存入r8的值(即保护现场)。执行
ldr r9,=INTOFFSET,ldrr9,[r9]把此时产生中断的偏移量存入r9中,然后add r8,r8,r9,lsl #2,及等价于r8 = IntVTAddress +4*n,n为r9里的值,即找到中断发生的地址,然后r8内的值(中断服务程序的起始地址)存入堆栈的12中,即指令:ldr r8,[r8],
str r8,[sp,#8]。最后是恢复现场,关键的指令ldmfd sp!,{r8-r9,pc};依次把r8,r9的值返回去,同时,把PC指针指向中断服务程序的起始地址。
另外关于具体的指令解释,就得看《ARM体系结构与编程》——杜春雷。
接下来Reset_Addr DCD Reset_Handler;………………
以及Undef_Handler B Undef_Handler;…………之类的
我们都知道,发生中断时,硬件都是自动跳到一个地址,这个地址对应的是该中断类型,此时也不例外,程序刚开始时就有:LDR PC,Reset_Handler;这句指令的地址便是0x0,所以当复位发生时,PC便指向了0x00,便从该地址开始执行复位程序。文件中除了Reset(后边有标号)与IRQ(即标号IRQ_Entry下的代码)引起的中断有程序外,其他都是Undef_Handler B Undef_Handler这样的无限循环。
DCD与DCDU都是用于分配一段字内存单元,前者是字对齐的,后者不严格字对齐。
如data1 DCD 1,5,20;data1的值就为3个子单元,1,5,20.
data2 DCD memaddr + 4;分配一个字单元,值为程序中标号memaddr加上四个字节。
Reset_Addr DCD Reset_Handler的意思是为Reset_Addr分配一个字单元,其值为
Reset_Handle标号所对应的地址。
B与BL指令,都是跳转到目标出执行指令,不同的是BL同时还将当前PC寄存器的值保存到LR寄存器中。
Undef_Handler B Undef_Handler ;中断处理程序的入口地址//B为跳转指令,Undef_Handler为标号|| ??难道自己跳转到自己??事实是就是自己跳到自己,如果想不要死循环,有资料说这是可以人为修改添加代码的。
这样看来,进入中断的就清楚了。当有中断来的时候,PC跳到中断地址,即LDR PC,***_Addr;然后***_Addr放的是中断服务程序的地址。就可以执行中断服务程序了。
3,下面是各个模块的配置了。以一个模块为例就能说明问题。
EXPORT Reset_Handler
Reset_Handler
IF WT_SETUP <> 0
LDR R0, =WT_BASE
LDR R1, =WTCON_Val
LDR R2, =WTDAT_Val
STR R2, [R0, #WTCNT_OFS]
STR R2, [R0, #WTDAT_OFS]
STR R1, [R0, #WTCON_OFS]
ENDIF
首先,EXPORT 声明一个符号可以被其他文件引用。也就是下边标号Reset_Handler下的代码可以供其他文件使用。可见,这些初始化上后最先完成的。
其他过程就很easy了,先判断开关量是否满足条件,然后加载寄存器地址,然后把数据送给这些地址,仅此而已。通过这一部分,处理器的,时钟,看门狗,内存,IO口便初始化好了,当然可以通过软件界面来设置决定哪个模块要不要初始化。
4,这部分为设置各个模式下的堆栈地址。只要说明一部分就OK。
; 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
先设置最开始的栈顶: LDR R0, =Stack_Top,然后
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit;
MSR指令,用于将通用寄存器的内容或一个立即数传送到状态寄存器中。
CPSR_C,代表的是CPSR寄存器的bit[7:0];还有些其他的,看书了!!SUB指令,(书上写的很详细了),其实从这里就可以看出:配置完之后就是进入各模式,然后配置堆栈指针的初始指向地址。注意:每一个模式都有自己的SP指针,因此,RESET_HANDLER执行后的堆栈指针的地址分配便能知道了。
5,跳到主程序main();
; Enter the C code
IMPORT __main
LDR R0, =__main
BX R0
IMPORT __main
该语句是说明,__main在其他文件中定义,在这里引用。然后BX R0,BX指令跳转到指令中指定的目标地址,BX {<cond>} <Rm>,cond为条件,当没有时,无条件执行。
6,最后用户初始化堆与栈
; 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
首先定义一个名为|.text|的段,这个段代表的是:|.text|表示C语言编译器产生的代码段或者是与C语言库相关的代码段,下边的语句都属于|.test|段,。
从BX LR,可以看出来(因为这个用于子程序的返回),__user_initial_stackheap下边的是这个函数的内容,然后在这个函数里把堆与栈的首地址或者末地址给寄存器,R0,1,2,3.而IMPORT __use_two_region_memory是告诉是一段模式,还是两端(包括堆,栈)模式!!这里显然是两段模式。
用来做什么了??不是很懂??有待进一步学习。。。
通过一篇博文《基于TQ2440的ARM启动代码注释分析(基于MDK)》有点懂了。
首先解决这个段是来干什么的??
用户设置堆栈程序(C外部接口:用于动态申请内存使用),通过这段程序,设置堆栈地址,即开辟的这个空间用来放用户程序中用于动态分配到的空间的,如new。其分配的图(两段模式与一段模式)如下:
说明:R0,R1,R2,R3中的返回值是由你使用的一段存储模式还是二段存储模式决定。
1. 一段模式中R1比R0大。R2和R3被忽略了。
2. 二段模式中R0和R2用来初始化堆,R1和R3用来初始化栈。R2 >= R0,R3 < R1
然后解决第二个问题:系统怎么调用__user_initial_stackheap这个函数来进行堆栈地址的赋值的??
1,没分散加载文件。系统默认通过|Image$$ZI$$limit|来调用的。
2,有分散加载文件。直接引用原文内容,说得很清楚了。
默认情况下__user_initial_stackhep()使用的是符号|Image$$ZI$$limit|的值。这个符号在用户使用分散加载文件时不会产生。然而,C库提供了比较折中的方法:利用分散加载文件中的一些信息来执行这个函数。
注意:
如果你重新执行__user_initial_stackhep()函数,那么其他的库的执行将被忽略。
选择使用一段存储模式:
定义一段特殊的执行域在你的分散加载文件中。使用这种符号:ARM_LIB_STACKHEAP,并使用EMPTY 属性。 这样库管理器就选择了一个把这个域当作堆和栈合并在一起的__user_initial_stackhep()函数。在这个函数中使用了Image$$ARM_LIB_STACKHEAP$$Base和Image$$ARM_LIB_STACKHEAP$$ZI$$Limit符号
选择使用二段存储模式:
定义两个特殊的执行域再你的分散加载文件中。使用这两种符号:ARM_LIB_STACK, ARM_LIB_HEAP。并且两个段都要使用EMPTY属性。这样库管理器就会选择使用符号:Image$$ARM_LIB_HEAP$$Base mage$$ARM_LIB_STACK$$ZI$$ZI$$limit、Image$$ARM_LIB_STACK$$Base、Image$$ARM_LIB_STACK$$ZI$$Limit的__user_initial_stackhep()函数。
例子:
FLASH_LOAD 0x0000 0x00200000
{
VECTORS +0 0x400
{
* (:gdef:__vectab_stack_and_reset, +FIRST)
; 其他域可以放在这里
}
;; 最高256 异常深度 (256*4bytes == 1024 == 0x400)
CODE 0x400 FIXED
{
* (+RO)
}
DATA 0x20000000 0x00200000
{
* (+RW, +ZI)
}
;; 堆开始于 1MB ,向高地址生长
ARM_LIB_HEAP 0x20100000 EMPTY 0x100000-0x8000
{}
;; 堆安排在 2MB RAM的最高地址处
;; 向低地址生长,空间为32KB
ARM_LIB_STACK 0x20200000 EMPTY - 0x8000
{}
}