前回の記事では、とりあえずコンパイルとリンクを行って、参照未解決エラーが出たところまで説明した。今回は、足りない関数を補って Raspberry Pi pico で動作させるところまでやる。
割り算の商と余りを求める関数を用意する
前回のエラーメッセージはこんなものだった。(一部抜粋)
reset_hdr.c:(.text+0xaa): undefined reference to `__aeabi_uidiv'
reset_hdr.c:(.text+0x1b4): undefined reference to `__aeabi_uldivmod'
要するに __aeabi_uidiv と __aeabi_uldivmod が不足しているということであり、それぞれ2つの非負整数の割り算の商と余りを求める関数にあたる。通常は標準ライブラリで提供される類の関数だが、そういったものに依存せずにビルドしたいので、自分で実装することにする。
他にもこういう事態はありそうなので、Try Kernel で用意されているディレクトリ内に入れるのではなく、別途 lib ディレクトリを用意してその中に記述することにした。
#include <typedef.h>
UINT __aeabi_uidiv(UINT lhs, UINT rhs)
{
UINT quotient = 0;
UINT remainder = 0;
for (int i = 31; i >= 0; i --) {
quotient <<= 1;
remainder <<= 1;
remainder |= (lhs >> 31) & 1;
lhs <<= 1;
if (rhs <= remainder) {
remainder -= rhs;
quotient |= 1;
}
}
return quotient;
}
UINT __aeabi_uldivmod(UINT lhs, UINT rhs)
{
UINT remainder = 0;
for (int i = 31; i >= 0; --i) {
remainder <<= 1;
remainder |= (lhs >> 31) & 1;
lhs <<= 1;
if (rhs <= remainder) {
remainder -= rhs;
}
}
return remainder;
}
このコードを divmod.c というファイル名で lib ディレクトリに保存した。このプログラムも含めてコンパイル、リンクを行った。
$ /usr/bin/arm-none-eabi-gcc -c -I include -mthumb -mcpu=cortex-m0plus application/*.c boot/*.c lib/*.c
$ /usr/bin/arm-none-eabi-gcc -nostartfiles -Wl,--script=linker/pico_memmap.ld -Wl,-z,max-page-size=4096 -Wl,--gc-sections *.o -o out.elf
何もエラーが出ずに終了し、out.elf というファイルが生成された。file で確認すると 32bit ARM用のELF バイナリが生成されていることがわかる。
$ file out.elf
out.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
UF2ファイルを生成する
Raspberry Pi pico へプログラムを書き込むには、USBでPCに接続し、外付けメディアとして認識させれば、ファイルをコピーするだけで簡単なのだが、先のELFバイナリファイルのままではダメで、UF2という形式に変換する必要がある。
UF2ファイル形式とは、MicrosoftがMakeCodeのために作ったファイル形式らしく、以下のgithubに詳細が書かれている。
https://github.com/microsoft/uf2
手っ取り早く ELFバイナリを UF2 形式に変換するツールのソースが pico-sdk に含まれており、cmake & make で簡単に生成できるのだが、pico-sdk の github からソースファイルさえ入手すれば自分でビルドすることもできる。
必要なファイルは、pico-sdk/tools/elf2uf2 ディレクトリの elf.h と main.cpp に、pico-sdk/src/common/boot_uf2/include/boot ディレクトリの uf2.h の3つである。これらをダウンロードして tools ディレクトリへ配置する。
(githubからの直接ダウンロードなので要注意。素直に pico-sdk をクローンした方がいいと思う)
$ wget https://github.com/raspberrypi/pico-sdk/raw/master/tools/elf2uf2/elf.h -P tools
(..中略..)
$ wget https://github.com/raspberrypi/pico-sdk/raw/master/tools/elf2uf2/main.cpp -P tools
(..中略..)
$ wget https://github.com/raspberrypi/pico-sdk/raw/master/src/common/boot_uf2/include/boot/uf2.h -P tools/boot
(..中略..)
$ tree tools/
tools/
├── boot
│ └── uf2.h
├── elf.h
└── main.cpp
1 directory, 3 files
あとは単純に g++ でコンパイルすればよい。
$ cd tools
$ g++ main.cpp -o elf2uf2
$ cd ..
tools/elf2uf2 を使って変換できるようになったので、out.elf を out.uf2 に変換して完了。
$ tools/elf2uf2 out.elf out.uf2
ディレクトリ構成
最終的なディレクトリ/ファイル構成は以下のとおり。書籍提供のコードには(今のところ)一切手を加えていない。
$ tree .
.
├── application
│ └── main.c
├── boot
│ ├── boot2.c
│ ├── reset_hdr.c
│ └── vector_tbl.c
├── boot2.o
├── divmod.o
├── include
│ ├── knldef.h
│ ├── sysdef.h
│ ├── syslib.h
│ └── typedef.h
├── lib
│ └── divmod.c
├── linker
│ └── pico_memmap.ld
├── main.o
├── out.elf
├── out.uf2
├── reset_hdr.o
├── tools
│ ├── boot
│ │ └── uf2.h
│ ├── elf2uf2
│ ├── elf.h
│ └── main.cpp
└── vector_tbl.o
7 directories, 21 files
今後を考えると build用のディレクトリや Makefile を用意するなりしたほうがよさそう。
Raspberry Pi pico に転送して実行する
作成したプログラムを実行するには、Raspberry Pi pico のボード上面にある BOOTSEL ボタンを押したまま、PCにUSB接続すればよい。少し待つとドライブとして認識されるので、そこにUF2形式のファイルをコピーする。
Raspberry Pi pico が RPI-RP2 と認識されたので、以下のように out.uf2 をコピーする。
$ cp out.uf2 /media/${USER}/RPI-RP2/
転送が終わるとすぐにリセットがかかって実行が開始され、Raspberry Pi pico でLチカが始まる。
ということで eclipse をインストールせずとも、Lチカ実行までは何とかなった。