经过查阅各种官方文献和对代码进行单步跟踪,详细地叙述了STM32加电启动的具体过程。对于关键性的语句都指明了出处。下面将学习成果分享给大家,由于笔者知识有限,不当之处敬请指出。

为了更好的说明问题,先来看STM32的内存映射(以STM32L1xx为例)

由于固定的内存映射,代码区(code area)从0x00000000开始,通过指令总线(ICode Bus)和数据总线(DCode Bus)访问。数据区(SRAM)从0x20000000开始,通过系统总线(System Bus)访问。Cortex™-M3 CPU总是通过指令总线(ICode Bus)取得复位向量,这就意味着启动空间(boot space)只能处于代码区(code area),典型的就是Flash。STM32系列使用了一种特殊的机制,能够从代码区以外的区域启动(如,内部的SRAM)。(《cortex-m3编程指南》)

这就意味着,STM32系列可以有3种启动模式,由BOOT1与BOOT0的设置决定选择Flash、System memory还是SRAM作为启动空间(boot space)。

STM32把从0x00000000到0x0005FFFF的区域作为启动空间(boot space)的别名区。

从上面两个表中可以看出:

当从主闪存(Main Flash memory)启动时,启动空间别名区映射到Flash。

当从系统内存(System memory)启动时,启动空间别名区映射到System memory。

当从内部SRAM(Embedded SRAM)启动时,启动空间别名区映射到SRAM。

注:默认是这样的映射关系,但上述的映射关系启动后可以由软件修改(通过修改SYSCFG启动器的MEM_MODE位域)。

这样,Flash、System memory和SRAM分别可以从别名区和原始地址处访问。

Flash:访问地址为0x00000000或0x08000000

System memory:访问地址为0x00000000或0x1FF00000

SRAM:启动时地址为0x00000000或0x20000000(STM32Fxx的参考手册上说,启动后只能在0x20000000开始访问,即启动后这个映射消失,需要重定位中断向量表,这是特例)

System memory内置了ST提供的boot loader,可以通过该boot loader下载程序到Flash中。

用户程序实际只能存储在Flash中,且能在Flash和SRAM中执行(因为cortex-m3核采用哈佛结构,代码可直接在Flash运行,冯•诺依曼结构则必须将代码拷贝至RAM运行)。

Flash就像是电脑的硬盘,用于存储代码。

System memory就像是电脑的ROM,里面的程序有芯片厂商写好,用户不可改写。

SRAM就像是电脑的内存,里面的数据都是动态的,掉电就丢失的,用于建立堆栈等。

对于用户代码来说,只需要考虑从Flash启动和从SRAM启动两种情况。

了解上述映射关系之后,就可以讨论中断向量表了。

Cortex-m3核的中断向量表是不变的(中断向量表每一项为4个字节,中断向量表的第一项:栈顶,中断向量表的第二项:复位向量……,中断向量表每项内容可以看官方的启动文件,或者查看相关的手册),只需要用户设定表头的地址。

默认情况下,从Flash启动,中断向量表从Flash的起始地址(0x08000000)开始存放。同时映射到0x00000000处。向量表偏移寄存器(VTOR)的值为0x00000000(实际映射到0x08000000)。

若从SRAM启动,中断向量表还是存放在Flash中(Flash才能固化存储,SRAM只能加电才有效),只不过拷贝到SRAM的首地址0x20000000处。此时向量表偏移寄存器(VTOR)的值也是0x00000000(实际映射到0x20000000)。而启动过程结束后,这个特殊的映射不复存在了(据参考手册推测的),所以,需要修改向量表偏移寄存器(VTOR)的值为0x20000000以后的值(其中TBLOFF位域要是0x200的倍数,这个是字对齐的要求(见《cortex-m3编程手册》),由于STM32的中断向量一共有68+16=84个,应该把这个数增加到下一个2的整数倍即128,然后换算成地址范围128*4=512,就得到了0x200),以便处理中断。

因此,无论用哪种模式启动,复位时栈顶指针总能在0x00000000(或0x08000000)处找到,而复位向量总能在0x00000004(或0x08000004)处找到。

