作者:刘老师,华清远见嵌入式学院讲师。
启动代码的一般作用
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)启动代码分析的更多相关文章
-
STM32启动代码分析 IAR 比较好
stm32启动代码分析 (2012-06-12 09:43:31) 转载▼ 最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...
-
Linux内核启动代码分析二之开发板相关驱动程序加载分析
Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c start_ke ...
-
ARM Linux启动代码分析
前言 在学习.分析之前首先要弄明白一个问题:为什么要分析启动代码? 因为启动代码绝大部分都是用汇编语言写的,对于没学过或者不熟悉汇编语言的同学确实有一定难度,但是如果你想真正深入地学习Linux,那么 ...
-
Android 4.2启动代码分析(一)
Android系统启动过程分析 Android系统的框架架构图如下(来自网上): Linux内核启动之后----->就到Android的Init进程 ----->进而启动Android ...
-
STM32启动代码分析
STM32启动文件简单分析(STM32F10x.s适用范围)定时器, 型号, 名字在<<STM32不完全手册里面>>,我们所有的例程都采用了一个叫STM32F10x.s的启动文 ...
-
S3C6410的启动代码分析&;nbsp;一
本文开始第一篇,启动代码的编写,注意,仅仅是启动代码,并不是bootloader,因为只有boot,没有loader. 第一要明确:CPU上电之后,会从某个固定地址执行指令.ARM结构的CPU从地址0 ...
-
STM32启动代码分析及其汇编学习-ARM
STM32 启动代码 Author By YuCloud 边看启动文件边学汇编 汇编 see ARM: Assembler User Guide see: https://blog.csdn.net/ ...
-
MSVC CRT运行库启动代码分析
原文链接:http://www.programlife.net/msvc-crt-startup.html 在程序进入main/WinMain函数之前,需要先进行C运行库的初始化操作,通过在Visua ...
-
KEA128单片机启动代码分析
;/*****************************************************************************; * @file: startup_SK ...
随机推荐
-
C语言状态机模板
转载声明:如果转载本博客内容,请联系869119842@qq.com,获得作者书面授权. 前言 上一篇我的博客中探讨了一种非swtich-case结构的状态机写法,但是个人感觉写起来比较麻烦,如果增加 ...
-
Javascript题库
一.填空题 JavaScript有两种引用数据类型 :__数组___.__对象__. Javascript通过__setTimeout___延迟指定时间后,去执行某程序. Javascript里Str ...
-
virtual关键字的本质是什么?
MSDN上对virtual方法的解释:试着翻译如下 当一个方法声明包含virtual修饰符,这个方法就是虚方法.如果没有virtual修饰符,那么就不是虚方法. 非虚方法的实现是不变的:不管该方法是被 ...
-
cereal:C++实现的开源序列化库
闲来无事发现了一个基于C++实现的序列化工具,相比于其他(比如Boost serialization或Google protobuf,恰巧都用过,以后再介绍),使用简单,感觉不错,下面做个摸索. ce ...
-
命令passwd报错因inode节点处理记录
命令passwd报错因inode节点处理记录故障现象:1.修改密码时报错 passwd: Authentication token manipulation error2.添加用户报错:unable ...
-
TortoiseSVN 文件关联图标不显示的解决方法
对于SVN来说,因为每个图标都代表着不同的含义,预示着不同的状态,是指示灯的作用,如果没有正确的图标很可能造成数据的丢失等 之前看了网上其他人写的帖子,,有一些是直接删除注册表下“ShellIconO ...
-
Javascript Fromdata 与jQuery 实现Ajax文件上传以及文件的删除
前端HTML代码: <!DOCTYPE html> <html> <head> <title>ajax</title> <script ...
-
【踩坑】angularJS 1.X版本中 ng-bind 指令多空格展示
做项目的时候遇到的问题 1.问题描述 用户在表单某个值输入多个空格,例如:A B,保存至服务器 在列表查询页面中使用bg-bind的指令单向绑定,结果展示位A B,连续的空格被替换为单个空格 ...
-
阿里云自定义镜像可以免费保存,ECS实例到期后自定义镜像手动快照不会被删除
阿里云自定义镜像可以免费保存,ECS实例到期后自定义镜像手动快照不会被删除 4. ECS 实例释放后,自定义镜像是否还存在? 存在. 5. ECS 实例释放后,快照是否还存在? 保留手动快照,清除自动 ...
-
POJ 1182 食物链 (种类并查集)
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.有人用两种说 ...