6

vesperW · 2021年07月01日

ARM探索之旅03 | 如何使用 ARM FPU 加速浮点计算

Cortex-M

引言

笔者接触嵌入式领域软件开发以来,几乎用的都是 ARM Cortex M 内核系列的微控制器。感谢C语言编译器的存在,让我不用接触汇编即可进行开发,但是彷佛也错过了一些风景,没有领域到编译器之美和CPU之美,所以决定周末无聊的休息时间通过寻找资料、动手实验、得出结论的方法来探索 ARM CPU 架构的美妙,以及C语言编译器的奥秘。(因为我个人实在是不赞同学校中微机原理类课程的教学方法)。

一、浮点数的存储

浮点数按照 IEEE 754 标准存储在计算机中,ARM浮点环境是遵循 「IEEE 754-1985」 标准实现的。

IEEE 754 标准规定浮点数的存储格式有三个域,如图:

1.png

具体的编码规则过多,本文重点不在此,不再展开,感兴趣可以阅读我之前的文章:浮点数在计算机中的存储 —— IEEE 754标准[1](可点击阅读原文查看)。

二、浮点支持软件库fplib

1. fplib介绍

ARM Cortex-M处理器中计算浮点数的方式有软件和硬件两种。

对于不带 FPU 的处理器,ARM提供了一个「浮点支持软件库」用于计算浮点数:fplib

fplib提供的 API 以 __aeabi开头,比如:

除此之外,fplib库还提供取余、开方等非常多的浮点数操作函数,如有兴趣可以查阅文末我列出的参考文档[2]。

2. 测试代码与优化等级

编写如下测试代码:

float a = 5.625;
float b = 5.625;
float res_add, res_sub, res_mul, res_div;

res_add = a + b;
res_sub = a - b;
res_mul = a * b;
res_div = a / b;

printf("res_add = %f\r\n", res_add);
printf("res_sub = %f\r\n", res_sub);
printf("res_mul = %f\r\n", res_mul);
printf("res_div = %f\r\n", res_div);

使用这段测试代码,「编译器优化等级推荐设置为-O0」,否则聪明的编译器会直接将结果计算出来编译到程序中,我们就没法研究了。

2.png

3. armcc测试结果

这节我们验证是否ARM使用 fplib 库来计算浮点数,在设置中关闭FPU:

3.png

使用MDK编译之后,进入调试模式查看反汇编结果。

在反汇编中可以看到,变量a是float类型,所以编译器分配了一个寄存器用于存储值:

4.png

查看0x080031C4处的值,小端存储模式下(低位在低地址),变量a的值是0x40B40000,存储方式符合IEEE 754标准。

5.png
666.png

再来看看浮点数运算操作的反汇编结果,果然调用fplib库提供的函数完成浮点数的操作:

6.png

这里还有一个有趣的小细节,在反汇编中可以看到「使用 %f 占位符打印浮点数时,printf是按照double型传参的」

7.png

4. arm-none-eabi-gcc测试结果

使用STM32CubeMX生成makeifle工程,修改makeifle中的等级为-O0,设置为软件浮点计算:

8.png

另外还需要注意,默认gcc编译时不支持printf打印浮点数,需要在 makefile 中手动加入以下链接选项:

LDFLAGS += -u _printf_float

编译完成之后进行反汇编(注意文件名):

arm-none-eabi-objdump -s -d build/usart1-fpu-test.elf  > build/usart1-fpu-test.dis

同样,在反汇编文件中即可找到浮点计算代码:

9.png

三、使用 ARM FPU 加速浮点计算

1. ARM FPU的魅力

FPU(Floating Point Unit,浮点单元)是ARM内核中的硬件外设,用于硬件计算浮点数,要想使用FPU计算浮点数,需要程序和编译器配合。

目前Cortex-M4、Cortex-M7、Cortex-M33、Cortex-M35P、Cortex-M55处理器中都具备FPU硬件。

在上一节中我们使用fplib软件库来计算浮点数,但是fplib终归还是软件方式,每个计算函数的实现都是通过很多的指令去完成计算,并且最终的程序中还会把函数链接进可执行程序,导致程序体积变大。

「ARM FPU的魅力在于,浮点计算可以通过简单的FPU操作指令去完成,相比之下,不仅计算快,也不会增大程序体积。」

2. 如何使能FPU硬件

ARM Cortex - M4内核中将 FPU 作为协处理器设计的,所以通过设置协处理器访问控制(CPACR,Co-processor access control register)来控制是否使能FPU。

10.png

