Cortex-M4F 惰性 FPU 堆叠
Cortex-M4F lazy FPU stacking
我正在为 Cortex M4F 编写线程代码。一切正常,我现在正在研究通过惰性堆栈使 FPU 上下文切换更高效。
我已经阅读了 ARM 的 AN298 并且我实现了基于禁用 FPU 和处理 UsageFault 的替代方法,但是较低的 (S0-S15
) 寄存器没有被 saved/restored 正确硬件。我认为问题出在图 11 中:
据此,当PendSV运行时FPCAR
应该指向Task A的堆栈中保留的space。但正如我所见,由于 CONTROL.FPCA
在任务 C 中处于高位,因此 FPCAR
将在进入 PendSV 时更新为指向任务 C 的堆栈。如果是这样,S0-S15
和 FPSCR
将被保存到任务 C 的堆栈而不是任务 A 的堆栈,这当然是不正确的。
我是不是遗漏了什么,还是应用笔记有误?
附带说明一下,我检查了一些开源 RTOS。 FreeRTOS 和 mbed RTOS 在上下文切换期间始终堆栈 S16-S31
,导致自动 S0-S15
堆栈,即它们使用惰性堆栈只是为了减少中断延迟,但对任务进行完整的状态保存(如第一个appnote 中概述的方法)。 M4F 的 TNKernel 端口使用 UsageFault 方法,但完全通过软件 saves/restores S0-S31
,有效地绕过了 FPCAR
的任何问题(代价是 48 load/stores 而不是 32, 16 个硬件在恢复时被覆盖)。似乎没有人在仅保留 S16-S31
.
的情况下使用 UsageFault 方法
(顺便说一下,这也发布在 ARM Community,但很多问题似乎在那里没有得到解答。如果我在那里得到答案,我也会在这里复制它)
花了一段时间,但最后我找到了如何尽可能高效地做到这一点。
首先,appnote 是错误的。我对 FPCAR
更新方式的初步解释是正确的。请注意,即使 FPU 被禁用,FPCAR
也会更新。另外,通过测试,我确定 FPCAR
确实总是指向中断的堆栈。
我的第一个方法是操纵 FPCAR
、LSPACT
和 EXC_RETURN
,以及 UsageFault pending PendSV。当然,要做到这一点,从惰性堆栈的角度来看,FPCAR
操作不能算作 FPU 操作。当缺少文档时,我们只能从 CPU...
中破解答案
LDR R2, =0xE000EF38
LDR R3, =0xDEADBEEF
STR R3, [R2]
VSTM R1, {S16-S31}
UDF
FPCAR
在 0xE000EF38
。 VSTM
是上下文保存例程的一部分。这个想法是,如果 FPCAR
操作是 FPU 操作,惰性堆栈将停止 FPCAR
存储并将成功,因为 FPCAR
仍然有效。这将在 UDF
上出错。否则,惰性堆栈将在 VSTM
上发生,并且 FPCAR
已损坏,从而导致总线故障。
的确,我遇到了总线故障。耶!我用一个有效的地址重复了测试:没有错误,工作完美。所以储蓄很简单。恢复需要挂起的 PendSV 并在其中操作 FPCAR
、LSPACT
和 EXC_RETURN
以导致 S0-S15
在异常 return 时恢复当前线程。这里的问题是您不能将当前线程的状态保留在其堆栈上,因为它会被弹出。复制效率低下,因此最好的办法是将 FPCAR
指向持久的 TCB 状态,而不是保存 CPU 生成的状态。
这变得相当复杂,它需要在 UsageFault 之后执行 PendSV,并且它有很多极端情况和竞争。有更好的方法。
我最终使用的方法完全在 UsageFault 内部运行并绕过硬件堆栈,而不会因此而降低效率。在启用 FPU 并确定需要进行 FPU 上下文切换后,我:
- 将
LSPACT
设置为零;
- Save/restore完整
S0-S31
状态to/fromTCB;
- 将
LSPACT
设置回一。
通过这样做,我可以在整个 S0-S31
状态下工作,而不会出现延迟堆叠,因为 CPU 认为它已经堆叠了上下文,因为 LSPACT
是零。这当然依赖于 UsageFault 处理程序不使用 save/restore 之外的 FPU ops 并且不被使用 FPU 的 ISR 抢占,这是非常微不足道的假设,因为它是手工编码的 ASM 并且故障处理程序不能被 ISR 抢占.我还尝试通过 ASPEN
/LSPEN
禁用延迟堆叠,而不是在 LSPACT
上工作,但它似乎不起作用(它仍然触发延迟堆叠,通过设置无效 FPCAR
).
效率方面,这与硬件堆叠一样高效。如果我想挑剔,它可以节省一个周期,因为我不需要写回递增的指针。
顺便说一句,我包括了第一种方法,尽管我最终没有使用它,因为我认为它有一些有用的信息,如果有人来找这个的话。
我正在为 Cortex M4F 编写线程代码。一切正常,我现在正在研究通过惰性堆栈使 FPU 上下文切换更高效。
我已经阅读了 ARM 的 AN298 并且我实现了基于禁用 FPU 和处理 UsageFault 的替代方法,但是较低的 (S0-S15
) 寄存器没有被 saved/restored 正确硬件。我认为问题出在图 11 中:
据此,当PendSV运行时FPCAR
应该指向Task A的堆栈中保留的space。但正如我所见,由于 CONTROL.FPCA
在任务 C 中处于高位,因此 FPCAR
将在进入 PendSV 时更新为指向任务 C 的堆栈。如果是这样,S0-S15
和 FPSCR
将被保存到任务 C 的堆栈而不是任务 A 的堆栈,这当然是不正确的。
我是不是遗漏了什么,还是应用笔记有误?
附带说明一下,我检查了一些开源 RTOS。 FreeRTOS 和 mbed RTOS 在上下文切换期间始终堆栈 S16-S31
,导致自动 S0-S15
堆栈,即它们使用惰性堆栈只是为了减少中断延迟,但对任务进行完整的状态保存(如第一个appnote 中概述的方法)。 M4F 的 TNKernel 端口使用 UsageFault 方法,但完全通过软件 saves/restores S0-S31
,有效地绕过了 FPCAR
的任何问题(代价是 48 load/stores 而不是 32, 16 个硬件在恢复时被覆盖)。似乎没有人在仅保留 S16-S31
.
(顺便说一下,这也发布在 ARM Community,但很多问题似乎在那里没有得到解答。如果我在那里得到答案,我也会在这里复制它)
花了一段时间,但最后我找到了如何尽可能高效地做到这一点。
首先,appnote 是错误的。我对 FPCAR
更新方式的初步解释是正确的。请注意,即使 FPU 被禁用,FPCAR
也会更新。另外,通过测试,我确定 FPCAR
确实总是指向中断的堆栈。
我的第一个方法是操纵 FPCAR
、LSPACT
和 EXC_RETURN
,以及 UsageFault pending PendSV。当然,要做到这一点,从惰性堆栈的角度来看,FPCAR
操作不能算作 FPU 操作。当缺少文档时,我们只能从 CPU...
LDR R2, =0xE000EF38
LDR R3, =0xDEADBEEF
STR R3, [R2]
VSTM R1, {S16-S31}
UDF
FPCAR
在 0xE000EF38
。 VSTM
是上下文保存例程的一部分。这个想法是,如果 FPCAR
操作是 FPU 操作,惰性堆栈将停止 FPCAR
存储并将成功,因为 FPCAR
仍然有效。这将在 UDF
上出错。否则,惰性堆栈将在 VSTM
上发生,并且 FPCAR
已损坏,从而导致总线故障。
的确,我遇到了总线故障。耶!我用一个有效的地址重复了测试:没有错误,工作完美。所以储蓄很简单。恢复需要挂起的 PendSV 并在其中操作 FPCAR
、LSPACT
和 EXC_RETURN
以导致 S0-S15
在异常 return 时恢复当前线程。这里的问题是您不能将当前线程的状态保留在其堆栈上,因为它会被弹出。复制效率低下,因此最好的办法是将 FPCAR
指向持久的 TCB 状态,而不是保存 CPU 生成的状态。
这变得相当复杂,它需要在 UsageFault 之后执行 PendSV,并且它有很多极端情况和竞争。有更好的方法。
我最终使用的方法完全在 UsageFault 内部运行并绕过硬件堆栈,而不会因此而降低效率。在启用 FPU 并确定需要进行 FPU 上下文切换后,我:
- 将
LSPACT
设置为零; - Save/restore完整
S0-S31
状态to/fromTCB; - 将
LSPACT
设置回一。
通过这样做,我可以在整个 S0-S31
状态下工作,而不会出现延迟堆叠,因为 CPU 认为它已经堆叠了上下文,因为 LSPACT
是零。这当然依赖于 UsageFault 处理程序不使用 save/restore 之外的 FPU ops 并且不被使用 FPU 的 ISR 抢占,这是非常微不足道的假设,因为它是手工编码的 ASM 并且故障处理程序不能被 ISR 抢占.我还尝试通过 ASPEN
/LSPEN
禁用延迟堆叠,而不是在 LSPACT
上工作,但它似乎不起作用(它仍然触发延迟堆叠,通过设置无效 FPCAR
).
效率方面,这与硬件堆叠一样高效。如果我想挑剔,它可以节省一个周期,因为我不需要写回递增的指针。
顺便说一句,我包括了第一种方法,尽管我最终没有使用它,因为我认为它有一些有用的信息,如果有人来找这个的话。