Kintex-7にMicroblazeを載せる のバックアップソース(No.5)

更新

[[公開メモ]]

* 概要 [#kb3d846d]

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

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

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

#contents

* 設計方針 [#h7b56e86]

FPGA ボードは以下を装備しているものとする。

- Kintex-7 FPGA
- LED が2つ
- スイッチが2つ
- USB-Serial 変換モジュール

MicroBlaze のプログラムから
自由に LED を光らせたり、スイッチの状態を読んだり、
USB 経由でホスト PC と通信をしたり、
を可能にしたい。

OS を走らせるようなたいそうなことを考えているわけではなく、
あくまでベアメタルのマイクロコントローラ的な使い方を想定している。

以下の方法でOSを走らせるのはたぶんかなり大変だと思われる。

* Vivado での作業 [#cdb702d3]

** プロジェクトの作成 [#r75c892d]

&ref(new-project.png,,50%); [File]-[Project]-[New]

&ref(project-name.png,,50%); 適当に名前を付ける

&ref(project-type.png,,50%); RTL プロジェクトを選ぶ

&ref(project-add-source.png,,50%); Block Design をトップにするのでここでは何もしない

&ref(project-add-xdc.png,,50%); 制約ファイルを1つ作っておく

&ref(project-select-kintex.png,,50%); 対象とする FPGA を選択する

&ref(project-finish.png,,50%); 以上でプロジェクト作成できた

** ブロック図の作成 [#gde59039]

こちらが完成形:

&ref(block-diagram-final.png,,33%); 

- 入力端子として
-- クロック(差動入力)
-- リセット(負論理)
-- スイッチ2つ
-- UART-RX (USB-Serial 変換器の TX 端子と繋ぐ)
- 出力端子として
-- LED 2つ
-- UART-TX (USB-Serial 変換器の RX 端子と繋ぐ)

を持っており、それらが適切に IP に繋がっている。

以下が手順:

&ref(create-block-design.png,,50%); Block Design を新規作成

&ref(block-design-name.png,,50%); プロジェクトに1つしか作らないなら名前は適当でも

&ref(add-microblaze.png,,50%); Add IP ボタンから MicroBlaze を追加する

&ref(add-uart.png,,50%); 同様にシリアル通信用の UART モジュール を追加

&ref(add-gpio.png,,50%); 同様に LED やスイッチの制御のための GPIO モジュール を追加

&ref(regenerate-layout.png,,50%); Regenerate Layout ボタンで配置を調整

上部の Run Block Automation を押し、MicroBlaze についていろいろ設定する

&ref(block-automation-microblaze.png,,50%);

OK すると以下が自動的に追加される。

- クロックジェネレータ (Clocking Wizard)
- リセットコントローラ (Processor System Reset)
- ローカルメモリ (local memory)
- AXI ハブ (AXI Interconnect)
- 割り込みコントローラ (AXI Interrupt Controller)

&ref(block-automation-result.png,,50%);

Clocking Wizard をダブルクリックして

&ref(adjust-clocking-wizard.png,,50%); 入力周波数を 200MHz に変更

&ref(adjust-clocking-wizard2.png,,50%); リセットを反転入力とする

GPIO をダブルクリックして

&ref(adjust-gpio.png,,50%); 下位2ピンを出力にし、割り込みを有効にする

&ref(add-slice.png,,50%); Slice を追加しダブルクリック

&ref(adjust-slice.png,,50%); From を 1 にする

&ref(add-concat.png,,50%); Concat を追加しダブルクリック

&ref(adjust-concat.png,,50%); 28bit, 2bit, 2bit のポートを用意する

&ref(add-const.png,,50%); Const を追加しダブルクリック

&ref(adjust-const.png,,50%); 幅を 28bit, 値をゼロに設定

GPIO モジュールの GPIO バスをクリックして開き、slice, concat, const と結ぶ。~
また、Slice の出力、Concat の2本の入力について、右クリックから Make External して外部端子を出す。

&ref(gpio-connection.png,,50%);

&ref(interrupt-connection.png,,50%); UART, GPIO からの割り込み線を INTC へ繋がる Concat へ接続

&ref(uart-make-external.png,,50%); UART の UART 端子を右クリックして Make External

&ref(clock-make-external.png,,50%); Clocking Wizard の入力端子を Make External

左上の Run Connection Automation をクリック

&ref(connection-automation.png,,50%);

これで UART と GPIO が AXI ハブに繋がる。

Regenerate Layout して完成。

&ref(block-diagram-final.png,,33%); 

*** 一部訂正 [#i12624d8]

- reset_rtl_0 と resetn_0 は被ってしまうので、reset_rtl_0 を削除して resetn_0 から ext_reset_in へ線を引いた。
- gpio の入力に繋がっている concat を 28bit, 2bit, 2bit として、外部からの入力線を1つにまとめた


** アドレスを確認 [#ee3ee978]

Diagram のとなりのタブに Address Editor がある。

ここで、それぞれの IP のベースアドレスを確認できる。~
必要に応じて変更することもできる。

&ref(address-editor.png,,50%);


** ブロック図から HDL を生成する [#c14fab92]

上図の左下で Generate Block Design を押すと以下のダイアログが現れる。

&ref(generate-block-design.png,,50%); Global を選ぶ

design_1 上で右クリックして Create HDL Wrapper を選択。

&ref(create-hdl-wrapper.png,,50%);

扱いは Vivado に任せる。

&ref(create-hdl-wrapper2.png,,50%);

design_1_wrapper.v が自動生成され、以降、必要に応じて自動更新される。

&ref(generated-wrapper.png,,50%);

** Implement してみる [#oca1cdc7]

まだ制約をまったく与えていないものの、この時点で Implement が可能になっている。

&ref(synthesis-success.png,,50%); &ref(post-synthesis-utilization.png,,50%);

Clocking Wizard に

&ref(post-synthesis-report-timing-summary.png,,50%);

** 制約を与える [#w8e9114b]

module design_1_wrapper のインタフェースを見ると、

 module design_1_wrapper(
   input wire CLK_IN1_D_0_clk_n,
   input wire CLK_IN1_D_0_clk_p,
   output wire [1:0] Dout_0,
   input wire [0:0] In1_0,
   input wire [0:0] In2_0,
   input wire UART_0_rxd,
   output wire UART_0_txd,
   input wire resetn_0
 );

となっている。


** Bit Stream を生成する [#m9a1dc63]
(未稿)
** 制約が満たされていることを確認 [#h1ad174d]
(未稿)
** ハードウェアを SDK にエクスポート [#ua700724]

以下に見るように、MicroBlaze 用のソフトウェアは WebPack 版の Xilinx SDK では開発できないので、
実はこの作業は無意味であった。

EDK を持っていて、SDK でソフト開発ができる場合にはこの手順で正しいはず。

(以下未稿)

* SDK でエラーが出る [#cfd860ef]

SDK でプロジェクトを作成しようとすると、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 で抜けて、

 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: 17467 (from 2010/06/03), today: 2, yesterday: 3