Xtensa 微处理器介绍
Xtensa使用的指令集架构属于类RISC架构,主要针对嵌入式应用场合。在移植/编写操作系统的时候需要额外注意以下几点:
- 不同的指令其宽度可能不同
- window寄存器的使用
- 处理器的可配置性
- 处理器的扩展性(指令集可扩展)
Xtensa 硬件抽象层HAL
编译时HAL——CHAL
包括C语言预处理器和汇编语言的宏定义(用来表征不同xtensa处理器的不同配置)
链接时HAL——LHAL
- 给操作系统移植层调用
- 给底层软件(需要处理ISA相关的功能)调用,例如提供保存现场window frame的接口
窗寄存器函数调用规范(Windowed Calling Convention)
现代处理器为了更好的支持高级编程语言的高效编译,通常处理器所拥有的通用寄存器的数目有16个甚至32个之多,如此多的寄存器在比较复杂的应用程序上实现深度嵌套调用的时候,为了保证程序的正确执行,寄存器要频繁地进行入栈和出栈的操作,这样频繁的堆栈memory的访问将明显恶化应用程序的性能。为了有效解决这一问题,Xtensa架构设计了一种Windows旋转
方式的寄存器管理
机制,将逻辑寄存器和物理寄存器分开,在函数调用的时候通过windows滑动切换逻辑寄存器,从而避免寄存器覆盖,减少压栈和出栈的操作。
AR物理寄存器环形Buffer
基本实现原理:使用更多的物理AR寄存器组成一个环形的buffer。这些寄存器每4个为一组(pane),WindowStart中的每个比特依次表示该组是否作为逻辑寄存器窗口的起始位置或者被占用。当前的逻辑寄存器的起始位置则用WindowBase状态寄存器来表示。在发生函数调用的时候是通过修改WindowBase寄存器,滑动逻辑寄存器窗口,从而父子函数看到的是不同的物理寄存器,避免了寄存器的压栈和出栈。
以每4个寄存器(pane)为单位,函数调用的时候窗口可以滑动4个,8个或者12个物理寄存器,分别可以用call4,call8,call12指令来实现,而最典型的应用则为call8。
- a0用来保存函数返回地址
- a1保存sp堆栈指针
- a2~a7用来传递函数入参,参数超过6个的时候则需要使用堆栈
- 对调用者函数和被调用函数来说,a0~a7是独立的寄存器,可以自由使用,而a8~a15则为scratch寄存器,随时会被子函数使用,调用者函数如果要使用,则在调用子函数前进行压栈保存
为了方便寄存器正常的保存与恢复,还要调用栈的高效回溯,还有必要对函数的Frame栈空间做统一的安排。
- Base Area用于存储其父函数的基本寄存器
a0
~a3
Windows寄存器覆盖问题
在发生函数调用,执行call指令的时候,窗递增值(call4,call8,call12分别对应1,2,3)存入PS处理器状态寄存器的CALLINC
域,在进入函数的入口处用entry
指令进行Window重叠检测,条件满足的时候将触发相应的windows overflow异常,引导程序进行覆盖寄存器的入栈保护。
Windows寄存器underflow问题
当子函数返回时,RETW或者RETW.N指令执行,此时也仅此时处理器将进行上溢检查。如果当Windowbase所在的位置的前3个windows pane的WindowStart比特都为0,则意味着它返回后的父函数发生过WindowOverflow,父函数的窗口寄存器曾经被压入stack。如果不是全为0,则应该不为零0的点和正常window返回的点对应,就返回,如果不同,则说明发生了不正常的调用,a0被破坏掉了,要产生非法指令错误。
参数传递
前6个参数会传递给 AR 寄存器,剩余的参数会被保存在stack中。
对于callN
指令(N取值4,8或者12)来说,函数调用者会将参数保存到寄存器AR[N+2]到AR[N+7]中(特别注意,call12
指令是能用于调用只有两个或者更少参数的函数,只能使用AR[N+2]和AR[N+3]寄存器),函数被调用者会从寄存器AR[2]到AR[7]中接收这些参数。
如果参数的数量多于6个,剩下的参数就会被保存在函数调用者的堆栈,即第七个参数保存在[sp+0]处,第八个参数保存在[sp+4]处,依此类推。函数被调用者需要从内存的[sp+FRAMESIZE]处获取这些额外的参数,其中FRAMESIZE
是被调用者的stack frame
大小,通常会用entry
指令指定。
以下C程序代码
1 | int func( int a, int b, int c, int d, int e, int f, |
对应的汇编程序代码为
1 | .align 4 |
函数调用和返回
编写Xtensa汇编代码
部分底层驱动只能使用汇编语言编写,比如:
- 用户异常处理
- 内核异常处理
- window处理
- 复位处理
使用汇编实现16比特点积运算
1 | #include "dsls_dotprod_16s_m_ae32.S" |
- 有些指令会以
.n
作为后缀,Xtensa处理器为了进一步提高代码密度,提供了一些常用指令的16比特版本,这里的n
代表narrow