关于ARM CM3的启动文件分析

发布者:创意小巨人最新更新时间:2019-12-26 来源: eefocus关键字:ARM  CM3  启动文件 手机看文章 扫描二维码
随时随地手机看文章

下面以ARM Cortex_M3裸核的启动代码为例,做一下简单的分析。首先,在启动文件中完成了三项工作:

1、堆栈以及堆的初始化

2、定位中断向量表

3、调用Reset Handler。


在介绍之前,我们先了解一下ARM芯片启动文件中涉及到的一些汇编指令的用法。

 

补充一下,其中DCD相当于C语言当中的&,定义地址。

 

1、堆栈以及堆的初始化

1.1 堆栈的初始化

Startup_xxx.s中的堆栈初始化代码

  Stack_Size  EQU  0x00000400,这个语句相当于Stack_Size这个标号(标号:链接器的术语,下文中提到的所有“标号”,指的都是指的链接器中的标号)等于0x00000400相当于C语言中的#define  Stack_Size  0x00000400 ,也就是说此语句只是一个声明,并未分配地址。


  AREA    STACK, NOINIT, READWRITE, ALIGN=3,此语句定义了一个叫STACK的代码段,并指明8字节对齐(ALIGN = 3)。其中NOINIT表示未初始化,READWRITE表示可读可写,ALIGN = 3,即表示2^3 = 8,八字节对齐。


  Stack_Mem    SPACE   Stack_Size,为Stack_Mem分配Stack_Size大小的一块内存区域,注意这里分配的是RAM,即分配了大小为1KB的内存空间(0x00000400 = 1024)。


  __initial_sp ,紧跟着栈分配内存后,所以其为栈顶(满递减栈)。此标号有一层隐含的意思就是在M3中堆栈是满递减堆栈,因为它指定了堆栈指针位于堆栈的高地址(在Stack_Mem之后),具体如下图所示。

堆栈指针sp位置

  上图来自Cortex_M3的一个工程的xxx.map文件。可以看出栈的起始地址为0x20000c68,大小为1024字节(即0x00000400 = Stack_Size)。而堆栈指针的位置在0x20001068,其等于栈的起始地址0x2000c68+0x00000400,说明本系列的Cortex_M3微控制器的堆栈为满递减堆栈。


  所以__initial_sp为1KB空间栈的栈顶,栈主要用于局部变量和形参的调用过程的临时存储,属于编译器自动分配和释放的内存,所以这里需要注意如果你的函数所占的内存过大,那么这个空间应调整其大小但一定要小于内部SRAM的大小。堆是程序员空间是程序员进行分配和释放的,如果程序中未释放最后由系统回收。

 

1.2 堆的初始化

 

Startup_xxx.s中的堆初始化代码

堆的初始化过程与堆栈的初始化相同。

 

2、中断向量表的初始化

 

中断向量表的初始化代码(部分)

PRESERVE8指定了以下的代码为8字节对齐,这是keil编译器的一个编程要求,对齐情况如下图所示:

xxx.list文件中的8字节对齐示意图

  THUMB指定了接下来的代码为THUMB指令集。

  AREA    RESET, DATA, READONLY,此语句声明RESET数据段。

  EXPORT  __Vectors,导出向量表标号,EXPORT作用类似于C语言中的extern。之后的代码就是为向量表分配存储区域。中断向量表从FLASH的0x00000000地址开始放置,以4个字节为一个单位,地址0存放的是栈顶指针(sp)的地址,0x00000004存放的是复位程序的地址,往后以此类推,这里我们只设置了一个Reset_Handler向量。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道C语言中的函数名就是一个地址。(由此我们知道,中断函数的函数名都已经知道了,我们在写对应的中断服务程序时,从对应的地址取服务例程的入口地址并跳入执行)。但是此处有一个要注意的,就是0号地址不是什么入口地址,而是给出的复位后的MSP的初值。

 

3、调用Reset Handler

调用Reset Handler的代码

  此段代码只完成了一个功能,引导程序进入__main。__main的具体行为在后面做具体描述。


  PROC与ENDP组合在汇编中定义了一段子函数。


用户堆栈的初始化

 

具体的堆栈以及堆的初始化行为


  这一部分也就是把初始化的堆栈地址赋值给单片机的对应寄存器以方便C程序进行分配释放使用。

 

4、其他代码

有一些芯片厂商对芯片的加密的加密级别的代码也会放在这里,芯片上电后会自动读取这一地址的值以确定芯片的加密方式。

 

5、ARM芯片的启动过程详解

  接下来介绍__main函数的具体实现过程。

  首先在介绍__main函数之前,我们先了解一些关于ARM芯片在启动过程中的基本知识。

