揭秘ARM FPU 加速浮点计算
關(guān)注+星標(biāo)公眾號(hào),不錯(cuò)過精彩內(nèi)容
轉(zhuǎn)自?|?Mculover666
引言
筆者接觸嵌入式領(lǐng)域軟件開發(fā)以來,幾乎用的都是 ARM Cortex M 內(nèi)核系列的微控制器。感謝C語言編譯器的存在,讓我不用接觸匯編即可進(jìn)行開發(fā),但是彷佛也錯(cuò)過了一些風(fēng)景,沒有領(lǐng)域到編譯器之美和CPU之美,所以決定周末無聊的休息時(shí)間通過尋找資料、動(dòng)手實(shí)驗(yàn)、得出結(jié)論的方法來探索 ARM CPU 架構(gòu)的美妙,以及C語言編譯器的奧秘。(因?yàn)槲覀€(gè)人實(shí)在是不贊同學(xué)校中微機(jī)原理類課程的教學(xué)方法)。
ARM探索之旅 01 | ?帶你認(rèn)識(shí)ARM Cortex-M陣營
ARM探索之旅 02 | ?ARM Cortex-M 用什么指令集?
一、浮點(diǎn)數(shù)的存儲(chǔ)
浮點(diǎn)數(shù)按照 IEEE 754 標(biāo)準(zhǔn)存儲(chǔ)在計(jì)算機(jī)中,ARM浮點(diǎn)環(huán)境是遵循 「IEEE 754-1985」 標(biāo)準(zhǔn)實(shí)現(xiàn)的。
IEEE 754 標(biāo)準(zhǔn)規(guī)定浮點(diǎn)數(shù)的存儲(chǔ)格式有三個(gè)域,如圖:
sign:符號(hào)位,0表示正數(shù)、1表示負(fù)數(shù);
exponent:二進(jìn)制小數(shù)的指數(shù)值編碼;
fraction:二進(jìn)制小數(shù)的有效值編碼;
具體的編碼規(guī)則過多,本文重點(diǎn)不在此,不再展開,感興趣可以閱讀我之前的文章:浮點(diǎn)數(shù)在計(jì)算機(jī)中的存儲(chǔ) —— IEEE 754標(biāo)準(zhǔn)[1](可點(diǎn)擊閱讀原文查看)。
二、浮點(diǎn)支持軟件庫fplib
1. fplib介紹
ARM Cortex-M處理器中計(jì)算浮點(diǎn)數(shù)的方式有軟件和硬件兩種。
對(duì)于不帶 FPU 的處理器,ARM提供了一個(gè)「浮點(diǎn)支持軟件庫」用于計(jì)算浮點(diǎn)數(shù):fplib。
fplib提供的 API 以__aeabi開頭,比如:
__aeabi_fadd:計(jì)算兩個(gè)float型浮點(diǎn)數(shù)(float占4個(gè)字節(jié),32位)
__aeabi_dadd:計(jì)算兩個(gè)double型浮點(diǎn)數(shù)(double占8個(gè)字節(jié),64位)
__aeabi_f2d:float型轉(zhuǎn)為double型
__aeabi_d2f:double型轉(zhuǎn)為float型
除此之外,fplib庫還提供取余、開方等非常多的浮點(diǎn)數(shù)操作函數(shù),如有興趣可以查閱文末我列出的參考文檔[2]。
2. 測(cè)試代碼與優(yōu)化等級(jí)
編寫如下測(cè)試代碼:
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); ?使用這段測(cè)試代碼,「編譯器優(yōu)化等級(jí)推薦設(shè)置為-O0」,否則聰明的編譯器會(huì)直接將結(jié)果計(jì)算出來編譯到程序中,我們就沒法研究了。
?3. armcc測(cè)試結(jié)果
這節(jié)我們驗(yàn)證是否ARM使用 fplib 庫來計(jì)算浮點(diǎn)數(shù),在設(shè)置中關(guān)閉FPU:
使用MDK編譯之后,進(jìn)入調(diào)試模式查看反匯編結(jié)果。
在反匯編中可以看到,變量a是float類型,所以編譯器分配了一個(gè)寄存器用于存儲(chǔ)值:
查看0x080031C4處的值,小端存儲(chǔ)模式下(低位在低地址),變量a的值是0x40B40000,存儲(chǔ)方式符合IEEE 754標(biāo)準(zhǔn)。
再來看看浮點(diǎn)數(shù)運(yùn)算操作的反匯編結(jié)果,果然調(diào)用fplib庫提供的函數(shù)完成浮點(diǎn)數(shù)的操作:這里還有一個(gè)有趣的小細(xì)節(jié),在反匯編中可以看到「使用 %f 占位符打印浮點(diǎn)數(shù)時(shí),printf是按照double型傳參的」:
4. arm-none-eabi-gcc測(cè)試結(jié)果
使用STM32CubeMX生成makeifle工程,修改makeifle中的等級(jí)為-O0,設(shè)置為軟件浮點(diǎn)計(jì)算:另外還需要注意,默認(rèn)gcc編譯時(shí)不支持printf打印浮點(diǎn)數(shù),需要在 makefile 中手動(dòng)加入以下鏈接選項(xiàng):
LDFLAGS?+=?-u?_printf_float編譯完成之后進(jìn)行反匯編(注意文件名):
arm-none-eabi-objdump?-s?-d?build/usart1-fpu-test.elf??>?build/usart1-fpu-test.dis同樣,在反匯編文件中即可找到浮點(diǎn)計(jì)算代碼:
三、使用 ARM FPU 加速浮點(diǎn)計(jì)算
1. ARM FPU的魅力
FPU(Floating Point Unit,浮點(diǎn)單元)是ARM內(nèi)核中的硬件外設(shè),用于硬件計(jì)算浮點(diǎn)數(shù),要想使用FPU計(jì)算浮點(diǎn)數(shù),需要程序和編譯器配合。
在程序中使能/開啟FPU硬件外設(shè),「使 FPU 硬件可以正常工作」;
在編譯器中設(shè)置使用FPU,編譯器會(huì)將所有浮點(diǎn)計(jì)算的代碼都編譯為「使用FPU操作指令完成」。
目前Cortex-M4、Cortex-M7、Cortex-M33、Cortex-M35P、Cortex-M55處理器中都具備FPU硬件。
在上一節(jié)中我們使用fplib軟件庫來計(jì)算浮點(diǎn)數(shù),但是fplib終歸還是軟件方式,每個(gè)計(jì)算函數(shù)的實(shí)現(xiàn)都是通過很多的指令去完成計(jì)算,并且最終的程序中還會(huì)把函數(shù)鏈接進(jìn)可執(zhí)行程序,導(dǎo)致程序體積變大。
「ARM FPU的魅力在于,浮點(diǎn)計(jì)算可以通過簡(jiǎn)單的FPU操作指令去完成,相比之下,不僅計(jì)算快,也不會(huì)增大程序體積。」
2. 如何使能FPU硬件
ARM Cortex - M4內(nèi)核中將 FPU 作為協(xié)處理器設(shè)計(jì)的,所以通過設(shè)置協(xié)處理器訪問控制(CPACR,Co-processor access control register)來控制是否使能FPU。
復(fù)位之后CP11=0、CP10=0,默認(rèn)禁止訪問FPU,因?yàn)檫@是Cortex-M內(nèi)核的外設(shè),寄存器定義CMSIS-Core中,所以可以直接通過下面這行代碼設(shè)置CP11=1、CP10=1來允許訪問FPU:
SCB->CPACR?=?0x00F00000;?//?Enable?the?floating?point?unit?for?full?access無論是STM32 HAL庫還是標(biāo)準(zhǔn)庫,在SystemInit()函數(shù)中已經(jīng)存在使能代碼,通過__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 中已經(jīng)使能__FPU_PRESENT宏定義:__FPU_PRESENT宏定義是一直使能的,那么如何來控制FPU的使能呢?
別忘了還有一個(gè)宏定義__FPU_USED,這是留給編譯器來控制的!
3. ARMCC編譯器如何開啟FPU
MDK編譯器開啟FPU的方法非常簡(jiǎn)單,如圖:在MDK中使能FPU,一方面編譯器會(huì)設(shè)置宏定義__FPU_USED == 1,不放心的話可以在任意位置添加下面的預(yù)處理代碼,分別在使用/不使用的情況編譯一下,查看編譯器輸出結(jié)果:
#if?__FPU_USED?==?1 #error?"ok!" #endif另一方面,編譯器在編譯的時(shí)候,會(huì)將所有的浮點(diǎn)運(yùn)算都編譯為使用FPU操作指令去完成,比如本文最開始的測(cè)試代碼編譯結(jié)果如下:
4. gcc編譯器如何開啟FPU
在Makefile中加入以下gcc編譯設(shè)置項(xiàng):
#?fpu FPU?=?-mfpu=fpv4-sp-d16#?float-abi FLOAT-ABI?=?-mfloat-abi=hardABI是應(yīng)用程序二進(jìn)制接口(Application Binary Interface),-mfloat-abi用來指定使用哪種方式:
soft:使用CPU寄存器組+軟件庫(fplib)完成浮點(diǎn)操作;
softfp:使用CPU寄存組+FPU硬件+軟件庫完成浮點(diǎn)操作;
hard:使用FPU寄存器組+FPU硬件+軟件庫完成浮點(diǎn)操作;
mfpu選項(xiàng)用來指定FPU架構(gòu),具體值可以閱讀我在文末給出的參考文檔,本文所使用的值fpv4-sp-d16,意味著僅僅使能Armv7 FPv4-SP-D16 單精度浮點(diǎn)單元擴(kuò)展。
同樣,對(duì)之前的測(cè)試代碼編譯,查看反匯編結(jié)果,可以看到使用了浮點(diǎn)操作全部使用了FPU相關(guān)指令。
四、使用Julia測(cè)試FPU加速性能
1. 測(cè)試準(zhǔn)備
需要準(zhǔn)備一份裸機(jī)工程,具有屏幕打點(diǎn)顯示功能和串口打印功能。
參考:STM32CubeMX_17 | 使用硬件SPI驅(qū)動(dòng)TFT-LCD(ST7789)。
2. 移植Julia分形測(cè)試代碼
Julia測(cè)試是通過計(jì)算幾幀Julia分形的數(shù)據(jù)來測(cè)試單精度浮點(diǎn)運(yùn)算的性能,測(cè)試代碼參考正點(diǎn)原子,如下:
/*?Private?user?code?---------------------------------------------------------*/ /*?USER?CODE?BEGIN?0?*/ #define?ITERATION?128?//迭代次數(shù) #define?REAL_CONSTANT?0.285f?//實(shí)部常量 #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++)?{//產(chǎn)生?RGB?顏色值red?=?(i*8*256/ITERATION)?%?256;green?=?(i*6*256/ITERATION)?%?256;blue?=?(i*4*256?/ITERATION)?%?256;//將?RGB888,轉(zhuǎn)換為?RGB565red?=?red?>>?3;red?=?red?<<?11;green?=?green?>>?2;green?=?green?<<?5;blue?=?blue?>>?3;clut[i]?=?red?+?green?+?blue;} }//產(chǎn)生?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?=?2*num_real*num_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函數(shù)中創(chuàng)建一些需要的變量:
??/*?USER?CODE?BEGIN?1?*/uint8_t?zoom_index?=?0;uint32_t?start_time?=?0,?end_time?=?0;/*?USER?CODE?END?1?*/調(diào)用初始化函數(shù):
/*?USER?CODE?BEGIN?2?*/ printf("Julia?test?by?Mculover666\r\n");lcd_init();//初始化顏色表 InitCLUT(color_map);/*?USER?CODE?END?2?*/調(diào)用測(cè)試函數(shù):
/*?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. 測(cè)試結(jié)果
使用-O2優(yōu)化等級(jí),在不開 FPU 的情況下,「顯示一幀平均需要11s左右」:程序大小情況:使用-O2優(yōu)化等級(jí),在開啟 FPU 的情況下,「顯示一幀平均需要4s左右」:程序大小情況:最后放上好看的Julia分形圖:
五、參考資料
[1] 浮點(diǎn)數(shù)在計(jì)算機(jī)中的存儲(chǔ) —— IEEE 754標(biāo)準(zhǔn)(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權(quán)威指南
●嵌入式專欄精選教程
●精選匯總 | ST工具、下載編程工具
●精選匯總 | 嵌入式軟件設(shè)計(jì)與開發(fā)
●精選匯總 | STM32、MCU、單片機(jī)
歡迎關(guān)注我的公眾號(hào),回復(fù)“加群”按規(guī)則加入技術(shù)交流群,回復(fù)“1024”查看更多內(nèi)容。
歡迎關(guān)注我的視頻號(hào):
點(diǎn)擊“閱讀原文”查看更多分享,歡迎點(diǎn)分享、收藏、點(diǎn)贊、在看。
總結(jié)
以上是生活随笔為你收集整理的揭秘ARM FPU 加速浮点计算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CEF3:用CEF3实现最简单的浏览器
- 下一篇: 中国新一代人工智能治理原则发布 | 发展