公開日: 2023年10月20日

Raspberry Pi Pico のミニマムなクロス開発環境を FreeBSD に構築する

今後しばらく Raspberry Pi Pico (以下 Pico) を使っていろいろ実験してみたいと考えています。初回となる今回は FreeBSD 上にクロス開発環境を構築します。個人的に統合開発環境 (IDE) はなんとなく苦手なので、GCC を中心に、なるべくミニマムな構成となるように試みます。目標は取りあえずツールチェインのインストールを終わらせるところまでとします。機材を購入したり、コンパイルして書き込んで LED をチカチカさせたりといった楽しい話題は次回以降に譲りたいと思います。

はじめに

本稿はかなりの試行錯誤の末になんとかなった顛末をまとめたものです。したがって、これが正しい手順かどうかはよくわからず、冗長だったり抜けがあったりするかもしれません。一応 GitHub で公開されている下記のプロジェクトのビルド、フラッシュメモリへの書き込み、GDB でのデバッグができることは確認しました (詳細は次回以降の記事で)。

本稿でインストールするツールチェインは、すべてソースコードからビルドします。FreeBSD の ports/packages に収録されていたりもしますが、Cortex-M0+ (Pico に搭載されている RP2040 マイコンのプロセッサコア) をサポートしていないためか、なんなのか、よくわかりませんが動いてくれませんでした。

環境構築にあたっては、下記のサイトが大変参考になりました。

[1]Cross-Compiling GCC Toolchain for ARM Cortex-M Processors
https://www.linkedin.com/pulse/cross-compiling-gcc-toolchain-arm-cortex-m-processors-ijaz-ahmad
[2]Try Kernel を eclipse 抜きでビルドする(1)
https://oohito.com/nqthm/archives/3749
[3]Guide: GCC and Bare Metal Programming
http://cs107e.github.io/guides/gcc/

開発環境の概要

  • arm-none-eabi-binutils

    アーカイバ ar、リンカ ld、オブジェクトファイルのデータをダンプする objdump、ELF ファイルの情報を読み出して表示する readelf などのユーティリティツールが含まれます。

  • arm-none-eabi-gcc

    ARM 向けの C/C++ コンパイラ本体です。同梱の libgcc には ARM アーキテクチャ用ランタイム ABI の実装が含まれているようで、Cortex-M0+ で除算などを行う場合には事実上必須だと思います。

  • arm-none-eabi-newlib (任意)

    組み込み用の C 標準ライブラリです。なるべくなら依存しないようにしたいところですが、 せっかくあるのでインストールしておきます。ちなみに μT-Kernel 3.0 の ビルドには Newlib が必要でした。1)

  • libaeabi-cortexm0 (任意)

    いろいろ試行錯誤する中で見つけた、ARM アーキテクチャ用ランタイム ABI の実装のひとつ。libgcc.a の代わりにリンクしてみたところ、実行ファイルのサイズが少し削減されました (後述)。

  • gmake

    GNU make のことでコマンド名は gmake です。プロジェクトをビルドするのに用います。FreeBSD に標準インストールされている BSD make (コマンド名 make) とは別物です。

  1. μT-Kernel 3.0 のソースコード自体は C 標準ライブラリに依存しないように書かれていると思われますが、msdrvif.c という C ファイルの中で構造体の代入を行っている部分があり、これを GCC で Cortex-M0+ 向けにコンパイルすると memcpy に置き換わるようです。この点も含めて、μT-Kernel 3.0 についてはいずれ記事にしたいと思っています。

準備

依存パッケージのインストール

はじめに、ツールチェインのビルドに必要なパッケージをインストールしておきます。下記のサイトを参考にしました。

# pkg install -y gmake gcc gmp expat guile3 iconv lzma mpfr python zlib-ng

作業用とインストール用のディレクトリを作る

ツールチェインのビルド作業を行うディレクトリと、インストール先のディレクトリを作っておきます。本稿ではそれぞれ ~/pico/arm-none-eabi-build/、~/pico/arm-none-eabi/ とします。

% mkdir -p ~/pico/arm-none-eabi-build/ ~/pico/arm-none-eabi/
% cd ~/pico/arm-none-eabi-build/
% mkdir binutils-build/ gcc-build/ newlib-build/ gdb-build/

ソースコードを取得する

作業用のディレクトリに移動してソースコードを取得し、展開します。

