ubuntu 16.04LTS 上で試しています。
記事ではFPGAボードとしてMAX10-FBを使っていますが、手持ちの Arty-7 への実装を試みます。
いつも環境構築で挫折する私ですが、一念発起してRISCVの開発環境を作ってみることにします。
リスト1 RISC-V用GNUツールチェーンの準備
まずは riscv-gnu-toolchain を clone します。githubで riscv toolchain で検索した結果ヒットしたリポジトリをクローンして、続いて ./configure を実行したのですが、早速うまく行きません。記事だと ./configure のオプションが ?prefix=/opt/riscv となっているけれど、これは --prefix の間違いかなーと思い、念の為./configure --help で確認したところ、--prpefix で良さそう。改めて./congifure --prefix=/opt/riscv
を実行したら、いくつか今度は no という表示が出ました。どうやらライブラリをインストールし忘れていたようです。
- mpfr
- gmp
- mpc
とか。なので、apt-cache search mpfr
としてみたところ、たくさんのパッケージが見つかりました。ライブラリ単体と開発用 (-dev が後ろにつく) の2種類があるらしいので(そこからかよ)、apt-cache search mpfr.*dev
として改めて検索すると、8個ほどのパッケージが見つかりました。mpfr という文字列が入っていないのも引っかかるのが謎ですが、記事を見直すと、libmpfr-dev が必要らしいので、これが漏れていたのでしょう。sudo apt-get install libmpfr-dev
して、再度 make -j$(nproc)
でもう少し進みました。
次の問題は、
checking size of unsigned long... 8
checking size of unsigned __int128... 16
checking for library containing dlopen... (cached) -ldl
checking whether to use expat... yes
checking for libexpat... no
configure: error: expat is missing or unusable
Makefile:10389: ターゲット 'configure-gdb' のレシピで失敗しました
というログ。libexpat が必要そうなので、探してみる(apt-cache search libexpat
)と、libexpat-dev なるパッケージが見つかったので、 sudo apt-get install libexpat-dev
してから再度 make -j $(nproc)
.
これで無事、ツールチェーンのmake ができました。
リスト2 OpenOCD の準備
つづいて、OpenOCD のビルドに移ります。./configure ...
まで実行すると、以下のようなエラーが。
configure: error: libusb-1.x is required for the MPSSE mode of FTDI based devices
apt-cache search libusb-1\..*-dev
とすると、libusb-1.0-0-dev というのが見つかったので、これをインストールします。
sudo apt-get install libusb-1.0-0-dev
で、もう一回 ./configure --enable-maintainer-mode --disable-werror --enable-ftdi
とすると、今度は最後まで進んで以下のようなログが出ました。
OpenOCD configuration summary
MPSSE mode of FTDI based devices yes
ST-Link JTAG Programmer yes (auto)
TI ICDI JTAG Programmer yes (auto)
Keil ULINK JTAG Programmer yes (auto)
Altera USB-Blaster II Compatible yes (auto)
Versaloon-Link JTAG Programmer yes (auto)
OSBDM (JTAG only) Programmer yes (auto)
eStick/opendous JTAG Programmer yes (auto)
Andes JTAG Programmer yes (auto)
USBProg JTAG Programmer yes (auto)
Raisonance RLink JTAG Programmer yes (auto)
Olimex ARM-JTAG-EW Programmer yes (auto)
CMSIS-DAP Compliant Debugger no
Cypress KitProg Programmer no
Altera USB-Blaster Compatible no
ASIX Presto Adapter no
OpenJTAG Adapter no
SEGGER J-Link Programmer yes (auto)
よさげなので、つづきの作業へ。
リスト3 Eclipseの準備
java --version
を実行したところ、以下のようなメッセージが出ました。
プログラム 'java' は以下のパッケージで見つかりました:
- default-jre
- gcj-4.9-jre-headless
- gcj-5-jre-headless
- openjdk-8-jre-headless
- gcj-4.8-jre-headless
- openjdk-9-jre-headless
次の操作を試してください: sudo apt install <選択したパッケージ>
headless というのは GUIのためのコンポーネントを含んでいないという意味らしいのですが、Eclipse を使うのにGUIはないと困るので、default-jre を選択することにします。
つづいて Eclipse 本体をダウンロードして、記事の手順どおりインストールしたのですが、図3の作業をしようとすると、RISC-Vが見つかりません。どうやらGNU MCU Eclipse Plug-in をインストールしなければいけないようです。Market Place から検索してインストールを試みましたが、失敗しました。手動でURLを指定するとうまくいくらしいので、Install New Software の work with に https://gnu-mcu-eclipse.github.io/plugins/install/ をいれてみたところ、成功しました。Eclipseを再起動して、無事完了。
リスト4 SCR1コア本体とMAX10-FB基板用のリソースを用意する
ここは特に問題なく進みました。
リスト5 インテルHEXフォーマットのバイナリ・ファイルを Verilog HDL の $readmemh で読み込める形式に変換
ここも特に問題なし。
リスト6 回路シミュレータModelSim の起動と論理シミュレーションの実行
Xilinx の FPGAを使おうとしているので、このあたりから怪しくなって来ます。SCR1_AMX10FB/ の下に
mkdir xilinx
としてディレクトリを作り、その中で作業をすることにします。
PLLだけは決定的に違うので、新たに作らないといけないのですが、よくわかからないので中身のないセルでごまかします。入出力のピン名だけは、元のpll.vに合わせました。
module pll(
input wire inclk0
, output wire c0
);
assign c0 = inclk0;
endmodule
あとは、tb_top.v を実行するために必要な RTLを順に追加していくと、走るようになります。
ただし、PROJ_MAX10FB.memh の読み込みパスだけは相対パスでうまく指定ができず、フルパスで指定しています。
読み込めているかどうかは、シミュレーションを走らせて hex という reg が X でないことで確認しました。読み込みの段階でエラーを出してくれれば良いのですが、やり方がわかりません。
program counter を見れば良いのではないかとかんがえて、pipe と付いているRTL を眺めていくと、curr_pc という信号がそれらしい。シミュレーションでモニタしてみると、ずっと0のままになっているようです。readmemh は読み込めているようで、TB の hex には入っています。ただし、0でない値が入っているのは8143 番目以降で、0番めから順に読み込んでいては0が帰り続けるのは当然です。
scr1_arch_description.svh で、parameter bit [
SCR1_XLEN-1:0] SCR1_ARCH_RST_VECTOR = SCR1_XLEN'h7F40;
とすると(もともとは h200)一応 imem で値が読み込まれているようです。でもGPIOはZのままなので、なにかおかしい・・・・・・
elf を逆アセンブルして見てみる
大熱血!アセンブラ入門(以下、大熱血)を思い出し、実行ファイルを逆アセンブルしてみようと考えました。逆アセンブラの名前がわからないので、gnu 逆アセンブラ で google先生に聞いてみると、 objdump というのがそれらしい。 /opt/riscv/bin/ を覗くと、riscv64-unknown-objdump というのがあったので、多分これだろうとおもって実行してみました。
riscv64-unknown-objdump -d PROJ_MAX10FB.efl > PROJ_MAX10FB.d
できたファイルを見てみと、main は ffffd0f6 にありました。というわけで、
// CSR parameters:
parameter bit [`SCR1_XLEN-1:0] SCR1_ARCH_RST_VECTOR = `SCR1_XLEN'hffffd0f6;
としてRTLシミュレーションを実行すると、機械語を順に読み込んでくれました。ただし、gpio_init ではGPIOの下位3bitを出力に設定するだけで出力値の設定はないので、シミュレーション上は0xXX -> 0xXX というあまりおもしろくない結果です。
念の為、Cコードのソースに gpio_wr(0x0007);
を追加してみたのですが、これをやるとmainのアドレスがずれるので、 scr1_arch_description.svh を書きなおさなければならなくなりました。そんな面倒なわけはないので、main は必ずきまったアドレスに来るようにするとか、なにか方法があるはずなのですが、力尽きたのでとりあえずここまで。
(追記 2018/02/08)
気をとりなおして続き。
リンカスクリプトを追ってみる。
KEEPは参照されていなくてもシンボルを残す指定らしいです。volatile みたいなもんか。
で、link.ld を読んでいくと ENTRY(_start) という記述があるのですが、C言語のソースにはこのような名前の関数はありません。
riscv-unknown-objdump -d PROJ_MAX10FB.elf
として逆アセンブルしてみても見つかりませんでした。でも
riscv-unknown-nm PROJ_MAX10FB.elf
とやると、0xffffff00 に _start が存在することになっています。これがどこから来たのか悩んだ挙句、苦し紛れにリンカスクリプトを一部コメントアウトしてビルドしてみたところ、eclipse のコンソールのエラーメッセージから startup.S に飛べました。この中に、_start:
というラベルがあったので、どうやらここのようです。
(余談ですが、startup.S ではラベルとして 1:
や 3:
などが使われていて数値と見分けづらいのですが、アセンブラはこういうものなのでしょうか?)
_start のアドレスを特定する
main.c を一部コメントアウトしたりして、.text セクションのサイズを変えて riscv-unknown-nm を繰り返してみましたが、_start は常に 0xffffff00 にいるようです。というわけで、scr1_arch_description.svh の SCR1_ARCH_RST_VECTOR のところは、デフォルトでは 0x200 となっているので、なぜこんなに違うのかはわかりませんでいたが、とりあえず
parameter bit [
SCR1_XLEN-1:0] SCR1_ARCH_RST_VECTOR =
SCR1_XLEN'hffffff00;
と書きなおしてRTLシミュレーションをかけ直すと、一応読み込んでくれました。
(続く)