出处:http://blog.csdn.net/cwcwj3069/article/details/10828007 补充资料:https://wenku.baidu.com/view/4f9d2eb11a37f111f1855bfc.html 补充资料:https://wenku.baidu.com/view/6132c9c158f5f61fb73666db.html 补充资料:https://segmentfault.com/q/1010000004829859
Cortex_m3的启动过程 一.arm的启动过程 arm的启动代码一般是用汇编写的,在堆栈建立以后才可以运行C代码,因为C函数调用需要把参数,函数返回地址入栈,堆栈没有建立不能运行C代码。
应用程序启动过程 应用程序启动过程: 1.映像入口地址,一般为0X00000000地址,也可以指定为其他的地址,硬件复位起来,从地址0x00000000处取指,地址0x00000000处放的复位服务函数的地址,就会进入复位服务函数。在复位函数里做一些系统的初始化,然后调用系统函数_main(); 2._main 直接跳转到 __scatterload,__scatterload 执行代码和数据复制以及 ZI 数据的清零。根据分散加载文件,拷贝RW数据到RAM,在RAM空间里建立ZI的数据空间,建立运行时的映像存储器映射,然后跳转到 __rt_entry(运行时的入口)则负责初始化 C 库。还设置应用程序的栈和堆,初始化库函数及其静态数据。 3.这时应用程序的堆栈建立了,跳转到main()函数,运行用户代码。 GD32F130FXP6启动文件: ; Reset handler routine
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT System_Init
IMPORT User_Reset_Handler
LDR R0, =User_Reset_Handler
BLX R0
LDR R0, =System_Init
BLX R0
LDR R0, =__main
BX R0
ENDP
刚开始,我直接调用的Reset_Handler,执行完了后,就直接进入HardFault_Handler。后来查看资料,采知道Reset_Handler,它的作用就是将保存于flash中的初始化数据复制到sram中,做一些系统的初始化。所以自己定义的Reset_Handler要实现这部分功能,但是我又看不见代码,只能调式看汇编。我采取了另外一个办法,直接在Reset_Handler执行完之后,System_Init执行之前,加入一个自己的User_Reset_Handler,这样就可以解决问题,(但是仍然可能存在问题,刚上电时候,电源不稳,初始化SRAM里面数据,可能不对)。
二. 存储器映射的建立 1.编译链接生成的ELF文件
ELF文件格式 链接器根据输入节的属性在一个区内对它们进行排序。 具有相同属性的输入节在区内形成相邻块。链接生成的ELF文件里的数据节。 RO:包括代码和只读数据(.init .text .rodata) RW:读写数据(.data) ZI:未初始化的数据,在装载区不分配空间,执行区才分配空间。(.bss)
2. 映像的加载区和执行区 加载区: 根据映像加载到内存时所在的地址(即映像开始执行之前的位置)。 执行区: 映像执行时所在的地址。 根区: 加载区和执行区的地址相同。
加载区和执行区 一般下载到FLASH里的2进制文件就放在加载区,上图中的0X0000-0X4000空间。应用程序启动时,__scatterload函数根据分散加载文件把RW数据节拷贝到RAM空间,然后在RAM空间分配ZI数据节的空间。因为对RAM空间里数据的读写比FLASH快。一般把RW的数据拷贝到RAM。RO节的数据不做处理。这样运行时的存储器空间就建立起来了。
二.堆栈的设置 __user_initial_stackheap() 可用 C 或 ARM 汇编语言来编写。它必须返回以下参数: • r0 中的堆基址; • r1 中的栈基址; • r2 中的堆限制(双区模型); • r3 中的栈限制(双区模型)。 堆栈的模式有2种:单区模式,双区模式。 1.单区模型 默认情况下为单区模型,应用程序的堆和栈在同一存储器区中互相朝向对方增长,其中栈从地址 0x40000 向下增长,堆从地址 0x20000 向上增长。将相应的值加载到寄存器 r0 和 r1,然后返回。r2 和 r3 保持不变,因为在单区模型中不使用堆限制和栈限制。
EXPORT __user_initial_stackheap __user_initial_stackheap LDR r0, =0x20000 ; LDR r1, =0x40000 ; ; r2 not used (HL) ; r3 not used (SL) MOV pc, lr
2.双区模型 使用双区模型必须使用汇编命令 IMPORT 引入符号 __use_two_region_memory。将堆和栈分别放置在存储器不同的区中,__user_initial_stackheap() 建立的专用堆限制来检查堆。需要设置堆栈的长度。
汇编代码的实现,。栈从 0x40000 向0x20000 的限制向下增长。为使用该栈限制,所有使用此实现的模块必须进行编译以便进行软件栈检查。堆从 0x28000000 到 0x28080000 向上增长。
IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR r0, =0x28000000 ;HB LDR r1, =0x40000 ;SB LDR r2, =0x28080000 ;HL LDR r3, =0x20000 ;SL MOV pc, lr
|