Open7

手動Arduino

okuokuokuoku

面倒すぎて死にそう

... WioTerminalを動かすためのライブラリはArduino(やtinygoやrust)くらいしか提供されていないため、Arduino Coreを普通のビルドシステムに載せ替えておく必要がある。しかも、このArduino CoreがWio Terminalの場合LGPLで、これは Atmelの標準ライブラリのライセンスと互換性が無い (Atmelのチップ以外での実行を禁止している) ので混ざらないように注意する必要まである。

okuokuokuoku

Arduinoライブラリの仕様

Arduinoライブラリの仕様は arduino-cli にドキュメントされている。

https://arduino.github.io/arduino-cli/0.19/library-specification/#layout-of-folders-and-files

要は、

  1. library.properties のあるディレクトリを検出する EDIT: 無くても良い。。
  2. ライブラリのフォーマットを判別する: src ディレクトリがあればArduino1.5、無ければレガシー(1.0)フォーマットとなる
  3. Arduino1.5フォーマットライブラリの場合、srcそのサブディレクトリ をソースコードとして追加し、 src をインクルードパスに含める
  4. レガシーなライブラリの場合、ルートディレクトリと utility ディレクトリをソースコードとして追加し、ルートディレクトリをインクルードパスに含める

仕様ではソースコードとして認識すべきglob式は特に決まっていないが、まぁ常識的に考えて *.c *.cpp*.S だろう。。(例えば素の *.s (小文字) はソースコードなんだろうか..?)

.ino の処理

Arduinoの"スケッチ"(通常のIDEの"プロジェクト"に相当する)は、.ino の拡張子を持つファイルを含む。この拡張子の由来はArduino 1.0リリースノートにある。

https://www.arduino.cc/en/Main/ReleaseNotes

ARDUINO 1.0 - 2011.11.30

[environment]

* The file extension for sketches has changed from .pde to .ino, to avoid
  conflicts with the Processing software ("ino" are the last three letters
  in "Arduino").

Arduino IDEは自動的に #include <Arduino.h> と関数プロトタイプを補った上で全体をC++コードとしてコンパイルするようになっている。

https://arduino.github.io/arduino-cli/0.19/sketch-build-process/

If not already present, #include <Arduino.h> is added to the sketch. This header file (found in the core folder for the currently selected board) includes all the definitions needed for the standard Arduino core.

Prototypes are generated for all function definitions in .ino/.pde files that don't already have prototypes. In some rare cases, prototype generation may fail for some functions. To work around this, you can provide your own prototypes for these functions.

今回は他人のスケッチをコンパイルする必要は無いので、これらを実装する必要はない(= Arduinoライブラリは、これらのC++拡張部分を使用できない)。

okuokuokuoku

とりあえずビルドを通す

https://github.com/okuoku/arduinotest/commit/43aa35da38ede57af5ed5f3e2fc2fa7a59b7ad24

ここまででビルドが通った。まだリンクしている内容が正しいかはわからんが。。

古いCMSISに依存している

https://github.com/okuoku/arduinotest/commit/0f194d518ada8ea6d61ab7d2a03c3233c685e7ad

レジスタ値等にCMSIS依存があるようなので追加しておいた。

... これCMSIS4なのか。。CMSISはよくバグってるのでできれば新しいのに追従して欲しいけど。。というかやっぱりバグってんじゃん。。更新。

https://github.com/okuoku/arduinotest/commit/fa8c05db533cbc7db7e077b7e3c09f39076a0737

GNU GOLDで COPY section type is unsupported

/cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/bin/ld: error: /home/oku/repos/arduinotest/ArduinoCore-samd/variants/wio_terminal/linker_scripts/gcc/flash_with_bootloader.ld:191:9: COPY section type is unsupported
/cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/bin/ld: error: /home/oku/repos/arduinotest/ArduinoCore-samd/variants/wio_terminal/linker_scripts/gcc/flash_with_bootloader.ld:202:16: COPY section type is unsupported

マジかよ。。 BFDの ld を使うように修正。

-fuse-ld=bfd で使うリンカを変更できる(ようにcrosstool-ngをconfigureしてある)。

_gettimeofday がnewlibとArduinoの両方にある

/cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/bin/ld.bfd: /cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/lib/libc.a(lib_a-syscalls.o): in function _gettimeofday': /cygdrive/f/wiowork/crosstool-ng/.build/arm-unknown-eabi/src/newlib/newlib/libc/sys/arm/syscalls.c:604: multiple definition of _gettimeofday'; libArduinoCore.a(delay.c.obj):/home/oku/repos/arduinotest/build/../ArduinoCore-samd/cores/arduino/delay.c:55: first defined here

newlibのSyscall stubはLinux用なので要らない。ビルドしないようにする。これはcrosstool-ngで言うところの Disable the syscalls supplied with newlib オプション、newlibのconfigureで言うと --disable-newlib-supplied-syscalls にあたる。

何故か getpid 等が必要になる

/cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/bin/ld.bfd: /cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/lib/libc.a(lib_a-abort.o): in function abort': /cygdrive/f/wiowork/crosstool-ng/.build/arm-unknown-eabi/src/newlib/newlib/libc/stdlib/abort.c:59: undefined reference to _exit'
/cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/bin/ld.bfd: /cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/lib/libc.a(lib_a-signalr.o): in function _kill_r': /cygdrive/f/wiowork/crosstool-ng/.build/arm-unknown-eabi/src/newlib/newlib/libc/reent/signalr.c:53: undefined reference to _kill'
/cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/bin/ld.bfd: /cygdrive/f/wiowork/crosstool-ng/prefix/lib/gcc/arm-unknown-eabi/11.2.0/../../../../arm-unknown-eabi/lib/libc.a(lib_a-signalr.o): in function _getpid_r': /cygdrive/f/wiowork/crosstool-ng/.build/arm-unknown-eabi/src/newlib/newlib/libc/reent/signalr.c:83: undefined reference to _getpid'

これはCRTが間違ってるっぽいな。。とりあえずstubを用意してビルドだけ通した。

extern "C"{
    void _exit(int bogus){(void)bogus;}
    void _kill(void){}
    int _getpid(void){return 1;}
}
okuokuokuoku

起動ロジックの確認

ここまでで、C++コードをELF形式のバイナリにすることはできた。実際のWioTerminalで動かすためには、更にCPU内蔵flashに書くバイナリに変換する必要がある。

適当にブートローダやArduinoのコードを確認したところ、単に .text セクションと .data セクションを連結すれば十分なようだ。

ブートローダ → Arduino

WioTerminalのブートローダのソースコードがどこにあるのかはわからなかったが、たぶんMSのUF2ローダと同じものなのでそれを確認する。

  1. UF2ローダーは APP_START_ADDRESS + 4 に書かれているアドレスをフェッチして、ジャンプ先 app_start_address として保存 する。 APP_START_ADDRESS はブートローダ領域の直後で、 APP_START_ADDRESS + 4 = 0x4004exception_table の2エントリ目になる
  2. Arduino側の exception_table の2エントリ目 は関数 Reset_handler で、これが __etext (= .text セクションの終端) から .data セクションのサイズぶんだけデータをコピーしたり、 .bss 部分をゼロクリアしたりする