作者:刘老师,华清远见嵌入式学院讲师。

启动代码的一般作用

1、堆和栈的初始化;

2、向量表定义;

3、地址重映射及中断向量表的转移;

4、初始化有特殊要求的断口;

5、处理器模式;

6、进入C应用程序。

ARM复位后程序从0x00地址开始执行代码,所以一般都会有将Flash地址映射到0x00的过程。但对于这一款Cortex M0的启动代码比较简单,从存储分布图中我们可以看到LPC11C14拥有32K的片内Flash,地址范围是0x0000 0000 ~ 0x0000 8000,当我们将程序(小于32K)烧写进片内Flash时,启动代码中就可以不用再对Flash的地址重新映射。

NXP LPC11C14存储分布图主要看Flash

CortexM0的启动代码进行分析:

一、堆栈初始化部分

在程序开始处,首先定义栈的大小及属性,然后对堆进行初始化操作,ARM-Thumb过程调用标准和ARM、Thumb C/C++ 编译器总是使用满减(Full descending)类型堆栈。

; <h> Stack Configuration

; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>

; <h> Stack Configuration
        ; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
        ; </h>
        Stack_Size EQU 0x00000020
        //定义堆栈大小

AREA STACK, NOINIT, READWRITE, ALIGN=3  //定义一个数据段按8个字节对齐 AREA伪指令用于定义一//个代码段或者数据段
                                                                                          //NOINIT定义此数据段仅仅保留了内存单元,而没有将各初
                                                                                          //始值写入内存单元,或者将各个内存单元值初始化为0
        Stack_Mem SPACE Stack_Size        //保留Stack_Size 大小的堆栈空间,来分配连续Stack_Size
                                                                          //节的存储单元并初始化为0

__initial_sp        //标号--堆栈顶部地址。M0中堆栈式满递减堆栈,堆栈指针位//于堆栈的高地址

Stack_Size EQU 0x00000020        //定义堆栈大小

; <h> Heap Configuration
        ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
        ; </h>

Heap_Size EQU 0x00000000        // 定义堆空间大小

AREA HEAP, NOINIT, READWRITE, ALIGN=3        // 定义了一个数据段,8字节对齐

__heap_base        // 标号--代表为堆末底部地址

Heap_Mem SPACE Heap_Size        // 保留Heap_Size的堆空间
        __heap_limit        // 标号--堆的界限地址

PRESERVE8        // 指令指定当前文件保持堆栈8字节对齐。它设置PRES8编译属性,以
                                                      //通知链接器。链接器检查要求堆栈8字节对齐的任何代码是否仅由保持//堆栈8字节对齐的代码直接或者间接地调用。

THUMB          //指示编译器以后的伪指令为Thum指令

二、中断量表定义

在MDK生成分散加载文件中,RESET被设置在flash的0地址处,这样就规定了向量表的地址。

; Vector Table Mapped to Address 0 at Reset

AREA RESET, DATA, READONLY         //定义只读数据段,位于0地址,其实放在CODE区
                                                                        //EXPORT在程序中声明一个全局的标号__Vetors号可
                                                                        //可以在其他文件中使用

/*
        DCD伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。DCD也可用“&”代替。
        */

__Vectors   DCD   __initial_sp ; Top of Stack // 给__initial_sp分配4字节32位的地址

DCD   Reset_Handler   ; Reset Handler//给标号Reset_Handler分配地址
        DCD   NMI_Handler   ; NMI Handler //给标号NMI_Handler分配地址
        DCD   HardFault_Handler ; Hard Fault Handler
        DCD  MemManage_Handler; MPU Fault Handler
        DCD  BusFault_Handler ; Bus Fault Handler
        DCD   UsageFault_Handler; Usage Fault Handler
        DCD  0; Reserved
//保留的,不给任何标号分配
        DCD   0 ; Reserved
                        DCD   0  ; Reserved
                        DCD   0  ; Reserved
                        DCD   SVC_Handler   ; SVCall Handler
                        DCD   DebugMon_Handler  ; Debug Monitor Handler
                        DCD   0  ; Reserved
                        DCD   PendSV_Handler   ; PendSV Handler
                        DCD   SysTick_Handler   ; SysTick Handler

