FPU与VFP最全面解释

本文希望能将FPU以及ARM中的FPU介绍清楚。

1. FPU(Floating-Point Unit)

浮点运算单元是处理器内部用于执行浮点数计算的逻辑部件,或者说硬件电路。不是所有的处理器都有该功能。浮点运算满足IEEE 754的标准,所谓IEEE 754标准,定义了浮点数字的存储和计算方式、计算异常等,比如IEEE 754标准准确地定义了单精度(32位)和双精度(64位)浮点格式。
对于含有FPU的处理器,我们可以通过控制编译选项的方式使编译器编译出的代码使用FPU指令。

2. -mfloat-abi=name来指定浮点运算处理方式

gcc为例,对应的-mfloat-abi参数值有三个:soft,softfp,hard。

  1. soft是指所有浮点运算全部在软件层实现,比如ARM提供了一个「浮点支持软件库」用于计算浮点数:fplib。使用软件的方式来计算浮点最终是通过执行了很多的指令来完成一次运算,不仅慢而且程序体积大。fplib提供的 API 以__aeabi开头,比如:__aeabi_fadd:计算两个float型浮点数加法(float占4个字节,32位)
  2. softfp是指浮点运算的实际计算是在FPU上来做,但是传递浮点运算的参数是用整形的通用寄存器(r0-r3)来传递的。比如float fadd(float a, float b),a和b实际上是通过r0,r1传入到被调函数的,结果也是通过r0传出的。
  3. hard与softfp的区别是不仅运算实际发生在FPU上,浮点参数的传递使用的也是浮点专用寄存器。以上同样的例子,a和b是通过s0, s1寄存器来传的,结果是通过s0传出的。softfp和hard两种模式不兼容,所以你不能将一个使用softfp方式的库和另外一个使用hard的库或目标文件链接起来。因为如此,armv7有些编译好的库(比如glibc使用softfp)没法在使用另一种使用hard的应用中编译。
    FPU与VFP最全面解释_第1张图片

使用软件库编译出的代码实例:
FPU与VFP最全面解释_第2张图片
使用hard编译出的使用FPU的代码实例:
FPU与VFP最全面解释_第3张图片

3. -mfpu=name来指定浮点协处理的类型

在ARM中的FPU被称为VFP(Vector Floating Point),即向量浮点运算,之所以称为“向量”浮点是因为在ARMv6及以前这个扩展不只是用于增加浮点数计算处理,同时也用于类似向量的SIMD浮点数计算处理。在ARMv7架构中,这种使用VFP来处理向量浮点数据的方式不建议采用,相关的功能可以使用“先进SIMD扩展”来替代。而vfp经过几代的发展和完善,出现了vfpv3, vfpv4, fpv5等多个版本,每个版本又可以配置为不同的使用方式,所以,这个-mfpu参数就是指定所采用的vfp硬件。
在文档《Arm Compiler for Embedded Reference Guide》中列举了可以使用的值,可选类型如none, softvfp,vfpv3,vfpv3-d16,vfpv3-d16-fp16,vfpv3xd, neon, vfpv4,fpv4-sp-d16,fpv5-d16,fpv5-sp-d16等。简单从文档中摘出几个:

  • none, softvfp: Use either -mfpu=none or -mfpu=softvfp to prevent the compiler from using hardware-based floating-point functions. If the compiler encounters floating-point types in the source code, it uses software-based floating-point library functions. This option is similar to the -mfloatabi=soft option.
  • vfpv3: Enable the Armv7 VFPv3 Floating-point Extension. Disable the Advanced SIMD extension.
  • vfpv3-d16: Enable the Armv7 VFPv3-D16 Floating-point Extension. Disable the Advanced SIMD extension.
  • vfpv3-fp16: Enable the Armv7 VFPv3 Floating-point Extension, including the optional half-precision extensions. Disable the Advanced SIMD extension.
  • vfpv4:Enable the Armv7 VFPv4 Floating-point Extension. Disable the Advanced SIMD extension.
  • neon-vfpv4:Enable the Armv7 VFPv4 Floating-point Extension and the Advanced SIMD extension.
  • fpv5-sp-d16: Enable the Armv7 FPv5-SP-D16 Floating-point Extension for single precision only.

