Cortex-M FPU的一点点小问题

FPU全称Float Point Unit,即浮点运算单元,是CPU中针对浮点运算进行加速的单元,在Cortex-M系列CPU中,FPU是一个可选选项,引入FPU之后可以有效加速浮点运算速度,提高CPU性能。

理论上是这么个说法,但是实际使用中我却遇到了这么一个问题:使用了FPU之后,程序运行时间反而更长,这个着实奇怪。

于是我展开了比较深入的Debug,首先FPU的开启和关闭是通过IAR的选项实现的,对于这个可不可靠,刚开始我还是怀疑的,这个是本能,毕竟找不到问题,就只能怀疑工具了。后面为了测试我简单写了以下一个简单测试:

double a = 3.1415926;
double c = 1.0;
long long t = 0;
t = get_timer_value();
for(int i = 0; i < 0xffff; i++){
    c *= a;
}
t = get_timer_value() - t;
printf("time = %d\r\n", t);

然后我分别用不同的IAR FPU选项对程序进行测试,后面发现结果近似,并且FPU开启后时间还稍微大一些,这个着实出乎我的意料,没办反,工具也没啥研究的地方,只好研究汇编了,首先我们来看看FPU没开的时候的汇编:

no_fpu_asm.png

嗯,看起来没啥问题,都是标准指令,跳转到__aeabi_dmul函数,函数返回结果,完全没有问题。

好了,我们再来看看开了FPU之后的汇编:

double_fpu_en.png

嗯,VMOV典型的FPU指令呀,挺好,说明FPU确实开了。等等,怎么又跳转到__aeabi_dmul里面去了,不是有FPU吗?怎么还是CPU算!!是在想不通,难不成和数据类型相关?毕竟名字叫float point unit嘛,真的就是支持float类型?实践出真知,我们改一下程序试试:

float a = 3.1415926;
float c = 1.0;
long long t = 0;
t = get_timer_value();
for(int i = 0; i < 0xffff; i++){
    c *= a;
}
t = get_timer_value() - t;
printf("time = %d\r\n", t);

编译下载试一把,我勒个去去,一闪而过,运行飞快呀,FPU看来大显神通了,速度确实快了几倍!我们依然来看看汇编:

float_fpu_en.png

依然是典型的VMOV指令,但是后面出来一个新的VMUL.F32指令,一条指令搞定乘法运算,这不就是FPU嘛,怪不得这么快!不过从汇编指令上也能看出,这个乘法指令只能支持F32,似乎只是32位的浮点,刚好是float类型,而Double类型是64位的,所以无法调用FPU进行计算,看来这个就是原因了。

后面也从官方文档中找到了详细的解释,看来FPU用起来还是要注意一些东西的。

另外一篇文章也提到了相关的说明,对于Double类型数据,乘法运算依然是CPU完成的。除此之外,文章还提到以下几点:

  1. 因为<math.h>库中类似sin(), cos()函数的实现也是Double类型的,所以这些函数依然是CPU执行的,和FPU没有关系,如果想用FPU,可以调用float类型版本的,也就是在后面加一个f后缀,比如sinf(), cosf()这样函数就可以调用FPU进行计算了!
  2. 对于多任务或者中断中使用FPU的时候,要注意FPU是不支持重入的,简单来说就是FPU运算不能被打断后执行新的FPU运算,用户必须避免这种情形,而这条限制明显会增加代码复杂度。

看来FPU虽好,但是用起来也没有那么简单呀!

相关文章

发表新评论