The PCを自作する。 その8: STM32は早かった。
The PCを自作するシリーズ。
その4で、STMマイコンが思ったより早くなくて困ったけど、 その後進展があったので、再調査したい。
新情報
前回、LTOが動かなかった時に、 STM32duinoのフォーラムで質問しておいた。
そこで、digitalWrite関数より、 digitalWriteFastという関数を使った方が高速だというアドバイスをもらった。 これを検証したい。
また、1つ前のバージョンであるSTM32Duino 1.7.0ではLTOが動いたという情報ももらった、 これも検証したい。
digitalWrite
まずはおさらいで、digitalWriteを使った場合。 次のようなコードになる。
void setup() {
pinMode(PB8, OUTPUT);
}
void loop() {
while(1) {
digitalWrite(PB8,HIGH);
digitalWrite(PB8,LOW);
digitalWrite(PB8,HIGH);
digitalWrite(PB8,LOW);
}
}
このコードは次のようなバイナリにコンパイルされた。
arm-none-eabi-objdump -D digitalwrite.ino.elf
のように逆アセンブルした。
08002154 <loop>:
8002154: b508 push {r3, lr}
8002156: 2101 movs r1, #1
8002158: 200d movs r0, #13
800215a: f002 fa9d bl 8004698 <digitalWrite>
800215e: 2100 movs r1, #0
8002160: 200d movs r0, #13
8002162: f002 fa99 bl 8004698 <digitalWrite>
8002166: 2101 movs r1, #1
8002168: 200d movs r0, #13
800216a: f002 fa95 bl 8004698 <digitalWrite>
800216e: 2100 movs r1, #0
8002170: 200d movs r0, #13
8002172: f002 fa91 bl 8004698 <digitalWrite>
8002176: e7ee b.n 8002156 <loop+0x2>
引数をr1とr0に入れてblしてる。
これをオシロスコープで観察すると次のようになる。
オシロの計測だと、オンになってる幅は900nsほどだ。
digitalWriteFast
教えてもらった、digitalWriteFastは次のようになる。
void setup() {
pinMode(PB8, OUTPUT);
}
void loop() {
while(1) {
digitalWriteFast(PB_8, HIGH);
digitalWriteFast(PB_8, LOW);
digitalWriteFast(PB_8, HIGH);
digitalWriteFast(PB_8, LOW);
}
}
使い方はほぼ同じだ。 このコードは次のようにコンパイルされた。
08002154 <loop>:
8002154: 4b05 ldr r3, [pc, #20] ; (800216c <loop+0x18>)
8002156: 685a ldr r2, [r3, #4]
8002158: 4b05 ldr r3, [pc, #20] ; (8002170 <loop+0x1c>)
800215a: 6a1b ldr r3, [r3, #32]
800215c: f3c3 230f ubfx r3, r3, #8, #16
8002160: 6113 str r3, [r2, #16]
8002162: 6153 str r3, [r2, #20]
8002164: 6113 str r3, [r2, #16]
8002166: 6153 str r3, [r2, #20]
8002168: e7fa b.n 8002160 <loop+0xc>
800216a: bf00 nop
800216c: 2000000c andcs r0, r0, ip
8002170: 08005bd0 stmdaeq r0, {r4, r6, r7, r8, r9, fp, ip, lr}
コア部分である、2160番地から2168番地を見るとstr命令1つだけでオンオフが切り変わってた。 これは速そうだ。
速すぎてオシロスコープがおいついてないが、26nsぐらいの幅でオンが終わってる。
このマイコンは72Mhzで動いているので、1命令あたり13nsぐらいで実行できる。 また、マニュアルによるとCortexM3マイコンではSTR命令は2クロックで処理が終わるようだ。
26nsはほぼ計算通りだ。
digitalToggleFast
同じようにdigitalToggleFastも教えてもらった。 次のようなコードになる。
void setup() {
pinMode(PB8, OUTPUT);
}
void loop() {
while(1) {
digitalToggleFast(PB_8);
}
}
コンパイルすると次のようになった。
08000154 <loop>:
8000154: 4b04 ldr r3, [pc, #16] ; (8000168 <loop+0x14>)
8000156: 6859 ldr r1, [r3, #4]
8000158: 4b04 ldr r3, [pc, #16] ; (800016c <loop+0x18>)
800015a: 6a1b ldr r3, [r3, #32]
800015c: f3c3 220f ubfx r2, r3, #8, #16
8000160: 68cb ldr r3, [r1, #12]
8000162: 4053 eors r3, r2
8000164: 60cb str r3, [r1, #12]
8000166: e7fb b.n 8000160 <loop+0xc>
8000168: 2000000c andcs r0, r0, ip
800016c: 08001d14 stmdaeq r0, {r2, r4, r8, sl, fp, ip}
ビット反転を毎回実行しているため、digitalWriteFastよりは遅そうだ。
実際次のようになった。
このコードはループあたり13クロックかかるようだ。
1.7.0 + LTO
ぶっちゃけ、digitalWriteFastで十分だが、STM32Duin o1.7.0では、LTOが動いたという情報を得たので、 試してみる。
コードはdigitalWriteバージョン。
まず、O3+LTOを試してみたが、なんとバイナリが64kbを越えてしまい、 実行できなかった。
とりあえずOs+LTOにしてみたところ、次のようになった。
残念!
考察
digitalWriteFastやdigitalToggleFastの定義は次のヘッダファイルにあった。
cores/arduino/stm32/digital_io.h
ヘッダファイルなので、LTOが無くてもインラインにできるみたい。
まとめ
Chip | Method | time |
---|---|---|
Arduino | digitalWrite | 5000ns |
Arduino | Register | 125ns |
STM32 | digitalWrite | 900ns |
STM32 | digitalWriteFast | 26ns |
STM32 | digitalToggleFast | 152ns |
STM32 | Os+LTO | 360ns |
つづく
次回こそは、i2c用のレベルコンバータを繋ぎたい。 次回につづく。