“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的.bin(.axf,.hex)映像(image)文件。


一个ARM程序包含3部分:RO,RW和ZI

         RO 就是只读数据,是程序中指令和常量;

         RW是可读写的数据,程序中已初始化变量;

         ZI 是程序中未初始化的变量和初始化为0的变量。

简单理解就是:

         RO就是readonly,RW就是read/write,ZI就是zero initial。

 

ARM芯片的启动过程详解


注意,以上的过程并非绝对的,不同的ARM架构或者是不同的代码以上的执行过程是不同的。


复位处理程序是在汇编器中编写的短模块,系统一启动就立即执行。复位处理程序最少要为应用程序的运行模式初始化堆栈指针。对于具有本地内存系统(如缓存、TCM、MMU和MPU)的处理器,某些配置必须在初始化过程的这一阶段完成。复位处理程序在执行之后,通常跳到__main以开始C库初始化序列。


 __main中的__scatterload负责设置内存,而__rt_entry负责设置运行时的环境。__scatterload中负责把RO/RW(非零)输出段从装载域地址复制到运行域地址(执行代码和数据复制、解压缩),并完成ZI段运行域数据的0初始化工作。然后跳到__rt_entry设置堆栈和堆、初始化库函数和静态数据。然后,__rt_entry跳转到应用程序的入口main()。主应用程序结束执行后,__rt_entry将库关闭,然后把控制权交换给调试器。函数标签main()具有特殊含义。Main()函数的存在强制链接器链接到__main和__rt_entry中的代码。如果没有标记为main()的函数,则没有链接到初始化序列,因而部分标准C库功能得不到支持。

 

6、结合代码来看芯片启动过程

         上电后硬件设置sp、pc,刚上电复位后,硬件会自动根据向量表地址找到向量表。

 

在离开复位状态后, CM3 做的第一件事就是读取下列两个 32 位整数的值:


1、从地址 0x0000 0000 处取出 MSP 的初始值。

2、从地址 0x0000 0004 处取出 PC 的初始值,这个值是复位向量, LSB 必须是 1。 然后从这个值所对应的地址处取指。


硬件自动从0x0000 0000位置处读取数据赋给栈指针sp,然后从0x0000 0004位置处读取数据赋给pc指针,完成复位,结果为:

SP = 0x2000 1068

PC = 0x0000 011D

 

这与传统的 ARM 架构不同——其实也和绝大多数的其它单片机不同。传统的 ARM 架构总是从 0 地址开始执行第一条指令。它们的 0 地址处总是一条跳转指令。在 CM3 中,在 0 地址处提供 MSP 的初始值,然后紧跟着就是向量表。向量表中的数值是 32 位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令,就是我们上面分析的Reset_Handler这个函数。

进入__main

  LDR   R0, =__main

  BX   R0

 

执行上两条指令,跳转到__main程序段运行,__main的地址是0x0000 0080,上一步指令pc = 0x0000 011D的地址没有对齐,硬件自动对齐到0x0000 011C,执行__main。

 

pc指针通过立即数寻址,跳转到0x0000 0081处执行,同上这里也会自动对齐到0x0000 0080处。

 

在__scatterload函数中又会进入__scatterload_copy,在__scatterload_copy中进行代码搬运,主要是加载已经初始化的数据段和未初始化的数据段,同时还会初始化栈空间,即ZI段清零(其中搬运次数由代码中声明的变量类型和变量多少来决定)。

 

然后会跳转到__rt_entry函数执行,__rt_entry是使用ARM C库的程序的起点。将所有分散加载区重新定位到其执行地址后,会将控制权传递给__rt_entry。如下图,在__rt_entry中主要实现如下几个功能:

  1、  设置用户的堆和堆栈

  2、  调用__rt_lib_init以初始化C库

  3、  调用main()

  4、  调用__rt_lib_shutdown以关闭C库

  5、  退出

 

__rt_lib_init函数是库函数初始化函数,它与__rt_lib_shutdown配合使用。并且这个函数紧靠__rt_stackheap_init()后面调用,即紧跟堆和堆栈初始化后面调用,并且传递一个要用作堆的初始内存块。此函数是标准ARM库初始化函数,不能重新实现此函数。


注意:最后两步是在程序退出main()函数的时候才会执行,而我们嵌入式程序一般都是死循环,所以基本上不会执行这两个过程。还有以上过程是针对使用标准C Library而言的,不包括使用MDK提供的microlib库的情况。

  

在__rt_entry_main中,用户程序就开始正式执行了(进入C的世界)。在此之前初始化 MSP 是必需的,因为可能第 1 条指令还没来得及执行,就发生了 NMI 或是其它 fault。 MSP 初始化好后就已经为它们的服务例程准备好了堆栈。这也就是__main中做的事情。

 