; External Interrupts
                        DCD   WAKEUP_IRQHandler   ; 15 wakeup sources for all the
                        DCD   WAKEUP_IRQHandler   ; I/O pins starting from PIO0 (0:11)
                        DCD   WAKEUP_IRQHandler   ; all 40 are routed to the same ISR
                        DCD   WAKEUP_IRQHandler  
                        DCD   WAKEUP_IRQHandler  
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler
        DCD   WAKEUP_IRQHandler
                        DCD   WAKEUP_IRQHandler   ; PIO1 (0:11)
                        DCD   CAN_IRQHandler   ; CAN
                        DCD   SSP1_IRQHandler   ; SSP1
                        DCD   I2C_IRQHandler   ; I2C
                        DCD   TIMER16_0_IRQHandler   ; 16-bit Timer0
                        DCD   TIMER16_1_IRQHandler   ; 16-bit Timer1
                        DCD   TIMER32_0_IRQHandler   ; 32-bit Timer0
                        DCD   TIMER32_1_IRQHandler   ; 32-bit Timer1
                        DCD   SSP0_IRQHandler   ; SSP0
                        DCD   UART_IRQHandler   ; UART
                        DCD   USB_IRQHandler   ; USB IRQ
                        DCD   USB_FIQHandler   ; USB FIQ
                        DCD   ADC_IRQHandler   ; A/D Converter
                        DCD  WDT_IRQHandler   ; Watchdog timer
                        DCD   BOD_IRQHandler   ; Brown Out Detect
                        DCD   FMC_IRQHandler   ; IP2111 Flash Memory Controller
                        DCD   PIOINT3_IRQHandler   ; PIO INT3
                        DCD   PIOINT2_IRQHandler   ; PIO INT2
                        DCD  PIOINT1_IRQHandler   ; PIO INT1
                        DCD   PIOINT0_IRQHandler   ; PIO INT0

IF   :LNOT::DEF:NO_CRP   //进行宏定义判断
        AREA   |.ARM.__at_0x02FC|, CODE, READONLY

/******************************************************************************************************************************

这有几个关键的地方“NO_CRP”、 0x02FC和0xFFFFFFFF,如果我们在前面定义有“NO_CRP”,那么我们后面的代码也就不起作用了,所以在需要加密的时候前面就一定不能再定义了 代码读保护,也就是加密的关键字,经过加密后芯片再也无法擦除,除非之前烧写的程序带有IAP,IAP可以使芯片进入ISP模式。

******************************************************************************************************************************/

CRP_Key   DCD  0xFFFFFFFF // 加密等级
        ENDIF

AREA   |.text|, CODE, READONLY   //代码段定义

利用PROC,ENDP这一对伪指令把程序段分为若干个过程,是程序的结构更加清晰

; Reset Handler
        Reset_Handler   PROC
//过程的开始
        EXPORT   Reset_Handler [WEAK]   //[WEAK]弱定义,意思是如果在别处也定义该标
                          //(函数),在连接时,用别处的地址。如果没有
                          //他地方定义,编译器也不报错,以此处地址进行链接

IMPORT __main  // 通知编译要使用标号在其他文件
        LDR R0, =__main   //使用“=”表示LDR目前是伪指令,不是标准指令。把__main
                         //地址赋给R0。
        BX R0   //BX是ARM指令集和THUMB指令集之间程序的跳转
        ENDP //过程结束

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler   PROC
        EXPORT NMI_Handler   [WEAK]
        B   .
//原地跳转(即无限循环)
        ENDP
        HardFault_Handler\
          PROC
          EXPORT HardFault_Handler   [WEAK]
          B   .
          ENDP
        MemManage_Handler\
          PROC
          EXPORT MemManage_Handler   [WEAK]
          B   .
          ENDP
        BusFault_Handler\
          PROC
          EXPORT BusFault_Handler   [WEAK]
          B   .
          ENDP
        UsageFault_Handler\
          PROC
          EXPORT UsageFault_Handler   [WEAK]
          B   .
          ENDP
        SVC_Handler   PROC
          EXPORT SVC_Handler   [WEAK]
          B   .
          ENDP
        DebugMon_Handler\
          PROC
          EXPORT DebugMon_Handler   [WEAK]
          B   .
          ENDP
        PendSV_Handler PROC
         EXPORT PendSV_Handler   [WEAK]
         B   .
         ENDP
        SysTick_Handler PROC
          EXPORT SysTick_Handler   [WEAK]
          B   .
          ENDP

