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++にしたらとか、そんなのを検討してみます.
#勇者の方ありがとうございました.
かしこ
先のポストでのコメントは両方とも自分が行ったものなのですが、言葉足らずでしたね。STM32cubeIDEの標準コンパイラがgccっぽかったので、そのgccでREV命令を使うにはどうすればいいか、という答えとして__builtin_bswap32を紹介したつもりでした。
返信削除インラインアセンブラでゴリゴリ書いてもいいんですが、レジスタの退避・復帰、レジスタへのロード・ストアなどおまじないが多いですし、関数にすると呼び出し規則(引数・戻り値の渡し方)を知らないといけないし、関数呼び出しのオーバーヘッドもばかにならないので、なるべく関数呼び出しが発生しない形にしたいところです。
その点、builtin関数なら最適化でインライン展開も期待できるようなので、高速化にはうってつけかな、と思われます。
ちなみに、逆アセンブルをみると(第一)引数も戻り値もr0に入れる規則のようですので、以下のコードで置き換えられそうですね。
le2be32:
08001384: rev r0,r0
08001386: bx lr
そうでしたか.いろいろとありがとうございます.
削除STM32cubeIDEのアセンブラは何から記述したらよいのかわからないので助かります.
#define __REV(value) __builtin_bswap32(value)
削除こんなのがproject内にありますね.便利なもんだ.
まずは、上手くいったようでおめでとうございます。
削除さらなる高速化についてですが、
昔は、配列への添え字でアクセスするのは遅いといわれていましたが、今時のコンパイラだと差はつかないような気がします。
Cortex-Mは投機的実行をしないとのことなので、条件分岐でパイプラインがクリアされてしまうため、条件分岐を減らすのが有効ではないかと思います。具体的にはループの中の__REVの回数を増やす方向です。その場合には、ポインタアクセスにしたほうが簡潔に書けてよいでしょう。
しっかり改善しました、次の投稿でご説明、、、
削除トラブルシュート中に雑魚っぽい故障とか足を引っ張られるとゲンナリしますねー。
返信削除エンディアン問題はきれいに解決してよかったですね。
自分は論理的に手間をかけて追い詰めているのだが、正体はオカルトだったときの徒労.
削除もうヤケ酒でしょう.ゴクゴク、ぷはぁ~っ
ARMの速度であれこれとやった昨今、Intel,Ryzenの偉さが身に沁みます.
削除豊富なキャッシュ、投機的実行、そういうものにお世話になっているんですね.
ARMの世界観は30年前の8bitマイコンの延長線上ですね。インテルアーキテクチャは違う世界を作ってますね。馬力は正義。
削除AppleはARMと共同でどういう仕掛けで来るのか、楽しみです.
削除たぶんhardware acceleratorの方向かな.CPUはただのシーケンサに格下げ.