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()函数,运行用户代码。
二. 存储器映射的建立
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