又根据《cortex-m3编程手册》,复位时,CPU从0x00000000处获取栈顶指针MSP(默认使用主堆栈),从0x00000004处获取程序计数器PC(复位向量)。则印证了上述说法。

下面通过追踪STM32L1xx标准外设库V1.2.0(STM32L1xx_StdPeriph_Lib_V1.2.0)中的启动文件startup_stm32l1xx_md.s来验证上述说法。

先复位CPU,从寄存器窗口可以看到:

R13(SP)的值为0x20000FC0(MSP),R15(PC)的值为0x08000420。这两个值就是地址0x00000000和地址0x00000004处存放的内容。由于是从Flash启动,所以,实际上也就是0x08000000和0x08000004处存放的内容。可以从内存窗口中看出:

内存地址

内容

0x00000000

C0H

0x00000001

0FH

0x00000002

00H

0x00000003

20H

0x00000004

21H

0x00000005

04H

0x00000006

00H

0x00000007

08H

从0x00000000处,取出1个字(32位,4个字节),为0x20000FC0,是堆栈指针SP的值。

从0x00000004处,取出1个字(32位,4个字节),为0x08000421,而PC指针读回0x08000420

它把末位的1变成了0,这个是由于内存对齐造成的,因为cortex-m3核PC的LSB一定读回0,因此指令至少是半字对齐的(《cortex-m3编程手册》)。

同样的结果在反汇编窗口也可以得到:

继续跟踪:此时程序寻址到PC指针指示的地址0x08000420处执行

