【网友经验分享】飞思卡尔Cortex M0+系列 Keil MDK启动文件详解
本帖最后由 FSL_TICS_ZJJ 于 2014-9-11 14:56 编辑入手飞思卡尔,从启动文件学起吧,这是查阅官方帮助文档整理的版本,是已知目前最详细的启动文件介绍,结合了MAP文件和SCT文件
如有错误还请指正{:tongue:}
在其他平台,启动文件功能基本一致,这里是我整理的MDK下的版本,修正了网上其它版本的一些错误,描述更详细
启动文件
;/*****************************************************************************
; * @file: startup_MKL02Z4.s
; * @purpose: CMSIS Cortex-M0plus Core Device Startup File for the
; * MKL02Z4
; * @version: 1.0
; * @date: 2012-10-4
; *
; * Copyright: 1997 - 2013 Freescale Semiconductor, Inc. All Rights Reserved.
;*
; *------- <<< Use Configuration Wizard in Context Menu >>> ------------------
; *
; *****************************************************************************/
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x00000100
;EQU定义符号指示Stack大小
AREA STACK, NOINIT, READWRITE, ALIGN=3
;AREA 通知汇编器汇编一段新的代码或数据节(section)
;STACK 为AREA所定义的新的节的节名称(sectionname)
;NOINIT 指示这段数据节不初始化或初始化为0,以SPACE等指示的空间初始化为0,但不能初始化为其它
; 备注:即这段不能指定初始化值,如果不加NOINIT的话,MAP文件SPACE的数据类型为Data,为RW Data
; 如果加NOINIT的话,MAP文件SPACE的数据类型为Zero,为ZI Data
;READWRITE 指示该节类型为RW
;ALIGN=3 指示该节对齐到8字节boundary,即为双字地址
; 备注:在MAP文件中,可以发现是通过添加PAD类型的数据进行节的对齐
; PAD取何值可以指定
Stack_Mem SPACE Stack_Size
;Stack_Mem 为一个标签(label),标签值为在当前节中的地址
;SPACE 指示(directive)一块内存(memory),这块内存所在节的特性由上面的AREA指定
__initial_sp
;__initial_sp 为上面SPACE块的顶端地址+1,即说明堆栈的初始值为顶端地址,堆栈是向下生长的满栈
; 备注:因为CM的入栈操作是,*(--R13)=R0类似的一种方式,R13指向最后的数据
; <h> Heap Configuration
; <o>Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000100
;定义符号指示Heap大小
AREA HEAP, NOINIT, READWRITE, ALIGN=3
;定义一个新的节,节名称为HEAP,其它与STACK相同
;EXPORT __heap_base
__heap_base
;定义一个标签,指示HEAP的基地址,这个标签只用在使用MICROLIB时被EXPORT,即只有这时有效
;使用MICROLIB时,这个标签被EXPORT,类似于C语言中的extern,但只有当HEAP被使用的时候
; 才出现在MAP文件中,如在main()中可以这样来访问__heap_base
; void main(void)
; {
; extern int __heap_base;
; static int HEAPBASE; //定义静态变量,在内存中有固定的存储空间
; ......
; HEAPBASE = __heap_base; //读取HEAP的即地址,只在MICROLIB时有效
; ......
; }
;如果要在不使用MICROLIB时,读取__heap_base, 那只需要在__heap_base前添加EXPORT __heap_base
Heap_Mem SPACE Heap_Size
;EXPORT Heap_Mem
;定义HEAP的内存段,标签为Heap_Mem,将Heap_Mem EXPORT之后,可以这样访问:
; extern int Heap_Mem;
; static int* pHEAPMEM;
; pHEAPMEM = (int*)Heap_Mem;
__heap_limit
;定义一个标签,HEAP的地址范围为__heap_base ~ (__heap_limit - 1)
;这里结合MAP文件外插上几句
;1. 当:STACK 所在节属性为 AREA STACK, NOINIT, READWRITE, ALIGN=3
; HEAP所在节属性为 AREA HEAP,NOINIT, READWRITE, ALIGN=3
; 时,MAP文件的执行时期RAM镜像为
; Execution Region RW_IRAM2 (Base: 0x20000000, Size: 0x00000268, Max: 0x00000c00, ABSOLUTE)
;
; Base Addr Size Type Attr Idx E Section Name Object
;
; 0x20000000 0x00000004 Data RW 4 .data system_mkl02z4.o
; 0x20000004 0x00000004 Data RW 61 .data blinky.o
; 0x20000008 0x00000060 Zero RW 139 .bss c_p.l(libspace.o)
; 0x20000068 0x00000100 Zero RW 48 HEAP startup_mkl02z4.o
; 0x20000168 0x00000100 Zero RW 47 STACK startup_mkl02z4.o
;
; 可见,在程序运行时,数据在内存中存放的顺序为有初始化的Data类型,和0初始化的Zero类型
; 此时,HEAP向上增长,STACK向下生长,二者的方向是相向的
; 全局变量,如果没有指定初始值,如static int gTestData; 是存放在.dada节,而不是.bss
;
;2. 当:STACK 所在节属性为 AREA STACK, READWRITE, ALIGN=3
; HEAP所在节属性为 AREA HEAP,NOINIT, READWRITE, ALIGN=3
; 时,MAP文件的执行时期RAM镜像为
; Execution Region RW_IRAM2 (Base: 0x20000000, Size: 0x00000268, Max: 0x00000c00, ABSOLUTE)
;
; Base Addr Size Type Attr Idx E Section Name Object
;
; 0x20000000 0x00000004 Data RW 4 .data system_mkl02z4.o
; 0x20000004 0x00000004 Data RW 61 .data blinky.o
; 0x20000008 0x00000100 Data RW 47 STACK startup_mkl02z4.o
; 0x20000108 0x00000060 Zero RW 139 .bss c_p.l(libspace.o)
; 0x20000168 0x00000100 Zero RW 48 HEAP startup_mkl02z4.o
;
; 此时HEAP向上增长,STACK向下生长,二者的方向是相背的
; 同时可以看出,不用NOINIT修饰时,STACK的数据类型为Data,是一种RW Type,否则是一种ZI Type
;
;部分细节看参见Linker User Guide: Type 2 image, one load region and non-contiguous execution regions
PRESERVE8
;通知链接器(linker)当前文件是以堆栈8字节对齐,使用PRESERVE8的话,说明我们现在的文件堆栈是以8字节对齐的
;这样的话,这里的函数可以调用由REQUIRE8指示的文件的函数,而不引起错误,如LDRD and STRD指令,因为
;LDRD and STRD instructions (double-word transfers) only work correctly if the address they access
;is 8-byte aligned.
THUMB
;通知编译器采用THUMB指令集
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
;指示当前节为RESET,节的属性为DATA(Contains data, not instructions),RO
EXPORT__Vectors
EXPORT__Vectors_End
EXPORT__Vectors_Size
;全局变量__Vectors,__Vectors_End,和__Vectors_Size
;分散加载文件SCT为
; LR_IROM1 0x00000000 0x00008000{ ; load region size_region
; ER_IROM1 0x00000000 0x00008000{ ; load address = execution address
; *.o (RESET, +First) ; +First指示执行时RESET节置于最开始
; *(InRoot$Sections) ; InRoot$Sections指代所有必须放在根区域的节
; ; 如__main.o, __scatter*.o,
; ;__dc*.o, and * Region$Table
; .ANY (+RO) ; Read only的数据执行时在这里
; }
; RW_IRAM1 0x1FFFFC00 0x00000400{ ; RW data
; .ANY (+RW +ZI)
; }
; RW_IRAM2 0x20000000 0x00000C00{
; .ANY (+RW +ZI)
; }
; }
;我们可以这样更改分散加载文件为
; LR_IROM1 0x00000000 0x00008000{ ; load region size_region
; ER_IROM1 0x00000000 0x00008000{; load address = execution address
; *.o (RESET, +First)
; *(InRoot$Sections)
; .ANY (+RO)
; }
; RW_IRAM1 0x1FFFFC00 0x00000400{; RW data
; .ANY (+ZI)
; }
; RW_IRAM2 0x20000000 0x00000C00{
; .ANY (+RW)
; *.o (.bss)
; }
; }
;这样,执行时候的内存镜像变成这样子了,我们利用起来了两块RAM区的特点
; Execution Region RW_IRAM1 (Base: 0x1ffffc00, Size: 0x00000200, Max: 0x00000400, ABSOLUTE)
;
; Base Addr Size Type Attr Idx E Section Name Object
;
; 0x1ffffc00 0x00000100 Zero RW 48 HEAP startup_mkl02z4.o
; 0x1ffffd00 0x00000100 Zero RW 47 STACK startup_mkl02z4.o
;
;
; Execution Region RW_IRAM2 (Base: 0x20000000, Size: 0x00000068, Max: 0x00000c00, ABSOLUTE)
;
; Base Addr Size Type Attr Idx E Section Name Object
;
; 0x20000000 0x00000004 Data RW 4 .data system_mkl02z4.o
; 0x20000004 0x00000004 Data RW 61 .data blinky.o
; 0x20000008 0x00000060 Zero RW 139 .bss c_p.l(libspace.o)
__Vectors DCD __initial_sp ; Top of Stack
; DCD 申请4字节的空间,存放数据
; 由于分散加载文件SCT中的*.o (RESET, +First)属性
; RESET节被放在了LR_IROM1的开始位置,即__initial_sp存放在0地址
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD 0; Reserved
DCD 0; Reserved
DCD 0; Reserved
DCD 0; Reserved
DCD 0; Reserved
DCD 0; Reserved
DCD 0; Reserved
DCD SVC_Handler ; SVCall Handler
DCD 0; Reserved
DCD 0; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD Reserved16_IRQHandler; Reserved interrupt 16
DCD Reserved17_IRQHandler; Reserved interrupt 17
DCD Reserved18_IRQHandler; Reserved interrupt 18
DCD Reserved19_IRQHandler; Reserved interrupt 19
DCD Reserved20_IRQHandler; Reserved interrupt 20
DCD FTFA_IRQHandler ; FTFA command complete/read collision interrupt
DCD LVD_LVW_IRQHandler; Low Voltage Detect, Low Voltage Warning
DCD Reserved23_IRQHandler; Reserved interrupt 23
DCD I2C0_IRQHandler; I2C0 interrupt
DCD I2C1_IRQHandler; I2C1 interrupt
DCD SPI0_IRQHandler; SPI0 interrupt
DCD Reserved27_IRQHandler; Reserved interrupt 27
DCD UART0_IRQHandler; UART0 status/error interrupt
DCD Reserved29_IRQHandler; Reserved interrupt 29
DCD Reserved30_IRQHandler; Reserved interrupt 30
DCD ADC0_IRQHandler; ADC0 interrupt
DCD CMP0_IRQHandler; CMP0 interrupt
DCD TPM0_IRQHandler; TPM0 fault, overflow and channels interrupt
DCD TPM1_IRQHandler; TPM1 fault, overflow and channels interrupt
DCD Reserved35_IRQHandler; Reserved interrupt 35
DCD Reserved36_IRQHandler; Reserved interrupt 36
DCD Reserved37_IRQHandler; Reserved interrupt 37
DCD Reserved38_IRQHandler; Reserved interrupt 38
DCD Reserved39_IRQHandler; Reserved interrupt 39
DCD Reserved40_IRQHandler; Reserved interrupt 40
DCD Reserved41_IRQHandler; Reserved interrupt 41
DCD Reserved42_IRQHandler; Reserved interrupt 42
DCD MCG_IRQHandler; MCG interrupt
DCD LPTimer_IRQHandler; LPTimer interrupt
DCD Reserved45_IRQHandler; Reserved interrupt 45
DCD PORTA_IRQHandler; Port A interrupt
DCD PORTB_IRQHandler; Port B interrupt
__Vectors_End
;这里__Vectors_End的值为0x000000c0 (在MAP文件里查看)
__Vectors_Size EQU __Vectors_End - __Vectors ;计算中断向量表的大小
; 以下为Freescale特有的配置方式(我的理解,在ST中不是采用的这种配置方法)
; Flash配置区域在Flash的0x0000_0400开始的空间,一共16字节
; 关于Flash配置区域在帖子 http://www.amobbs.com/thread-5588189-1-1.html 中有描述
; 以下定义的变量值,在下面有采用,这边略过Flash配置字的含义,详见Reference Mannual
;
;
; <h> Flash Configuration
; <i> 16-byte flash configuration field that stores default protection settings (loaded on reset)
; <i> and security information that allows the MCU to restrict acces to the FTFL module.
; <h> Backdoor Comparison Key
; <o0>Backdoor Key 0<0x0-0xFF:2>
; <o1>Backdoor Key 1<0x0-0xFF:2>
; <o2>Backdoor Key 2<0x0-0xFF:2>
; <o3>Backdoor Key 3<0x0-0xFF:2>
; <o4>Backdoor Key 4<0x0-0xFF:2>
; <o5>Backdoor Key 5<0x0-0xFF:2>
; <o6>Backdoor Key 6<0x0-0xFF:2>
; <o7>Backdoor Key 7<0x0-0xFF:2>
BackDoorK0 EQU 0xFF
BackDoorK1 EQU 0xFF
BackDoorK2 EQU 0xFF
BackDoorK3 EQU 0xFF
BackDoorK4 EQU 0xFF
BackDoorK5 EQU 0xFF
BackDoorK6 EQU 0xFF
BackDoorK7 EQU 0xFF
; </h>
; <h> Program flash protection bytes (FPROT)
; <i> Each program flash region can be protected from program and erase operation by setting the associated PROT bit.
; <i> Each bit protects a 1/32 region of the program flash memory.
; <h> FPROT0
; <i> Program flash protection bytes
; <i> 1/32 - 8/32 region
; <o.0> FPROT0.0
; <o.1> FPROT0.1
; <o.2> FPROT0.2
; <o.3> FPROT0.3
; <o.4> FPROT0.4
; <o.5> FPROT0.5
; <o.6> FPROT0.6
; <o.7> FPROT0.7
nFPROT0 EQU 0x00
FPROT0 EQU nFPROT0:EOR:0xFF
; </h>
; <h> FPROT1
; <i> Program Flash Region Protect Register 1
; <i> 9/32 - 16/32 region
; <o.0> FPROT1.0
; <o.1> FPROT1.1
; <o.2> FPROT1.2
; <o.3> FPROT1.3
; <o.4> FPROT1.4
; <o.5> FPROT1.5
; <o.6> FPROT1.6
; <o.7> FPROT1.7
nFPROT1 EQU 0x00
FPROT1 EQU nFPROT1:EOR:0xFF
; </h>
; <h> FPROT2
; <i> Program Flash Region Protect Register 2
; <i> 17/32 - 24/32 region
; <o.0> FPROT2.0
; <o.1> FPROT2.1
; <o.2> FPROT2.2
; <o.3> FPROT2.3
; <o.4> FPROT2.4
; <o.5> FPROT2.5
; <o.6> FPROT2.6
; <o.7> FPROT2.7
nFPROT2 EQU 0x00
FPROT2 EQU nFPROT2:EOR:0xFF
; </h>
; <h> FPROT3
; <i> Program Flash Region Protect Register 3
; <i> 25/32 - 32/32 region
; <o.0> FPROT3.0
; <o.1> FPROT3.1
; <o.2> FPROT3.2
; <o.3> FPROT3.3
; <o.4> FPROT3.4
; <o.5> FPROT3.5
; <o.6> FPROT3.6
; <o.7> FPROT3.7
nFPROT3 EQU 0x00
FPROT3 EQU nFPROT3:EOR:0xFF
; </h>
; </h>
; </h>
; <h> Flash nonvolatile option byte (FOPT)
; <i> Allows the user to customize the operation of the MCU at boot time.
; <o.0>LPBOOT0
; <0=> Core and system clock divider (OUTDIV1) is 0x7 (divide by 8) or 0x3 (divide by 4)
; <1=> Core and system clock divider (OUTDIV1) is 0x1 (divide by 2) or 0x0 (divide by 1)
; <o.4>LPBOOT1
; <0=> Core and system clock divider (OUTDIV1) is 0x7 (divide by 8) or 0x1 (divide by 2)
; <1=> Core and system clock divider (OUTDIV1) is 0x3 (divide by 4) or 0x0 (divide by 1)
; <o.2>NMI_DIS
; <0=> NMI interrupts are always blocked
; <1=> NMI pin/interrupts reset default to enabled
; <o.3>RESET_PIN_CFG
; <0=> RESET pin is disabled following a POR and cannot be enabled as RESET function
; <1=> RESET pin is dedicated
; <o.3>FAST_INIT
; <0=> Slower initialization
; <1=> Fast Initialization
FOPT EQU 0xFF
; </h>
; <h> Flash security byte (FSEC)
; <i> WARNING: If SEC field is configured as "MCU security status is secure" and MEEN field is configured as "Mass erase is disabled",
; <i> MCU's security status cannot be set back to unsecure state since Mass erase via the debugger is blocked !!!
; <o.0..1> SEC
; <2=> MCU security status is unsecure
; <3=> MCU security status is secure
; <i> Flash Security
; <i> This bits define the security state of the MCU.
; <o.2..3> FSLACC
; <2=> Freescale factory access denied
; <3=> Freescale factory access granted
; <i> Freescale Failure Analysis Access Code
; <i> This bits define the security state of the MCU.
; <o.4..5> MEEN
; <2=> Mass erase is disabled
; <3=> Mass erase is enabled
; <i> Mass Erase Enable Bits
; <i> Enables and disables mass erase capability of the FTFL module
; <o.6..7> KEYEN
; <2=> Backdoor key access enabled
; <3=> Backdoor key access disabled
; <i> Backdoor key Security Enable
; <i> These bits enable and disable backdoor key access to the FTFL module.
FSEC EQU 0xFE
; </h>
;
; 在这里,定义了一个新的节,名称是.ARM,位于地址0x400,在MAP文件里是这样的
; ......
; 0x000003cc 0x00000030 Data RO 263 Region$Table anon$obj.o
; 0x000003fc 0x00000004 PAD
; 0x00000400 0x00000010 Code RO 50 .ARM.__at_0x400 startup_mkl02z4.o
; 0x00000410 0x00000174 Code RO 59 .text blinky.o
; 0x00000584 0x0000015a Code RO 93 .text c_p.l(aeabi_sdiv.o)
; ......
; 可见PAD补充字节空间,.ARM.__at_0x400放在了0x0400地址,而这些地址存放的数据,是CODE,RO类型
; 下面的DCB填充了这16个字节
IF :LNOT::DEF:RAM_TARGET
; :DEF:A 语句,如果A定义,返回真,否则返回假
; :LNOT:A 语句,是对A的逻辑取反
; 结合上面的 IF,所以以下代码尽在RAM_TARGET没定义的时候会产生
; 如果在RAM中调试的话,应该定义RAM_TARGET,那样的话也就不需要FLASH配置字了
;
AREA |.ARM.__at_0x400|, CODE, READONLY
; 定义一个节,存放配置数据,单片机复位后,FLASH控制器加载这些配置
;
DCB BackDoorK0, BackDoorK1, BackDoorK2, BackDoorK3
DCB BackDoorK4, BackDoorK5, BackDoorK6, BackDoorK7
DCB FPROT0, FPROT1, FPROT2, FPROT3
DCB FSEC, FOPT, 0xFF, 0xFF
ENDIF
; 下面是程序端的开始
AREA |.text|, CODE, READONLY
; AREA定义.text节存放代码,因为.text不是有效的变量名,所以加||限定
; Reset Handler
Reset_Handler PROC
; 同FUNCTION,标记一个函数的开始
EXPORTReset_Handler
; 弱引用,通知链接器,其它文件中的同名函数的优先级高于这个函数,即只有当其它文件中无
; Reset_Handler函数时,Reset_Handler()才指向这里
IMPORTSystemInit
; 告诉链接器到其它文件中查找SystemInit函数
IMPORT__main
; 告诉链接器到其它文件中查找__main函数
LDR R0, =SystemInit
; 执行SystemInit();
BLX R0
LDR R0, =__main
; 执行__main(); __main()调用main(),因而正常情况下不返回
BX R0
ENDP
; 标记函数的结束
; Dummy Exception Handlers (infinite loops which can be modified)
; 异常函数,弱引用,需要在其它文件中自定义相同名称的函数,以进行异常的处理
;
NMI_Handler PROC
EXPORTNMI_Handler
B . ; B . 表示跳转到当前地址
ENDP
HardFault_Handler\
PROC
EXPORTHardFault_Handler
B .
ENDP
SVC_Handler PROC
EXPORTSVC_Handler
B .
ENDP
PendSV_HandlerPROC
EXPORTPendSV_Handler
B .
ENDP
SysTick_Handler PROC
EXPORTSysTick_Handler
B .
ENDP
Default_Handler PROC
EXPORTReserved16_IRQHandler
EXPORTReserved17_IRQHandler
EXPORTReserved18_IRQHandler
EXPORTReserved19_IRQHandler
EXPORTReserved20_IRQHandler
EXPORTFTFA_IRQHandler
EXPORTLVD_LVW_IRQHandler
EXPORTReserved23_IRQHandler
EXPORTI2C0_IRQHandler
EXPORTI2C1_IRQHandler
EXPORTSPI0_IRQHandler
EXPORTReserved27_IRQHandler
EXPORTUART0_IRQHandler
EXPORTReserved29_IRQHandler
EXPORTReserved30_IRQHandler
EXPORTADC0_IRQHandler
EXPORTCMP0_IRQHandler
EXPORTTPM0_IRQHandler
EXPORTTPM1_IRQHandler
EXPORTReserved35_IRQHandler
EXPORTReserved36_IRQHandler
EXPORTReserved37_IRQHandler
EXPORTReserved38_IRQHandler
EXPORTReserved39_IRQHandler
EXPORTReserved40_IRQHandler
EXPORTReserved41_IRQHandler
EXPORTReserved42_IRQHandler
EXPORTMCG_IRQHandler
EXPORTLPTimer_IRQHandler
EXPORTReserved45_IRQHandler
EXPORTPORTA_IRQHandler
EXPORTPORTB_IRQHandler
EXPORTDefaultISR
Reserved16_IRQHandler
Reserved17_IRQHandler
Reserved18_IRQHandler
Reserved19_IRQHandler
Reserved20_IRQHandler
FTFA_IRQHandler
LVD_LVW_IRQHandler
Reserved23_IRQHandler
I2C0_IRQHandler
I2C1_IRQHandler
SPI0_IRQHandler
Reserved27_IRQHandler
UART0_IRQHandler
Reserved29_IRQHandler
Reserved30_IRQHandler
ADC0_IRQHandler
CMP0_IRQHandler
TPM0_IRQHandler
TPM1_IRQHandler
Reserved35_IRQHandler
Reserved36_IRQHandler
Reserved37_IRQHandler
Reserved38_IRQHandler
Reserved39_IRQHandler
Reserved40_IRQHandler
Reserved41_IRQHandler
Reserved42_IRQHandler
MCG_IRQHandler
LPTimer_IRQHandler
Reserved45_IRQHandler
PORTA_IRQHandler
PORTB_IRQHandler
DefaultISR
;上面这些函数的地址都是一样的,即标号的值都相同,但都是弱引用
B .
ENDP
ALIGN
; ALIGN不指定参数时,将当前地址对起到4字节,填0或NOP,或其它给定值
; User Initial Stack & Heap
IF :DEF:__MICROLIB
; 如果定义使用MICROLIB,则将下面的标号声明为全局变量,供MICROLIB使用
;
EXPORT__initial_sp
EXPORT__heap_base
EXPORT__heap_limit
ELSE
IMPORT__use_two_region_memory
; 指定存储器模式为双区模式
; 单区模型:(r0, r1)是单个堆栈和堆区,r1大于r0,忽略r2和r3
; 堆和栈共用一块内存区域
; 双区模型:(r0, r2)是初始堆,(r3, r1)是初始栈
; 堆和栈分别指定了单独的内存区域
;
EXPORT__user_initial_stackheap
; 声明一个全局标号 __user_initial_stackheap作为函数使用
__user_initial_stackheap
; C标准库调用__user_initial_stackheap()函数,获得堆栈信息
; 这里没有使用PROC标记
;
LDR R0, =Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem +Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
里面我有一个没搞懂的地方,就是最后的__user_initial_stackheap为什么不加PROC标记
KL02官方Keil MDK例程文件
附图1:堆栈分配
附图2:加载镜像和执行镜像
启动文件很长啊需要仔细看下
感谢 厉害,这个我还没仔细看过呢,有空了跟着楼主学习下 楼楼能否将指令集也传上来下呢 这个···强烈支持一下 ,慢慢消化 楼主的资料太好了,正是我需要的。 柯南兄的资料室一如既往的犀利啊!! abszy 发表于 2014-8-25 16:09
启动文件很长啊需要仔细看下
感谢
其实不是很长,因为我加了好多注释 浪里白条 发表于 2014-8-25 16:11
厉害,这个我还没仔细看过呢,有空了跟着楼主学习下
我也是边查资料边整理的,整理了一个下午,嘿嘿 sunnyqd 发表于 2014-8-25 16:23
我也是边查资料边整理的,整理了一个下午,嘿嘿
佩服,要弄懂这些语句,必须要有一定的功底才行的。 一直没搞懂启动文件是怎么工作的
好像全部是汇编 浪里白条 发表于 2014-8-25 16:15
楼楼能否将指令集也传上来下呢
我找找我电脑上有没有 不错,收藏,鼎鼎! 浪里白条 发表于 2014-8-25 16:15
楼楼能否将指令集也传上来下呢
指令集从ARM官网上下载下来了 cn_x 发表于 2014-8-25 16:17
这个···强烈支持一下 ,慢慢消化
多谢支持 sunnyqd 发表于 2014-8-25 16:55
指令集从ARM官网上下载下来了
看到了的,给你顶起了,多谢楼楼了。 laotui 发表于 2014-8-25 16:19
楼主的资料太好了,正是我需要的。
我也是刚好想看,以前这方面都不懂,整理了一遍就都懂啦 sdlibin007 发表于 2014-8-25 16:19
柯南兄的资料室一如既往的犀利啊!!
还第一次被叫柯南兄。。 xlxbangel 发表于 2014-8-25 16:25
一直没搞懂启动文件是怎么工作的
详细的看上一遍基本都懂了 sunnyqd 发表于 2014-8-25 16:57
还第一次被叫柯南兄。。
嗯??难道改叫柯南姐??O(∩_∩)O~ sunnyqd 发表于 2014-8-25 16:56
我也是刚好想看,以前这方面都不懂,整理了一遍就都懂啦
不知道KEIL哪个版本能支持到KL、KE系列?我的芯片列表里没有这两种。 laotui 发表于 2014-8-25 16:59
不知道KEIL哪个版本能支持到KL、KE系列?我的芯片列表里没有这两种。
4.72我的是 sdlibin007 发表于 2014-8-25 16:59
嗯??难道改叫柯南姐??O(∩_∩)O~
嗯,南哥挺好的{:shy:} sunnyqd 发表于 2014-8-25 17:01
4.72我的是
谢谢,我用的4.7。 sunnyqd 发表于 2014-8-25 17:01
嗯,南哥挺好的
你真是女生啊?? sdlibin007 发表于 2014-8-25 17:11
你真是女生啊??
。。。。你见过女生写这个么。。。 强烈支持一下 ,很少有人这么弄了。 jianbo513 发表于 2014-8-25 18:08
强烈支持一下 ,很少有人这么弄了。
我是恰好自己学了一下,终于都搞懂了,嘿嘿 对这个不是很了解了。 sunnyqd 发表于 2014-8-25 17:35
。。。。你见过女生写这个么。。。
论坛里面不是还有女生招亲的么?? 谢谢分享 sdlibin007 发表于 2014-8-25 19:12
论坛里面不是还有女生招亲的么??
哈哈 sdlibin007 发表于 2014-8-25 19:12
论坛里面不是还有女生招亲的么??
这个明显不一样好不。。 楼主要继续更下去哦 看楼楼的签名,女娃儿明显做不来这些事情。 湛泸骏驰 发表于 2014-8-25 19:50
楼主要继续更下去哦
码这么一篇累啊。。 浪里白条 发表于 2014-8-25 19:51
看楼楼的签名,女娃儿明显做不来这些事情。
那也可能是一个强悍的女汉子 谢谢分享,楼主很细心。
xlxbangel 发表于 2014-8-25 19:40
哈哈
前段时间确实见到有个帖子招亲,O(∩_∩)O~ 注释的那叫相当详细啊 chenguanghua 发表于 2014-8-25 20:27
注释的那叫相当详细啊
加完这一遍注释之后,我是全部了解啦,嘿嘿,本来也是一知半解 有空学习了,谢谢 跟着楼主好好学习。 __user_initial_stackheap楼主可以看下ARM官方的doc,直接进主页的doc下面搜索关键字看一下
一般在高版本中已经不需要重写他了,内部有各种实现方式,能够自动检测
可以通过分散加载描述文件或者 Image$$ZI$$Limit 的值等
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0349bc/Cihhdahf.html
2.10.5. __user_initial_stackheap()
如果您使用的是旧式源代码,则可能会看到 __user_initial_stackheap()。 这是旧函数,支持它只是为了与旧式源代码向后兼容。 其当前等效项是 __user_setup_stackheap()。
从 RVCT v2.x 及更低版本移植到 RVCT v4.0
在 RVCT 2.x 及更低版本中,__user_initial_stackheap() 的缺省实现使用符号 Image$$ZI$$Limit 的值。 如果链接器使用分散加载描述文件(使用 --scatter 命令行选项指定),则不会定义该符号;因此,如果使用的是分散加载描述文件,则必须重新实现 __user_initial_stackheap(),否则,链接步骤将会失败。
另外,您也可以使用 __user_setup_stackheap()(而不是 __user_initial_stackheap())来升级源代码。
从 RVCT v3 移植到 RVCT v4.0
在 RVCT v3.x 和更高版本中,库包含更多的 __user_initial_stackheap() 实现,并且可通过分散加载描述文件中提供的信息自动选择正确的实现。 这意味着,如果使用的是分散加载文件,则不需要重新实现该函数。 有关详细信息,请参阅使用分散加载描述文件。
语法
extern __value_in_regs struct __user_initial_stackheap __user_initial_stackheap(unsigned R0, unsigned SP, unsigned R2, unsigned SL);
用法
__user_initial_stackheap() 返回:
r0 中的堆基址
r1 中的堆栈基址,即堆栈区中的最高地址
r2 中的堆限制
r3 中的堆栈限制,即堆栈区中的最低地址。
如果重新实现了此函数,则必须满足下列条件:
最多使用堆栈中的 88 个字节
不能破坏 r12 (ip) 以外的寄存器
保持堆的 8 字节对齐方式。
对于缺省单区模型,将忽略 r2 和 r3 中的值,并且 r0 和 r1 之间的所有内存始终可供堆使用。 对于双区模型,堆限制是由 r2 设置的,堆栈限制是由 r3 设置的。
调用 __main() 时的 sp (r13) 值将作为 r1 中的自变量进行传递。 __user_initial_stackheap() 的缺省实现(使用半主机 SYS_HEAPINFO)是由 sys_stackheap.o 模块中的库提供的。
要创建从执行环境中继承 sp 并且不使用堆的 __user_initial_stackheap() 版本,请将 r0 和 r2 设置为 r3 的值并返回。 有关详细信息,请参阅__user_setup_stackheap()。
rt_misc.h 中的 __initial_stackheap 的定义是:
struct __initial_stackheap{
unsigned heap_base, stack_base, heap_limit, stack_limit;
};
Note
stack_base 的值比堆栈使用的最高地址大 0x1,这是因为使用的是满降序堆栈。
有关该函数的重新实现示例,请参阅示例目录。
返回值
r0 到 r3 中返回的值取决于是使用单区内存模型,还是使用双区内存模型:
单区
(r0,r1) 是单个堆栈和堆区。r1 大于 r0,并忽略 r2 和 r3。
双区
(r0, r2) 是初始堆,(r3, r1) 是初始堆栈。r2 大于或等于 r0。r3 小于 r1。
使用分散加载描述文件
__user_initial_stackheap() 的缺省实现使用 Image$$ZI$$Limit 符号的值。 如果链接器使用分散加载描述文件,则不会定义此符号。 不过,C 库提供了替代实现,可通过分散加载描述文件中的信息来使用这些实现。
自动选择单区模型
在分散加载描述文件中定义一个特殊执行区,即 ARM_LIB_STACKHEAP。 该区具有 EMPTY 属性。
这导致库选择一个 __user_initial_stackheap() 实现,它将该区用作复合堆/堆栈区。 它使用 Image$$ARM_LIB_STACKHEAP$$Base 和 Image$$ARM_LIB_STACKHEAP$$ZI$$Limit 符号的值。
自动选择双区模型
在分散加载描述文件中定义两个特殊执行区: ARM_LIB_HEAP 和 ARM_LIB_STACK。 两个区均具有 EMPTY 属性。
这导致库选择一个使用以下符号值的 __user_initial_stackheap() 实现:Image$$ARM_LIB_HEAP$$Base、Image$$ARM_LIB_HEAP$$ZI$$Limit、Image$$ARM_LIB_STACK$$Base 和 Image$$ARM_LIB_STACK$$ZI$$Limit。
Example 2.10 显示了一个用于定义 ARM_LIB_HEAP 和 ARM_LIB_STACK 的分散加载描述文件示例。 (它是在主示例目录 install_directory\RVDS\Examples 中作为 Cortex-M3.scat 提供的。)
Example 2.10. ARM_LIB_HEAP 和 ARM_LIB_STACK 分散加载描述
FLASH_LOAD 0x0000 0x00200000
{
;; Maximum of 256 exceptions (256*4 bytes == 0x400)
VECTORS 0x0 0x400
{
; Exception table provided by the user in exceptions.c
exceptions.o (exceptions_area, +FIRST)
}
;; Code is placed immediately (+0) after the previous root region
;; (so code region will also be a root region)
CODE +0
{
* (+RO)
}
DATA 0x20000000 0x00100000
{
* (+RW, +ZI)
}
;; Heap starts at 1MB and grows upwards
ARM_LIB_HEAP 0x20100000 EMPTY 0x100000-0x8000
{
}
;; Stack space starts at the end of the 2MB of RAM
;; And grows downwards for 32KB
ARM_LIB_STACK 0x20200000 EMPTY -0x8000
{
}
}
将从 ARM_LIB_STACKHEAP(对于单区模型)或 ARM_LIB_STACK(对于双区模型)中对 sp 进行相应的初始化。
错误消息
如果使用分散文件,而没有指定任何特殊区名称,并且没有重新实现 __user_initial_stackheap(),库将生成一条错误消息。
看图比较清晰,相关启动文件中所有不清楚的地方都可以参考官方的doc文档说明,非常详细 lyzhangxiang 发表于 2014-8-25 23:13
2.10.5. __user_initial_stackheap()
如果您使用的是旧式源代码,则可能会看到 __user_initial_stackheap() ...
很给力,居然还是中文的,解释的很详细呢 lyzhangxiang 发表于 2014-8-25 23:15
看图比较清晰,相关启动文件中所有不清楚的地方都可以参考官方的doc文档说明,非常详细 ...
找到了,多谢!很好的文档 sunnyqd 发表于 2014-8-25 23:51
很给力,居然还是中文的,解释的很详细呢
是啊中文版本的也有
当初我也很惊讶
哎,最近在arm9上移植rtos,网上看了各种资料,参差不齐。还是官方的doc比较靠谱点。 lyzhangxiang 发表于 2014-8-26 00:11
是啊中文版本的也有
当初我也很惊讶
是的,许多书籍都是抄官方文档的 “里面我有一个没搞懂的地方,就是最后的__user_initial_stackheap为什么不加PROC标记”
LZ搞懂了么? fengyunyu 发表于 2014-8-26 10:01
“里面我有一个没搞懂的地方,就是最后的__user_initial_stackheap为什么不加PROC标记”
LZ搞懂了么? ...
这个没搞懂,但我反汇编查看,这就是作为一个函数调用
我记得有一个地方说,PROC加于不加,只是为了阅读方便和书写条理,但我忘了在哪里看到过了。。。所以我也不确定
PS:兄台看的好仔细 shejian001 发表于 2014-8-26 10:44
好长代码。
我加了很多注释在里面 感谢楼主位和47楼的资料 慢慢学习 Lengxue 发表于 2014-8-26 14:18
感谢楼主位和47楼的资料 慢慢学习
多谢支持 我给lz提个意见哈,栈和堆地址不是刚好放在你想的地方的,而是分散加载文件决定的,mdk默认把这个搞定了,去看看这个文件就知道了,在你的当前项目下 至于后面提到不用proc指定,原因是proc指导的就是函数了,会对使用的寄存器有个入栈操作,而这个很明显不需要的,类似于rtos里的任务 第一个问题在坛里搜下xinv1987的帖子,那里bluesky有详细解释 myxiaonia 发表于 2014-8-30 20:53
我给lz提个意见哈,栈和堆地址不是刚好放在你想的地方的,而是分散加载文件决定的,mdk默认把这个搞定了, ...
对的,是通过分散加载文件决定的,我在里面有描述,实际map文件里的位置就是实际的位置吧? myxiaonia 发表于 2014-8-30 20:56
至于后面提到不用proc指定,原因是proc指导的就是函数了,会对使用的寄存器有个入栈操作,而这个很明显不需 ...
多谢指点,原来这样呢 myxiaonia 发表于 2014-8-30 20:57
第一个问题在坛里搜下xinv1987的帖子,那里bluesky有详细解释
好的,明天电脑搜一下试试,现在在高铁上。。多谢! 好长,收藏先~ sunnyqd 发表于 2014-8-30 21:05
对的,是通过分散加载文件决定的,我在里面有描述,实际map文件里的位置就是实际的位置吧? ...
map文件就是链接后的绝对地址了,你分析的很详细啊,而我却对你的帖子看的还不够仔细
关于flash配置部分,stm32有单独的选项字节配置文件叫stm32option.s,具体在mdk哪个目录我也记不清了 启动文件有中文解析的啊 理解启动文件,更好地连接单片机的工作特点 taojie 发表于 2014-8-31 05:53
启动文件有中文解析的啊
这个是分析的最详细的版本 qinshiysb 发表于 2014-8-31 07:43
理解启动文件,更好地连接单片机的工作特点
是这样子的 楼主要拿3万的那个 sunnyqd 发表于 2014-8-31 07:52
是这样子的
看这个东西,有点枯燥哈'也不好理解 lyzhangxiang 发表于 2014-8-25 23:15
看图比较清晰,相关启动文件中所有不清楚的地方都可以参考官方的doc文档说明,非常详细 ...
这是一个软件还是一份帮助文档 DiaoMao_Huang 发表于 2014-8-31 07:54
楼主要拿3万的那个
那个鸭梨太大 qinshiysb 发表于 2014-8-31 07:55
看这个东西,有点枯燥哈'也不好理解
很好理解的,整理了一遍我也理解比较透彻了 不错,支持楼主。 chjf 发表于 2014-8-31 08:41
不错,支持楼主。
多谢支持,嘿嘿 sunnyqd 发表于 2014-8-31 08:18
很好理解的,整理了一遍我也理解比较透彻了
那倒也是,你整理的,你会细心的看 厉害,强帖要MARK,留着学习 好资料,学习了 非常好的文章,必须支持! 多谢分享留着备用 谢谢分享,收藏。 真够用功的,先赞一下啊 FSL_TICS_ZJJ 发表于 2014-11-25 13:17
非常好的文章,必须支持!
多谢,刚来看又有一个精华啦{:lol:} 写贴不易啊,这么多内容,感谢了,顶起! 要认真学习一下,先收藏看看~ 这个东西 以前不在意 后来发现还是很有用的 这个很给力啊 有时候,一款芯片都用好久了才想起来研究启动文件。 这启动文件,看得眼花啊~没坚持看完 sunnyqd 发表于 2014-8-25 16:57
还第一次被叫柯南兄。。
最近见你的身影比较少了; 如果不使用堆空间。,我看到map文件中。都是先分配 .data 在分配rodata 最后.bss 段。确实在SRAM 中都是连续的。随后就是stack 的地址。 栈顶地址就是stack 的地址加上他的大小了、
1、在SRAM 中。这些是连续分布的。。
2、ke02 有两快 RAM .如果keil 中都设置了。那么首先就会从高端开始分配那么变量。
3、这样的话。SRAM_L 是不是就没有使用呢。 厉害,这个我还没仔细看过呢,有空了跟着楼主学习下 你代码的map部分有这段 代码 ; 0x20000068 0x00000100 Zero RW 48 HEAP startup_mkl02z4.o
1、 请问是不是在程序中使用malloc()函数
2、我在启动文件中设置了堆的大小,但是看不到map 文件的有关堆的信息、 mark。。。。。。。。。。。 注释写的很仔细 , 很好的帖子 ,好好学习一下 。 这么详细,竟然还有注释, 7inspire 发表于 2014-12-11 10:45
这么详细,竟然还有注释,
学习一下启动文件。确实对理解单片机很有帮助。。 好东西,正需要呢,谢谢!{:tongue:} 最近要用到,收藏看看再说!谢谢楼主了. 认真学习一下,收藏看看~
页:
[1]