FPGAマガジンNo.18の「俺々RISC-V CPU」をシミュレータで動かす手順

プログラミングor技術
特集「俺々RISC-V CPU」のサンプルコードをどうにか動かし、シミュレーションまで行う手順をなるべく細かく記す。 経緯はこう…。
コロナで自粛中なので時間ができ、FPGA上にRISC-V CPUを作成する特集「俺々RISC-V CPU」を目的に、FPGAマガジンNo.18を買った。
この本の説明は、分かっている人向けみたいだったので、初心者でも特集のサンプルコードを動かせるように、具体的に手順を書き記す。
サンプルコードは、開いたらそのまま動かせるわけじゃないみたいだったので、直し方も書いた。 (FPGA初心者なので間違ったこと書いてるかも。その時はゴメンナサイ。) マガジンの特集「俺々RISC-V CPU」はMAX-10とArtix-7の2つのFPGAをターゲットとしている。使うツールやファイルが違うので、それぞれについてやり方を書いていく。

MAX-10版とArtix-7版に共通の手順

実行環境

バージョンや使用したコミット番号を列挙する。FPGAマガジンNo.18に合わせて、Ubuntu16.04LTS日本語版を使用した。
#OSはUbuntu16.04日本語版
$ uname -a
Linux ubuntu 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