如果你对d16之类的后缀感兴趣,下面这张浮点寄存器表可能解答你的疑惑,其中D代表double,64bit寄存器,sp表示single precision, 表示只允许32位单精度浮点计算:
FPU与VFP最全面解释_第4张图片
还有个小问题就是vfpv3, vfpv4, vfpv5之间有什么区别呢?从维基百科ARM architecture family 4.10章节中摘录如下:https://en.wikipedia.org/wiki/ARM_architecture_family#Floating-point_(VFP)

  • VFPv1
    Obsolete
  • VFPv2
    An optional extension to the ARM instruction set in the ARMv5TE, ARMv5TEJ and ARMv6 architectures. VFPv2 has 16 64-bit FPU registers.
  • VFPv3 or VFPv3-D32
    Implemented on most Cortex-A8 and A9 ARMv7 processors. It is backward-compatible with VFPv2, except that it cannot trap floating-point exceptions. VFPv3 has 32 64-bit FPU registers as standard, adds VCVT instructions to convert between scalar, float and double, adds immediate mode to VMOV such that constants can be loaded into FPU registers.
  • VFPv3-D16
    As above, but with only 16 64-bit FPU registers. Implemented on Cortex-R4 and R5 processors and the Tegra 2 (Cortex-A9).
  • VFPv3-F16
    Uncommon; it supports IEEE754-2008 half-precision (16-bit) floating point as a storage format.
  • VFPv4 or VFPv4-D32
    Implemented on Cortex-A12 and A15 ARMv7 processors, Cortex-A7 optionally has VFPv4-D32 in the case of an FPU with Neon. VFPv4 has 32 64-bit FPU registers as standard, adds both half-precision support as a storage format and fused multiply-accumulate instructions to the features of VFPv3. 相比v3增加了半精度和乘加指令的支持。
  • VFPv4-D16
    As above, but it has only 16 64-bit FPU registers. Implemented on Cortex-A5 and A7 processors in the case of an FPU without Neon.
  • VFPv5-D16-M
    Implemented on Cortex-M7 when single and double-precision floating-point core option exists. 单双精度可以选择。

此外,关于VFP架构和功能上的一些解释可以参考:《ARM Compiler armasm User Guide Version 5.06》 VFP Programming一节,写得比较清楚: https://developer.arm.com/documentation/dui0473/m/vfp-programming/architecture-support-for-vfp

4. NEON和VFP

在上面的一些介绍中,可以看到NEON经常和VFP一起出现,ARM中的NEON即SIMD(单指令多数据)技术,是一种真正的向量化运算方式,他们之所以耦合在一起是因为他们公用浮点寄存器。
FPU与VFP最全面解释_第5张图片FPU与VFP最全面解释_第6张图片
这里插一句题外话,如果想入门ARM SIMD编程,【张先轶】SIMD向量化编程入门——https://www.bilibili.com/video/BV1KZ4y1Y7VH/?spm_id_from=333.337.search-card.all.click&vd_source=d9ead405209236cb507b6c90c75777f9 这个课程可以借鉴。

以下是NEON与VFP的几点异同。

  1. ARM架构中支持 没有NEON和VFP, 只有VFP,NEON和VFP都有 三种配置,不支持只有NEON的配置。
  2. NEON使用32个双精度浮点寄存器,即32D,因此如果一些VFP配置D16则就无法使用NEON了。
  3. NEON不支持双精度浮点的计算,也不支持如square root和divide等一些比较复杂的浮点运算。

5. 编译工具中配置FPU

在上述2和3中描述了如何配置编译选项能够让编译器编译出来使用FPU指令的代码,那对于没有makefile的GUI用户如何设置呢?
MDK:
FPU与VFP最全面解释_第7张图片
IAR:
FPU与VFP最全面解释_第8张图片
ARM Cortex - M4内核中将 FPU 作为协处理器设计的,所以通过设置协处理器访问控制(CPACR,Co-processor access control register)来控制是否使能FPU。在这些工具软件中使能FPU后,编译器会设置宏定义__FPU_USED == 1,该配置将在代码中启用FPU硬件。

#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
 SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
#endif

参考:

  1. ARM技术杂谈:何谓FPU、VFP、ASE、NEON、MPE、SVE、SME以及MVE —— https://www.eefocus.com/article/521046.html
  2. Armv8上不弃不离的NEON/FPU —— https://www.jianshu.com/p/a19cc2ca59da
  3. ARM FPU 加速浮点计算 介绍 —— https://blog.csdn.net/u014436243/article/details/122078377
  4. ARM Cortex-A Series Programmer’s Guide for ARMv7-A —— https://developer.arm.com/documentation/den0013/d/Introducing-NEON/NEON-architecture-overview/Commonality-with-VFP
  5. ARM Cortex-M7 Processor Technical Reference Manual r0p2:About the FPU —— https://developer.arm.com/documentation/ddi0489/b/floating-point-unit/about-the-fpu
  6. IEEE 754浮点数标准详解 —— http://c.biancheng.net/view/314.html
  7. arm浮点运算 —— http://t.zoukankan.com/-9-8-p-9254964.html

你可能感兴趣的:(系统架构设计,浮点,VFP,ARM,FPU)