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"