Xtensa处理器窗寄存器函数调用机制与应用
tion(void) {
}
使用特别长的局部数组,如
void foo(void) {
int an_array[8192]; // 32,768 bytes
int another_array[100]; // 400 bytes
…
精确的size限制是32,760,包含16~48字节的Frame开销。
当然,这里列出的不是全部的可能情况,仅仅列出几个常见用例,读者不能认为自己的代码没有以上情况,编译器就不会产生movsp指令,从而不会产生alloc异常。
由于alloc是一种不容易避免的正常的异常,应用软件需要积极的处理。处理的思路有两种,其一是用异常或者中断栈作为临时储存来搬移,这里要介绍另外一种比较巧妙的方法,如下述代码:
rsr a2,PS
rsr a4,WINDOWBASE
extui a3,a2,XCHAL_PS_OWB_SHIFT,XCHAL_PS_OWB_BITS
xor a3,a3,a4
slli a3,a3,XCHAL_PS_OWB_SHIFT
xor a2,a2,a3
wsr a2,PS
l32i a4,a1,A4_SAVE
l32i a3,a1,A3_SAVE
l32i a2,a1,A2_SAVE
addi a1,a1,USER_EXCEPTION_FRAME_SIZE
rsync
// Now branch on the call increment
// We could branch earlier rotw instructions prior to the handler
// which would avoid executing two rotw instructions in the underflow
// eight case. However,the underflow 8 handler is right at a
// cache-line,so that would likely involve an extra cache miss better
// to just take the single cycle penalty here.
rotw -1
// what was a0 (that had the return address) is now a4
_bbci.l a4,31,_WindowUnderflow4
rotw -1
// what was a0 (that had the return address) is now a8
_bbci.l a8,30,_WindowUnderflow8
j _setup_WindowUnderflow12
这段代码的巧妙之处在于首先通过将当前的Windowbase保存到PS的OWB域中,然后通过反向旋转窗,根据a0的高端2bit表示的调用类型,跳转到相应的下溢exception的位置进行出栈恢复save area的寄存器,当sp指针正确偏移后,利用寄存器的引用触发overflow异常自动进行再次入栈,从而实现搬移。
Context Switch下的寄存器保存与恢复
在windows窗口寄存器机制下,多任务RTOS的上下文环境切换问题变得非常有趣。 那么该如何保存这些通用寄存器呢,是不是所有的物理寄存器都要保存与恢复呢? 答案是否定的,除了当前窗口的逻辑寄存器要保存外,只需要保存当前任务进程里的live寄存器(置1的寄存器pane),而恢复则只需要恢复逻辑寄存器。
对于任务软件来说,寄存器的实现机制是透明的,因此特别要说明的是WindowsStart和WindowsBase寄存器则完全不需要保存与恢复。参考如下进程切换的环境保护核心代码:
.Lspill_loop:
// Top of save loop.
// Find the size of this call and branch to the appropriate save routine.
beqz a2,.Ldone // if no start bit remaining,we're done
bbsi.l a2,0,.Lspill4 // if next start bit is set,it's a call4
bbsi.l a2,1,.Lspill8 // if 2nd next bit set,it's a call8
bbsi.l a2,2,.Lspill12 // if 3rd next bit set,it's a call12
j .Linvalid_window // else it's an invalid window!
// SAVE A CALL4
.Lspill4:
addi a3,a9,-16 // a3 gets call[i+1]'s sp - 16
s32i a4,a3,0 // store call[i]'s a0
s32i a5,a3,4 // store call[i]'s a1
s32i a6,a3,8 // store call[i]'s a2
s32i a7,a3,12 // store call[i]'s a3
srli a6,a2,1 // move and shift the start bits
rotw 1 // rotate the window
j .Lspill_loop
// SAVE A CALL8
.Lspill8:
……
该代码的基本思路是通过处理WindowStart寄存器,解析各live窗口的相对偏移,基于调用栈布局规范进行入栈处理。 当任务切换到其他的进程后,这些live窗口可能会被破坏(相应的WindowStart比特清零),这没有关系, 当进程重新切换回来时,如果这些live窗口已经在切换过的进程恢复(WindowStart比特置1),则切换回来后无需出栈,程序可正常继续执行,如果没有恢复(相应的WindowStart比特继续为零),那么该进程就可以根据这个清零的状态位将原先入栈的live寄存器正确恢复。
本文小结
Xtensa处理器的寄存器窗口旋转函数调用是一种非常巧妙的实现机制,通过这一机制嵌入式软件可明显提高性能,并且其alloc异常,多任务上下文切换等等衍生和应用问题也可高效而经济的解决,其和TIE(Tensilica Instruction Extension),其他诸多可配置选项等等正充分说明了Xtensa架构经久不衰,广泛应用,焕发持久生命力的原因所在。
- 一种通信电源监控系统组网方案的设计(01-05)
- 通信电源监控系统下位机硬件电路的设计(01-05)
- 支持CAN总线的电动车辅助逆变电源的设计(02-23)
- 设计高性能、低成本的笔记本电脑处理器电源 (05-12)
- 微处理器电源监控芯片SGM803及其应用(06-18)
- 微控制器的市场前景及发展趋势(06-21)