Default_Handler PROC

  EXPORT    WAKEUP_IRQHandler    [WEAK]
          EXPORT   CAN_IRQHandler    [WEAK]
          EXPORT    SSP1_IRQHandler    [WEAK]
          EXPORT    I2C_IRQHandler    [WEAK]
          EXPORT    TIMER16_0_IRQHandler     [WEAK]
          EXPORT    TIMER16_1_IRQHandler     [WEAK]
          EXPORT   TIMER32_0_IRQHandler     [WEAK]
          EXPORT   TIMER32_1_IRQHandler    [WEAK]
          EXPORT   SSP0_IRQHandler     [WEAK]
          EXPORT   UART_IRQHandler     [WEAK]

  EXPORT    USB_IRQHandler    [WEAK]
          EXPORT    USB_FIQHandler     [WEAK]
          EXPORT    ADC_IRQHandler    [WEAK]
          EXPORT    WDT_IRQHandler     [WEAK]
          EXPORT    BOD_IRQHandler     [WEAK]
          EXPORT   FMC_IRQHandler     [WEAK]
          EXPORT   PIOINT3_IRQHandler     [WEAK]
          EXPORT   PIOINT2_IRQHandler     [WEAK]
          EXPORT   PIOINT1_IRQHandler     [WEAK]
          EXPORT   PIOINT0_IRQHandler     [WEAK]

WAKEUP_IRQHandler
        CAN_IRQHandler
        SSP1_IRQHandler
        I2C_IRQHandler
        TIMER16_0_IRQHandler
        TIMER16_1_IRQHandler
        TIMER32_0_IRQHandler
        TIMER32_1_IRQHandler
        SSP0_IRQHandler
        UART_IRQHandler
        USB_IRQHandler
        USB_FIQHandler
        ADC_IRQHandler
        WDT_IRQHandler
        BOD_IRQHandler
        FMC_IRQHandler
        PIOINT3_IRQHandler
        PIOINT2_IRQHandler
        PIOINT1_IRQHandler
        PIOINT0_IRQHandler
          B .
          ENDP

  ALIGN填充字节使地址对齐

; User Initial Stack & Heap

三、堆和栈的初始化

IF   :DEF:__MICROLIB   // “DEF”的用法:DEF:xx就是说xx定义了则为真,否则为假

EXPORT __initial_sp   //则将栈顶定制
        EXPORT __heap_base   // 堆起始地址赋予全局属性
        EXPORT __heap_limit   //堆末端界限地址赋予全局属性,使外部程序可调用

ELSE   //如果没有定义__MICROLIB,则使用默认的C运行时库

IMPORT __use_two_region_memory   // 通知编译器要使用的标号在其他文件//__use_two_region_memory
        EXPORT __user_initial_stackheap   // 声明全局标号__user_initial_stackheap,这样外程序也可调用此标号
                          //则进行堆栈和堆的赋值,在__main函数执行过程中调用
                          //如果了使用默认的C库,程序启动过程中不会执行标号下//的代码

/******************************************************************************************************************************

_user_initial_stackheap() 返回:

· r0 中的堆基址
        · r1 中的堆栈基址,即堆栈区中的最高地址
        · r2 中的堆限制
        · r3 中的堆栈限制,即堆栈区中的最低地址。

有单区模型和双区模型。

单区模型:(r0,r1)是单个堆栈和堆区。r1 大于 r0,并忽略 r2和r3。
        r0--r1这一块内存区域被堆和栈共用,堆从r0向上生长,栈从r1向下生长。

双区模型:(r0, r2)是初始堆,(r3, r1) 是初始堆栈。r2 大于或等于r0,r3小于r1。
        堆和栈分别指定了单独的内存区域。

******************************************************************************************************************************/

