3.7 Cortex-M3 的堆栈实现

http://www.softwarehistory.net/h/Cortex_M3_DefinGuide/25.php

3.7 Cortex-M3 的堆栈实现

《Cortex-M3 权威指南》,嵌入式处理器开发教程。

Cortex-M3 使用的是“向下生长的满栈”模型。堆栈指针SP 指向最后一个被压入堆栈的32 位数值。 在下一次压栈时,SP 先自减4,再存入新的数值。

图3.13 Cortex-M3 堆栈的 PUSH 实现方式

POP 操作刚好相反:先从SP 指针处读出上一次被压入的值,再把SP 指针自增4。

图3.14 Cortex-M3 堆栈的 POP 实现方式

译注:虽然POP 后被压入的数值还保存在栈中,但它已经无效了,因为为下次的PUSH 将覆盖它的值!

在进入 ESR 时,CM3 会自动把一些寄存器压栈,这里使用的是发生本异常的瞬间正在使用的 SP 指针(MSP 或者是 PSP)。离开 ESR 后,只要 ESR 没有更改过 CONTROL[1],就依然使用发生本次异 常的瞬间正在使用的 SP 指针来执行出栈操作。

3.7.1 再论 Cortex-M3的双堆栈机制

我们已经知道了 CM3 的堆栈是分为两个:主堆栈和进程堆栈,CONTROL[1]决定如何选择。
当 CONTROL[1]=0 时,只使用 MSP,此时用户程序和异常 handler 共享同一个堆栈。这也是复位 后的缺省使用方式。

图 3.15 CONTROL[1]=0 时的堆栈使用情况
当 CONTROL[1]=1 时,线程模式将不再使用MSP,而改用 PSP(handler 模式永远使用MSP)。这 样做的好处在哪里?原来,在使用 OS 的环境下,只要 OS 内核仅在 handler 模式下执行,用户应用 程序仅在用户模式下执行,这种双堆栈机制派上了用场——防止用户程序的堆栈错误破坏 OS 使用 的堆栈。

译注:此时,进入异常时的自动压栈使用的是进程堆栈,进入异常 handler 后才自动改为 MSP,退出异常时切换回

PSP,并且从进程堆栈上弹出数据。


图 3.16 CONTROL[1]=1 时的堆栈切换情况
在特权级下,可以指定具体的堆栈指针,而不受当前使用堆栈的限制,示例代码如下:

MRS

R0,

MSP

; 读取主堆栈指针到R0

MSR

MSP,

R0

; 写R0的值到主堆栈中

MRS

R0,

PSP

; 读取进程堆栈指针到R0

MSR

PSP,

R0

; 写R0的值到进程堆栈中

通过读取 PSP 的值,OS 就能够获取用户应用程序使用的堆栈,进一步地就知道了在发生异常时,
被压入寄存器的内容,而且还可以把其它寄存器进一步压栈(使用 STMDB 和 LDMIA 的书写形式)。

OS 还可以修改PSP,用于实现多任务中的任务上下文切换。

你可能感兴趣的:(stm32)