我正在为Cortex M4F编写线程代码。一切正常,我现在正在考虑通过延迟堆叠使FPU上下文切换更有效。
我已经阅读了ARM的AN298并且我实现了基于禁用FPU和处理UsageFault的替代方法,但是硬件没有正确保存/恢复较低的(S0-S15
)寄存器。我认为问题出在图11中:
根据这一点,当PendSV运行时FPCAR
应该指向任务A堆栈中保留的空间。但是正如我所看到的,由于CONTROL.FPCA
在任务C中很高,因此在进入PendSV时,FPCAR
将更新为指向任务C的堆栈。如果是这样,S0-S15
和FPSCR
将保存到任务C的堆栈而不是任务A,这当然是不正确的。
我在这里遗漏了什么,还是说明错了?
另一方面,我检查了一些开源RTOS。 FreeRTOS和mbed RTOS总是在上下文切换期间堆叠S16-S31
,导致自动S0-S15
堆叠,即它们仅使用延迟堆叠来减少中断延迟,但为任务执行完全状态保存(如第一个appnote中概述的方法)。 M4F的TNKernel端口使用UsageFault方法,但通过软件完全保存/恢复S0-S31
,有效地绕过了FPCAR
的任何问题(以48个加载/存储而不是32个,16个硬件为代价)在恢复时被覆盖)。似乎没有人使用UsageFault方法,只保留S16-S31
。
(顺便说一句,这也发布在ARM Community,但很多问题似乎都没有得到答案。如果我在那里得到答案,我也会在这里复制它。
答案 0 :(得分:1)
花了一段时间,但最后我发现了如何尽可能高效地完成这项工作。
首先,appnote是错误的。我对FPCAR
更新方式的初步解释是正确的。请注意,即使禁用FPU,FPCAR
也会更新。此外,通过测试,我确定FPCAR
确实总是指向中断的堆栈。
我的第一个方法是操纵FPCAR
,LSPACT
和EXC_RETURN
,以及UsageFault待定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
在异常返回时恢复当前线程。这里的问题是你不能保持当前线程在其堆栈上的状态,因为它将被弹出。复制效率很低,因此最好将FPCAR
指向持久TCB状态,而不是保存CPU生成的状态。
这变得非常复杂,它需要在UsageFault之后执行PendSV,并且它有很多极端情况和种族。有更好的方法。
我最终使用的方法完全在UsageFault中运行并绕过硬件堆叠,而不会失去效率。启用FPU并确定需要FPU上下文切换后,I:
LSPACT
设为零; S0-S31
状态; LSPACT
设置为一个。通过这样做,我可以处理整个S0-S31
状态,而不会延迟堆叠,因为CPU认为它已经堆叠了上下文,因为LSPACT
为零。这当然依赖于UsageFault处理程序,不使用保存/恢复之外的FPU操作,而不是被FPU使用的ISR抢占,这是非常简单的假设,因为它的手动编码ASM和故障处理程序不能被ISR抢先一步。我还尝试通过ASPEN
/ LSPEN
禁用延迟堆叠,而不是LSPACT
,但它似乎无法正常工作(它仍会触发延迟堆叠,通过设置无效验证FPCAR
)。
效率方面,这与硬件堆叠一样高效。如果我想挑剔,它会节省一个周期,因为我不需要回写递增的指针。
顺便说一下,即使我没有最终使用它,我也采用了第一种方法,因为我认为它有一些有用的信息,如果有其他人来寻找它。