7、最后关于microlib库

Microlib 是缺省C库的备选库。它旨在与需要装入到极少量内存中的深层嵌入式应用程序配合使用。这些应用程序不在操作系统中运行,因此microlib 进行了高度优化以使代码变得很小,当然它的功能相比缺省C库少,并且根本不具备某些ISO C特性。某些库函数的运行速度也比较慢,比如memcpy()。


Microlib与缺省C库之间的主要差异是:

  Microlib不符合ISO C 库标准。不支持,某些ISO特性,并且其他特性具有的功能也比较少;

  Microlib不符合IEEE754 二进制浮点算法标准;

  Microlib进行了高度优化以使代码变得很小;

  无法对区域设置进行配置。缺省C区域设置是唯一可用的区域设置;

  不能将main()声明为使用参数,并且不能返回内容;

  不支持stdio,但未缓冲的stdin、stdout和stderr除外;

  Microlib对C99函数提供有限的支持;

  Microlib不支持操作系统函数;

  Microlib不支持与位置无关的代码;

  Microlib不提供互斥锁来防止非线程安全的代码;

  Microlib不支持宽字符或多字节字符串;

  与stdlib不同,microlib不支持可选的单或双区内存模型。Microlib只提供双区内存模型,即单独的堆栈和堆区。

 

8、关于生成的xxx.map文件

想要更好的了解启动代码的运行机制,我们就有必要了解一下由Keil的链接器“armlink”生成的描述文件,即xxx.map文件。

 

目标文件的组成

上图即是armlink的链接器为测试代码生成的xxx.map文件中的一部分,其描述了镜像文件的组成信息,其中可以明显看到其由两部分构成:


User Code生成的目标文件

C Library生成的目标文件


可见我们在上文中所描述的启动过程中看到的__main、__rt_entry、__scartterload以及__rt_lib_init等,就是C library中的代码。


所以,我们每次烧录的可执行的ARM的bin文件中不仅有开发者编写的代码,还有C Library的代码。

上图为存放在RAM中的RW段。

 

以上就是CM3芯片的基本启动过程

关键字:ARM  CM3  启动文件 引用地址:关于ARM CM3的启动文件分析

上一篇:ARM命名规则
下一篇:arm-linux启动过程

推荐阅读最新更新时间:2024-11-12 18:03

