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してる。

これをオシロスコープで観察すると次のようになる。

digitalWrite

オシロの計測だと、オンになってる幅は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つだけでオンオフが切り変わってた。 これは速そうだ。

digitalWriteFast

速すぎてオシロスコープがおいついてないが、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よりは遅そうだ。

実際次のようになった。

digitalToggleFast

このコードはループあたり13クロックかかるようだ。

1.7.0 + LTO

ぶっちゃけ、digitalWriteFastで十分だが、STM32Duin o1.7.0では、LTOが動いたという情報を得たので、 試してみる。

コードはdigitalWriteバージョン。

まず、O3+LTOを試してみたが、なんとバイナリが64kbを越えてしまい、 実行できなかった。

とりあえずOs+LTOにしてみたところ、次のようになった。

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用のレベルコンバータを繋ぎたい。 次回につづく