__user_initial_stackheap   // 标号__user_initial_stackheap,表示拥护堆栈初始化程序入口
                       //则进行堆栈和堆得赋值,在__main函数执行过程中调用
        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

Cortex-M0(NXP LPC11C14)启动代码分析的更多相关文章

  1. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

  2. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  3. ARM Linux启动代码分析

    前言 在学习.分析之前首先要弄明白一个问题:为什么要分析启动代码? 因为启动代码绝大部分都是用汇编语言写的,对于没学过或者不熟悉汇编语言的同学确实有一定难度,但是如果你想真正深入地学习Linux,那么 ...

  4. Android 4.2启动代码分析(一)

    Android系统启动过程分析 Android系统的框架架构图如下(来自网上):   Linux内核启动之后----->就到Android的Init进程 ----->进而启动Android ...

  5. STM32启动代码分析

    STM32启动文件简单分析(STM32F10x.s适用范围)定时器, 型号, 名字在<<STM32不完全手册里面>>,我们所有的例程都采用了一个叫STM32F10x.s的启动文 ...

  6. S3C6410的启动代码分析&nbsp;一

    本文开始第一篇,启动代码的编写,注意,仅仅是启动代码,并不是bootloader,因为只有boot,没有loader. 第一要明确:CPU上电之后,会从某个固定地址执行指令.ARM结构的CPU从地址0 ...

  7. STM32启动代码分析及其汇编学习-ARM

    STM32 启动代码 Author By YuCloud 边看启动文件边学汇编 汇编 see ARM: Assembler User Guide see: https://blog.csdn.net/ ...

  8. MSVC CRT运行库启动代码分析

    原文链接:http://www.programlife.net/msvc-crt-startup.html 在程序进入main/WinMain函数之前,需要先进行C运行库的初始化操作,通过在Visua ...

  9. KEA128单片机启动代码分析

    ;/*****************************************************************************; * @file: startup_SK ...

随机推荐

  1. CXF学习(2) helloworld

    0.新建一个项目取名wsserver. pom.xml 文件如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xml ...

  2. POJ3415 Common Substrings(后缀数组 单调栈)

    借用罗穗骞论文中的讲解: 计算A 的所有后缀和B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k 的部分全部加起来.先将两个字符串连起来,中间用一个没有出现过的字符隔开.按height ...

  3. android 入门-git之上传本地代码到github

    github部分: 1.首先去github网站 上注册一个用户 2.说明 https://guides.github.com/activities/hello-world/ 2.点击 New repo ...

  4. World of Warcraft

    一大堆快捷键 鼠标中键:致盲 上滚:闪避 下滚:疾跑 通用:~伺机待发 Q加速 E斗篷 R徽章 T消失 alt+Q脚踢,alt+W毒刃 alt+E亡灵意志 alt+D烟幕弹 alt+F死亡标记 Z天降 ...

  5. Sizeof与Strlen的区别与联系(转)

    Sizeof与Strlen的区别与联系 一.sizeof     sizeof(...)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组.指针.类型 ...

  6. bzoj1691[Usaco2007 Dec]挑剔的美食家 平衡树treap

    Description 与很多奶牛一样,Farmer John那群养尊处优的奶牛们对食物越来越挑剔,随便拿堆草就能打发她们午饭的日子自然是一去不返了.现在,Farmer John不得不去牧草专供商那里 ...

  7. Linux下autoconf和automake使用

    转载:www.cnblogs.com/itech/archive/2010/11/28/1890220.html

  8. POJ 2464 Brownie Points II(树状数组)

    一开始还以为对于每根竖线,只要与过了任意一点的横线相交都可以呢,这样枚举两条线就要O(n^2),结果发现自己想多了... 其实是每个点画根竖线和横线就好,对于相同竖线统计(一直不包含线上点)右上左下总 ...

  9. 廖雪峰js教程笔记8 date对象介绍和处理

    在JavaScript中,Date对象用来表示日期和时间. 要获取系统当前时间,用: var now = new Date(); now; // Wed Jun 24 2015 19:49:22 GM ...

  10. loopback 02

    数据库连接操作,以mongodb为例 安装loopback-connector-mongodb 修改datasources.json //例子 { "db": { "na ...