Try Kernel を eclipse 抜きでビルドする(2)

前回の記事では、とりあえずコンパイルとリンクを行って、参照未解決エラーが出たところまで説明した。今回は、足りない関数を補って 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チカ実行までは何とかなった。