在龙芯1C上移植硬浮点FPU到RT-Thread

本文详细介绍了如何在龙芯1C上移植硬浮点FPU到RT-Thread操作系统,包括中断和上下文切换时保存浮点寄存器的实现,涉及到SAVE_FPU和RESTORE_FPU的编写,以及解决潜在的小bug和优化策略。通过分析和实践,确保了RT-Thread在浮点运算方面的正确性和效率。
摘要由CSDN通过智能技术生成
已经将龙芯1C的FPU移植到裸机编程了,如果对龙芯1C的FPU不太了解,不妨先移步到《【龙芯1c库】移植硬浮点FPU》http://blog.csdn.net/caogos/article/details/77219853,该篇文章以《see mips run》为理论依据,并结合龙芯1C的实际,详细讲解了移植FPU的要点,包括移植到实时系统。

本文以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) 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值