此处,指令 LDR R0,=SystemInit 将函数SystemInit的入口地址传给R0,从反汇编窗口可以看到指令被写成LDR r0,[pc,#36],表明SystemInit的入口地址在存放在PC指针偏移36处,即0x08000420(+0x4)+0x24=0x08000448(因为CM3内部使用了指令流水线,读PC时返回的值是当前指令的地址+4《Cortex™-M3权威指南》)下一条指令在0x08000422处,BLX R0 将R0的值传给PC(必须保证加载到PC的数值是奇数(即LSB=1),传给PC后,PC的LSB读回0),调用SystemInit函数。这个函数里面开启了外部晶振,设置了PLL,除能了所有中断,设置了时钟为32MHz,并且重定位中断向量表在0x08000000处(这句在Flash启动时可以不需要,因为能从0x00000000映射到0x08000000)。从该函数返回后,用同样的方式调用C/C++标准实时库的__main函数,进行了一些处理(如用户堆栈初始化等,具体可以跟踪汇编代码,这部分网上已有过分析,不再赘述)后,调用main函数,进入C语言环境。

关于启动文件的中断向量表部分:

以上是在建立中断向量表(DCD伪指令表明,为每个中断向量分配了1个字的连续空间)。

实际上,对启动文件分析的资料已经很多,也足够详细,此处就不再赘述了。

基于上面分析,可以总结STM32启动的大体过程。

1、上电复位,CPU从0x00000000处获取栈顶指针MSP(默认使用主堆栈),从0x00000004处获取程序计数器PC(复位向量)。

2、MSP指针必然指向SRAM区的,因为堆栈必须建立在该区。

3、根据PC的值找到复位中断处理函数Reset_Handler

4、调用SystemInit函数。

5、调用__main函数,初始化用户堆栈

6、调用main函数,进入C语言环境

STM32启动过程解读与跟踪验证的更多相关文章

  1. 专家揭秘:STM32启动过程全解

    电子发烧友网核心提示:本文主要阐述了STM32启动过程全面解析,包括启动过程的介绍.启动代码的陈列以及深入解析. 相对于ARM上一代的主流ARM7/ARM9内核架构,新一代Cortex内核架构的启动方 ...

  2. STM32启动过程--启动文件--分析

    一.概述 1.说明 每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道.通过了解启动文件,我们可以体会到处理器的架构.指令集.中断向量安排等内容,是非常值得玩味的. ST ...

  3. STM32启动文件深度解析

    STM32启动过程全面解析,包括启动过程的介绍.启动代码的陈列以及深入解析.相对于ARM上一代的主流ARM7/ARM9内核架构,新一代Cortex内核架构的启动方式有了比较大的变化.ARM7/ARM9 ...

  4. linux内核学习之三 跟踪分析内核的启动过程

    一   前期准备工作       1 搭建环境 1.1下载内核源代码并编译内核 创建目录,并进入该目录: 下载源码: 解压缩,并进入该目录:xz -d linux-3.18.6.tar.xz tar ...

  5. Linux第三周——跟踪分析内核的启动过程

    跟踪分析内核的启动过程实验 张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这周主要学习的是对内核 ...

  6. Linux内核分析之跟踪分析Linux内核的启动过程

    一.实验过程 使用实验楼虚拟机打开shell cd LinuxKernel/ qemu -kernel linux-/arch/x86/boot/bzImage -initrd rootfs.img ...

  7. openstack学习笔记一 虚拟机启动过程代码跟踪

    openstack学习笔记一 虚拟机启动过程代码跟踪 本文主要通过对虚拟机创建过程的代码跟踪.观察虚拟机启动任务状态的变化,来透彻理解openstack各组件之间的作用过程. 当从horizon界面发 ...

  8. 跟踪分析Linux内核的启动过程--实验报告 分析 及知识重点

    跟踪分析Linux内核的启动过程 攥写人:杨光  学号:20135233 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.stud ...

  9. 20135202闫佳歆--week3 跟踪分析Linux内核的启动过程--实验及总结

    实验三:跟踪分析Linux内核的启动过程 一.调试步骤如下: 使用gdb跟踪调试内核 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd r ...

随机推荐

  1. SQL server 数据库用户表名称

    转自(http://blog.163.com/jlj_sk/blog/static/22579293200861422833924/) 取得SQL server 数据库中 所有用户表名称 select ...

  2. SpringMVC启动和执行流程

    Spring框架大家用得很多,相当熟悉,但是我对里面的运作比较好奇,例如bean的加载和使用,和我们定义的配置文件有什么联系;又例如aop在什么时候起作用,原理又是怎样.经过一个了解后,整理了启动和执 ...

  3. HDU 3779 Railroad(记忆化搜索)

    Railroad Time Limit : 4000/2000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Subm ...

  4. cx_freeze打包EXE文件

    创建setup.py文件 import osimport sysfrom cx_Freeze import setup, Executable build_exe_options = dict(pac ...

  5. MongoDB中MapReduce不同的写法,不同的结果

    MapReduce有两种写法,也可以说mongodb所有的命令行都有两种写法. 但突然间发现MapReduce不同的写法会有不同的结果,接下来我们一起来看: 第一种:直接使用扩展属性. 1)emit函 ...

  6. Ubuntu与ROS的Docker桌面系统与ROS在线练习课程(在线Linux虚拟机)

    ROS在线练习课程正在逐步完善中,目前以ROS官网中文资料制作,可参考: https://www.shiyanlou.com/courses/854  邀请码 U23ERF8H 安装Ubuntu+RO ...

  7. Amazon面试题

    亚马逊面试题: 如下所示的Map中,0代表海水,1代表岛屿,其中每一个岛屿与其八领域的区间的小岛能相连组成岛屿群.写代码,统计Map中岛屿个数. /* Q1. Map [ 0 0 0 0 0 0 0 ...

  8. golang slice 与list 的性能分析。

    一 · 比较slice 与 list 遍历创建和添加元素速度. package main import ( "time" "fmt" "contain ...

  9. Mac安装三方软件

    安装提示“xxx软件已损坏,打不开,您应该将它移到废纸篓”的提示,其实并不是软件本身有问题,而是Mac系统的一个安全机制问题步骤1:Spotlight搜索(快捷键:command+空格或右上角搜索的符 ...

  10. Scala函数式编程——近半年的痛并快乐着

    从9月初啃完那本让人痛不欲生却又欲罢不能的<七周七并发模型>,我差不多销声匿迹了整整4个月.这几个月里,除了忙着讨食,便是继续啃另一本"锯著"--<Scala函数 ...