汽车电子expert成长之路

本博客发布的个人原创精品----嵌入式系统技术文章,欢迎大家参考学习,并转发分享!

S32K14x系列MCU使用Tips之硬件FPU特性介绍和使用详解

0
阅读(13935)

内容提要

引言

1. Cortex M4F CPU内核的FPU特性介绍

1.1. Cortex M4F CPU内核FPU的寄存器组以及控制状态寄存器功能介绍

1.2 Cortex M4F CPU内核FPU的汇编指令集介绍

1.3 CM4F内核的FPU异常

2. S32K14X系列MCU的FPU使用(基于S32K144的S32DS应用工程)

2.1 创建使能硬件FPU的S32DS应用工程

2.2 配置S32K14x的S32DS应用工程使能内核硬件FPU

2.3 关于S32DS应用工程FPU Support选项配置的说明

3. 编译结果分析与使用Tips

3.1  EWL库with FP instructions(hard)

3.2 EWL库with Libary with FP(softfp)

3.3 EWL库with Libary(soft)

3.4 EWL库with Toolchain default

3.5 使用单精度浮点数进行数学函数运算

3.6 S32K14x系列MCU硬件FPU使用注意事项(Tips)

总结


引言


S32K14x系列汽车级MCU使用了ARM Cortex M4F作为CPU内核,其配置了硬件的FPU--Float Process Unit, 浮点数处理单元,可以支持IEEE 754规范的32-bit单精度浮点数(Single Precision floating point)运算。但是关于Cortex M4F CPU内核的FPU特性和具体在应用程序中如何使用以及使用时的注意事项,并不清楚。

2.jpg


基于此,本文先介绍一下Cortex M4F CPU内核的FPU特性,然后结合S32K144的S32DS应用工程,介绍如何配置使能M4F内核的FPU单元,并给出一些FPU使用的注意事项供大家参考学习。


1. Cortex M4F CPU内核的FPU特性介绍


CM4F CPU内核的硬件FPU单元具有如下特性:


  • 包含专用的32个32-bit的寄存器组,既可以当做32个32-bit寄存器使用也可以当做16个双字(Double-word 64-bit)寄存器使用;

  • 支持IEEE 754规范的32-bit单精度的浮点数运算;

  • 提供转换指令支持:

  • 整型数<-->单精度浮点数;

  • 定点数<-->单精度浮点数;

  • 半精度<-->单精度浮点数;

  • 支持FPU寄存器组与存储器之间的单精度或者双字数据搬移;

  • 支持FPU寄存器组与CPU整型数寄存器组之间的单精度或者双字数据搬移;


1.1. Cortex M4F CPU内核FPU的寄存器组以及控制状态寄存器功能介绍


Cortex M4F CPU内核为FPU提供了专门的数据寄存器S0~S31,这些寄存器时可以作为单独的32-bit寄存器为FPU使用,也可以两两组合为64-bit的D0~D15为FPU所使用,这样其不占用CPU做整数运算和逻辑控制的通用寄存器R0~R11,大大提高了计算效率。

3.jpg

FPU的通用数据寄存器组如下:

4.jpg


此外,为了获取FPU浮点数计算的结果,还提供FPU专用的状态和控制寄存器--FPSCR(Floating Point Status and Control Register),其定义如下:

5.jpg

其各bit位定义如下表:

6.jpg


其中计算结果对N、Z、C和V标志位的影响如下表:

7.jpg


    协处理器访问寄存器--CPACR


    该寄存器提供了对FPU的访问权限配置:

8.jpg


Tips: 复位后CP10和CP11都是0'b00,为关闭FPU,允许低功耗的配置。因此使用FPU之前必须通过以下配置将其配置为0'b11:


SCB->CPACR= 0x00F00000; // Enable the floating point unit for full access


除此之外,FPU还提供可以下可以通过CPU地址映射进行访问的额外寄存器:

9.jpg


①浮点数上下文控制寄存器--FPCCR


其可以配置中断发生时FPU数据寄存器和状态自动保护与恢复(ASPEN)、lazy stack的支持与否(LSPEN)以及是否使能调试监控(MONRDY),以及只读位用于记录当FPU的上下文压栈开始时CPU内核异常BusFault、MemManage、HardFault等是否发生(BFRDY/MMRDF/HFRDY)以及CPU是否处于线程模式(THREAD)、用户模式(USER)以及lazy 状态保护是否激活(LSPACT)等信息:

10.jpg

CM4F内核可以支持的上下文保护配置以及相应的中断压栈流程如下表:

11.jpg

②FPU上下文地址寄存器--FPCAR


使能FPU后,若发生CPU中断或者异常,则不但CPU通用寄存器(R0~R3, R12, LR)返回地址(Return Address)和状态寄存器(xPSR)需要压栈保存,FPU数据寄存器(S0~S15)以及状态寄存器(FPSCR)也需要压栈保存:

