2020年8月24日月曜日

STM32でDCC/DDCを作る方向で (37) HARD FAULTからの解放、LEBE変換高速化

告知です.
コミケ99にて当社のDDC/DACを頒布いたします.
  日付   2021年12月31日(金) 東地区 テ-40b  東5ホール
  サークル名    bangflat
コミケにお越しの際はお立ち寄りいただけますとありがたいです.
商品紹介ページを作りました.
ーーーー

STM32でDDC/DCCを作ろう!        INDEXページへ

さて本日も勇者諸君と共にSTM32をいじろうではないか.........

HARD FAULTが治りました.その原因が心が挫けそうになるようなものでして.

真の原因は調査せずに放置しますけど、現象は次のようなものでした.

プリント基板にプローブとST-LINK/V2を接続してあります.USBが2系統接続してあるのは、白いUSBはaudioのため、黒いUSBはCOM port経由の内部モニタです.このような接続で電源を10日間ぐらい入れっぱなしで作業していました.

ところが昨夜から、HARD FAULTどころかもっと症状が悪化し、焼いても起動すらしなくなってしまいました.あ~ぁ基板が死んだなと思って全てのコネクタやプローブを外しました.それで目視チェックなどして再度電源投入したら、完全に治ってしまいました.げっそり....

故障モードはこうです.再現性あり.
1)ST-LINK/V2で焼く
2)焼いた後にSTM32F405にresetがかかり自動的に起動するはずだがそうならない
3)起動させるには基板の電源をOFF/ONするしかない(resetボタンではダメ)

すなわち、故障の本質は2なわけです.STM32F205を載せた基板ではこのような不具合は生じません.ST-LINK/V2に焼かれた後の405の起動不良が真の原因と思われます.がっ、それを探索するのはやめときます.

------
そもそもSTM32F405に移植した理由は、LE→BE変換を高速化するためでした.→35回目

405が動いたので動作速度をようやく測定できるようになりました.

結果です.
STM32F205     CPU120MHz    3840wordのLEBE変換      4.04mSec
STM32F405     CPU168MHz    3840wordのLEBE変換      2.84mSec

405の恩恵で4.04→2.84mSecに1.422倍高速化されました.これはCPU clock周波数のup分1.4倍とほぼ一致するのでそういう事なんだね、でおしまいです.これを確認したいがために何日ロスったんだオレは! 解決するまでは一歩も引かないんだからね、プンプン!

405はこれにて投了でございます.2倍になるんなら少し考えるんだけどなぁ.

なお、LEBE変換のC sourceはこんなものです.

for(int i=0; i<size/4; i++) pdst32top[i] = le2be32(psrc32top[i]);

uint32_t le2be32(uint32_t little) {
     return ((little & 0xff000000) >> 24)
          + ((little & 0xff0000) >> 8)
          + ((little & 0xff00) << 8)
          + ((little & 0xff) << 24);  }

------
勇者の方々からコメント欄に寄せられたLEBE変換高速化プランが2ヶあります.
勇者諸氏に深謝いたします.

プランA: LEBE変換命令がある.REV r3, r7 みたいな

プランB: gccに__builtin_bswap32関数、__builtin_rev関数というのがある

次はこれをやってみましょう.
REV命令についてはinline assemblerを使ったりするのかな?やったことないので知らなーい.

