本文以RT-Thread为例,继续分享我是如何在上一篇文章的指导下一步一步移植FPU到RT-Thread的。
移植要点
先来回顾《【龙芯1c库】移植硬浮点FPU》中的移植要点
1,和裸机编程一样,需要初始化FPU,初始化的函数也是一样的
2,需要在中断和上下文切换时保存用于浮点运算的16个寄存器$f0,$f2,$f4, ...... ,$f28,$f30
其中,要点一说的FPU初始化函数如下,
/**
* init hardware FPU
*/
void rt_hw_fpu_init(void)
{
rt_uint32_t c0_status = 0;
rt_uint32_t c1_status = 0;
// 使能协处理器1--FPU
c0_status = read_c0_status();
c0_status |= (ST0_CU1 | ST0_FR);
write_c0_status(c0_status);
// 配置FPU
c1_status = read_c1_status();
c1_status |= (FPU_CSR_FS | FPU_CSR_FO | FPU_CSR_FN); // set FS, FO, FN
c1_status &= ~(FPU_CSR_ALL_E); // disable exception
c1_status = (c1_status & (~FPU_CSR_RM)) | FPU_CSR_RN; // set RN
write_c1_status(c1_status);
return ;
}
只需在“bsp\ls1cdev\drivers\board.c”的函数rt_hw_board_init()中调用即可。
本文重点放在要点2上
在中断和上下文切换时保存用于浮点运算的浮点寄存器
分析中断时保存通用寄存器的SAVE_ALL和RESTORE_ALL_AND_RET
分析SAVE_ALL
先来看下SAVE_ALL的源码截图
宏SAVE_ALL有多个宏组成,第一个宏为SAVE_SOME,下面详细分析SAVE_SOME。
move k1, sp 的功能是k1=sp,即sp的值赋给k1
move k0, sp 的功能是k0=sp
PTR_SUBU sp, k1, PT_SIZE 的功能是 sp = k1 - PT_SIZE,即把栈指针sp向下移动
LONG_S k0, PT_R29(sp) 的功能是 *(sp + PT_R28) = k0,即将k0(之前的sp值)压栈
LONG_S $3, PT_R3(sp) 的功能是 *(sp + PT_R3) = $3,即将通用寄存器$3中的值压栈
……
后面的类似,都是压栈,包括后面的宏SAVE_AT,宏SAVE_TEMP,宏SAVE_STATIC。
汇编中用到的宏PT_R29、PT_R3等,表示相应的寄存器$29、$3等在栈中相对sp的偏移地址。宏PT_SIZE为所有寄存器的入栈后占用的总的空间大小。具体源码如下
#define PT_R0 (0) /* 0 */
#define PT_R1 ((PT_R0) + LONGSIZE) /* 1 */
#define PT_R2 ((PT_R1) + LONGSIZE) /* 2 */
#define PT_R3 ((PT_R2) + LONGSIZE) /* 3 */
#define PT_R4 ((PT_R3) + LONGSIZE) /* 4 */
#define PT_R5 ((PT_R4) + LONGSIZE) /* 5 */
#define PT_R6 ((PT_R5) + LONGSIZE) /* 6 */
#define PT_R7 ((PT_R6) + LONGSIZE) /* 7 */
#define PT_R8 ((PT_R7) + LONGSIZE) /* 8 */
#define PT_R9 ((PT_R8) + LONGSIZE) /* 9 */
#define PT_R10 ((PT_R9) + LONGSIZE) /* 10 */
#define PT_R11 ((PT_R10) + LONGSIZE) /* 11 */
#define PT_R12 ((PT_R11) + LONGSIZE) /* 12 */
#define PT_R13 ((PT_R12) + LONGSIZE) /* 13 */
#define PT_R14 ((PT_R13) + LONGSIZE) /* 14 */
#define PT_R15 ((PT_R14) + LONGSIZE) /* 15 */
#define PT_R16 ((PT_R15) + LONGSIZE) /* 16 */
#define PT_R17 ((PT_R16) + LONGSIZE) /* 17 */
#define PT_R18 ((PT_R17) + LONGSIZE) /* 18 */
#define PT_R19 ((PT_R18) + LONGSIZE) /* 19 */
#define PT_R20 ((PT_R19) + LONGSIZE) /* 20 */
#define PT_R21 ((PT_R20) + LONGSIZE) /* 21 */
#define PT_R22 ((PT_R21) + LONGSIZE) /* 22 */
#define PT_R23 ((PT_R22) + LONGSIZE) /* 23 */
#define PT_R24 ((PT_R23) + LONGSIZE) /* 24 */
#define PT_R25 ((PT_R24) + LONGSIZE) /* 25 */
#define PT_R26 ((PT_R25) + LONGSIZE) /* 26 */
#define PT_R27 ((PT_R26) + LONGSIZE) /* 27 */
#define PT_R28 ((PT_R27) + LONGSIZE) /* 28 */
#define PT_R29 ((PT_R28) + LONGSIZE) /* 29 */
#define PT_R30 ((PT_R29) + LONGSIZE) /* 30 */
#define PT_R31 ((PT_R30) + LONGSIZE) /* 31 */
/*
* Saved special registers
*/
#define PT_STATUS ((PT_R31) + LONGSIZE) /* 32 */
#define PT_HI ((PT_STATUS) + LONGSIZE) /* 33 */
#define PT_LO ((PT_HI) + LONGSIZE) /* 34 */
#define PT_BADVADDR ((PT_LO) + LONGSIZE) /* 35 */
#define PT_CAUSE ((PT_BADVADDR) + LONGSIZE) /* 36 */
#define PT_EPC ((PT_CAUSE) + LONGSIZE) /* 37 */
#define PT_SIZE ((((PT_EPC) + LONGSIZE) + (PTRSIZE-1)) & ~(PTRSIZE-1))
比如,寄存器$0的偏移地址为0,所以有“#define PT_R0 (0)”,寄存器$1的偏移地址为寄存器$0的地址加上寄存器$0的大小(4字节),所以有“#define PT_R1 ((PT_R0) + LONGSIZE)”,其它的类似。
分析RESTORE_ALL_AND_RET
宏RESTORE_ALL_AND_RET的源码为
.macro RESTORE_ALL_AND_RET
RESTORE_TEMP
RESTORE_STATIC
RESTORE_AT
RESTORE_SOME
RESTORE_SP_AND_RET
.endm
从宏名字和执行顺序看,首先是将temp类的寄存器出栈,倒数第二才是RESTORE_SOME,最后才是RESTORE_SP_AND_RET。
宏RESTORE_SP_AND_RET的源码为
.macro RESTORE_SP_AND_RET
LONG_L sp, PT_R29(sp)
.set mips3
eret
.set mips0
.endm
真正的汇编指令就两条“LONG_L sp, PT_R29(sp)”和“eret”。其中“LONG_L sp, PT_R29(sp)”的功能是把sp的值从栈中弹出,“eret”为中断返回
依葫芦画瓢,实现SAVE_FPU和RESTORE_FPU
计算各个寄存器在栈中相对sp的偏移
寄存器$f0的偏移 = 0,
用代码表示为“#define PT_FPU_R0 (0)”
寄存器$f2的偏移 = $f0的偏移 + 8字节,
用代码表示为“#define PT_FPU_R2 ((PT_FPU_R0) + 2*LONGSIZE)”
寄存器$f4的偏移 = $f2的偏移 + 8字节
#define PT_FPU_R4 ((PT_FPU_R2) + 2*LONGSIZE)
以此类推
……
寄存器$f30的偏移 = $f28的偏移 + 8字节
#define PT_FPU_R30 ((PT_FPU_R28) + 2*LONGSIZE)
这16个寄存器在栈中占用的大小 = $f30的偏移 + 8字节
#define PT_FPU_SIZE ((((PT_FPU_R30) + 2*LONGSIZE) + (2*PTRSIZE-1)) & ~(2*PTRSIZE-1))
为什么还要加上(2*PTRSIZE-1),然后& ~(2*PTRSIZE-1)