12.jpg

为了减少中断延迟,默认lazy stack是使能的,从而必须为FPU寄存器保留堆栈空间,但是一般情况下并不对FPU寄存器进行压栈,直到真正需要压栈时才将其压栈。


FPCAR 作为lazy stack机制的一部分,用于保存FPU寄存器在堆栈中的压栈地址,从而保证在lazy stack压栈时能够知道具体的压栈地址,其最低3-bit为0,因为CM4F内核要求堆栈按照双字(8 Byte)对齐:

13.jpg


③FPU默认状态与控制寄存器-FPDSCR


其保存FPU状态和控制数据的默认配置信息,该寄存器的值将在异常入口拷贝到FPSCR寄存器中使用:

14.jpg

④FPU功能特性寄存器--MVFR0/1


MVFR0/1这两个只读寄存器通过了FPU支持的指令特性信息,在芯片设计时即被确定:

15.jpg

具体寄存器位和支持的浮点数处理指令集定义如下:

16.jpg


1.2 Cortex M4F CPU内核FPU的汇编指令集介绍


在CM4F内核架构中, FPU时作为协处理器(Co-processor)工作的,为了和其他ARM架构兼容, FPU被定义为协处理器#10和#11,CM4F CPU内核和FPU共用相同的3级流水线的取指(Ftech),但译码和执行是分开独立的:

17.jpg

由于硬件FPU的存在,CM4F内核提供了额外的浮点数处理汇编指令集,如下图,CM4F内核使用的FPU为基于ARMv7-M架构的扩展,称作FPUv4-SP(Floating Point version 4 --Single Precision),其为ARMv-7A 和ARMv7-R架构扩展VFPv4-D16(VFP stands for Vector Floating Point--向量浮点数)的子集。这两个版本中很多浮点数指令都是相同的,因此通常浮点数操作也被称作VFP,且所有的浮点数汇编指令助记符都以字母V开头:

18.jpg

具体浮点数汇编指令如下:

19.jpg

20.jpg

21.jpg

22.jpg


1.3 CM4F内核的FPU异常


CM4F内核的FPU支持IEEE 754定义的以下浮点数计算异常:

23.jpg


2. S32K14X系列MCU的FPU使用(基于S32K144的S32DS应用工程)


基于以上介绍,我们下面来看看在S32K14x系列MCU中具体如何使用FPU。


首先,我们需要在需要使用浮点数计算的S32DS应用工程中中使能FPU,其方法有如下两种:


2.1 创建使能硬件FPU的S32DS应用工程


第一种使能应用工程使用FPU的方法是,在创建S32DS应用工程的工程向导中选择FPU Support为Harware:

24.jpg


2.2 配置S32K14x的S32DS应用工程使能内核硬件FPU


第二种方法是,对于已经创建好的应用工程,可以通过配置其工程属性使能FPU:

25.jpg


2.3 关于S32DS应用工程FPU Support选项配置的说明


在进行以上S32DS应用工程FPU Support配置时有如下4个选项,其对应的编译器命令行配置和意义如下:

26.jpg

①Libary(soft): GCC编译时调用软件库;

②Libary with FP(softfp):允许使用硬件FPU指令,但依然使用GCC库的软件浮点数调用约定(conventions);

③FP instructions(hard):生成硬件FPU指令,且使用FPU规范的调用约定;

④Toolchain default:为新建应用工程默认设置,取决于具体的目标配置;


Tips: 其中选项①Libary(soft)和④Toolchain default都不会使用硬件FPU,在应用程序中遇到浮点数计算时,将使用CM4 CPU内核的整数指令通过软件库的方式实现;

27.jpg

Tips:请注意,硬件浮点和软件浮点 ABI链接时是不兼容的,所以必须使用兼容的ABI库,对于S32DS IDE的GNU工具链来说,使用的ABI库可以为EWL和newlib两种,然后按照是否为简配(nano)和支持标准输入输出接口的方式(no I/O还是使用Debugger Console)又分为不同的子库:


用户可以通过应用工程属性(Properties)-->C/C++ Build-->Settings-->Target Processor-->Libraries support在下拉菜单中选择:

28.jpg


若用户配置S32DS应用工程的FPU Support为②Libary with FP(softfp)或者③FP instructions(hard)时,将使能FPU,其对FPU的使能配置通过在Startup过程中调用SDK的系统初始化函数SystemInit()完成,其中,通过使能ENABLE_FPU的宏定义,对SBC模块的CPACR和FPCCR寄存器进行FPU使能配置:

29.jpg


3. 编译结果分析与使用Tips


为了对FPU进行测试,对于定义的单精度浮点数:


float a, b, c, d, e, f;


进行如下简单的数学运算:


    e = a * b;

    f = c/d;

    a *= a;

    c = a*b;

    d + a*b;


然后使用不同的应用工程FPU Support配置选项进行编译,查看结果(通过反汇编获得)分析如下:


3.1  EWL库with FP instructions(hard)


此时,编译器配置参数如下:


-mfloat-abi=hard

-mfpu=fpv4-sp-d16

-specs=ewl_c9x_noio.specs


编译结果如下,使用了相应的FPU单精度浮点数计算汇编指令VMUL和VDIV来完成两个float类型数据的乘法和除法运算,并使用VLDR和VSTR来完成数据float类型数据的加载和储存:

30.jpg


3.2 EWL库with Libary with FP(softfp)


此时,编译器配置参数如下:


-mfloat-abi=softfp

-mfpu=fpv4-sp-d16

-specs=ewl_c9x_noio.specs


相应的编译结果如下,与EWL库with FP instructions(hard)配置编译结果相同,使用了相应的FPU单精度浮点数计算汇编指令VMUL和VDIV来完成两个float类型数据的乘法和除法运算,并使用VLDR和VSTR来完成数据float类型数据的加载和储存:

31.jpg


3.3 EWL库with Libary(soft)


此时,编译器配置参数如下(无-mfpu配置,因为其未使能FPU):


-mfloat-abi=soft

-specs=ewl_c9x_noio.specs


相应的编译结果如下,未使用任何FPU汇编指令,通过调用EWL库(EABI)的fmul和fdiv完成两个float类型数据的乘法和除法运算:

32.jpg


3.4 EWL库with Toolchain default


此时,编译器配置参数如下(无-mfloat配置和无-mfpu配置,因为其未使能FPU):


-specs=ewl_c9x_noio.specs


相应的编译结果如下,其结果与EWL库with Libary(soft)相同--未使用任何FPU汇编指令,通过调用EWL库(EABI)的fmul和fdiv完成两个float类型数据的乘法和除法运算:

33.jpg


3.5 使用单精度浮点数进行数学函数运算


对于数学运算,比如三角函数,C语言中默认使用的是双精度(Double-precision),比如下面的 Whetstone benchmark代码,直接调用标准的C语言三角函数库实现,即使将变量X, Y, T和T2定义为float(单精度)类型,在S32K14x系列MCU中,其依然会调用double来进行中间计算:


X=T*atan(T2*sin(X)*cos(X)/(cos(X+Y)+cos(X-Y)-1.0));

Y=T*atan(T2*sin(Y)*cos(Y)/(cos(X+Y)+cos(X-Y)-1.0));


要知道,在S32K14x系列MCU的CM4F内核中硬件FPU并不支持双精度浮点数(double)处理,故其需要先将其转换为double,再调用数学库实现,这样既增加了CPU的负荷(不使用FPU)又降低了速度:

34.jpg

因此,如果单精度浮点数计算结果即可满足应用要求,则可以调用如下单精度浮点数函数实现:


X=T*atanf(T2*sinf(X)*cosf(X)/(cosf(X+Y)+cosf(X-Y)-1.0F));

Y=T*atanf(T2*sinf(Y)*cosf(Y)/(cosf(X+Y)+cosf(X-Y)-1.0F));


这样编译器就会产生调用FPU指令完成相应的计算,从而提高CPU的执行效率:

35.jpg


3.6 S32K14x系列MCU硬件FPU使用注意事项(Tips)


下面给出一些S32K14x系列MCU硬件FPU的使用Tips,供大家参考:


①执行FPU指令之前必须确保FPU已经使能,否则运行FPU指令将触发CPU内核异常--HardFault,同时SCB模块的CFSR[NOCP]将置1;

通过本文第2小节介绍的两种方法使能FPU之后,即可使用FPU了,SDK的startup文件会调用SystemInit()函数帮用户配置使能FPU:

36.jpg

②在使能FPU之后,中断发生时(或者进行RTOS移植时,进行任务切换过程中),要根据浮点数上下文控制寄存器--FPCCR的ASPEN和LSPEN位配置,进行必要的FPU寄存器组现场保护和恢复(默认复位配置,FPCCR[ASPEN]和FPCCR[LSPEN]都为1,FPU会自动进行中断现场保护和恢复):

37.jpg

③FPU相关的状态和配置寄存器查看


FPU的S0~S31通用寄存器组和FPSCR寄存器的值在S32DS调试界面中的Registers窗口即可查看:

38.jpg

FPU的其他配置和状态寄存器通过存储器映射到SCB模块,所以需要在EmbSys Registers窗口中查看:

39.jpg


总结


通过以上分析,S32K14x系列MCU的S32DS应用工程中,不同的FPU Support选项配置对于的编译结果如下表:

40.jpg

本文对S32K14x系列MCU使用的硬件FPU单元特性进行了详细介绍,然后介绍在实际的S32DS应用工程,如何配置和使用FPU并对不同的FPU Support配置选项进行测试,给出分析结果,最后还给出了一些使用Tips,希望能够帮助大家更好的学习和使用FPU优化自己的软件设计。