[开发工具]

上电之后,main函数之前-Cortex M处理器的启动过程

[复制链接]
5586|38
手机看帖
扫描二维码
随时随地手机跟帖
zhanzr21|  楼主 | 2018-3-10 00:37 | 显示全部楼层 |阅读模式
上电之后,main函数之前-Cortex M处理器的启动过程以及C Lib初始化

Keil MDK工具的实现为例
Picture1.png
这个是准备演讲的一个话题,因为某些原因演讲取消了.想了想准备了一场,材料别浪费了,也许对感兴趣的读者有点参考价值,所以当贴子发出来.才疏学浅,请高手多多指教.
Picture2.png

zhanzr21|  楼主 | 2018-3-10 00:38 | 显示全部楼层
目录
}1.Cortex M系列内核的启动过程以及向量存储

}2.具体到STM32系列启动过程.

}3.以最常见的Flash启动为例看启动的最初位置

}4.发生中断时的情形

}5.复位向量

}6.__mainmain

}7.bootloader的注意要点(MDK版本例子)



使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:38 | 显示全部楼层
Cortex M系列内核的启动过程以及向量存储
}Cortex M都在0x00000000的地址启动,第一个word存放MSP的初始值,第二个word开始存放向量,根据内核不同向量数目不同.


}对于除了Cortex M0的内核,向量可以有偏移量,为写bootloader与多应用提供了方便.VTORSCB.



使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:41 | 显示全部楼层
本帖最后由 zhanzr21 于 2018-3-10 00:43 编辑

SCB Type in Cortex M0
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
        uint32_t RESERVED0;
  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset ControlRegister */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
        uint32_t RESERVED1;
  __IOM uint32_t SHP[2U];                /*!< Offset: 0x01C (R/W)  System Handlers Priority Registers. [0] isRESERVED */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
} SCB_Type;


可以看出Cortex M0的SCB中没有VTOR,即Cortex M0不支持向量表重新定位,但是某些Cortex M0的向量表的外部向量部分位于RAM区,间接地提供了向量表重定向的方法.

但是STM32F0的向量表都在Flash中, 这样做也是符合Cortex M0追求简单的设计目标.

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:43 | 显示全部楼层
SCB Type in Cortex M0+
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */

#if (__VTOR_PRESENT == 1U)
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
#else
        uint32_t RESERVED0;
#endif

  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
        uint32_t RESERVED1;
  __IOM uint32_t SHP[2U];                /*!< Offset: 0x01C (R/W)  System Handlers Priority Registers. [0] is RESERVED */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
} SCB_Type;
可以看出Cortex M0+的VTOR是可选的, 也就是由实现者来决定是否包含VTOR以实现向量表重定向, 一般而言目前见到的Cortex M0+内核都实现了此功能. 比如STM32L0系列.

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:47 | 显示全部楼层
SCB Type in Cortex M3
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
  __IOM uint8_t  SHP[12U];               /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
  __IOM uint32_t CFSR;                   /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */
  __IOM uint32_t HFSR;                   /*!< Offset: 0x02C (R/W)  HardFault Status Register */
  __IOM uint32_t DFSR;                   /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */
  __IOM uint32_t MMFAR;                  /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */
  __IOM uint32_t BFAR;                   /*!< Offset: 0x038 (R/W)  BusFault Address Register */
  __IOM uint32_t AFSR;                   /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */
  __IM  uint32_t PFR[2U];                /*!< Offset: 0x040 (R/ )  Processor Feature Register */
  __IM  uint32_t DFR;                    /*!< Offset: 0x048 (R/ )  Debug Feature Register */
  __IM  uint32_t ADR;                    /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */
  __IM  uint32_t MMFR[4U];               /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */
  __IM  uint32_t ISAR[5U];               /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */
        uint32_t RESERVED0[5U];
  __IOM uint32_t CPACR;                  /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */
} SCB_Type;
从Cortex M3开始, VTOR成为标配. 注意Cortex M3基于ARMv7M架构, Cortex M0/M0+/M1基于ARMv6M. 且Cortex M3是先推出市场的Cortex M系列, 后来因为有简化的需求才推出基于v6M的Cortex M0/M0+/M1. M1是专用于软核实现的可综合IP. 这里先不扯远.

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:49 | 显示全部楼层
SCB Type in Cortex M4
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
  __IOM uint8_t  SHP[12U];               /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
  __IOM uint32_t CFSR;                   /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */
  __IOM uint32_t HFSR;                   /*!< Offset: 0x02C (R/W)  HardFault Status Register */
  __IOM uint32_t DFSR;                   /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */
  __IOM uint32_t MMFAR;                  /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */
  __IOM uint32_t BFAR;                   /*!< Offset: 0x038 (R/W)  BusFault Address Register */
  __IOM uint32_t AFSR;                   /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */
  __IM  uint32_t PFR[2U];                /*!< Offset: 0x040 (R/ )  Processor Feature Register */
  __IM  uint32_t DFR;                    /*!< Offset: 0x048 (R/ )  Debug Feature Register */
  __IM  uint32_t ADR;                    /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */
  __IM  uint32_t MMFR[4U];               /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */
  __IM  uint32_t ISAR[5U];               /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */
        uint32_t RESERVED0[5U];
  __IOM uint32_t CPACR;                  /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */
} SCB_Type;
可以看出Cortex M4与Cortex M3十分类似. M4对于M3增强性能体现在: 可能的FPU接口, SIMD指令集扩充, 当然还有参考实现工艺的提升.

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:52 | 显示全部楼层
Cortex M7当然也有VTOR了,非此处重点,先不提.