% cd ~/pico/arm-none-eabi-build/
% wget https://ftp.gnu.org/gnu/binutils/binutils-2.41.tar.xz
% wget https://ftp.gnu.org/gnu/gcc/gcc-13.2.0/gcc-13.2.0.tar.xz
% wget wget ftp://sourceware.org/pub/newlib/newlib-4.3.0.20230120.tar.gz
% wget https://ftp.gnu.org/gnu/gdb/gdb-13.2.tar.xz
% tar Jxf binutils-2.41.tar.xz
% tar Jxf gcc-13.2.0.tar.xz
% tar xzf newlib-4.3.0.20230120.tar.gz
% tar Jxf gdb-13.2.tar.xz

ツールチェインのビルドとインストール

最初は binutils です。configure オプションについては上記参考サイト [1] に詳しく書かれていますので、そちらを参照してください (以下同様)。

% cd ~/pico/arm-none-eabi-build/binutils-build
% ../binutils-2.41/configure --target=arm-none-eabi --prefix=/home/mijinco/pico/arm-none-eabi --with-cpu=cortex-m0plus --with-no-thumb-interwork --with-mode=thumb
% gmake all install

いまインストールした binutils のコマンド群は以降の作業で必要となるので、パスを通しておきます。

% export PATH="$PATH:~/pico/arm-none-eabi/bin"

次は gcc と libgcc です。

% cd ~/pico/arm-none-eabi-build/gcc-build
% ../gcc-13.2.0/configure --target=arm-none-eabi --prefix=/home/mijinco/pico/arm-none-eabi --with-cpu=cortex-m0plus --enable-languages=c,c++ --without-headers --with-newlib --with-no-thumb-interwork --with-mode=thumb
% gmake all-gcc install-gcc
% gmake all-target-libgcc install-target-libgcc

続いて Newlib。

% cd ~/pico/arm-none-eabi-build/newlib-build
% ../newlib-4.3.0.20230120/configure --target=arm-none-eabi --prefix=/home/mijinco/pico/arm-none-eabi --disable-newlib-supplied-syscalls
% gmake all install

Newlib への対応のため、再度 GCC を configure して make します。configure の際--without-headersは付けないようです。よくわかっていませんが、取りあえず参考サイト [1] のとおりにやっています。

% cd ~/pico/arm-none-eabi-build/gcc-build
% ../gcc-13.2.0/configure --target=arm-none-eabi --prefix=/home/mijinco/pico/arm-none-eabi --with-cpu=cortex-m0plus --enable-languages=c,c++ --with-newlib --with-no-thumb-interwork --with-mode=thumb
% gmake all-gcc install-gcc

