堆栈是一种存储器的使用模型,如图3.7所示。它由一块连续的内存和一个堆栈指针(SP)组成,用于实现“后进先出”的缓冲区。其最典型的应用,就是在数据处理前先保存寄存器的值,再在处理任务完成后从中恢复先前保护的这些值。在Cortex-M3中,有专门的指令负责堆栈操作——PUSH和POP,PUSH将数据压入堆栈,POP将数据从堆栈中取出,需注意的是,POP操作并不会删除原来堆栈中的数据。在执行PUSH和POP操作时,堆栈指针会由硬件自动调整它的值,以避免后续操作破坏先前的数据。
图3.7 堆栈内存的基本概念图
Cortex-M3使用的是“向下生长的满栈”模型。堆栈指针SP指向最后一个被压入堆栈的32位数值。在下一次PUSH操作时,SP先自减4,再存入新的数值。堆栈的PUSH操作如图3.8所示。
图3.8 堆栈的PUSH操作
POP操作刚好相反,先从SP指针处读出上一次被压入的值,再把SP指针自增4,如图3.9所示。
图3.9 堆栈的POP操作
在Cortex-M3中,寄存器R13是堆栈指针SP,在程序中可以把R13写作SP,并且PUSH指令和POP指令默认使用SP。Cortex-M3处理器内核中共有两个堆栈指针,于是也就支持两个堆栈,但是任一时刻只能使用其中的一个。当引用R13时,引用到的是当前正在使用的那一个,另一个必须用特殊的指令来访问,可以通过MRS/MSR指令来访问指定的堆栈指针。这两个堆栈指针分别是:
①主堆栈指针(MSP):也写作SP_main,复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程)。(www.xing528.com)
②进程堆栈指针(PSP):也写作SP_process,用于普通的用户线程中(不处于异常服务例程中时)。
要注意的是,并不是每个程序都需要使用两个堆栈指针,简单的应用程序只使用MSP就够了。另外,Cortex-M3堆栈指针的最低两位永远是0,这意味着堆栈总是4字节对齐的,也就是说,他们的地址必须是0x4,0x8,0xc,…。事实上,R13的最低两位被硬件线路连接到0,因此读的时候总是为0。
使用PUSH、POP指令访问堆栈的汇编语言语法如下例所示:
请注意后面C程序风格的注释,它表明了Cortex-M3是采用“向下生长的满栈”。因此,在PUSH新数据时,堆栈指针先减一个单元,然后再将新数据放入堆栈。通常在进入一个子程序后,第一件事就是把子程序用到的寄存器先PUSH入堆栈中,在子程序退出前再POP曾经PUSH的那些寄存器。另外,PUSH和POP还能一次操作多个寄存器,如下所示:subroutine_1
为了避免系统堆栈因应用程序的错误使用而毁坏,可以给应用程序专门配一个堆栈,不让它共享操作系统内核的堆栈。在这个管理制度下,运行在线程模式的用户代码使用PSP,而异常服务例程则使用MSP。这两个堆栈指针的切换是全自动的,就在出入异常服务例程时由硬件处理。进入异常时的自动压栈使用的是PSP,将数据压入进程堆栈,进入异常后才自动改为MSP,退出异常时切换回PSP,并且从进程堆栈上弹出数据。
在特权级下,可以指定具体的堆栈指针,而不受当前使用堆栈的限制,示例代码如下:
通过读取PSP的值,操作系统就能够获取用户应用程序使用的堆栈,进一步地就知道了在发生异常时,被压入堆栈的寄存器内容,而且还可以把其他寄存器进一步压栈(使用STMDB和LDMIA的书写形式)。操作系统还可以修改PSP,用于实现多任务中的任务上下文切换。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。