And*_*ndo 6 arm fpu context-switch cortex-m
我正在为 Cortex M4F 编写线程代码。一切正常,我现在正在研究通过惰性堆栈使 FPU 上下文切换更有效。
我读过 ARM 的AN298,并实现了基于禁用 FPU 和处理UsageFault 的替代方法,但较低的 ( S0-S15) 寄存器没有被硬件正确保存/恢复。我认为问题出在图11:
据此,当PendSV运行时FPCAR应该指向任务A的堆栈中保留的空间。但正如我所见,由于CONTROL.FPCA在任务 C 中处于高位,FPCAR因此在进入 PendSV 时将更新为指向任务 C 的堆栈。如果是这样,S0-S15并且FPSCR将被保存到任务C的堆栈而不是任务A的堆栈中,这当然是不正确的。
我在这里遗漏了什么,还是应用程序注释错误?
附带说明一下,我检查了一些开源 RTOS。FreeRTOS 和 mbed RTOS 始终S16-S31在上下文切换期间进行堆栈,从而导致自动S0-S15堆栈,即它们仅使用惰性堆栈来减少中断延迟,但对任务进行完整的状态保存(如应用笔记中概述的第一种方法)。M4F 的 TNKernel 端口使用UsageFault 方法,但S0-S31通过软件完全保存/恢复,有效地绕过任何问题FPCAR(以 48 次加载/存储而不是 32 次为代价,16 个硬件加载/存储在恢复时被覆盖)。似乎没有人在只保留S16-S31.
(顺便说一句,这也发布在ARM Community 上,但那里似乎有很多问题没有答案。如果我在那里得到答案,我也会在这里复制它)
这花了一段时间,但最终我找到了如何尽可能有效地做到这一点。
首先,应用程序注释是错误的。我对FPCAR更新方式的最初解释是正确的。请注意,FPCAR即使 FPU 被禁用,也会更新。另外,通过测试,我确定FPCAR确实始终指向中断的堆栈。
我的第一个方法是操纵FPCAR、LSPACT和EXC_RETURN以及UsageFault 挂起的PendSV。当然,要做到这一点,FPCAR从惰性堆栈的角度来看,操作不被视为 FPU 操作是很重要的。当文档缺乏时,我们只能从CPU中破解答案……
LDR R2, =0xE000EF38
LDR R3, =0xDEADBEEF
STR R3, [R2]
VSTM R1, {S16-S31}
UDF
Run Code Online (Sandbox Code Playgroud)
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 上下文切换后,我:
LSPACT零;S0-S31状态保存到 TCB 或从 TCB 恢复完整状态;LSPACT回一。通过这样做,我可以处理整个S0-S31状态,而不会妨碍延迟堆栈,因为 CPU 认为它已经堆栈了上下文,因为LSPACT为零。当然,这依赖于UsageFault处理程序在保存/恢复之外不使用FPU操作,并且不被使用FPU的ISR抢占,考虑到它是手工编码的ASM,并且故障处理程序不能被ISR抢占,这是非常微不足道的假设。ASPEN我还尝试通过/禁用延迟堆栈,LSPEN而不是在 上工作LSPACT,但它似乎不起作用(它仍然触发延迟堆栈,通过设置 invalid 进行验证FPCAR)。
从效率角度来看,这与硬件堆叠一样高效。如果我想挑剔,它可以节省一个周期,因为我不需要写回递增的指针。
顺便说一句,我包含了第一种方法,尽管我最终没有使用它,因为我认为它有一些有用的信息,如果其他人来寻找这个。
| 归档时间: |
|
| 查看次数: |
3411 次 |
| 最近记录: |