【龙芯1c库】移植硬浮点FPU

本文介绍了如何将硬浮点FPU移植到龙芯1C平台,主要涉及FPU初始化、异常处理、寄存器管理和浮点运算。通过配置控制/状态寄存器,禁用浮点异常并设置舍入模式。在实时系统中,还需考虑中断和上下文切换时保存和恢复浮点寄存器。文章提供了源码示例和测试用例,确保移植正确性。
摘要由CSDN通过智能技术生成

龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库。完整源码请移步到https://gitee.com/caogos/OpenLoongsonLib1c

龙芯1C上有硬浮点协处理器,整个移植过程主要参考《see mips run 中文版.pdf》中第7章“浮点支持”。文档《see mips run 中文版.pdf》可以在“https://gitee.com/caogos/OpenLoongsonLib1c/tree/master/doc”里面下载。

硬浮点FPU的使用没什么说的,就是在程序中需要使用浮点数时正常使用就行,本文重点放在移植上。

按图索骥

看看《see mips run 中文版.pdf》中怎么说的

文档《see mips run 中文版.pdf》的第7章用了一节的内容来详细讲解CPU复位后,应该怎样初始化FPU。如下图

文中已经描述得很清楚了。
第一步:使能协处理器1——FPU
第二步:设置控制/状态寄存器FCSR中的舍入模式和自陷使能
如果是裸机编程,执行以上两步就足够了;如果是移植到实时系统(比如RT-Thread)下,还需要执行
第三步:在中断和上下文切换时保存和恢复浮点寄存器。

浮点异常(自陷)

通常禁止全部浮点自陷

再来仔细看下上面那段话

这段话明确讲了通常是怎样设置舍入模式和自陷使能的。通常会禁止全部自陷,还要设置SR(FS)位让很小的结果返回零,这样可以免去一个到仿真程序的自陷。

浮点异常产生的原因

文中说“当一个浮点操作不能产生正确的结果时,就会发生一个MIPS异常”。那在哪些情况下不能产生正确的结果呢?

上图为FPU的控制/状态寄存器中关于异常的几个位的解释。共五种可能产生异常的原因——无效操作、除以零、上溢、下溢和非精确。在FPU的控制/状态寄存器中的位置如下

如果使能了FPU异常,并且也发生了异常,那么会触发cpu的异常,具体为协处理器0的cause寄存器中的FPE位被置位。根据前面的介绍,一般情况是禁止全部浮点异常,所以可以不用考虑触发cpu的异常。

 

FPU寄存器简介

浮点控制状态寄存器

寄存器简介

控制/状态寄存器很重要,配置FPU就是配置这个寄存器。包括舍入模式和禁止全部中断都是操作这个寄存器。
另外还需要注意的一个地方是,操作控制/状态寄存器的汇编指令为ctc1和cfc1。其中cfc1用来读取寄存器的值,ctc1将值写入寄存器。

寄存器的读写

代码中将cfc1和ctc1封装为宏,并且进一步为控制/状态寄存器的读和写分别封装了一个宏,便于编程时直接调用。具体代码如下

 

/*
 * Macros to access the floating point coprocessor control registers
 */
#define read_32bit_cp1_register(source)                         \
({ int __res;                                                   \
	__asm__ __volatile__(                                   \
	".set\tpush\n\t"					\
	".set\treorder\n\t"					\
	/* gas fails to assemble cfc1 for some archs (octeon).*/ \
	".set\tmips1\n\t"					\
        "cfc1\t%0,"STR(source)"\n\t"                            \
	".set\tpop"						\
        : "=r" (__res));                                        \
        __res;})
#define write_32bit_cp1_register(register, value)   \
do {                                        \
    __asm__ __volatile__(                   \
        "ctc1\t%z0, "STR(register)"\n\t"      \
        : : "Jr" ((unsigned int)(value)));  \
} while (0)
        
#define read_c1_status()            read_32bit_cp1_register(CP1_STATUS)
#define read_c1_revision()          read_32bit_cp1_register(CP1_REVISION);
#define write_c1_status(val)        write_32bit_cp1_register(CP1_STATUS, val)


宏read_32bit_cp1_register(source)用于读取寄存器的值,宏write_32bit_cp1_register(register, value)向寄存器写入数据。
read_c1_status()读取控制/状态寄存器的值,read_c1_revision()读取浮点实现寄存器的值(浮点实现寄存器在后面介绍),write_c1_status(val)修改控制/状态寄存器。
其中,CP1_STATUS和CP1_REVISION为寄存器编号,如下

 

 

 

