これまでBlackFin DSPでFIRを実装してきましたが、開発toolのCCESがlicense expiredで使えなくなったのを機に、STM32でDSPの代わりになるくらい高速演算できないかを試します.
目標は、300tap FIRが1演算10uSecぐらいで動いて欲しい.整数演算でもいい.
※元は数日前に投稿したものですが、結論を追記して再投稿します.CMSIS-DSP libraryを使うと、浮動小数点277tap FIR演算1回に3.36uSecを出せたのが結論です.はぇ~
ーーーー
STM32HシリーズはハイエンドCPUです.dual CPUのモノもありますけど、ここではsingle CPUのSTM32H723を使います.
coretex-M7 550MHz SRAM: total 564 Kbytes (cash memを含む)
Nucleoを使います.
以下、いろんな条件でFIRを動かして所要時間を測定します.日々継ぎ足してゆくので長くなりそうですが、よろしくお願いします.
その1: 初歩の初歩 →FIR演算1回に56uSec
32bit整数演算の277tap FIR filter
何の色気もありません.たぶんcash memとかも使ってない.coretex-M7内蔵のFPUとかもたぶん使ってません.filter_tapsは.dataすなわちFLASHに格納されています.indataはRAM.56uSecは遅いですね.
↓codeは概念だけです、いい加減です.クリッピングはしてません.
int64 fir;
int32 filter_taps, indata;
for( int n = 0; n < 277; n++ ) fir += filter_taps[n] * indata[p];
その2: TAP係数をRAM上に配置 →FIR演算1回に60uSec
32bit整数演算の277tap FIR filter
その1はtap係数をFLASH置きでしたが、その2ではtap係数を通常RAM上に置きました.indataは元から通常RAM.
FLASHはアクセスが遅いと聞きますのでRAMに置いたら速くなるかなと期待したのですが、やや遅くなってしまいました.
その3: data cashのみON →FIR演算1回に35uSec
32bit整数演算の277tap FIR filter
tap係数をFLASH置きに戻しまして、coretex-M7のdata cashをONしてみる.
効果ありますねぇ.
その4: 命令cashのみON →FIR演算1回に40uSec
32bit整数演算の277tap FIR filter
tap係数FLASH置き.dataは通常RAM置き.
coretex-M7の命令cashをONしてみる.
効果ありますねぇ.
その5: 命令cashとdata cash両方ON →FIR演算1回に16uSec
32bit整数演算の277tap FIR filter
tap係数FLASH置き.dataは通常RAM置き.
coretex-M7の命令cash/data cashをONしてみる.
い・き・な・り・速くなった.ちゃんと動いてるのか心配になるレベル.
その6: 浮動小数点演算に切り替え →FIR演算1回に16uSec その5までは整数演算(int32 x int32=int64t)でしたが、ここで単精度floatにしてみます.float x float=floatです.
浮動小数点演算の277tap FIR filter
tap係数FLASH置き.dataは通常RAM置き.
coretex-M7の命令cash/data cash ON.
なんと整数演算でもfloat演算でも速度が同じ.ミラクルだねぇ...
ホントに動いてるのか心配なので.listを見ると、
vmul.f32
という浮動小数点乗算命令を使っているのでちゃんと動いてるみたいよ.
ちなみに、整数演算FIRのときは
mul.w
という32bit整数乗算命令を使っていました.
その6: DTCM-RAMにtapとdataを置く →FIR演算1回に16uSec
その5に加えて、最もCPUに近いcashであるところのDTCM-RAMにtapとdataを置きました. →DTCMの使い方を末尾に記す
結果は16uSecで変わらず.期待したんだけどなぁ.
ここまでの結果から思うのは、命令cashとdata cashをONしただけで結構うまい事やってくれちゃってるということです.あと、浮動小数点演算命令も強力なんでしょうなぁ.
あっという間に行き詰まってしまいまして、次はもうFMACを使ってみるしかなくない?
FMACの調査の結果、使えそうにない.
FMACとは、16bitx16bit=16bitの固定小数点FIRを実装できる.
しかし、内部256wordのRAMを3分割し、入力85個、係数85個、出力85個のように使うしかないらしい.300tap FIRは実装できないみたいよ.
新たなプラン.CMSIS-DSP libraryというのがあります.ちょっと導入がかったるいけど、使えそうなlibraryで期待です.
その7: CMSIS-DSP libraryを使う(cashなし) →FIR演算1回に3.36uSec
倍精度浮動小数点演算の277tap FIR filter
信じられんくらい高速になりました.これならDSP要らないレベル.
ホントに動いているのかと疑りたくなりますが、FIR出力波形をDACで観測して正常であることを確認しています.
STM32CubeIDEのproject folder詰め合わせを
こちらに置きます.説明は面倒なので略します.
その8: CMSIS-DSP libraryを使う(cashあり) →動かない
命令/data cash両方ともONにしたら、DMAが死んだみたいで動きません.早読みとDSPとは相性が悪いんじゃないかなと思ふ.
結論としては、CMSIS-DSP libraryは速くて便利で良いです.
どういう動かし方をしているかというと、こんな感じです.
1)RAMに500個のfloat32をFIR input dataとして置く、繰り返し使う
2)CMSISのFIRは倍精度演算277tap、500個を一括演算させる、所要1680uSec
3)500個のFIR output dataを、intに変換し、DMAでDACへ送る、Fs=32kHz
4)Fsを決めるのはTIM15が出力するTRIGで、DACをTRIGする
5)DMA終了割り込みで2へ戻る
実際にはbuffer操作をもう少し複雑にしています.
500個のFIR演算に1680uSecかかったので、1個当たり3.36uSecです.
Fs=32kHzで500個ですと15.6mSecですから、FIR=1.68mSecなら余裕の計算速度です.DSPとして使えます.
CMSIS-DSP libraryのインスト~動かすまではいろいろめんどくさいので、末尾にて説明します.
===DTCM-RAMに変数を配置するやり方===
・STM32CubeIDEのproject内にある「STM32H7232CTX_FLASH.ld」を開く
・最初の方にこうゆうのがある.20000000番地以降がDTCMRAMになっている
MEMORY{
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 320K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
・同ファイルの下の方にあるDISCARDの上にでも、.myDTCMを追記する
.myDTCM :
{
. = ALIGN(4);
*(.myDTCM)
. = ALIGN(4);
} >DTCMRAM
/* Remove information from the standard libraries */
/DISCARD/ : {
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
・DTCM-RAMに配置したい変数の宣言にattributeを追記する.
float filter_taps[] __attribute__ ((section(".myDTCM")));
float indata[] __attribute__ ((section(".myDTCM")));
・buildしてから、project内の.mapを開く.
.myDTCM 0x20000000 0x13f4 ./Core/Src/main.o
0x20000000 filter_taps
0x20000454 indata
20000000番地以降にfilter_taps、indataが割り当てられているので目的達成
===CMSIS-DSP libraryをprojectへ組み込む方法===
執筆時点のversionは、STM32CubeIDE Version: 1.15.0 です.またCPUはSTM32H7でcoretex-m7です.異なるCPUですが
こちらのページが参考になります.
CMSIS-DSP libraryはSTM32CubeIDEフォルダ内に存在しています.しかし、初期状態では組み込まれていないので自分でprojectに組み込む作業が面倒です.
最初は、project直下にこの2つのファイルを入れます.arm_xxxはすげーたくさんあるのですが、ここではFIR filterを使うのが目的なのでこれだけにしておきます.どこかのサイトで「STM32H7に有用なのはarm_coretexM7lfsp_mathだけ」との情報を得たので付和雷同します.
このファイルをdrug&dropで組み込めるのですが、どこに在るのでしょうか?
わたしの環境では、ずいぶん深い所です.
C:\Users\hira\STM32Cube\Repository\STM32Cube_FW_H7_V1.11.2\Drivers\CMSIS\DSP\Lib\ARM\arm_cortexM7lfsp_math.lib
C:\Users\hira\STM32Cube\Repository\STM32Cube_FW_H7_V1.11.2\Drivers\CMSIS\DSP\Lib\GCC\libarm_cortexM7lfsp_math.a
まだあります.この3ファイルをIncフォルダに入れます.
コピー元はここです.深いですねぇ.
C:\Users\hira\STM32Cube\Repository\STM32Cube_FW_H7_V1.11.2\Drivers\CMSIS\DSP\Include
まだやることがあります.
project→propertyを開き、下図の右の窓の2つを入力します.元は2つ共空欄でした.
上: arm_cortexM7lfsp_math
下: "${workspace_loc:/${ProjName}}"
===CMSIS-DSP libraryを眺める===
arm_math.hを開くと様々な関数があります.
arm_fir_f32() 今回使った倍精度FIR
arm_fir_q15() たぶん整数16bitのFIRです
arm_float_to_q31() float→int32に一括変換、あると便利ですね
arm_mean_f32() 平均計算float32
arm_rms_q31() 実効値int32、あらま便利
arm_cfft_f32() 複素FFTなのかな? rfftもあります
arm_dct4_q15() DCTもあるぜ
arm_mat_inverse_f64() 逆行列
こりゃ便利でいいや.
固有値(eigenvalue)は見当たらなかったかな?
CMSIS-DSP libraryを使ったproject folder詰め合わせを
こちらに置きます.説明は面倒なので略します.PA4 portにDAC1出力を出すようになっています.
かしこ