集邦咨询:英伟达若收购了Arm,美国将主导全球芯片产业
针对英伟达拟以400亿美金收购全球IP龙头Arm一案,集邦咨询(TrendForce)旗下拓墣产业研究院表示,倘若英伟达成功收购软银旗下的Arm,美国将掌握x86与Arm两大阵营的生态系统,从而在CPU领域建立起无可撼动的地位,无疑将加大美国对中国半导体发展的牵制力道。 拓墣产业研究院分析师姚嘉洋指出,此收购案完成后,英伟达短期内的重心还是以数据中心与自动驾驶汽车两大领域为主。在数据中心方面,英伟达过去一直是重要配角,但长年来数据中心一直被英特尔的x86架构所垄断,尽管Arm阵营近期在超级运算领域取得冠军,但终究是宣誓大于实质意义。如今英伟达的GPU加上Arm的CPU架构,或许有机会一改数据中心的生态面貌。 而英伟达在自动驾驶
[手机便携]
英特尔与Arm共建物联网愿景,安全实现“万物联云”
本文作者:英特尔公司软件和服务事业部副总裁兼物联网安全总经理Lorie Wigle 物联网正从最初的概念验证部署过渡到一个新的发展长阶段,据行业分析师预计,到2035年全球将部署1万亿台物联网设备。这一充满雄心壮志的预测主要源于数字驱动的物联网计划(如边缘计算、人工智能、预防性维修和自制系统)能够带来难以置信的商业收益。收集到的数据越多,数据的价值就会越高。然而,除非业界能够合作开发出更加开放和可扩展的方法,将设备及其数据安全地配置到云端,否则这一设想可能无法实现。 为了应对这些挑战,英特尔正在与Arm公司*合作,提供能够将英特尔和Arm公司的物联网设备安全接入任何应用程序或云框架的解决方案。 首先,让我来介绍一
[物联网]
英特尔与<font color='red'>Arm</font>共建物联网愿景,安全实现“万物联云”
2018 Arm人工智能开发者全球峰会召开在即
中国上海,2018年8月17日——首届Arm人工智能开发者全球峰会将于2018年9月14日在上海举办。此次开发者峰会由上海市徐汇区政府指导,Arm中国及Arm人工智能生态联盟AIEC联合主办,旨在通过汇聚Arm AI生态圈的主流框架(TensorFlow / Caffe / MxNet / Paddle / ArmNN / Tengine等)、芯片和算法领域的顶尖精英,与AI开发者就如何共同创新核心AI技术、建设开放AI生态、推动前端和边缘AI的普及和普惠进行现场互动和深度交流。 开发者是AI领域最重要的群体和最主要的推动力量。此次开发者峰会将汇聚600位全球人工智能领域技术精英、超过140家AIEC联盟成员与中国重量级的人工智能
[嵌入式]
2018 <font color='red'>Arm</font>人工智能开发者全球峰会召开在即
Intel开放22及10nm制程对ARM架构代工业务
在 2017 年的 ARM TechCon 大会上,在某些领域已经形成相互争关系的半导体大厂 Intel 和硅智财权厂商 AMD,两者宣布将建立广泛的合作关系。 在这样的关系下,其中一个相互合作的方式,就是基于 ARM 核心架构的行动芯片,预计将采用 Intel 的 22 奈米 FFL 制程技术,以及 10 奈米的 HPM/GP 制程技术来进行代工生产。 过去,在 Intel 专注的 x86 核心架构市场,与 ARM 核心架构专注的行动市场,彼此几乎是不太有所交集。 虽然,过去 Intel 也曾经试图以 x86 核心架构,进入智能型手机领域。 而以 ARM 核心架构为主的高通,也宣布在 2017 年结合微软 Windows
[手机便携]
移植 ffmpeg 到 ARM 平台
一、下载yasm: 到 http://www.tortall.net/projects/yasm/wiki/Download 下载yasm0.7.2(x264需要用到的汇编编译器) for x86: ./configure --enable-shared --prefix=/usr/local make make install for arm: ./configure --enable-shared --prefix=/usr/local/ --host=arm-linux make make install 下载安装x264 到 ftp://ftp.videolan.org/pub/videolan/x264/s
[单片机]
arm驱动linux异步通知与异步IO
《 linux异步通知与异步IO》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动三个 描述:设备文件IO访问:阻塞与非阻塞io访问,poll函数提供较好的解决设备访问的机制,但是如果有了异步通知整套机制就更加完整了 一、阻塞 I/O,非阻塞IO,异步I/O 1、阻塞 I/O :挂起进程一直等待设备可访问后再访问 2、非阻塞IO:进程进行对设备访问一次,不可访问时,继续执行下一条指令 3、异步I/O:非常类似于硬件上 中断 的概念(硬件去call软件,内核去call应用程序);信号是在软件层次上对中断机制的一种模拟; a)原理:信号是
[单片机]
<font color='red'>arm</font>驱动linux异步通知与异步IO
多工业应用理想选择,研华NXP i.MX8 ARM核心模块问市
NXP i.MX 8 系列 SOC 是 NXP 的首款 64 位 ARMv8处理器,最大支持 6 个 Cortex™-A 内核和 2 个 Cortex™-M4 内核。新颖强悍的硬件设计,i.MX 8 系列产品能够提供高性能的图像处理能力和显示性能,为此研华全新推出两款产品,分别是i.MX8M ROM-5720 SMARC 模块和i.MX8 QuadMax ROM-7720 Qseven 模块,为AI、机械视觉、医疗成像、图像处理等工业应用提供理想选择。 ROM-5720是一款搭载 NXP ARM® CortexTM-A53 i.MX8M 处理器的 SMARC 2.0 核心模块,功耗低,显示性能出众,支持 HDMI 2.0
[嵌入式]
多工业应用理想选择,研华NXP i.MX8 <font color='red'>ARM</font>核心模块问市
【嵌入式开发】ARM 芯片简介 (ARM芯片类型 | ARM处理器工作模式 | ARM 寄存器 | ARM 寻址)
一. ARM 芯片类型 1. ARM 分类 (1) ARM 分类类型(芯片 | 核 | 指令架构) ARM 分类 : -- ARM 芯片类型 : 6410, 2440, 210; -- ARM 核类型 : arm11, arm9, CortexA9; -- 指令架构 : armv7, armv6; (2) ARM芯片 与 ARM核 关系 芯片 和 核关系 : 芯片包含核; -- 2440 芯片 : 包含 arm9 核; -- 6410 芯片 : 包含 arm11 核; -- 210 芯片 : 包含 CortexA8 核; (3) ARM核 与 指令架构 关系 ARM 核 与 指令
[单片机]
【嵌入式开发】<font color='red'>ARM</font> 芯片简介 (<font color='red'>ARM</font>芯片类型 | <font color='red'>ARM</font>处理器工作模式 | <font color='red'>ARM</font> 寄存器 | <font color='red'>ARM</font> 寻址)
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved