前面学习了怎么样确定CPU加载运行第一行代码,在那里发现需要加载栈指针,那么你也许会问为什么要首先加载栈指针呢?难道栈就是这么重要?在这里,我们就来探讨一下栈的问题,比如栈的位置和大小。
在现代的CPU技术里,往往有中断系统,这就决定了CPU必须有栈的结构,因为中断出现时,需要把当时CPU运行的数据进行保存,以便中断处理之后再恢复回来。如下图这样处理:
如果没有栈,就没有办法保存当前的数据,必然被中断程序里运行的代码把当前的数据修改了,这样就没有办法恢复到原始状态了。从上图可以看到中断调用时,有中断栈,因此在CPU运行之后,时刻有可能被中断,这样需要栈来保存相应的数据。另外,我们来看一下C语言的运行,当一个函数被调用时,它是这样处理的:
从C语言运行过程来看,也需要调用栈来保存传递的参数、以及函数里的临时变量和局部变量,这样才可以运行C编译器编译的代码。基于上面两个原因,必须设置好栈的位置和大小才可以运行,所以第一行代码里,就设置了栈顶位置。
在前面也知道设置栈顶的变量为__initial_sp,但是这个栈顶元素是怎么样确定它的位置和大小的呢?现在就来解开这个谜题。我们可以看到汇编文件里有这么一段开头的代码:
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
这段代码放置在汇编文件最开始位置,同时通过AREA来描述这段代码功能。再来仔细地学习一下汇编指令AREA,它的功能是这样:
AREA指令指示汇编程序汇编新的代码节或数据节。节是不可分的已命名独立代码或数据块,它们由链接器处理。
从AREA后面的STACK参数,说明这段是栈段,NOINIT参数是表示不用进行初始化,READWRITE参数是表示这段需要进行读写的操作,ALIGN是表示按2的3次方来对齐,也就是按8字节的方式对齐。接着下来使用SPACE指令来分配一段连续的内存空间,大小是等于Stack_Size,而Stack_Size是由前面的代码指定为0x00000400。到这里就明白了栈的位置和大小,如果调用栈的层次太多,栈不够用时,就可以修改这里,把栈大小的变量Stack_Size改大,如果一个程序里没有函数调用,或者嵌套中断,就可以减小栈的大小,这样可以留出更多的内存空间来使用。
接着下来查看读写段放在内存什么位置,可以通过连接描述文件project.sct来查看,可以看到下面这行:
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
上面定义内存的开始位置为0x20000000,因此这段栈安排的位置,就是内存的开始位置,再加上栈的大小,就是栈顶元素了,可以通过文件project.map来查看它的值:
在这里可以看到它的值为0x20000400,这个就是栈顶的值。通过上面的学习,就可以理解栈的设置,以及为什么需要设置栈,以及栈设置的代码为什么要这样写。
https://blog.csdn.net/caimouse/article/details/51749579
上一篇:玩转STM32(14)运行第一行代码
下一篇:玩转STM32(10)CPU的脉搏
推荐阅读最新更新时间:2024-11-11 16:14
设计资源 培训 开发板 精华推荐
- L4940D2T5-TR 分布式电源的典型应用电路,带有板载 L4940 和 L4941 低压降稳压器
- LD29300 3 A 极低压降稳压器的典型应用电路
- LT6656ACS6-2.048 的典型应用,用于基本连接的 2.048V 电压基准
- ESP12f下载器
- AL9901EV1,基于具有集成 MOSFET 的 AL9901 通用高压 LED 驱动器的评估板
- DEV-14812,SparkFun Red Board Turbo - SAMD21 开发套件
- MCP1501T-25E/RW 2.500V 负参考电压的典型应用电路
- STEVAL-IKR002V4D、SPIRIT1 868-MHz 低数据速率收发器子板
- LF90CPT-TR 9V 顺序极低压降稳压器多输出电源的典型应用
- AD5343 并行接口、双电压输出、12 位 DAC 的典型应用