とりあえず、le2be32()のところと思われる個所の逆アセンブルを見ると、REV命令は登場していないっぽくて、シコシコとやってるみたいだね.ARMのマシン語は読み慣れてないのであんまりわからんけどもREV命令は有効かもって思ったりする.
le2be32:
08001384:   push    {r7}
08001386:   sub     sp, #12
08001388:   add     r7, sp, #0
0800138a:   str     r0, [r7, #4]
0800138c:   ldr     r3, [r7, #4]
0800138e:   lsrs    r2, r3, #24
08001390:   ldr     r3, [r7, #4]
08001392:   lsrs    r3, r3, #8
08001394:   and.w   r3, r3, #65280  ; 0xff00
08001398:   add     r2, r3
0800139a:   ldr     r3, [r7, #4]
0800139c:   lsls    r3, r3, #8
0800139e:   and.w   r3, r3, #16711680  ; 0xff0000
080013a2:   add     r2, r3
080013a4:   ldr     r3, [r7, #4]
080013a6:   lsls    r3, r3, #24
080013a8:   add     r3, r2
080013aa:   mov     r0, r3
080013ac:   adds    r7, #12
080013ae:   mov     sp, r7
080013b0:   pop     {r7}
080013b2:   bx      lr

今宵はこれまでにしとうございます.
明日は毒父を精神病院から老人ホームへ移送する日なんです.


------翌朝追記------
REV命令を実装してみました.
project内にこのような記述があります.
#define __REV(value)   __builtin_bswap32(value)
for文の右辺をこのように変えただけです.
for(int i=0; i<size/4; i++) pdst32top[i] = __REV(psrc32top[i]);

__REV()のキモの部分の逆アセンブルはこれだけになりました.みぢかっっ
08001658:   ldr     r2, [r7, #8]
0800165a:   
rev     r2, r2
0800165c:   str     r2, [r3, #0]

おかげさまで動作速度は改善しました.
STM32F205     CPU120MHz    3840wordのLEBE変換      4.04mSec
STM32F405     CPU168MHz    3840wordのLEBE変換      2.84mSec
STM32F205     CPU120MHz    3840wordのLEBE変換      2.30mSec  (REV命令)
REV命令は良好です.405を超えた.

これで32bit384kHzが動くか?
めでたくSTM32F205で動きました.
とはいえまだLEBE変換にCPU timeの65%ぐらい消費しているのでもう少し高速化して50%に近くなる方法はないものかと考えるに、for文をwhile文にしたらどうなんだろとかptr++にしたらとか、そんなのを検討してみます.

#勇者の方ありがとうございました.

かしこ

10 件のコメント:

  1. 先のポストでのコメントは両方とも自分が行ったものなのですが、言葉足らずでしたね。STM32cubeIDEの標準コンパイラがgccっぽかったので、そのgccでREV命令を使うにはどうすればいいか、という答えとして__builtin_bswap32を紹介したつもりでした。
    インラインアセンブラでゴリゴリ書いてもいいんですが、レジスタの退避・復帰、レジスタへのロード・ストアなどおまじないが多いですし、関数にすると呼び出し規則(引数・戻り値の渡し方)を知らないといけないし、関数呼び出しのオーバーヘッドもばかにならないので、なるべく関数呼び出しが発生しない形にしたいところです。
    その点、builtin関数なら最適化でインライン展開も期待できるようなので、高速化にはうってつけかな、と思われます。

    ちなみに、逆アセンブルをみると(第一)引数も戻り値もr0に入れる規則のようですので、以下のコードで置き換えられそうですね。
    le2be32:
    08001384: rev r0,r0
    08001386: bx lr

    返信削除
    返信
    1. そうでしたか.いろいろとありがとうございます.
      STM32cubeIDEのアセンブラは何から記述したらよいのかわからないので助かります.

      削除
    2. #define __REV(value) __builtin_bswap32(value)
      こんなのがproject内にありますね.便利なもんだ.

      削除
    3. まずは、上手くいったようでおめでとうございます。

      さらなる高速化についてですが、
      昔は、配列への添え字でアクセスするのは遅いといわれていましたが、今時のコンパイラだと差はつかないような気がします。
      Cortex-Mは投機的実行をしないとのことなので、条件分岐でパイプラインがクリアされてしまうため、条件分岐を減らすのが有効ではないかと思います。具体的にはループの中の__REVの回数を増やす方向です。その場合には、ポインタアクセスにしたほうが簡潔に書けてよいでしょう。

      削除
    4. しっかり改善しました、次の投稿でご説明、、、

      削除
  2. トラブルシュート中に雑魚っぽい故障とか足を引っ張られるとゲンナリしますねー。
    エンディアン問題はきれいに解決してよかったですね。

    返信削除
    返信
    1. 自分は論理的に手間をかけて追い詰めているのだが、正体はオカルトだったときの徒労.
      もうヤケ酒でしょう.ゴクゴク、ぷはぁ~っ

      削除
    2. ARMの速度であれこれとやった昨今、Intel,Ryzenの偉さが身に沁みます.
      豊富なキャッシュ、投機的実行、そういうものにお世話になっているんですね.

      削除
    3. ARMの世界観は30年前の8bitマイコンの延長線上ですね。インテルアーキテクチャは違う世界を作ってますね。馬力は正義。

      削除
    4. AppleはARMと共同でどういう仕掛けで来るのか、楽しみです.
      たぶんhardware acceleratorの方向かな.CPUはただのシーケンサに格下げ.

      削除