有些问题疑惑了很久,过来问下amanda_s,也希望大家都能够讨论、相互学习下。
STM32F4在MDK-ARM中的启动代码是这样的:
定义了READWRITE属性的栈区;定义了READWRITE属性的堆区;定义了READWRITE属性的异常向量表;定义了READONLY属性的异常向量处理函数,和一个堆栈的初始化汇编代码。
1.假设从用户flash启动,硬件加电稳定后,系统从0x00出获取了MSP,接着PC读取了Reset_Handler的地址值(还未开始执行)。
我的问题是:此时内核直接读取了Flash是吗?
我的理解是内核直接通过地址总线读取了0x00处的sp,和0x04处的一个地址(就是复位向量地址值)。
2.系统通过PC的值开始执行如下代码(songbin,如何添加带有格式的代码啊?)
可以看到系统会转向执行SystemInit,在SystemInit中,会调用SystemInit_ExtMemCtl(我用了外部的SDRAM)。
我的问题是:此时在SystemInit及其迭代调用的任何函数中不能出现任何的C语言相关的变量,如果全局的、局部的等,因为此时还未初始化C语言需要的堆、栈等必须的配置。是这样的吗?
我看到,有些局部的变量被定义为了 register uint32_t index;这说明还是在直接使用寄存器,并未使用C相关的东西。
3.接着系统调用了__main,我在《Libraries and Floating-Point Support User Guide》找到了__rt_entry、__initial_sp等编译器相关的库函数,但是唯独没有找到__main的详细说明,这是为什么、在哪可以找到?
我在"Initialization of the execution environment and execution of the application"小结中,找到了如下内容
The entry point of a program is at __main in the C library where library code:
Copies non-root (RO and RW) execution regions from their load addresses to their execution addresses. Also, if any data sections are compressed, they are decompressed from the load address to the execution address.
Zeroes ZI regions.
Branches to __rt_entry.
这个__main也可以自己写,它主要是为了C语言能够执行,完成对内存的初始化等。
__rt_entry是这样说明的:
The library function __rt_entry() runs the program as follows:
__rt_entry()
Sets up the stack and the heap by one of a number of means that include calling __user_setup_stackheap(), calling __rt_stackheap_init(), or loading the absolute addresses of scatter-loaded regions.
__user_setup_stackheap()
__rt_stackheap_init()
Calls __rt_lib_init() to initialize referenced library functions, initialize the locale and, if necessary, set up argc and argv for main().
__rt_lib_init()
main()
For C++, calls the constructors for any top-level objects by way of __cpp_initialize__aeabi_.
Calls main(), the user-level root of the application.
From main(), your program might call, among other things, library functions.
Calls exit() with the value returned by main().
exit()
__rt_entry()完成了堆栈的初始化、初始化C/C++库,并转向C的main函数开始用户程序代码,如果main返回则调用exit(),但exit()干什么了不知道?如果用户从main中返回,那么是否从exit()也返回了,这样代码就回到了Reset_Handler,此时系统应该是停止了吧?
另外,在调用main之前,都不应该使用C相关的变量分配,因为在main之前还未对C/C++的库运行做好准备,是吧?
回答你第2问题:
Reset之后, Cortex-M处理器会自动从vector table 获取SP的初始值,因此只要这个SP指向的是有效的memory, 栈还是可以用的。
Reset之后,scatter-loading 和库初始化并没有做。 只要一个变量的load address=execution address, 不需要scatter-loading,那就可以在SystemInit中访问。
建议:
你的问题比较细节,你可以提供keil的serial number, 如果有support agreement 的话 ARM technical support 团队会提供进一步支持。你可以发邮件到 support-sw@arm.com
谢谢Zenon Xiu的回复。针对您的几句话:
1.我认为即使MSP中的栈指向了内存,并且内存也可用,但这也没什么用,因为这里(Reset_Handler)的汇编不需要栈,C还未初始化(当然也没有C代码会用到这个栈)。我的疑问是,在main(不是__main)之前都不应该使用C的一些局部的或全局的变量(即使用也像初始化SDRAM一样,使用寄存器关键字来声明),应该是这样的吧?
2.你的load address=execution address,也许是对应了我上述的第1个疑问,就是只要这个PC的值是一个有效的函数地址,程序就可以按照预定的汇编程序执行。内核通过总线把代码读入执行序列开始执行。
我使用MDK-Lite测试STM32F429IDISCOVERY评估版。
还使用了STM32F030开发板做测试,这个时候使用的是免费的SN: U1E21-CM9GY-L3G4L。
在上述情况下,为了想知道一些__main细节的话(但help中又没有),该如何呢?
或许谁知道在GCC中是如何处理的呢?