教你在RISCV中使用DSP指令!
掃描二維碼
隨時(shí)隨地手機(jī)看文章
教你在RISCV中使用DSP指令!
-
1.概述
-
2.RISCV P擴(kuò)展編程實(shí)踐(內(nèi)聯(lián)匯編)
-
ADD16 (SIMD 16-bit Addition)
-
3.RISCV P擴(kuò)展編程實(shí)踐(庫(kù)函數(shù))
-
4.總結(jié)
1.概述
DSP有相關(guān)的專業(yè)芯片,能夠?qū)iT實(shí)現(xiàn)計(jì)算功能,相比于通用處理器,DSP芯片專門用于計(jì)算,可以在一個(gè)周期內(nèi)執(zhí)行多條計(jì)算。隨著單片機(jī)對(duì)計(jì)算功能的需求越來(lái)越多,如果用傳統(tǒng)的通用處理器去執(zhí)行大數(shù)據(jù)的計(jì)算,將會(huì)消耗許多的機(jī)器周期,導(dǎo)致系統(tǒng)的實(shí)時(shí)性變低。于是,一些通用芯片上也開始集成DSP擴(kuò)展,比如常見的ARM Cortex-R和ARM Cortex-M內(nèi)核。
有了這些DSP擴(kuò)展支持,其功能更加強(qiáng)大,使用上,許多的辦法都可以進(jìn)行。比如常用的CMSIS-DSP。就是arm提供的DSP的編程庫(kù)。
https://arm-software.github.io/CMSIS_5/DSP/html/deprecated.html
使用上可以只需要將lib庫(kù)和頭文件包含到項(xiàng)目中即可。這樣就可以使用CMSIS里面的函數(shù)功能,比如求正余弦函數(shù)。
arm_cos_f32(radians);
如果用標(biāo)準(zhǔn)的數(shù)學(xué)庫(kù)中的cos函數(shù),同樣也能夠達(dá)到目的,標(biāo)準(zhǔn)庫(kù)函數(shù)則需要消耗更多的機(jī)器周期,而使用了DSP庫(kù),則更加方便高效的進(jìn)行計(jì)算。
上述是ARM對(duì)DSP支持的使用,RISCV也支持DSP擴(kuò)展,在RISCV的架構(gòu)手冊(cè)上,就對(duì)DSP擴(kuò)展有著一些描述。
https://github.com/riscv/riscv-p-spec
目前的支持riscv dsp的riscv core已經(jīng)有了,但是實(shí)際的硬件芯片,市面上還沒有見到。目前riscv 的 p擴(kuò)展還是處于沒有穩(wěn)定的階段,通過(guò)文檔的閱讀,也能夠大致的描述最終的模型。
首先其特點(diǎn)如下:
RISCV DSP擴(kuò)展是采用的通用寄存器進(jìn)行數(shù)據(jù)的存儲(chǔ),這意味著SIMD的寄存器的單位是以通用寄存器的寬度作為標(biāo)準(zhǔn),如果是RV32,寄存器的長(zhǎng)度是32,如果是RV64,則寄存器的長(zhǎng)度為64。
相比于RISCV 的RVV,DSP擴(kuò)展其寄存器的長(zhǎng)度有限,但是對(duì)于并不復(fù)雜的計(jì)算來(lái)說(shuō),已經(jīng)足夠,特別是簡(jiǎn)單的音頻,圖形編解碼,電機(jī)控制等等,都是非常好用的。
下面來(lái)描述一下具體如何在RISCV上進(jìn)行DSP的編程。
2.RISCV P擴(kuò)展編程實(shí)踐(內(nèi)聯(lián)匯編)
riscv-p-spec規(guī)定了P擴(kuò)展的一些常用的函數(shù)功能。
ADD16 (SIMD 16-bit Addition)
Type: SIMD
Format:
31 25 | 24 20 | 19 15 | 14 12 | 11 7 | 6 0 |
---|---|---|---|---|---|
ADD16 0100000 | Rs2 | Rs1 | 000 | Rd | OP-P 1110111 |
Syntax:
ADD16 Rd, Rs1, Rs2
Purpose: Perform 16-bit integer element additions in parallel.
Description: This instruction adds the 16-bit integer elements in Rs1 with the 16-bit integer elements in Rs2, and then writes the 16-bit element results to Rd.
Operations:
Rd.H[x] = Rs1.H[x] + Rs2.H[x]; for RV32: x=1..0, for RV64: x=3..0
Exceptions: None
Privilege level: All
Note: This instruction can be used for either signed or unsigned addition.
Intrinsic functions:
-
Required:
uintXLEN_t __rv__add16(uintXLEN_t a, uintXLEN_t b);
-
Optional (e.g., GCC vector extensions):
RV32: uint16x2_t __rv__v_uadd16(uint16x2_t a, uint16x2_t b); int16x2_t __rv__v_sadd16(int16x2_t a, int16x2_t b); RV64: uint16x4_t __rv__v_uadd16(uint16x4_t a, uint16x4_t b); int16x4_t __rv__v_sadd16(int16x4_t a, int16x4_t b);
在上述的指令中,規(guī)定了add16的編碼規(guī)則,對(duì)于RV32來(lái)說(shuō),一個(gè)寄存器的位寬是16,那么可以將一個(gè)寄存器拆分成兩個(gè)單元,一個(gè)機(jī)器周期,同時(shí)執(zhí)行兩條加法。同樣的指令,在RV64上,則可以拆分成四個(gè)單元,一個(gè)機(jī)器周期,可以執(zhí)行四條加法。
通過(guò)對(duì)編譯出來(lái)的程序進(jìn)行反匯編,可以得到對(duì)應(yīng)的匯編代碼。
當(dāng)然,如果要實(shí)現(xiàn)dsp指令的擴(kuò)展,目前官方的編譯器還沒有完全支持riscv的dsp擴(kuò)展。如果要完成帶有dsp指令的支持的gcc編譯器,需要對(duì)編譯器進(jìn)行一定的定制。因?yàn)槟壳皉iscv的p擴(kuò)展,并未完全定稿,如果完善后,應(yīng)該會(huì)被合并到主線主線。
其中編程的方式采用gcc內(nèi)部的內(nèi)聯(lián)函數(shù)的方式進(jìn)行,在《P-ext-proposal.adoc》中,規(guī)定了Intrinsic functions的形式,比如add16。
uintXLEN_t __rv__add16(uintXLEN_t a, uintXLEN_t b); RV32: uint16x2_t __rv__v_uadd16(uint16x2_t a, uint16x2_t b); int16x2_t __rv__v_sadd16(int16x2_t a, int16x2_t b); RV64: uint16x4_t __rv__v_uadd16(uint16x4_t a, uint16x4_t b); int16x4_t __rv__v_sadd16(int16x4_t a, int16x4_t b);
那么有上述函數(shù)可以供調(diào)用,不需要任何的庫(kù)文件的支持,因?yàn)樵趃cc編譯器中,內(nèi)部自己可以根據(jù)這些內(nèi)聯(lián)函數(shù)進(jìn)行匯編實(shí)現(xiàn)。
使用時(shí),只需要包含gcc自帶的dsp相關(guān)的頭文件即可。
#includestatic __attribute__ ((noinline)) unsigned long add16 (unsigned long ra, unsigned long rb) { return __rv__add16 (ra, rb); }
使用技巧上并未特殊方法,但是目前,這基本上是比直接寫匯編更加高效的dsp編程方式了。
3.RISCV P擴(kuò)展編程實(shí)踐(庫(kù)函數(shù))
在很多情況下,底層的DSP指令雖然可以完成很多功能,不同的組合方式將能夠帶來(lái)不同效果,但是這些基礎(chǔ)庫(kù)的使用,在很多方面也需要編程人員有很強(qiáng)的數(shù)學(xué)基礎(chǔ),并不能提供通用的math計(jì)算方法,這時(shí)使用庫(kù)函數(shù)將能夠在很大程度上解決這個(gè)問題。類似ARM的CMSIS-DSP。RISCV生態(tài)上也有一個(gè)NMSIS。
https://github.com/Nuclei-Software/NMSIS
可以將riscv的標(biāo)準(zhǔn)的dsp指令通過(guò)組合,形成更加通用的數(shù)學(xué)庫(kù),比如sin或者cos,fft,matrix等等,一些常用的標(biāo)準(zhǔn)庫(kù)函數(shù),都可以在里面找到。對(duì)于做嵌入式AI來(lái)說(shuō),已經(jīng)十分完善。
使用方法上,首先需要添加NMSIS的的lib文件,然后包含頭文件。
#include "riscv_math.h"
直接調(diào)用NMSIS庫(kù)中暴露出來(lái)的函數(shù)即可。
float32_t xx = riscv_cos_f32(float32_t cos);
這種方式更加直接,也能減少編程人員對(duì)DSP函數(shù)的使用不熟悉,帶來(lái)的一些人為的錯(cuò)誤,所以NMSIS可以說(shuō)是DSP指令的上層軟件。使用該庫(kù)可以很容易的進(jìn)行高效的數(shù)據(jù)運(yùn)算。
4.總結(jié)
在riscv的芯片中,如果要使用DSP,首先需要該芯片的硬件設(shè)計(jì)實(shí)現(xiàn)了riscv的p擴(kuò)展,硬件支持的情況下,再適配編譯器,編譯器也將DSP的支持添加進(jìn)去。這樣可以直接使用DSP擴(kuò)展的指令了。然而直接使用DSP提供的指令進(jìn)行計(jì)算,工作量還是很大,同時(shí)優(yōu)化也不一定非常的好,此時(shí)使用NMSIS庫(kù)提供的函數(shù),直接利用優(yōu)化好的數(shù)學(xué)函數(shù)進(jìn)行數(shù)據(jù)計(jì)算,這樣才是高效最簡(jiǎn)單的方式。