/*
 * Coprocessor 1 (FPU) register names
 */
#define CP1_REVISION   		$0
#define CP1_STATUS     		$31

 

 

 

寄存器的配置

根据上一章的分析,裸机编程时,FPU的初始化只需要两步。一:使能FPU,二:配置FPU的控制/状态寄存器(舍入模式、禁止全部中断)。用代码表示为

 

// 硬浮点初始化
void fpu_init(void)
{
    unsigned int c0_status = 0;
    unsigned int 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 ;
}


裸机编程中只需要在初始化的时候调用fpu_init(),就可以在裸机编程中使用硬浮点FPU了,

 

 

 

其中,FPU_CSR_FS、FPU_CSR_FO、FPU_CSR_FN、FPU_CSR_ALL_E、FPU_CSR_RM、FPU_CSR_RN为FPU的控制/状态寄存器的某个位域,用宏表示为

 

/*
 * FPU Status Register Values
 */
/*
 * Status Register Values
 */

#define FPU_CSR_FLUSH   0x01000000      /* flush denormalised results to 0 */
#define FPU_CSR_COND    0x00800000      /* $fcc0 */
#define FPU_CSR_COND0   0x00800000      /* $fcc0 */
#define FPU_CSR_COND1   0x02000000      /* $fcc1 */
#define FPU_CSR_COND2   0x04000000      /* $fcc2 */
#define FPU_CSR_COND3   0x08000000      /* $fcc3 */
#define FPU_CSR_COND4   0x10000000      /* $fcc4 */
#define FPU_CSR_COND5   0x20000000      /* $fcc5 */
#define FPU_CSR_COND6   0x40000000      /* $fcc6 */
#define FPU_CSR_COND7   0x80000000      /* $fcc7 */


/* FS/FO/FN */
#define FPU_CSR_FS      0x01000000
#define FPU_CSR_FO      0x00400000
#define FPU_CSR_FN      0x00200000

/*
 * Bits 18 - 20 of the FPU Status Register will be read as 0,
 * and should be written as zero.
 */
#define FPU_CSR_RSVD	0x001c0000

/*
 * X the exception cause indicator
 * E the exception enable
 * S the sticky/flag bit
*/
#define FPU_CSR_ALL_X   0x0003f000
#define FPU_CSR_UNI_X   0x00020000
#define FPU_CSR_INV_X   0x00010000
#define FPU_CSR_DIV_X   0x00008000
#define FPU_CSR_OVF_X   0x00004000
#define FPU_CSR_UDF_X   0x00002000
#define FPU_CSR_INE_X   0x00001000

#define FPU_CSR_ALL_E   0x00000f80
#define FPU_CSR_INV_E   0x00000800
#define FPU_CSR_DIV_E   0x00000400
#define FPU_CSR_OVF_E   0x00000200
#define FPU_CSR_UDF_E   0x00000100
#define FPU_CSR_INE_E   0x00000080

#define FPU_CSR_ALL_S   0x0000007c
#define FPU_CSR_INV_S   0x00000040
#define FPU_CSR_DIV_S   0x00000020
#define FPU_CSR_OVF_S   0x00000010
#define FPU_CSR_UDF_S   0x00000008
#define FPU_CSR_INE_S   0x00000004

/* Bits 0 and 1 of FPU Status Register specify the rounding mode */
#define FPU_CSR_RM	0x00000003
#define FPU_CSR_RN      0x0     /* nearest */
#define FPU_CSR_RZ      0x1     /* towards zero */
#define FPU_CSR_RU      0x2     /* towards +Infinity */
#define FPU_CSR_RD      0x3     /* towards -Infinity */

 

 

 

浮点实现寄存器

老规矩,先来看看《see mips run》怎么说的。

重点注意F64、L、W、D、S这几个位的值。
浮点实现寄存器的存取也是使用cfc1和ctc1,上一节已经给出了封装好的宏——read_c1_revision()
龙芯1c的芯片手册中说了龙芯1C有FPU,所以是否查看这个寄存器其实不太重要。但是如果要移植到实时系统(比如RT-Thread),就必须查看此寄存器的值了。特别是位域F64

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值