Kintex-7にMicroblazeを載せる のバックアップ差分(No.3)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

* 概要 [#kb3d846d]

WebPack 版の Vivado を使って Kintex-7 に MicroBlaze を載せ、UART 経由で PC と通信したい。

最近は無償の WebPack 版でも MicroBlaze IP を使えるようになっているのだけれど、
実はソフト開発に使う SDK は WebPack 版では MicroBlaze をサポートしないことを後から知った。

以下の手順で自ら MicroBlaze 用のコンパイラを用意すれば、
ちょっと面倒ながらも MicroBlaze を使ったベアメタルシステムの開発が可能なことが分かったので
まとめておく。

#contents

* 回路の作成 [#gde59039]

MicroBlaze, ClockWizard, UART, GPIO, INTC などを並べる。

(詳細手順未稿)
** SDK でエラーが出る [#cfd860ef]

プロジェクトを作成しようとすると、bsp 生成部分で "Couldn't figure out compiler's library directory" というエラーが出てしまう。

 ERROR	: (XSDB Server)ERROR: [Hsi 55-1545] Problem running tcl command ::sw_cpu_v2_7::generate : Couldn't figure out compiler's library directory
     while executing "error "Couldn't figure out compiler's library directory" "" "hsi_error""
    (procedure "::sw_cpu_v2_7::generate" line 139) invoked from within "::sw_cpu_v2_7::generate microblaze_0"
 ERROR	: (XSDB Server)si 55-1442] Error(s) while running TCL procedure generate()
 ERROR	: (XSDB Server)ERROR: [Hsi 55-1450] Error: run
 ERROR	: (XSDB Server)ning generate_bsp.
 ERROR	: Error generating bsp sources: Failed in generating sources

SDK を使って MicroBlaze 開発をするには EDK のライセンスが必要ということらしい。

* クロスコンパイラの準備 [#fee22d62]

そこで SDK を使わずにソフトウェアを開発する。

実は MicroBlaze 用のコードを出力する gcc を無償で利用することができるのだ。

ただし現状では、自分でビルドしなければならないみたい?

** Windows 上の Vagrant で Ubuntu を立ち上げる [#i2f75e69]

gcc のビルドには Linux 環境が必要なので、お手軽な環境として
Windows10 に VirtualBox を入れ、Vagrant から利用する。

ここではすでに VirtualBox と Vagrant は導入済みとして、
以下の手順で進める。

>ただし、ビルドする開発ツール自体はローカルフォルダ
(./work) にインストールすることになるため、必ずしもこれだけのために
仮想マシンが必要ではない。いくつかのパッケージを入れることをいとわなければ、
既存の 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

** 共有フォルダの設定 [#u0a2c50b]

一旦 exit 出抜けて、
一旦 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

に接続するおまじない。

** 必要なパッケージを追加する [#o29ee0bd]

もう一度、

 LANG:console
 $ vagrant up --provider virtualbox
 $ vagrant ssh

としてシェルを立ち上げ、

 LANG:console
 $ sudo apt-get -y install build-essential m4 g++ texinfo

** 開発用ツールチェインのビルド [#a90659f6]

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.

** パスに追加 [#v74c676c]

 LANG:console
 $ cat >> ~/.bashrc
  PATH=/home/vagrant/work/mb-elf-toolchain-linux/bin/:$PATH
  ^D

** 試しにコンパイルしてみる [#wc9a93b0]

 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

めでたくコンパイルが通る。

** コンパイルオプション [#fa20e3ea]

MicroBlaze を AXI バスと共に使う場合には

https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_1/ug984-vivado-microblaze-ref.pdf#page=11

にあるように Little Endian になるので、コンパイル時にもそうなるようにオプションを付ける必要がある。

 LANG:console
 $ mb-gcc -mlittle-endian -O2 hello.c -nostdlib -o hello.elf

* UART ライブラリを作成 [#lf3f59f8]

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 [#neaedd80]

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");
   }
 }

* コンパイル [#a302f96e]

 LANG:console
 $ mb-gcc -mlittle-endian -O2 hello.c uart.c -nostdlib -o hello.elf

* elf ファイルを bit ファイルへ埋め込む [#p094f6a5]

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 をプログラムする [#p818f079]

あとは以下のようにして 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]

* ツールボタンに割り当てる [#p8d08f3a]

コンパイル → bit 置き換え → FPGAプログラム

を何度も繰り返すのはかなり面倒なので、「bit 置き換え → FPGAプログラム」をワンクリックで可能なツールボタンを作成する。

Vivado の [Tools]-[Custom Commands]-[Customize Commands...] に
上記のスクリプトを ; で区切りながら1行に繋げた手順を入れてボタンにしておく。

コンパイルしたらボタンを押すだけでプログラムができるようになる。

* フラッシュメモリに書き込むには [#wca2c2e3]

普通なら 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"

* スタートアップコードが足りないみたい??? [#o876ab36]

上記の手順で一見動いているように見えたのだけれど、動作が不安定だ。

実は上で不足していると怒られた libxil のソースコードは Xilinx SDK に付いているので、

C:\Xilinx\SDK\2018.2\data\embeddedsw\lib\bsp\standalone_v6_7

不足するヘッダーファイル等を補うことでこれをそのまま上記環境でコンパイルして使えるっぽい。

ただ、そうしたときに完動するコードが、上記のように完全にベアな状態で走らせたときにうまく動かないケースがあった。

ここに付いてきたコードをそのままコンパイルして使っていいものかどうか、ライセンス的にちょっと心配なので、必要そうなコードを吟味して、独自のスタートアップルーチンを作るのがいいのかもしれない???


* 質問・コメント [#x5586ece]

#article_kcaptcha


Counter: 8486 (from 2010/06/03), today: 1, yesterday: 0