复位之后CP11=0、CP10=0,默认禁止访问FPU,因为这是Cortex-M内核的外设,寄存器定义CMSIS-Core中,所以可以直接通过下面这行代码设置CP11=1、CP10=1来允许访问FPU:

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

无论是STM32 HAL库还是标准库,在SystemInit()函数中已经存在使能代码,通过__FPU_PRESENT__FPU_USED来控制:

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

并且,在头文件 stm32l431xx.h 中已经使能__FPU_PRESENT宏定义:

11.png

__FPU_PRESENT宏定义是一直使能的,那么如何来控制FPU的使能呢?

别忘了还有一个宏定义__FPU_USED,这是留给编译器来控制的!

3. ARMCC编译器如何开启FPU

MDK编译器开启FPU的方法非常简单,如图:

12.png

在MDK中使能FPU,一方面编译器会设置宏定义__FPU_USED == 1,不放心的话可以在任意位置添加下面的预处理代码,分别在使用/不使用的情况编译一下,查看编译器输出结果:

#if __FPU_USED == 1 #error "ok!" #endif

另一方面,编译器在编译的时候,会将所有的浮点运算都编译为使用FPU操作指令去完成,比如本文最开始的测试代码编译结果如下:

13.png

4. gcc编译器如何开启FPU

在Makefile中加入以下gcc编译设置项:

# fpu
FPU = -mfpu=fpv4-sp-d16

# float-abi
FLOAT-ABI = -mfloat-abi=hard

ABI是应用程序二进制接口(Application Binary Interface),-mfloat-abi用来指定使用哪种方式:

mfpu选项用来指定FPU架构,具体值可以阅读我在文末给出的参考文档,本文所使用的值fpv4-sp-d16,意味着仅仅使能Armv7 FPv4-SP-D16 单精度浮点单元扩展。

同样,对之前的测试代码编译,查看反汇编结果,可以看到使用了浮点操作全部使用了FPU相关指令。

14.png

四、使用Julia测试FPU加速性能

1. 测试准备

需要准备一份裸机工程,具有屏幕打点显示功能和串口打印功能。

参考: STM32CubeMX\_17 | 使用硬件SPI驱动TFT-LCD(ST7789)。

2. 移植Julia分形测试代码

Julia测试是通过计算几帧Julia分形的数据来测试单精度浮点运算的性能,测试代码参考正点原子,如下:

/* Private user code ---------------------------------------------------------/
/ USER CODE BEGIN 0 */
#define ITERATION 128 //迭代次数
#define REAL_CONSTANT 0.285f //实部常量
#define IMG_CONSTANT 0.01f //虚部常量

//颜色表
uint16_t color_map[ITERATION];

//缩放因子列表
const uint16_t zoom_ratio[] =
{
    120, 110, 100, 150, 200, 275, 350, 450,
    600, 800, 1000, 1200, 1500, 2000, 1500,
    1200, 1000, 800, 600, 450, 350, 275, 200,
    150, 100, 110,
};

//初始化颜色表
//clut:颜色表指针
void InitCLUT(uint16_t * clut)
{

uint32_t i = 0x00;
    uint16_t red = 0, green = 0, blue = 0;

for (i = 0;i < ITERATION; i++) {
        //产生 RGB 颜色值
        red = (i8256/ITERATION) % 256;
        green = (i6256/ITERATION) % 256;
        blue = (i4256 /ITERATION) % 256;

        //将 RGB888,转换为 RGB565
        red = red >> 3;
        red = red << 11;
        green = green >> 2;
        green = green << 5;
        blue = blue >> 3;

clut[i] = red + green + blue;
    }
}

//产生 Julia 分形图形
//size_x,size_y:屏幕 x,y 方向的尺寸
//offset_x,offset_y:屏幕 x,y 方向的偏移
//zoom:缩放因子
void GenerateJulia_fpu(uint16_t size_x,uint16_t size_y,uint16_t offset_x,uint16_t offset_y,uint16_t zoom)
{

uint8_t i;
    uint16_t x,y;
    float tmp1,tmp2;
    float num_real,num_img;
    float radius;

for (y = 0; y < size_y; y++) {
        for (x = 0; x < size_x; x++) {
            num_real = y - offset_y;
            num_real = num_real / zoom;
            num_img = x-offset_x;
            num_img = num_img / zoom;

i = 0;
            radius = 0;
            while ((i < ITERATION-1) && (radius < 4)) {

tmp1 = num_real * num_real;
                tmp2 = num_img * num_img;
                num_img = 2num_realnum_img + IMG_CONSTANT;
                num_real = tmp1 - tmp2 + REAL_CONSTANT;
                radius = tmp1 + tmp2;
                i++;
            }
            //绘制到屏幕
            lcd_draw_color_point(x, y, color_map[i]);
        }
    }
}

