SNSへはこちら

AVR32マイコンやってみよう(6) - C言語を使えるようにする

そろそろアセンブリでゴリゴリ書くのに限界を感じたので、いきなりですが gcc の導入をしたいと思います。

ですが、gcc 公式で AVR32 はサポートされていませんし、Atmel からソースコードの配布も(多分)ありません。なので、有志の作った(と思われる) gcc をソースコードからビルドするという、いつもどおりのことをします。
ついでに、これまで使ってきたアセンブラのビルドコマンドもここで公開しますね。

この記事で扱う内容は以下です。毎度のことながら、ホスト環境は macOS です。Windows の方は簡単ですので、他のサイトをご参考にしてください。確か AVR32 Studio をインストールするだけだった気がします。

  1. gcc のビルド
    • binutils, gcc, newlib をソースからビルドします。
    • 古い gcc なので、ビルド時の注意点を述べています。
  2. ヘッダの導入
    • Microchip 公式サイトから IO ヘッダをダウンロードして導入します。
  3. プロジェクトディレクトリを整備する
    • Makefile 等を配置して、すぐにビルドができる状態にします。
  4. 番外編

1. gcc のビルド

AVR32 用に改造された gcc 等のツールチェインをビルドします。

配布サイト

全て GitHub のリポジトリです。

  • binutils
    • gdb をビルドしたい人はブランチを切り替えましょう。今回は省略。
    • binutils のバージョンは 2.23 をベースにしている。
  • gcc
    • バージョン 4.4.7 をベースにしている(流石に古代だ...)
  • newlib
    • バージョン 1.16 をベース。

コマンド

列挙するだけです。基本的にこれを作業ディレクトリ内で打ち込めばいいですが、下に注意点があるのでご一読ください。
また、この binutils、gcc、newlib ですが、それぞれ内部に(外部でもいいですが)サブディレクトリを作成し、その中でビルドを行ってください。

$ cd avr32-gcc
$ mkdir build # ビルド用サブディレクトリ作成
$ cd build
$ ../configure ... # ここで configure
$ make # ビルド開始
$ make install # インストール

それでは、それぞれの configure コマンドを以下に示します。

# binutils
$ ../configure --prefix=/usr/local/cross/avr32 --target=avr32 --disable-werror --disable-nls
# gcc(欄外の注意書き参照)
$ CC=gcc-8 CXX=g++-8 CFLAGS="$(pkg-config --libs --cflags gmp mpfr)" ../configure --prefix=/usr/local/cross/avr32 --target=avr32 --disable-werror --disable-nls --disable-bootstrap --enable-newlib --disable-libssp --enable-languages=c
# newlib
$ ../configure --prefix=/usr/local/cross/avr32 --target=avr32 --disable-werror --disable-nls --disable-bootstrap

gccビルド時の注意

そういえば古い gcc は、古すぎるがあまりビルド時にエラーが出たりして、注意が必要なんでした。割と典型的な fix です。

cgraph_node が無いとか言われる

前の記事でも言いましたが、gcc-9 系はバグがあるとの報告を見ました。どうやら gcc 内部で定義されている構造体(よく知りませんが)を暗黙に用いる際にその参照に失敗してコンパイルエラーを吐いてしまうようです。なので上のように gcc-8 系を使うのがベストかと思います。

redefinition of 'floor_log2' と怒られる

こちらの gcc のメーリングリストを参考にして、gcc/toplev.cgcc/toplev.h を修正します。ちなみに修正したら、一旦ビルドディレクトリ内 gcc/ にて make clean するのをお忘れなく。でないと途中からビルドが変わってリンク時に「シンボルが重複してるんだけど」とキレられるようです。

_libc_name_p が定義されていないと言われる

出ました。ちょっと古い gcc 典型のエラー。これ毎回対処法忘れるんですよね。このサイトに従えばいいようです。そうそう。gperf とか記憶あるわ。

C++は??

と思われるでしょう。流石に古すぎるので(gcc でも for(int i=0;...) とやるとエラーが吐かれるほど古代)、やる意味はあまりないのかなぁと思ってやってみましたが、案の定、ダメでした
libstdc++ にパッチが当たっていないようで、怒られてしまいます。

checking for unistd.h... (cached) yes
checking for wchar.h... (cached) yes
checking for wctype.h... (cached) yes
configure: error: No support for this host/target combination.
make[1]: *** [configure-target-libstdc++-v3] Error 1
make: *** [all] Error 2

まあ今回は見送りかな。libstdc++ 無しで運用することも可能ですが、止めておきました。

2. ヘッダの導入

ペリフェラル用のレジスタアクセスを容易にするヘッダを準備しましょう。別に自作でもいいんですが(私自身、最初はヘッダを自作していた)、脳筋プレイの連続 & アドレスのオフセット関係でミスをする ということなので、公式に頼り切ることにしました。

まずは AVR と Atmel SAM が一緒くたになっている公式のアーカイブから、Toolchain Archive Header Files の項目にある 8-bit & 32-bit AVR Toolchain をダウンロードし、展開します。