具体到STM32系列的启动细节.
}首先STM32一般有三种启动方式, 这是硬件在启动时将0x00000000映射到哪个地址所决定.

}BOOT1 BOOT0 不同配置所对应的启动地址:

   • Boot from user Flash
   • Boot from system memory
   • Boot from embedded SRAM

}1.Flash启动,对于STM32来讲一般在0x8000000

}2.系统memoryST官方的bootloader, 固化在flash

}3.SRAM,方便快速调试,SRAM运行比Flash运行快,因为没有等待延迟,所以某些关键函数可以置于SRAM中运行.


使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:54 | 显示全部楼层
Picture3.png
此是官方的手册中关于映射的说明, 基本上所有的STM32系列都是类似.作为上一楼的配图.

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:56 | 显示全部楼层
以最常见的Flash启动为例看启动的最初位置
}此时硬件连接上,BOOT1可以不管(如果有引出可以自由做其他IO使用),BOOT0拉低,直接接地或者10K电阻拉低都是常见的做法.

启动文件(MDK ARM版本)示例节选:
; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp                   ; Top of Stack
                DCD     Reset_Handler                  ; Reset Handler
.....
__initial_sp即是初始的MSP,指向stack顶部往上一个word.对于PSP,如果需要使用必须初始化,因为没有初始值. 此值由连接器生成.

Reset_Handler以及下面的一大排都是向量.这一块地址对于有VTOR的内核来讲可以放在存储器的任意位置(有对齐要求)

对于无
VTOR内核来讲此段必须位于Flash的初始位置(对于STM32来讲就是0x8000000.


如果写bootloader要跳转,注意不是跳转到__initial_sp这个位置,而是Reset_Handler这个位置也就是__initial_sp+4

前提当然是要将跳转目的的向量与向量偏移设置好.

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:58 | 显示全部楼层
发生中断时的情形
根据发生的中断号,硬件自动生成向量所在的地址进行跳转.对于前面的几个中断,不可软件屏蔽:


DCD    Reset_Handler                  ; Reset Handler
DCD    NMI_Handler                    ; NMI Handler
DCD    HardFault_Handler              ; Hard Fault Handler
以上是对于Cortex M0而言, 对于Cortex M3即以后的内核, 还有几个其他Fault属于此类

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 00:59 | 显示全部楼层
}如果用户没有提供Handler,则使用启动文件中的默认Handler.如用户有提供Handler,则自动使用用户的Handler.类似于C++中的覆盖现象.

注意
WEAK关键字.
Default_Handler PROC

                EXPORT  WWDG_IRQHandler                   [WEAK]                                       
......................
           
                B       .

                ENDP

                ALIGN



使用特权

评论回复
li880wert| | 2018-3-10 10:58 | 显示全部楼层
给力,赞个

使用特权

评论回复
mmuuss586| | 2018-3-10 11:03 | 显示全部楼层
不错,感谢分享;

使用特权

评论回复
tototm| | 2018-3-10 11:31 | 显示全部楼层
不错,很深入!

