Kintex-7にMicroblazeを載せる の履歴(No.2)
更新概要†
WebPack 版の Vivado を使って Kintex-7 に MicroBlaze を載せ、UART 経由で PC と通信したい。
最近は無償の WebPack 版でも MicroBlaze IP を使えるようになっているのだけれど、 実はソフト開発に使う SDK は WebPack 版では MicroBlaze をサポートしないことを後から知った。
以下の手順で自ら MicroBlaze 用のコンパイラを用意すれば、 ちょっと面倒ながらも MicroBlaze を使ったベアメタルシステムの開発が可能なことが分かったので まとめておく。
回路の作成†
MicroBlaze, ClockWizard, UART, GPIO, INTC などを並べる。
クロスコンパイラの準備†
Windows 上の Vagrant で Ubuntu を立ち上げる†
https://qiita.com/watame/items/1c17aaf266f14abef13f を見ながら
LANG:console $ cd \Users\osamu\Vagrant $ mkdir microblaze-gcc $ cd microblaze-gcc $ vagrant init bento/ubuntu-16.04 $ vagrant up --provider virtualbox $ vagrant ssh
これでコンソールが立ち上がる。
まずは日本語キーボードに変更し( https://blog.amedama.jp/entry/2017/03/10/210552 )、
タイムゾーンを日本にする( https://qiita.com/koara-local/items/32b004c0bf80fd70777c )。
LANG:console $ sudo dpkg-reconfigure keyboard-configuration > Generic 105-key (Intel) PC > Japanese > Japanese > The default for the keyboard layout > No compose key $ sudo timedatectl set-timezone Asia/Tokyo
共有フォルダの設定†
一旦 exit 出抜けて、
LANG:console $ vagrant halt
で仮想マシンも止める。
\Users\osamu\Vagrant\microblaze-gcc\Vagrantfile に
config.vm.synced_folder "./shared", "/home/vagrant/shared"
という行を追加した。
これはホストマシンの
\Users\osamu\Vagrant\microblaze-gcc\shared
の内容を仮想マシンの
/home/vagrant/shared
に接続するおまじない。
必要なパッケージを追加する†
もう一度、
LANG:console $ vagrant up --provider virtualbox $ vagrant ssh
としてシェルを立ち上げ、
LANG:console $ sudo apt-get -y install build-essential m4 g++ texinfo
開発用ツールチェインのビルド†
https://qiita.com/watame/items/1c17aaf266f14abef13f を参考にしつつ、 以下の内容を \Users\osamu\Vagrant\microblaze-gcc\shared\build-gcc.sh として保存した。
LANG:sh export MB_WORK=~/work export MB_LINUX_DEP=${MB_WORK}/mb-linux-dep export MB_LINUX_PREFIX=${MB_WORK}/mb-elf-toolchain-linux export MB_BUILD=x86_64-linux-gnu export MB_TARGET=microblaze-elf export MB_PREFIX=mb- export PATH=${MB_LINUX_PREFIX}/bin:$PATH # prepare -------------------------------------------------- mkdir -p ${MB_WORK}/build-linux mkdir -p ${MB_WORK}/source # download sources... cd ${MB_WORK}/source wget http://ftp.gnu.org/gnu/gmp/gmp-6.1.1.tar.xz wget http://ftp.gnu.org/gnu/mpfr/mpfr-3.1.4.tar.xz wget http://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz wget http://isl.gforge.inria.fr/isl-0.17.tar.xz wget http://ftp.gnu.org/gnu/binutils/binutils-2.26.1.tar.bz2 wget http://ftp.gnu.org/gnu/gcc/gcc-5.4.0/gcc-5.4.0.tar.bz2 wget ftp://sources.redhat.com/pub/newlib/newlib-2.4.0.20160527.tar.gz tar xzf gmp-6.1.1.tar.xz tar xzf mpfr-3.1.4.tar.xz tar xzf mpc-1.0.3.tar.gz tar xzf isl-0.17.tar.xz tar xzf binutils-2.26.1.tar.bz2 tar xzf gcc-5.4.0.tar.bz2 tar xzf newlib-2.4.0.20160527.tar.gz # linux ---------------------------------------------------- # gmp cd ${MB_WORK}/build-linux mkdir build-gmp cd build-gmp ../../source/gmp-6.1.1/configure --prefix=${MB_LINUX_DEP} make && make install && make check # mpfr cd ${MB_WORK}/build-linux mkdir build-mpfr cd build-mpfr ../../source/mpfr-3.1.4/configure \ --enable-static --disable-shared \ --with-gmp=${MB_LINUX_DEP} \ --prefix=${MB_LINUX_DEP} make && make install # mpc cd ${MB_WORK}/build-linux mkdir build-mpc cd build-mpc ../../source/mpc-1.0.3/configure \ --enable-static --disable-shared \ --with-gmp=${MB_LINUX_DEP} \ --with-mpfr=${MB_LINUX_DEP} \ --prefix=${MB_LINUX_DEP} make && make install # isl cd ${MB_WORK}/build-linux mkdir build-isl cd build-isl ../../source/isl-0.17/configure \ --without-piplib \ --enable-static --disable-shared \ --with-gmp-prefix=${MB_LINUX_DEP} \ --prefix=${MB_LINUX_DEP} make && make install # binutils cd ${MB_WORK}/build-linux mkdir build-binutils cd build-binutils ../../source/binutils-2.26.1/configure --target=${MB_TARGET} \ --prefix=${MB_LINUX_PREFIX} \ --program-prefix=${MB_PREFIX} make && make install # bootstrap gcc cd ${MB_WORK}/build-linux mkdir build-gcc1 cd build-gcc1 ../../source/gcc-5.4.0/configure \ --target=${MB_TARGET} \ --program-prefix=${MB_PREFIX} \ --with-gnu-as --with-gnu-ld \ --enable-static --disable-shared \ --with-gmp=${MB_LINUX_DEP} \ --with-mpfr=${MB_LINUX_DEP} \ --with-mpc=${MB_LINUX_DEP} \ --with-isl=${MB_LINUX_DEP} \ --with-newlib \ --without-headers \ --enable-languages="c,c++" --enable-interwork --enable-multilib \ --disable-decimal-float --disable-libffi \ --disable-libgomp --disable-libmudflap --disable-libquadmath --disable-libssp --disable-libstdcxx-pch \ --disable-nls --disable-shared --disable-threads --disable-tls \ --prefix=${MB_LINUX_PREFIX} \ --with-system-zlib make -j2 all-gcc make install-gcc # because newlib not support program-prefix, we need to create symbol link cd ${MB_LINUX_PREFIX}/bin ln -s mb-gcc mb-cc ln -s mb-ar microblaze-elf-ar ln -s mb-as microblaze-elf-as ln -s mb-c++ microblaze-elf-c++ ln -s mb-gcc microblaze-elf-gcc ln -s mb-gcc microblaze-elf-cc ln -s mb-gcc-ar microblaze-elf-gcc-ar ln -s mb-gcc-nm microblaze-elf-gcc-nm ln -s mb-gcc-ranlib microblaze-elf-gcc-ranlib ln -s mb-gcov microblaze-elf-gcov ln -s mb-gcov-tool microblaze-elf-gcov-tool ln -s mb-ld microblaze-elf-ld ln -s mb-ld microblaze-elf-ld.bfd ln -s mb-nm microblaze-elf-nm ln -s mb-objcopy microblaze-elf-objcopy ln -s mb-objdump microblaze-elf-objdump ln -s mb-ranlib microblaze-elf-ranlib ln -s mb-readelf microblaze-elf-readelf ln -s mb-size microblaze-elf-size ln -s mb-strings microblaze-elf-strings ln -s mb-strip microblaze-elf-strip # newlib cd ${MB_WORK}/build-linux mkdir build-newlib cd build-newlib ../../source/newlib-2.4.0.20160527/configure \ --target=${MB_TARGET} \ --prefix=${MB_LINUX_PREFIX} make && make install # gcc cd ${MB_WORK}/build-linux mkdir build-gcc2 cd build-gcc2 ../../source/gcc-5.4.0/configure \ --target=${MB_TARGET} \ --program-prefix=${MB_PREFIX} \ --with-gnu-as --with-gnu-ld \ --enable-static --disable-shared \ --with-gmp=${MB_LINUX_DEP} \ --with-mpfr=${MB_LINUX_DEP} \ --with-mpc=${MB_LINUX_DEP} \ --with-isl=${MB_LINUX_DEP} \ --with-newlib \ --enable-languages="c,c++" --enable-interwork --enable-multilib \ --disable-decimal-float --disable-libffi \ --disable-libgomp --disable-libmudflap --disable-libquadmath --disable-libssp --disable-libstdcxx-pch \ --disable-nls --disable-shared --disable-threads --disable-tls \ --prefix=${MB_LINUX_PREFIX} \ --with-system-zlib make -j 2 make install cd ${MB_LINUX_PREFIX}
リンク先のままだと make -j4 all-gcc で、
checking dynamic linker characteristics... configure: error: Link tests are not allowed after GCC_NO_EXECUTABLES. Makefile:8349: recipe for target 'configure-zlib' failed make: *** [configure-zlib] Error 1 make: *** Waiting for unfinished jobs....
というエラーが出たので、https://stackoverflow.com/questions/29481325/arm-gcc-build-error-under-fedora-21 に従って configure に --with-system-zlib を追加してある。
あとは、
LANG:console $ cd $ source shared/build-gcc.sh 2>1 | tee shared/build-gcc.log
とすれば、かな~り時間はかかるもののビルドに成功するはず。
LANG:console $ cd $ ls work/mb-elf-toolchain-linux/bin mb-addr2line mb-gcc-ar mb-ranlib microblaze-elf-gcc-5.4.0 microblaze-elf-objdump mb-ar mb-gcc-nm mb-readelf microblaze-elf-gcc-ar microblaze-elf-ranlib mb-as mb-gcc-ranlib mb-size microblaze-elf-gcc-nm microblaze-elf-readelf mb-c++ mb-gcov mb-strings microblaze-elf-gcc-ranlib microblaze-elf-size mb-cc mb-gcov-tool mb-strip microblaze-elf-gcov microblaze-elf-strings mb-c++filt mb-ld microblaze-elf-ar microblaze-elf-gcov-tool microblaze-elf-strip mb-cpp mb-ld.bfd microblaze-elf-as microblaze-elf-ld mb-elfedit mb-nm microblaze-elf-c++ microblaze-elf-ld.bfd mb-g++ mb-objcopy microblaze-elf-cc microblaze-elf-nm mb-gcc mb-objdump microblaze-elf-gcc microblaze-elf-objcopy $ microblaze-elf-gcc --version microblaze-elf-gcc (GCC) 5.4.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
パスに追加†
LANG:console $ cat >> ~/.bashrc PATH=/home/vagrant/work/mb-elf-toolchain-linux/bin/:$PATH ^D
試しにコンパイルしてみる†
LANG:console $ cd shared $ mkdir hello $ cd hello $ cat > hello.c int main() { return 0; } $ mb-gcc -c hello.c $ ls hello.c hello.o $ mb-gcc hello.c -o hello.elf /home/vagrant/work/mb-elf-toolchain-linux/lib/gcc/microblaze-elf/5.4.0/../../../../microblaze-elf/bin/ld: cannot find -lxil collect2: error: ld returned 1 exit status
コンパイルはできるけれど、リンクで libxil というライブラリが見つからないといって失敗する。
標準ライブラリなしでリンクするには -nostdlib を付けると良いらしいのだけれど、
LANG:console $ mb-gcc hello.c -nostdlib -o hello.elf /home/vagrant/work/mb-elf-toolchain-linux/lib/gcc/microblaze-elf/5.4.0/../../../../microblaze-elf/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000000050
今度は _start が見つからないと言われてしまう。
Wikipedia:フリースタンディング環境 などで解説されているとおり、-nostdlib を付けた場合には始めから main ではなく _start をエントリポイントとすれば良いらしい。
LANG:console $ cat > hello.c void _start() { } $ mb-gcc hello.c -nostdlib -o hello.elf $ ls hello.c hello.elf
めでたくコンパイルが通る。
コンパイルオプション†
MicroBlaze を AXI バスと共に使う場合には
にあるように Little Endian になるので、コンパイル時にもそうなるようにオプションを付ける必要がある。
LANG:console $ mb-gcc -mlittle-endian -O2 hello.c -nostdlib -o hello.elf
UART ライブラリを作成†
address.h
LANG:c #define UART_BASE 0x40600000 #define GPIO 0x40000000 #define INTC_BASE 0x41200000
uart.h
LANG:c #ifndef UART_LIB_INCLUDED #define UART_LIB_INCLUDED extern void uart_init(int enable_interrupt); extern int uart_status(); extern int uart_readable(); extern int uart_readc(); extern int uart_writable(); extern void uart_writec(char c); extern void uart_puts(const char *str); extern void uart_write_hex1(int n); extern void uart_write_hex2(int n); extern void uart_write_hex4(int n); extern void uart_write_hex8(int n); #endif
uart.c
LANG:c #include "address.h" #define UART_RX (UART_BASE+0) #define UART_TX (UART_BASE+4) #define UART_STATUS (UART_BASE+8) #define UART_CTRL (UART_BASE+12) ////////////////////////////////////////////////// static void reg_set(int addr, int value) { *(volatile int *)(addr) = value; } static int reg_get(int addr) { return *(volatile int *)addr; } ////////////////////////////////////////////////// void uart_init(int enable_interrupt) { reg_set(UART_CTRL, 3 + ((!!enable_interrupt) << 4)); } int uart_status() { return reg_get(UART_STATUS); } int uart_readable() { return uart_status() & 1; } int uart_readc() { while ( !uart_readable() ) // Empty ; return reg_get(UART_RX) & 0xff; } int uart_writable() { return 0 == ( uart_status() & 8 ); } void uart_writec(char c) { while ( !uart_writable() ) // Full ; reg_set(UART_TX, c & 0xff); } void uart_puts(const char *str) { while (*str) { uart_writec(*str); str++; } } void uart_write_hex1(int n) { n = n & 0xf; uart_writec('0' + n + (n >= 10 ? 'a'-'0'-10 : 0)); } void uart_write_hex2(int n) { uart_write_hex1(n >> 4); uart_write_hex1(n); } void uart_write_hex4(int n) { uart_write_hex2(n >> 8); uart_write_hex2(n); } void uart_write_hex8(int n) { uart_write_hex4(n >> 8); uart_write_hex4(n); }
hello.c†
hello.c
LANG:c #include "uart.h" void _start() { uart_puts("Hello World!!\n"); while(1) { char c = uart_readc(); uart_puts("detected key '"); uart_writec(c); uart_puts("'\n"); } }
コンパイル†
LANG:console $ mb-gcc -mlittle-endian -O2 hello.c uart.c -nostdlib -o hello.elf
elf ファイルを bit ファイルへ埋め込む†
updatemem と言うコマンドを使えばいいらしい。
LANG:tcl exec updatemem -force \ -meminfo C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/design_1_wrapper.mmi \ -bit C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/design_1_wrapper.bit \ -data C:/Users/osamu/Vagrant/microblaze-gcc/data/hello/hello.elf \ -proc design_1_i/microblaze_0 \ -out C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/download.bit
これで、design_1_wrapper.bit の中の design_1_i/microblaze_0 のメモリ内容を hello.elf で置き換えたものを download.bit として保存できる。
FPGA をプログラムする†
あとは以下のようにして download.bit を FPGA へダウンロードできる。
LANG:tcl set_property PROBES.FILE {} [get_hw_devices xc7k160t_0] set_property FULL_PROBES.FILE {} [get_hw_devices xc7k160t_0] set_property PROGRAM.FILE {C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/download.bit} [get_hw_devices xc7k160t_0] program_hw_devices [get_hw_devices xc7k160t_0]
ツールボタンに割り当てる†
コンパイル → bit 置き換え → FPGAプログラム
を何度も繰り返すのはかなり面倒なので、「bit 置き換え → FPGAプログラム」をワンクリックで可能なツールボタンを作成する。
Vivado の [Tools]-[Custom Commands]-[Customize Commands...] に 上記のスクリプトを ; で区切りながら1行に繋げた手順を入れてボタンにしておく。
コンパイルしたらボタンを押すだけでプログラムができるようになる。
フラッシュメモリに書き込むには†
普通なら Generate Bitstream のオプションで同時に bin ファイルを作成するようにしておけば良いのだけれど、elf を書き込んだ bit をフラッシュメモリに焼くにはどうしたらいいものか???
http://www3.hdl.co.jp/spc/xcm/466-q20160229.html
の手順で mcs ファイルを作れば良いらしい。
LANG:tcl write_cfgmem -format MCS -interface SPIx1 -size 64 \ -loadbit "up 0x0 C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/download.bit" \ "C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/download.mcs"