すると avr-headers があると思いますので、その中の avr32 ディレクトリを $PREFIX/avr32/include にコピーします。もし gcc のインストール先が /usr/local/cross/avr32 なら、

$ cp -r ~/Downloads/avr-headers/avr32 /usr/local/cross/avr32/avr32/include

と打ち込めば OK です。これで .c ファイル内にて #include <avr32/io.h> と書いてレジスタ用の構造体が使えるようになります。

3. プロジェクトディレクトリを整備する

make 一発でプログラムをビルドできるようにしましょう。まずは任意のディレクトリを作ります。僕は適当に Register というレジスタ弄る気マンマンのディレクトリ名にしました
後々のために、その中で obj というディレクトリを作成しておいてください。

リンカスクリプトの準備

ここの話は過去の記事と全く同じことをしていますので、済んでいる人はスキップしてください。

まずは先程インストールしたツールチェインの中から引っ張ってきます。が、そのままは使いません。FLASH の先頭 0x8000 0000 〜 0x8000 1FFF は DFU ブートローダーが配置されていて、通常書き換えないからです。このツールチェインは先頭から書き換えるようなことを(多分)意識しているので、今回の目的と異なっています。
なので、そこを書き換えましょう。まずはコピー。

$ cd Register
$ cp /usr/local/cross/avr32/avr32/lib/ldscripts/avr32elf_uc3b064.x linker.ld

では修正していきましょう。
まず 11 行目には FLASH の先頭アドレスが書かれています。それを以下のように数値をいじります。

FLASH (rxai!w) : ORIGIN = 0x80002000, LENGTH = 64K

続いて 19 行目にはよくわかりませんが、バイナリの開始アドレスを指定するらしいです。同様に書き換えます。これで以上です。

PROVIDE (__executable_start = 0x80002000); . = 0x80002000;

Makefileの配置

make を打った時に実行されるコマンドを記述したファイルです。とは言ってもイチから説明するわけには行きませんので、以下をコピーして Makefile としてください。

これで OK のハズです。そうしたら main.c でも作成して、make してみましょう。

// main.c
#include <avr32/io.h>

int main(void) {
    while(1) {}
    return 0;
}

4. 番外編

ところで、先程のアーカイブでは Windows 用のビルド済み/ヘッダ導入済みのバイナリが配布されています。
これをちょっとした出来心でダウンロードしてみましたが...

$ ls
avr32-addr2line.exe*  avr32-gcov.exe*     avr32-strip.exe*     libavr32sim.dll  libusb0_x64.sys  rm.exe*
avr32-ar.exe*         avr32-gdb.exe*      avr32gdbproxy.exe*   libavrtools.dll  make.exe*        rmdir.exe*
avr32-as.exe*         avr32-ld.exe*       avr32program.exe*    libgmp-3.dll     mingwm10.dll     testlibusb-win.exe*
avr32-c++.exe*        avr32-nm.exe*       avr32trace.exe*      libiconv2.dll    msgcmp.exe*      testlibusb.exe*
avr32-c++filt.exe*    avr32-objcopy.exe*  avrfwupgrade.exe*    libintl.dll      msgcomm.exe*     touch.exe*
avr32-cpp.exe*        avr32-objdump.exe*  gettext.exe*         libintl3.dll     msgfmt.exe*      xgettext.exe*
avr32-g++.exe*        avr32-ranlib.exe*   inf-wizard.exe*      libmpfr-1.dll    msgmerge.exe*
avr32-gcc-4.3.2.exe*  avr32-readelf.exe*  install-filter.exe*  libusb0.dll      msgunfmt.exe*
avr32-gcc.exe*        avr32-size.exe*     libavr32ocd.dll      libusb0.sys      ngettext.exe*
avr32-gccbug          avr32-strings.exe*  libavr32program.dll  libusb0_x64.dll  pthreadGC2.dll

$ wine avr32-gcc -v
Using built-in specs.
Target: avr32
Configured with: /home/mingwbuild/avr32src/gcc/configure none
Thread model: single
gcc version 4.3.2 (atmel-1.2.0-(mingw32_special))

ほほう...じゃあこれ、**wine を使えば Windows 用の gcc でビルドできますね!次のような main.c を用意します。

#include <avr32/io.h>

int returnMe(int a) {
    return a;
}

コンパイル & 逆アセンブルしてみましょうか。

$ wine avr32-gcc -Os -D __AVR32_UC3B064__ -c a.c
$ wine avr32-objdump -d a.o

a.o:     file format elf32-avr32


Disassembly of section .text:

00000000 <returnMe>:
   0:   5e fc           retal   r12

へ〜.........じゃあ今のやる意味なかった?
いえ、決してそうではありません。実際、wine は安定性に難がありますし、動作が遅いです。
既存のLチカプログラムにもこの Windows 版 gcc でコンパイルしてみましたけど、普通に動きます。当然かも知れませんが、これは面白いと思いました。

これで楽々プログラムを書く準備が整いました。コンパイラのバージョンがめっちゃ古いことがかなり不満ですが、まあ極力気にせずいきましょうか。

次回はアセンブリでゴリ押ししていたプログラムを C に移植します。