龙芯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