最後に GDB です。configure オプションに--disable-nlsを付けないと「undefined reference to `libintl_dgettext'」というエラーが出てビルドに失敗します。NLS サポートはなくても問題ないと思います。

% cd ~/pico/arm-none-eabi-build/gdb-build
% ../gdb-13.2/configure --target=arm-none-eabi --prefix=/home/mijinco/pico/arm-none-eabi --disable-nls
% gmake all install

これでツールチェインのインストールは完了です。インストールされたコマンド群を確認しておきましょう。

% ls ~/pico/arm-none-eabi/bin
arm-none-eabi-addr2line*        arm-none-eabi-gdb*
arm-none-eabi-ar*               arm-none-eabi-gdb-add-index*
arm-none-eabi-as*               arm-none-eabi-gprof*
arm-none-eabi-c++*              arm-none-eabi-ld*
arm-none-eabi-c++filt*          arm-none-eabi-ld.bfd*
arm-none-eabi-cpp*              arm-none-eabi-lto-dump*
arm-none-eabi-elfedit*          arm-none-eabi-nm*
arm-none-eabi-g++*              arm-none-eabi-objcopy*
arm-none-eabi-gcc*              arm-none-eabi-objdump*
arm-none-eabi-gcc-13.2.0*       arm-none-eabi-ranlib*
arm-none-eabi-gcc-ar*           arm-none-eabi-readelf*
arm-none-eabi-gcc-nm*           arm-none-eabi-run*
arm-none-eabi-gcc-ranlib*       arm-none-eabi-size*
arm-none-eabi-gcov*             arm-none-eabi-strings*
arm-none-eabi-gcov-dump*        arm-none-eabi-strip*
arm-none-eabi-gcov-tool*

次回ログイン時にこれらのコマンドへのパスが通るように、お使いのシェルの rc ファイルに書いておきましょう。zsh であれば次のようになります。

~/.zshrc
path=(~/pico/arm-none-eabi/bin(N-/) $path)

libgcc がないとどうなるかの実験

ここでちょっと脱線します。上のほうで「libgcc は事実上必須」と書きましたが、これをリンクしないとどうなるのかを確認しておきましょう。下記は『インターフェース』誌 2023 年 7 月号第 2 部第 3 章の L チカプログラムを -lgcc オプションなしでビルドした結果です (ビルド方法など詳しくは次回の記事で紹介します)。

% gmake
arm-none-eabi-gcc -Wall -march=armv6-m -mthumb -ffreestanding -I../../part_2/sect_3/include -MMD -MP -o build/obj/main.o -c ../../part_2/sect_3/application/main.c
...
arm-none-eabi-gcc -o ./build/blink.elf -nostartfiles -nostdlib -Wl,-Map,./build/blink.map,--gc-sections,-T,../../part_2/sect_3/linker/pico_memmap.ld build/obj/main.o build/obj/boot2.o build/obj/reset_hdr.o build/obj/vector_tbl.o
/usr/home/yone/pico/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: build/obj/main.o: in function `delay_ms':
main.c:(.text+0x3a): undefined reference to `__aeabi_uidiv'
/usr/home/yone/pico/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: build/obj/reset_hdr.o: in function `init_pll':
reset_hdr.c:(.text+0xaa): undefined reference to `__aeabi_uidiv'
/usr/home/yone/pico/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: reset_hdr.c:(.text+0xc8): undefined reference to `__aeabi_uidiv'
/usr/home/yone/pico/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: build/obj/reset_hdr.o: in function `clock_config':
reset_hdr.c:(.text+0x1b4): undefined reference to `__aeabi_uldivmod'
collect2: エラー: ld はステータス 1 で終了しました
gmake: *** [Makefile:65: build/blink.uf2] エラー 1

エラーが何件か見られますが、取りあえず 1 番上の「main.c:(.text+0x3a): undefined reference to `__aeabi_uidiv'」に注目し main.c の delay_ms 関数を見てみると、次のように除算を行っているところがあります。

interface_trykernel/part_2/sect_3/application/main.c
    UINT cnt = ms/TIMER_PERIOD;

そこで、コンパイルしてできた main.o を逆アセンブルしてみます。

% arm-none-eabi-objdump -d build/obj/main.o

build/obj/main.o:     file format elf32-littlearm


Disassembly of section .text:
...
0000002c <delay_ms>:
  2c:   b580        push    {r7, lr}
  2e:   b084        sub     sp, #16
  ...
  3a:   f7ff fffe   bl      0 <__aeabi_uidiv>
  ...

Cortex-M0+ は割り算器を持たないので、コンパイラによって除算が __aeabi_uidiv という関数に置き換えられています。__aeabi_uidiv 関数は ARM のランタイム・ヘルパー・ファンクションのひとつとして下記の資料で定義されていました。

というわけで libgcc.a の中にその実装があるものと思われ、Cortex-M0+ 向けの組み込みプログラミングにおいては、これがないと除算もできないということになります。とはいえ、libgcc に頼らず自力や他力で実装するという手もなくはありません。次の節はそんなお話です。

libgcc の代案: libaeabi-cortexm0

さて、__aeabi_uidiv ってなに?と思って調べたら ARM のランタイム・ヘルパー・ファンクションというものに行きついたことは上に書いたとおりですが、さらに調べるとそれを独自に実装し公開している方が居られました。

ARM Run-Time ABI for the Cortex-M0 processor
https://github.com/bobbl/libaeabi-cortexm0

ざっと見た感じかなりシンプルでコンパクトな作りです。実行ファイルのサイズを小さくできるかもと思って上記の L チカプログラムに取り入れてみたところ、libgcc 版に比べて 500 バイトほど節約することができました。こういうものもあるよということで、ここにビルド方法を書きとどめておきます。やり方はとても簡単で、上記のリポジトリを clone してきて make するだけです。

% git clone https://github.com/bobbl/libaeabi-cortexm0.git
% cd libaeabi-cortexm0/
% gmake

あとは生成された libaeabi-cortexm0.a をリンクするだけですが、具体的な利用方法は次回やります。

Raspberry Pi Pico 実験室