使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 13:11 | 显示全部楼层
昨天太困了,发到一半就睡了, 继续贴完.

}此默认Handler类似于C语言中的:
while(1)
{
;
}



使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 13:12 | 显示全部楼层
本帖最后由 zhanzr21 于 2018-3-10 13:15 编辑

复位向量
; Reset handler
Reset_Handler    PROC
        EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
这就是系统复位后,进行的第二步操作, 第一步是将__init_sp装入MSP

首先要调用SystemInit,此函数配置默认的时钟,存储器(如果有需要配置的存储器,如外接SRAM,SDRAM,如果这些存储器不是初期运行所必需的存储器则可以在进入用户程序之后再初始化),还有向量偏移(如果有VTOR且用户要求向量偏移).


SystemInit代码节选:
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
  SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
比较关键的是如果有外部存储要使用,则需要在SystemInit中初始化外部存储

还有向量表如需重定向, 需要定义VECT_TAB_OFFSET,此为偏移量, 有对齐要求


使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 13:16 | 显示全部楼层
注意SystemInit这个函数名为CMSIS标准所规定,用户可以不使用此名称的函数来完成上述功能,也可以玩全不调用任何函数直接下一步,那么违反了CMSIS标准,如无特殊理由不建议违反CMSIS标准.



紧接着调用__main,注意这个并非用户的main函数,__main中进行了一些操作之后才会调用用户的main函数.此处为ARM Keil MDK的行为,对于其他编译器如GCC,此处名称不同,__main不是CMSIS规定的函数名字.


使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 13:17 | 显示全部楼层
__mainmain
__mainmain主要是stack, heap初始化,以及其他的一些初始化,可以称作C Runtime初始化.如果程序有用到C++,还包括C++环境的初始化.此处省去C++内容以做简化,因为C++目前看来使用还不普遍.


对于CLib的使用,可以有多种选择,对于ARM Keil MDK,可以选择Microlib或者Standard Lib. 对于GCC系列工具可选Nanolib或者Standard Lib(大多是newlib实现).不管是microlib还是nanolib,目的只有一个:就是省去一些嵌入式环境少用到的功能以求镜像尺寸的减小.此处以MDKmicrolibstandard lib对比为例,看省去了哪些功能.



使用特权

评论回复
zhanzr21|  楼主 | 2018-3-10 13:21 | 显示全部楼层
Micro Lib 与 Standard Lib区别, 此处主要从ARM网站直接摘抄, 没有一一翻译, 很简单不难看懂:

Microlib is not compliant with the ISO C librarystandard. Some ISO features are not supported and others have lessfunctionality.

Microlib is not compliant with the IEEE 754 standard forbinary floating-point arithmetic.

Microlib is highly optimized for small code size.

Locales are not configurable. The default C locale isthe only one available.

main() must not be declared to take arguments and mustnot return.

Microlib provides limited support for C99 functions.

Microlib does not support C++.

Microlib does not support operating system functions.

Microlib does not support position-independent code.


Microlib does not provide mutex locks to guard againstcode that is not thread safe.

Microlib does not support wide characters or multibytestrings.

Microlib does not support selectable one or two regionmemory models as the standard library (stdlib) does. Microlib provides only thetwo region memory model with separate stack and heap regions.

Microlib does not support the bit-aligned memoryfunctions _membitcpy[b|h|w][b|l]() and membitmove[b|h|w][b|l]().


Microlib can be used with either --fpmode=std or--fpmode=fast.

The level of ANSI C stdio support that is provided canbe controlled with #pragma import(__use_full_stdio).

#pragma import(__use_smaller_memcpy) selects a smaller,but slower, version of memcpy().

setvbuf() and setbuf() always fail because all streamsare unbuffered.

feof() and ferror() always return 0 because the errorand EOF indicators are not supported.

主要宗旨是减少功能,为代码尺寸优化而考虑, 此外与C++的兼容性也不完全,使用C++的程序员最好不使用microlib,以免出现难以debug的冲突.


根据笔者的测试Dhrystone中,standardlib版本的性能大大超出microlib版本的性能,而对于Coremark,两个版本的性能类似.说明Coremark测试对于CPU/编译器的评估更加独立,不依赖运行时条件.


做完Runtime的初始化之后,即跳入用户的main函数,开始用户代码的运行过程.




使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:每天都進步

91

主题

1005

帖子

34

粉丝