/* USER CODE END 0 */

在main函数中创建一些需要的变量:

/* USER CODE BEGIN 1 */
    uint8_t zoom_index = 0;
    uint32_t start_time = 0, end_time = 0;
/* USER CODE END 1 */

调用初始化函数:

/* USER CODE BEGIN 2 */
printf("Julia test by Mculover666\r\n");

lcd_init();

//初始化颜色表
InitCLUT(color_map);

/* USER CODE END 2 */

调用测试函数:

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  start_time = HAL_GetTick();
  GenerateJulia_fpu(240, 240, 120, 120, zoom_ratio[zoom_index]);
  end_time = HAL_GetTick();
  printf("diff time is %d ms\r\n", end_time - start_time);
    zoom_index++;
      if (zoom_index > sizeof(zoom_ratio)) {
          zoom_index = 0;
      }
}
/* USER CODE END 3 */

3. 测试结果

使用-O2优化等级,在不开 FPU 的情况下,「显示一帧平均需要11s左右」

15.png

程序大小情况:

1616.png

使用-O2优化等级,在开启 FPU 的情况下,「显示一帧平均需要4s左右」

16.png

程序大小情况:
17.png

最后放上好看的Julia分形图:

18.png

五、参考资料

[1] 浮点数在计算机中的存储 —— IEEE 754标准( https://mculover666.blog.csdn.net/article/details/93382331)

[2] About floating-point support,ARM Keil( https://www.keil.com/support/man/docs/armlib/armlib\_chr1358938940990.htm)

[3] Compiler Reference Guide,ARM Keil https://www.keil.com/support/man/docs/armclang\_ref/armclang\_ref\_chr1392305424052.htm)

[4] ARM Cortex-M3与M4权威指南

作者:mculover666
原文链接: https://mp.weixin.qq.com/s/4z60sv5hN-M4rhIka-RG8A
6 阅读 2.5k
收藏 0
推荐阅读
Arm技术文档分享|Cortex-M 系列处理器Cortex-M System Design Kit文档(附PDF) 3 新书推荐 | Arm技术大牛所著《SoC设计指南》,系统学习Cortex-M系列处理器设计 极术干货| 孔祥刚 -Arm Cortex-M系列处理器进阶选型秘籍(PPT下载+视频回放) FreeRTOS系列第7篇---Cortex-M内核使用FreeRTOS特别注意事项 ARM探索之旅02 | ARM Cortex-M 用什么指令集? 招募 | 最新Arm Helium技术参考书中文版翻译合作
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息
  • 用户指南 · 用户协议 · 隐私政策
  • 关于我们 · 联系合作
  • 粤ICP备18004469号
  • 粤公网安备 44030502008014号
  • 版权所有 © 2024 极术社区
  • 由 SegmentFault 提供技术支持

  • 友情链接
    • 安谋科技
    • 安创空间
    • 开源中国
    • InfoQ
    • 边缘计算社区
    • 21IC 电子网
    • HarmonyOS 技术社区
    • 电子芯吧客
    • CSDN
    • 全志在线
    • 芒果派
    • 深度强化学习实验室
    • LiveVideoStack
    • 智东西

玻璃钢生产厂家玻璃钢动物雕塑实力厂家温州人物玻璃钢雕塑多少钱永州玻璃钢抽象雕塑江苏特色玻璃钢雕塑优势河北节日商场美陈福建室内商场美陈销售公司玻璃钢摆件雕塑现货节庆商场美陈生产企业阜阳人物玻璃钢雕塑施工安装芜湖玻璃钢雕塑加工公司有什么好的商场美陈网站茂名玻璃钢景观雕塑造型精美厦门玻璃钢雕塑制作工厂山西多彩玻璃钢雕塑批发北京大型商场美陈哪里有济源玻璃钢镂空雕塑浦口美陈商场布置泰州小型玻璃钢花盆天筑玻璃钢雕塑效果图温州玻璃钢雕塑厂家报价国外独角兽商场美陈济源玻璃钢卡通雕塑厂家定制甘肃玻璃钢仿真水果雕塑定制成都人物玻璃钢雕塑厂家焦作附近玻璃钢仿铜雕塑定制观山湖区玻璃钢雕塑厂家玻璃钢钢琴雕塑云南玻璃钢房地产水景雕塑公司大连玻璃钢人物雕塑河源玻璃钢客家文化雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化