Kintex-7にMicroblazeを載せる の変更点

更新


[[公開メモ]]

* 概要 [#kb3d846d]

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

最近は無償の WebPack 版でも MicroBlaze IP を使えるようになっているのだけれど、
実はソフト開発に使う SDK は WebPack 版では MicroBlaze をサポートしないことを後から知った。~
→ いやいやそんなことはない、とコメント欄で指摘を受けた。たまたま私の環境で動かなかっただけなのかもしれない???! orz

以下の手順で自ら 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 を選択する

あ、本当は xc7k160tfbg484-1 を選んだのにキャプチャした図は違うのを選んでるときのものだった。。。

&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

ext_reset_in にも繋ぐ

左上の Run Connection Automation をクリック

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

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

Regenerate Layout して完成。

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



** アドレスを確認 [#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(implementation-success.png,,50%);

まだ xdc ファイルには何の制約も追加していないのに「タイミング制約が満たされている」というような表示があるのは、Clocking Wizard に設定した入力周波数 200MHz で全体に対してクロック周波数制約がかかっているため。

[Open Implemented Design]-[Report Timing Summary] すると、

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

&ref(implementation-timing-result.png,,50%);

Setup, Hold, Pulse Width とも Slack (余裕) の値は正になっていて、タイミング制約を満たしていることを確認できる。そして Clock Summary で各クロックに正しくタイミング制約がかかっていることを確認できる。

&ref(clock-summary.png,,50%);

注意が必要なのは、[Open Synthesized Design] から [Report Timing Summary] を見ると、

&ref(synthesis-timing-result.png,,50%);

のように、Hold の Slack が負になっていて、タイミング制約を満たしていないように見えることだ。

Hold の Slack が負というのはあるゲートから次のゲートへ信号が「早く到達しすぎている」((厳密には後段のゲートへクロックよりも次のデータが早く到達してしまっている))ということで、よほどクロックラインのスキューが大きいような場合でない限り起きえない。Implement 前の情報ではゲート間の遅延を正しく見積もれず、そのために負になっているということ、なのかな?

ここで見た通り Synthesis で Hold に Negative Slack が出ても、
同じ回路で Implementation では問題がなくなる場合があるようなので、
Synthesis 時点の少々の Negative Slack で悩まず Implementation を試すべきだ。


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

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

 LANG:verilog
   input CLK_IN1_D_0_clk_n;  // オンボードクロックからの差動負入力
   input CLK_IN1_D_0_clk_p;  // オンボードクロックからの差動正入力
   output [1:0]Dout_0;       // LED0, LED1 への出力
   input [1:0]In1_0;         // SW0, SW1 からの入力
   input UART_0_rxd;         // USB-Serial 変換器の TX からの入力
   output UART_0_txd;        // USB-Serial 変換器の RX への出力
   input resetn_0;           // オンボードのリセットスイッチからの負論理入力

となっている。

そこで、以下のように制約ファイルを与えた。

 # 入出力ピン
 set_property PACKAGE_PIN V4 [get_ports CLK_IN1_D_0_clk_p]
 set_property IOSTANDARD LVDS [get_ports CLK_IN1_D_0_clk_p]
 set_property PACKAGE_PIN W4 [get_ports CLK_IN1_D_0_clk_n]
 set_property IOSTANDARD LVDS [get_ports CLK_IN1_D_0_clk_n]
 
 set_property PACKAGE_PIN R19 [get_ports Dout_0[0]]
 set_property IOSTANDARD LVCMOS33 [get_ports Dout_0[0]]
 
 set_property PACKAGE_PIN T19 [get_ports Dout_0[1]]
 set_property IOSTANDARD LVCMOS33 [get_ports Dout_0[1]]
 
 set_property PACKAGE_PIN T20 [get_ports In_1[0]]
 set_property IOSTANDARD LVCMOS33 [get_ports In_1[0]]
 
 set_property PACKAGE_PIN A21 [get_ports In_1[1]]
 set_property IOSTANDARD LVCMOS33 [get_ports In_1[1]]
 
 set_property PACKAGE_PIN K17 [get_ports resetn_0]
 set_property IOSTANDARD LVCMOS33 [get_ports resetn_0]
 
 set_property PACKAGE_PIN AA19 [get_ports UART_0_txd]
 set_property IOSTANDARD LVCMOS33 [get_ports UART_0_txd]
 set_property SLEW SLOW [get_ports UART_0_txd]
 
 set_property PACKAGE_PIN W22 [get_ports UART_0_rxd]
 set_property IOSTANDARD LVCMOS33 [get_ports UART_0_rxd]
 set_property SLEW SLOW [get_ports UART_0_rxd]
 
 # 電源に固定されているため使ってはいけないピン
 set_property PROHIBIT true [get_sites Y11]
 set_property PROHIBIT true [get_sites Y12]
 
 # コンフィギュレーションブロックの設定
 set_property CONFIG_VOLTAGE 3.3 [current_design]
 set_property CFGBVS VCCO [current_design]
 
 ## 以下は FPGA のプログラムを短時間で行えるようにするおまじない
 
 # 周波数を 50MHz に設定
 set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
 # 圧縮を有効化
 set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]

このうち「FPGA のプログラムを短時間で行えるようにするおまじない」はかなり重要で、
これを書いておくのとおかないのとでプログラム時間がもの凄く違う。


** Bit Stream を生成する [#m9a1dc63]

 launch_runs impl_1 -to_step write_bitstream -jobs 2
 [Sat Sep  1 20:36:10 2018] Launched synth_1...
 Run output will be captured here: C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/synth_1/runme.log
 [Sat Sep  1 20:36:10 2018] Launched impl_1...
 Run output will be captured here: C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/runme.log
 close_design; open_run impl_1
 INFO: [Netlist 29-17] Analyzing 305 Unisim elements for replacement
 INFO: [Netlist 29-28] Unisim Transformation completed in 0 CPU seconds
 INFO: [Project 1-479] Netlist was created with Vivado 2018.2
 INFO: [Device 21-403] Loading part xc7k160tfbg484-1
 INFO: [Project 1-570] Preparing netlist for logic optimization
 INFO: [Timing 38-478] Restoring timing data from binary archive.
 INFO: [Timing 38-479] Binary timing data restore complete.
 INFO: [Project 1-856] Restoring constraints from binary archive.
 INFO: [Project 1-853] Binary constraint restore complete.
 Reading XDEF placement.
 Reading placer database...
 Reading XDEF routing.
 Read XDEF File: Time (s): cpu = 00:00:00 ; elapsed = 00:00:00.417 . Memory (MB): peak = 2962.469 ; gain = 0.000
 Restored from archive | CPU: 0.000000 secs | Memory: 0.000000 MB |
 Finished XDEF File Restore: Time (s): cpu = 00:00:00 ; elapsed = 00:00:00.418 . Memory (MB): peak = 2962.469 ; gain = 0.000
 Generating merged BMM file for the design top 'design_1_wrapper'...
 INFO: [Memdata 28-144] Successfully populated the BRAM INIT strings from the following elf files: c:/Users/osamu/MicroBlazeHello/MicroBlazeHello.srcs/sources_1/bd/design_1/ip/design_1_microblaze_0_1/data/mb_bootloop_le.elf 
 INFO: [Project 1-111] Unisim Transformation Summary:
   A total of 223 instances were transformed.
   LUT6_2 => LUT6_2 (LUT5, LUT6): 127 instances
   RAM16X1D => RAM32X1D (RAMD32, RAMD32): 32 instances
   RAM32X1D => RAM32X1D (RAMD32, RAMD32): 64 instances
 
 open_run: Time (s): cpu = 00:00:29 ; elapsed = 00:00:28 . Memory (MB): peak = 3021.410 ; gain = 673.984
 open_hw
 reset_run impl_1 -prev_step 
 launch_runs impl_1 -to_step write_bitstream -jobs 2
 [Sat Sep  1 21:02:00 2018] Launched impl_1...
 Run output will be captured here: C:/Users/osamu/MicroBlazeHello/MicroBlazeHello.runs/impl_1/runme.log

となってうまく行った。

ここまでで回路は完成だ。

 INFO: [Memdata 28-144] Successfully populated the BRAM INIT strings from the following elf files: c:/Users/osamu/MicroBlazeHello/MicroBlazeHello.srcs/sources_1/bd/design_1/ip/design_1_microblaze_0_1/data/mb_bootloop_le.elf 

の行から分かるとおり、microblaze_0_1 に結びつけられたローカルメモリには bootloop という「何もしないプログラム」が書き込まれている。

以下はここに入れるべきソフトウェアプログラムを開発する話になる。


** ハードウェアを SDK にエクスポート [#ua700724]

Xilinx SDK を使ってソフトを開発する場合、ハードウェアの情報を SDK に伝えるために、
[File]-[Export]-[Export Hardware] を使って情報をエクスポートする。

&ref(export-hardware.png,,50%);

&ref(include-bitstream.png,,50%); include bitstream にチェックを入れる

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

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

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

vivado の [File]-[Launch SDK] から SDK を立ち上げると、

&ref(SDK-overview.png,,50%);

のように、利用した IP の情報が正しく SDK にエクスポートされていることを確認できる。

そこで SDK の [File]-[New]-[Application Project] からソフトウェア開発用の
プロジェクトを作成しようとするとエラーが出てしまう。

&ref(sdk-new-project.png,,50%);

&ref(sdk-new-project-dialog.png,,50%);

&ref(sdk-new-project-error.png,,50%);

log を見ると、bsp 生成部分で "Couldn't figure out compiler's library directory" というエラーが出ているようだ。

&ref(sdk-new-project-error-log.png,,50%);

 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

>"Couldn't figure out compiler's library directory" というエラーメッセージからだとエラーの理由が分からず途方に暮れることになるのだけれど、実は SDK を使って MicroBlaze 開発をするには EDK のライセンスが必要であるため、意図してエラーを起こしている、ということらしい。
>
>「ライセンスが足りない」ということがすぐに分かるエラーメッセージにしてくれることを強く望むところだ。

上記の部分について下にコメントをいただきました。これ、私のところでだけ起きている問題なのかもしれません。そうだとすると、ここから先の内容にはほぼ意味が無いことになります orz

余力があれば後ほど検証してみようと思います。
* クロスコンパイラの準備 [#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

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

ただ、そうしたときに完動するコードが、上記のように完全にベアな状態で走らせたときにうまく動かないケースがあった。
そのようにしてでっち上げた libxil を使うと完動するコードが、上記のように完全にベアな状態で走らせたときにうまく動かないケースがあったので、完全にベアな状況では何らかのスタートアップコードが不足しているのではないかと思う。

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


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

#article_kcaptcha
**Ubuntuだと、SDKは使えましたよ [#kb386ceb]
>[[通りすがり]] (&timetag(2018-09-04T10:23:11+09:00, 2018-09-04 (火) 19:23:11);)~
~
Ubuntu 18.04で同じようにやってみましたが、SDKでApplication Projectの作成はできましたよ。Vivadoは2018.2です。~
~
「FPGAプログラミング大全 Xilinx編」の本でも、WebPack版でもできるとありますし、当方も、Nexys4 DDRのボードで問題なく動いています。~
~
SDKのインストール関係で何かミスをしたのではないでしょうか?~
~
あと、~
~
set_property PACKAGE_PIN R19 [get_ports Dout_0[0]]~
set_property IOSTANDARD LVCMOS33 [get_ports Dout_0[0]]~
~
set_property PACKAGE_PIN R19 [get_ports Dout_0[1]]~
set_property IOSTANDARD LVCMOS33 [get_ports Dout_0[1]]~
~
と、同じR19に2つのポートを割り当てていますが、本当に大丈夫だったんでしょうか?~

//
- 情報ありがとうございます。WebPack でも MicroBlaze の Application Project を作成できるのだとすると、むだな遠回りをしてしまったことになりますね orz 後ほど Vivado の再インストールを試してみようと思います。R19 が2つあったところは Dout_0[1] の方が T19 だったのを間違えていました。本文を直しました。-- [[武内(管理人)]] 
&new{2018-09-04 (火) 19:38:02};

#comment_kcaptcha

Counter: 3980 (from 2010/06/03), today: 7, yesterday: 0