#aquaxis/FPGAMAG18。サンプルコード 
(https://github.com/aquaxis/FPGAMAG18.git)
aced689409b028ce4f2b9da7e6de685e2af29fb2 "update fmrv32im_axis_uart"

#riscv/riscv-tests。riscvのテストコード(コンパイル済みのがサンプルコードの中にあるみたいだから本当はいらなそう)
$ cd riscv-tests && git log --oneline | head -n 1
3d3f29e The HTIF device must live in its own page since it is (generally) a bus/hardware device (#274)

#riscv/riscv-gnu-toolchain。riscvのクロスコンパイル環境つくるためにつかったツールチェイン
$ cd riscv-gnu-toolchain/ && git log --oneline | head -n 1
e277764 Merge pull request #625 from gattaca-lab/riscv_tbi

#riscv/riscv-tools。riscvのクロスコンパイル環境つくるためにつかったツールチェイン(使おうとしたけどやめた)
$ cd riscv-tools/ && git log --oneline | head -n 1
2619062 Merge pull request #279 from riscv/toolchain

#クロスコンパイル用のgcc
$ riscv32-unknown-elf-gcc -v
Using built-in specs.
COLLECT_GCC=riscv32-unknown-elf-gcc
COLLECT_LTO_WRAPPER=/opt/riscv32im/libexec/gcc/riscv32-unknown-elf/9.2.0/lto-wrapper
Target: riscv32-unknown-elf
Configured with: /home/user1/riscv-gnu-toolchain/riscv-gcc/configure --target=riscv32-unknown-elf --prefix=/opt/riscv32im --disable-shared --disable-threads --enable-languages=c,c++ --with-system-zlib --enable-tls --with-newlib --with-sysroot=/opt/riscv32im/riscv32-unknown-elf --with-native-system-header-dir=/include --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libgomp --disable-nls --disable-tm-clone-registry --src=.././riscv-gcc --disable-multilib --with-abi=ilp32 --with-arch=rv32im --with-tune=rocket 'CFLAGS_FOR_TARGET=-Os   -mcmodel=medlow' 'CXXFLAGS_FOR_TARGET=-Os   -mcmodel=medlow'
Thread model: single
gcc version 9.2.0 (GCC) 


#ubuntuの標準のgcc
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 

RISC-Vのクロスコンパイル環境を整える

FPGAマガジンNo.18 P61 Appendix3に従い、ビルド環境を整える。 本によれば、サポートページにビルド済みのRV32IMツールチェーンを用意してくれてるらしいが、それらしきものは見当たらない。
FPGAマガジンのサポートページ
特集の著者のサポートwiki
自分でビルドするしかない! 本誌P62の図2、図3のあたりでは、riscv/riscv-toolsから得たものをビルドすることで、クロスコンパイラをビルドしようとしている。 しかしRISC-Vのtoolchainの32bit版のビルド&インストールによれば、riscv/riscv-toolsはもうgccとかを含まないらしい。以下引用。
「※ 2019/5/5時点で、gnu-toolchainはriscv-toolsのsubmoduleから外されたため、本記事は古い状態となります。最新版に対応するには、以下の記事をご参照下さい。」 by RISC-Vのtoolchainの32bit版のビルド&インストール

とのこと。
なので本誌P62の図2、図3のあたりではなく、
FPGAマガジンNo.18本誌のP63に従う。(基本、本誌に従えばよい)
#パッケージのインストール
$ sudo apt-get install -y autoconf automake \
 autotools-dev curl libmpc-dev libmpfr-dev \
 libgmp-dev gawk build-essential bison flex \
 texinfo gperf libtool patchutils bc zlib1g-dev

#クローン
$ sudo apt install -y git
$ git clone git://github.com/riscv/riscv-gnu-toolchain
$ cd riscv-gnu-toolchain
#サブモジュール(gitの入れ子)の中身をとってくる (8GBくらい必要だった気がする)
$ git submodule update --init --recursive

#インストール先ディレクトリを作成
$ mkdir /opt/riscv32im
$ export RISCV32IM=/opt/riscv32im

#コンフィグしてmake(本誌に書いてあるように--with-abiオプションで浮動小数点のオプションを追加できるそう)
$ ./configure --prefix=$RISCV32IM \
--disable-linux --with-arch=rv32im
$ make newlib

#パスの設定
$ echo "export PATH=${PATH}:/opt/riscv32im/bin/" >> ~/.bashrc
$ source ~/.bashrc

サポートページで公開されてるサンプルコードを取得する

特集の著者のサポートwikiに書いてあるようにcloneする。
$ git clone git://github.com/aquaxis/FPGAMAG18
中身を説明するとこんな感じ。
./FPGAMAG18-master
├── LICENSE
├── README.md                    # <- (もうちょっと説明増やしてくれないと、初心者にはなにがなにやら...)
├── fmrv32im-artya7.madd33       # <- ArtyボードのArtix-7がターゲット、拡張命令入れた場合。
│   ├── fmrv32im-artya7.srcs
│   └── fmrv32im-artya7.xpr      # <- Vivadoプロジェクトファイル
├── fmrv32im-artya7.nonos        # <- ArtyボードのArtix-7がターゲット、普通版。
│   ├── fmrv32im-artya7.srcs
│   ├── fmrv32im-artya7.xpr      # <- Vivadoプロジェクトファイル
│   └── vivado_pid20791.str
├── fmrv32im-max10               # <- MAX-10FBのMAX-10がターゲット
│   ├── fmrv32im_max10.qsys
│   ├── fmrv32im_max10.sopcinfo
│   ├── max10.qpf                # <- Quartus Primeのプロジェクトファイル(標準設定のままでは動かなかった、後述)
│   └── max10.qsf
├── modules                      # <- IPたち。MAX-10とArtix-7で共通..のはず(そのままではQuartusでは読み込んでくれなかったので、本手順の後の方でコピー&リネームする)
│   ├── axilm_v1
│   ├── axim_v1
│   ├── busdefine
│   ├── cache_v1
│   ├── dbussel_v1
│   ├── fmrv32im_v1
│   ├── gpio_v1
│   ├── plic_v1
│   ├── timer_v1
│   └── uart_v1
├── sample                       # <- このRISC-V CPU上で動かすプログラム
│   ├── bin2coe.c                # <- binをcoe(ROMの初期値とか書くファイル)にする。FPGAマガジンNo.18本誌ではでてこないが、途中で使う必要があった)
│   ├── bin2mif.c                # <- binをmifにして、Quartus Primeが読めるようにするもの。RISC-VのテストコードはSEGVになったのでなんでも変換できるわけではないみたい
│   ├── custom_madd              # <- カスタム命令つかうやつ
│   ├── hello
│   ├── hello2
│   ├── led                      # <- 標準で使うことが想定されてる、Lチカプログラム
│   ├── lui
│   ├── madd33
│   └── madd33_c
└── src                          # <- シミュレータを使う時用のコード
    ├── ArtyA7.v
    ├── ArtyA7_madd33.v
    ├── Arty_Master.xdc
    ├── MAX10.v
    ├── fmrv32im_artya7.v
    ├── fmrv32im_max10.v
    ├── tb_ArtyA7.v
    ├── tb_MAX10.v
    ├── tb_axi_slave_model.v
    ├── tb_axil_slave_model.v
    ├── tb_fmrv32im_artya7.v
    ├── tb_fmrv32im_core.v
    ├── tb_fmrv32im_max10.v
    └── testpt                   # <- githubのriscv/riscv-testsのbinたちみたい

26 directories, 24 files

記述ミス?を直す

FPGAMAG18-master/modules/uart_v1/src/fmrv32im_axis_uart.vで、コンパイルエラーがでる。 以下のuartcon_clk の後ろに#がいらないみたい。 ここを消す。 (Verilog HDLよくわかってないため本当にいらないか不明。でもVivadoもQuartus Primeもここでエラーを出す)
 485    uartcon_clk #
 486      #(
 487        .RESET_COUNT(RESET_COUNT)
 488      )
 489     u_uartcon_clk
 490      (
 491       .rst_n      ( rst_n     ),
 492       .clk        ( refclk    ),
 493       .out_clk    ( uart_clk  )
 494       );

Lチカプログラムをmakeする

マガジン本誌のP33に書いてあるように、「俺々RISC-V CPU」で実行するプログラムは、ハードウェアのコンパイル時に埋め込まれる。 そのため、プログラムがないとコンパイル時に文句を言われる。サンプルコードには含まれていないため、この手順は必須。
以下の画像の、 Can't find Memory Initialization File or Hexadecimal ... ってエラーがそれ。
(自分は先の工程も済ましたからこの画面が出てるが、この手順に従ってる場合、まだこの段階ではコンパイルできない)
以下の手順により、FPGAMAG18-master/romdata/の中にArtix-7用にartya7.hexmainram.coe、MAX-10用にmax-10.mifを作成する。これらが、ハードウェアのコンパイル時にメモリに仕込むプログラムとなる。
(mainram.coeはFPGAマガジンには記載がないみたいだが、無いとエラーが出てコンパイル等ができない。下のやり方で作成できるが、正しいかは不明。でも動く)
$ cd FPGAMAG18-master/samples/

#まずbinをmifに変換ツールをコンパイルしておく
$ gcc bin2mif.c -o bin2mif
$ gcc bin2coe.c -o bin2coe

$ cd led
#作成した実行ファイルを、開発ツールが読み込める形式にしたものをおくディレクトリを作成
$ mkdir ../../romdata

$ make

#coeファイルを作成
$ ../bin2coe -4 led
$ mv led.coe ../../romdata/mainram.coe

(おまけ: シミュレーションが短くて済むようにLチカの間隔を短くする)

標準ではLEDの点滅間隔が約1秒となっている。数秒間ぶんもシミュレーションを実行して、LEDが点滅してることを確認するには荷が重い。(特にVivadoを使う場合。) FPGAMAG18-master/samples/led/led.cは、25*10^6回ほどループするごとにLEDのON/OFFを切り替えることで、約1秒ごとの点滅を実現している。
例えば25回のループごとにON/OFF切り替えるようにしちゃえば、もっと短い周期で点滅してくれる。 数秒分もシミュレーションするのは多分ムリなので、短くしておくことをおすすめする。
(やり方が悪いだけなのかな?シミュレータの方でsimulation resolutionみたいなのを荒くすればよかったのかな…?)

おまけ: risv-testsをビルドする

RISC-Vが正しく実装されてるかを確かめられるテストコードを動かすため、ソースからコンパイルする。
今回の手順のなかでは、Lチカのプログラムを使い、RISC-Vのテストコードは使ってないため、本当はやる必要はないが、紹介だけする。

サンプルコード中にビルド済みのものがあるみたい

ちなみに、実はサンプルコード、FPGAMAG18-master/src/testpt/の中にコンパイル & hexファイルやdumpファイルに変換済みのものが入ってるみたい。動かしてないからわからないけど、ファイル名見るとおなじものみたい。
#2つのディレクトリに入ってるものを並べて個数をカウントする。片方にしかなければ1、両方にあれば2になる。
$ cat <(ls -1 riscv-tests/isa/ | grep .dump ) <(ls -1 FPGAMAG18-master/src/testpt/ | grep .dump) | sort| uniq -c
      2 rv32ui-p-add.dump
      2 rv32ui-p-addi.dump
      2 rv32ui-p-and.dump
      2 rv32ui-p-andi.dump
      2 rv32ui-p-auipc.dump
      2 rv32ui-p-beq.dump
      2 rv32ui-p-bge.dump
      ... (という風に、同じものが入ってることが確認できる)

自前でビルドする方法

以下、ビルド方法。FPGAマガジンNo.18のP46、Appendix2に書いてある通りに進めればよい。(がちょっとだけ違うところがあるので注意) リポジトリのクローン
$ git clone git://github.com/riscv/riscv- tests
$ cd riscv-tests
$ git submodule update --init --recursive
前準備
$ ./configure
32bitになるように、riscv-tests/Makefileを変更。
以下のように、XLENの値を64から32に書き換える。
XLEN            := 32
リンカファイル(riscv-tests/env/p/link.ld)も書き換える。diffを取ると以下のようになる。
$ diff -u  link.ld.old env/p/link.ld 

--- link.ld.old    2020-04-29 20:09:35.655215484 +0900
+++ link.ld        2020-04-28 19:10:22.161697259 +0900
@@ -5,11 +5,11 @@
 {
   . = 0x00000000;
   .text.init : { *(.text.init) }
-  . = ALIGN(0x1000);
+  . = ALIGN(0x800);
   .tohost : { *(.tohost) }
-  . = ALIGN(0x1000);
+  . = ALIGN(0x400);
   .text : { *(.text) }
-  . = ALIGN(0x1000);
+  . = ALIGN(0x400);
   .data : { *(.data) }
   .bss : { *(.bss) }
   _end = .;
次に、riscv-tests/isa/Makefileを書き換える。diffを取ると以下のようになる。
FPGAマガジンNo.18 P47で説明されてるのと違うところは、行番号が違う & 新しい行が追加されているところ。FPGAマガジンNo.18当時とは元のソースコードが変わった模様。
$ git diff -u isa/Makefile

diff --git a/isa/Makefile b/isa/Makefile
index 4e1ba20..449997b 100644
--- a/isa/Makefile
+++ b/isa/Makefile
@@ -49,7 +49,7 @@ vpath %.S $(src_dir)
        $(RISCV_SIM) --isa=rv64gc $< 2> $@

 %.out32: %
-       $(RISCV_SIM) --isa=rv32gc $< 2> $@
+       $(RISCV_SIM) --isa=rv32im $< 2> $@

 define compile_template

@@ -57,9 +57,9 @@ $$($(1)_p_tests): $(1)-p-%: $(1)/%.S
        $$(RISCV_GCC) $(2) $$(RISCV_GCC_OPTS) -I$(src_dir)/../env/p -I$(src_dir)/macros/scalar -T$(src_dir)/../env/p/link.ld $$< -o $$@
 $(1)_tests += $$($(1)_p_tests)

-$$($(1)_v_tests): $(1)-v-%: $(1)/%.S
-       $$(RISCV_GCC) $(2) $$(RISCV_GCC_OPTS) -DENTROPY=0x$$(shell echo \$$@ | md5sum | cut -c 1-7) -std=gnu99 -O2 -I$(src_dir)/../env/v -I$(src_dir)/macros/scalar -T$(src_dir)/../env/v/link.ld $(src_dir)/../env/v/entry.S $(src_dir)/../env/v/*.c $$< -o $$@
-$(1)_tests += $$($(1)_v_tests)
+#$$($(1)_v_tests): $(1)-v-%: $(1)/%.S
+#      $$(RISCV_GCC) $(2) $$(RISCV_GCC_OPTS) -DENTROPY=0x$$(shell echo \$$@ | md5sum | cut -c 1-7) -std=gnu99 -O2 -I$(src_dir)/../env/v -I$(src_dir)/macros/scalar -T$(src_dir)/../env/v/link.ld $(src_dir)/../env/v/entry.S $(src_dir)/../env/v/*.c $$< -o $$@
+#$(1)_tests += $$($(1)_v_tests)

 $(1)_tests_dump = $$(addsuffix .dump, $$($(1)_tests))

@@ -71,14 +71,14 @@ tests += $$($(1)_tests)

 endef

-$(eval $(call compile_template,rv32ui,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32uc,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32um,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32ua,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32uf,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32ud,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32si,-march=rv32g -mabi=ilp32))
-$(eval $(call compile_template,rv32mi,-march=rv32g -mabi=ilp32))
+$(eval $(call compile_template,rv32ui,-march=rv32im -mabi=ilp32))
+#$(eval $(call compile_template,rv32uc,-march=rv32im -mabi=ilp32))
+$(eval $(call compile_template,rv32um,-march=rv32im -mabi=ilp32))
+#$(eval $(call compile_template,rv32ua,-march=rv32im -mabi=ilp32))
+#$(eval $(call compile_template,rv32uf,-march=rv32im -mabi=ilp32))
+#$(eval $(call compile_template,rv32ud,-march=rv32im -mabi=ilp32))
+#$(eval $(call compile_template,rv32si,-march=rv32im -mabi=ilp32))
+#$(eval $(call compile_template,rv32mi,-march=rv32im -mabi=ilp32))
 ifeq ($(XLEN),64)
 $(eval $(call compile_template,rv64ui,-march=rv64g -mabi=lp64))
 $(eval $(call compile_template,rv64uc,-march=rv64g -mabi=lp64))
makeする!
$ cd riscv-tests/
$ make isa
riscv-tests/isa/にいろいろbinファイルが並ぶ。 以下のように、FPGAマガジンに書いてある通り、バイナリとhexの間の変換はできる。 バイナリ->hex
 $ riscv32-unknown-elf-objcopy -O binary rv32ui-p-add rv32ui-p-add.bin
hex→バイナリ
$ hexdump -v -e '1/4 "%08x" "\n"' rv32ui-p-add.bin > rv32ui-p-add.hex
XilinxのVivadoならこのまま使える。しかしIntelのQuartus Primeだとmifファイルにしないと使えないみたい。
binをmifに変えてくれるらしい、サンプルコード中のFPGAMAG18-master/sample/bin2mif.cを使いたいところだが、gccで普通にコンパイルしてRISC-Vのテストのbinファイルに使うとSEGVになってしまう。このツールではどんなbinでもmifにできるわけではないみたい?(使い方間違ってたらごめん) Quartus Primeにはhexファイルをmifファイルに変換する機能があるので、それで変換して使えば良い。(macnica: Quartus Prime / Quartus II で .mif から .hex へ変換する方法はありますか? 本ページではRISC-Vのテストコードは以降使わないので、この話はここまで。

MAX-10版のみの手順

QuartusPrimeなどのインストール

公式サイトでダウンロード。 Quartus Prime ライトエディションの17.0をダウンロード。

tarファイルを展開し、出てきたスクリプトを実行。このときsudoは付けないほうがいい。(quartusの実行時にアカウントが違うとかなんとかいわれるから。)
$ tar -xvf Quartus-lite-17.0.0.595-linux.tar
$ cd Quartus-lite-17.0.0.595-linux
$ ./setup.sh
インストールでは基本はデフォルト設定でいい。インストールするものは以下のように選ぶ。(いらないDevicesの選択を解除した)
だいたい11GBほど必要とするので注意。
終わったら、デスクトップにアイコンができるのでそこから実行する。
それか、以下のコマンドで起動する。(デフォルト設定でインストールするとこうなるはず。)
$ /home/<USERNAME>/intelFPGA_lite/17.0/quartus/bin/quartus --64bit

Quartus Primeでプロジェクトを開いて、動かせるようにする

Quartus Primeを起動して、メニューの File -> Open Project...から、FPGAMAG18-master/fmrv32im-max10/max10.qpfを開く。 しかしこのままでは 動かない!

1. IPのコードの移動 & ディレクトリのリネーム

Quartus Primeの左上ペイン、MAX10の下に何も出ない(本来ならIPがぶら下がる)し、Qsysを開くとError: <システム名>.<コンポーネント名>: Component <コンポーネントタイプ> 1.0 not found的なエラーが出る。
Intel Community: QSYS Custom component not foundを参考にすると、どうやらプロジェクトファイルがある階層にあるはずの ipというディレクトリを探そうとしたが無いのでこうなってるらしい。
I looked at the log screen that pops up when the QSys program opens and it appeared it was looking for a folder called ‘ip’. I created a folder called ‘ip’ under my Nios core directory, and I put the custom components in that directory. I closed and re-opened my QSys project and it worked; the components remained in my project.
– jon
なので、FPGAMAG18-master/modulesをmax-10用プロジェクトと同じところに移動させipとリネームすればよい。(ファイルたちをプロジェクトにAddしてもいいかも)
$ cp -r FPGAMAG18-master/modules FPGAMAG18-master/fmrv32im-max10/
$ mv FPGAMAG18-master/fmrv32im-max10/modules FPGAMAG18-master/fmrv32im-max10/ip 

2. 動かすプログラムのセット

FPGAマガジンNo.18に書いてある通り、「俺々RISC-V CPU」で実行するプログラムはハードウェアのコンパイル時にメモリに埋め込む。その埋め込む元となるファイルのパスを設定しなくてはならない。(最初はサンプルコードが書かれた当時のままになってる) Quartus Primeのメニュー、 Tools -> QsysでQsysを起動する。読み込むファイルを聞かれるので、FPGAMAG18-master/fmrv32im-max10/fmrv32im_max10.qsysを選択。 なんかワーニングでてても気にしない
左下のペインで右クリックしてEdit。 出てきたウィンドウの中のMEM_FILEってやつFPGAMAG18-master/romdata/max10.mifを指すように書き換える。
Qsysを閉じるときに、変更があったからハードウェアをgenerateしなおすか的なことを聞かれるので、YESにする。なんか警告が出るが、気にしない。
Quartus Primeでも You have created an IP Variation in the file...的な画面(下画像)が出るので、画面上部の緑の三角でコンパイルし直す。
できたっ!(けどなんか警告出てるけど気にしない)

シミュレーションしてみる

Quartus Primeと一緒にインストールされた、ModelSimってやつをつかう。

ModelSimのパスがちゃんと設定されてるか確認(しなくていい)

Quartus Primeから Tools -> Optionsでオプションを開く。そしたらEDA Tool Optionsという項目を開くと以下のようになっている。 ModelSim-Alteraのパスがちゃんと設定されているか確認する(最初から設定されてた。)

32bitアーキテクチャ用ライブラリを入れる

下のを実行すればよい。 Debian系Linux(64bit)でModelSim-Altera(Intel)が動かないときに見る記事
sudo dpkg --add-architecture i386 sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install gcc-multilib g++-multilib \
lib32z1 lib32stdc++6 lib32gcc1 \
expat:i386 fontconfig:i386 libfreetype6:i386 libexpat1:i386 libc6:i386 libgtk-3-0:i386 \
libcanberra0:i386 libpng12-0:i386 libice6:i386 libsm6:i386 libncurses5:i386 zlib1g:i386 \
libx11-6:i386 libxau6:i386 libxdmcp6:i386 libxext6:i386 libxft2:i386 libxrender1:i386 \
libxt6:i386 libxtst6:i386

ModelSimでシミュレーション

Quartus Primeのメニュー Tools -> Run Simulation Tool -> RTL Simulationを選ぶと、ModelSim-Alteraが起動する。 以下のようになる。
起動すると勝手にシミュレーションがスタートしてしまう。上のツールバーの赤いSTOPボタンで止める。
  1. 右上のsimペインのなかで、fmrv32imというCPUコアをクリックする。
  2. 真ん中の青いObjectsペインに信号線?一覧が出てくるので、見たいものを右のWaveペインにドラッグ&ドロップ
  3. 上のツールバーの実行間隔?の右にRunボタンがあるのでそれを何回か押す。
  4. 右の黒いWave画面でCtrl+スクロールでスケールを調整し、眺める
  5. 実行間隔_?の初期値が100psなので10000psに変更し、右の実行ボタンをぽちぽちするとその時間ぶん実行できるみたい
以上、できた! (LEDが1秒ごとに点滅するはずだが、そんなに長い間シミュレーションするのは難しいので点滅の確認はできず。)
そのため、もっと短い間隔でLEDを点滅させた。

Lチカのソースコード、2500000 (2510^5)ループで約1秒間隔のLED点滅を実現しているが、これを2510^3にして、約0.01s間隔にした。
シミュレーションを進めると、以下のようにLEDが1111(点灯)する時が来た。
8.75*10^6 ns つまり、0.00875s(≒0.01s)のときに点灯。だいたい合ってる。

Artix-7版のみの作業

Vivadoのインストール

公式ダウンロードページ 統合インストーラをやっとくのが面倒がなくてよい。画像だと2019.2になってるが、FPGAマガジンNo.18のサンプルコードはVivado2017.2で作られているので、2017.2をダウンロードしておく。
ちなみにダウンロード時にログイン + 住所や所属先入力がある。名前以外で日本語使うと弾かれる。
一般ユーザの権限で起動したインストーラは、そのユーザで書き込める/opt/Xilinxがないとダメ。作成し、一般ユーザでも書き込めるようにしておく。
$ sudo mkdir /opt/Xilinx

#777じゃダメだけど面倒なのでこうしちゃった。オーナー変えた方がよかった?
$ sudo chmod 777 /opt/Xilinx
ダウンロードしたbinファイルを起動する。sudoはつけない。(起動時にアカウントが違うとか言われて面倒だった記憶がある…)
$ ./Xilinx_Vivado_SDK_2017.2_0616_1_Lin64.bin
(ここでもしsudoつけてインストーラを起動した場合…。起動は以下のようにやる必要があり、面倒)
$ sudo bash -c  '/tools/Xilinx/Vivado/2019.2/settings64-Vivado.sh && vivado'
インストーラ起動後もログインが必要。
webpackを選択。
下のようにする。(余計なDevicesの選択を解除。)
最終的に以下のようなセッティングになる。
ダウンロードに30分くらいかかるので覚悟。

Vivadoでプロジェクトファイルを開く

FPGAMAG18-master/fmrv32im-artya7.nonos/fmrv32im-artya7.xpr を開く。 IPのアップグレードをするかとか聞かれるが、全部断る。

メインメモリにロードするプログラムの作成と配置

このままだと Could not find the file とかエラーが出る。 問題点は2つ。
  • mainram.coeがない
  • artya7.hexがない
これらを自分で作って、然るべき場所に配置し、その場所をVivado側で設定してやる必要がある。

(ファイルの作成)

(かなり上のほうの、MAX-10とArtix-7に共通の作業の中で行ったので特にすることはない。)

場所の指定

mainram.coe
下の図ように、mainram.coeの設定を確認する。なぜか最初から正しい場所を指していた。
artya7.hex
以下のように、
  1. Open Block Designを押す
  2. cacheブロックを選択
  3. Block Propertiesの MEM_FILEの値を正しいパスに変更

コンパイルとか

下の図の赤矢印のRun SynthesisとかRun ImplementationとかGenerate Bitstream(は要らないかも)を押してやってく。ワーニングがでても気にしない。

シミュレーションをする

以下のようにして準備をする。
そしたら、上のツールバーの時間を1msくらいにしてRUNすればよい。
(1msでもかなり時間かかる!)

実行するプログラムを変えてみる

ledの点滅が約1s周期だとシミュレーションに時間がかかりすぎる。なので、元のソースコードを変更しもっと短い周期で点滅するようにする。 サンプルコードのままだと25*10^6のループするとLEDのON / OFFが切り替わるが、これを25回周期で切り替わるようにした。つまり、1秒の10^-6倍、10μsくらいで切り替わる。(実機ではそんなはやく切り替えられないかもしれないが) FPGAMAG18/sample/led/led.cをそのように書き換え、次のようにファイルを準備する
#Lチカのコンパイル
$ mkdir ../../romdata
$ cd led 
#これで .hexと.mifのファイルが../../romdataに作成される
$ make

#coeファイルを作成する
$ ../bin2coe -4 led
$ cp led.coe ../../romdata
$ mv ../../romdata/led.coe ../../romdata/mainram.coe
実行するプログラムを変えると、Vivado上でまた操作が必要になる。(Quartus Primeは再コンパイルみたいなのやれば済んだんだけど…)
これに関してはFPGAマガジンNo.18 P51の図4のとおりに操作すればよい。
IPコアのソースコードの再生成?をするらしい


もっかい論理合成する。


そしてこのあと、Run ImplementationとかGenerate Bitstreamもやったほうがいい..のかも。Bitstreamのほうはいらないかもだけど。 ちなみに、上〜のほうのサンプルコードのクローンのところに書いた通りにFPGAMAG18/modules/uart_v1/src/fmrv32im_axis_uart.vを修正してないと、この際に以下のファイルに文法エラーが見つかる…はず。 FPGAMAG18/fmrv32im-artya7.nonos/fmrv32im-artya7.srcs/sources_1/bd/fmrv32im_artya7/ipshared/a1f5/src/fmrv32im_axis_uart.v これはおそらく、IPコアのソースコードの再生成?によりFPGAMAG18/modules/uart_v1/src/fmrv32im_axis_uart.v(記述ミスあり)をこの場所にコピってきたのでこのタイミングでエラーが出たと思われる。違かったらごめん。 上のツールバーで1msを指定して、実行。
できた!8~9μs(≒10μs)くらいでLEDのオンオフが切り替わってる。

コメント