FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

オープンソースRISC-VコアHummingBirdについて調査(4. Verilatorビルドでの波形デバッグ)

RISC-Vの実装であるHummingBirdを調査していくことにした。

https://camo.githubusercontent.com/f393ca23a6df9f640c1e4a43b78f2a503fdc47e8/68747470733a2f2f66647661643032316173666438712e6f73732d636e2d68616e677a686f752e616c6979756e63732e636f6d2f4c696368656554616e672f626f6f6b7069632e6a7067

Verilatorでのデバッグを行っているのだが、どうも上手く動いてくれていない。波形を見てもなんだか動いていないように見えるので、Verilatorでクロックゲーティングをすると何かおかしくなるのだろうか?まずは基本的なところが動いてほしいので、クロックゲーティングをすべて停止してみる。

  • rtl/e203/core/e203_clkgate.v
diff --git a/rtl/e203/core/e203_clkgate.v b/rtl/e203/core/e203_clkgate.v
index bd3a456..a29451d 100644
--- a/rtl/e203/core/e203_clkgate.v
+++ b/rtl/e203/core/e203_clkgate.v
@@ -45,9 +45,9 @@ always@(*)
   if (!clk_in)
     enb = (clock_en | test_mode);

-assign clk_out = enb & clk_in;
+// assign clk_out = enb & clk_in;
+assign clk_out = clk_in;

 `endif//}

 endmodule

すると何となく動いているような気がしてきた。しかしまだシミュレーションが終了しない。テストパタンも途中で止まっているように見えるのだが、何か繰り返して待ち状態に入っているような気がする。wait_loop1というルーチンは何のために待ち合わせをしているんだ?

f:id:msyksphinz:20191104011253p:plain
80000192:       0000100f                fence.i
80000196:       000012b7                lui     t0,0x1
8000019a:       a0028293                addi    t0,t0,-1536 # a00 <_start-0x7ffff600>

8000019e <waitloop1>:
8000019e:       12fd                    addi    t0,t0,-1
800001a0:       fe029fe3                bnez    t0,8000019e <waitloop1>
800001a4:       100083b7                lui     t2,0x10008
800001a8:       00838393                addi    t2,t2,8 # 10008008 <_start-0x6fff7ff8>
800001ac:       0003a283                lw      t0,0(t2)

一応ループは回っているようなのだが、何をしているのか、パイプラインがなんの命令を実行しているのかさっぱりわからないので、例によってパイプライントレーサを作り込んだ方が良いのではないかという気がしてきた。 そのあたりから取り組もうかな。

RustでRISC-Vの命令セットシミュレータを作ったので(いちおう)公開する

f:id:msyksphinz:20190224185310p:plain

といっても大昔に作ってみたもので、BitBucketの肥やしにしておくのももったいないし、最終的にどうにかして発展させたいので公開する。

今のところデコーダはあるが逆アセンブラはついていないのでspike-dasm頼みである。

github.com

大した実装ではなく、一応テストパタンを通すことができる程度だが、公開したのは今後色々と拡張をしたいから。今時点の私の知識ではこれが限界で、これ以上Rustっぽく書くためにはどうしたらいいのか良く分からない。

RustでRV32とRV64を上手く切り分ける方法はあるか?

もともとRustの勉強を始めたのはこのあたりだが、Scalaを通じてTraitなどの概念を知って、じゃあRV32とRV64で処理内容を同一のトレイトで管理して、実際のCPUのモデルのみを切り替えるという方法は上手く行くだろうかと思って始めた。

ただ、Rustの本質も良く分かっていないし、解説書などを読めば読むほど「それジェネリクスでi32とi64の両方作っておけば良いだけじゃない?」という気分もしてくるので、Rustっぽい綺麗な書き方というのはどうすればよいだろう、という疑問がある。

メモリ管理の厳しいRustで、シミュレータのメモリを効率的に管理する方法はあるか?

もともとC++で書いていたときは、シミュレータは一般的にどのアドレスにメモリアクセスするかは分からないので、ハッシュでメモリ空間を大雑把に管理しており、

  1. これまでにアクセスしたことのない場所へのアクセスが発生すると、ある程度の大きさで新たなメモリ領域を確保し、ハッシュに格納しておく(ある程度の大きさを確保するのは隣接する領域でのアクセスでこのルーチンを繰り返したくないから)。
  2. 別の機会に同じ領域へのアクセスが発生するとハッシュからこの領域を取り出してアクセスする。

という風にして最初から大容量のメモリを確保しなくても良い実装にしている。で、Rustはメモリ管理に厳しい、というか、アクセスの予測ができないメモリアクセスはどうやって実現すればよいのだろう?というのではたと止まってしまった。 でもこれは「実践Rust入門を見ていると、配列とVecを上手く使いながらメモリ領域を管理すると上手く行きそうな気がしてきた。。。

浮動小数点の実装はどうする?

まず一つはC++の実装をライブラリとして組み込む、という方法が挙げられるのと、Rustのモジュールにすでにsoftfloatがあるのでこれを使うという方法。 どちらが大変なのかは見積もりができないが、何か芸がないなあ...


という訳で全然完成形ではないが、いろんな人のアドバイスが欲しいので公開してしまおう。最終的には今管理しているC++RISC-Vシミュレータを、Rustに移行できれば楽しそうだ。

※ おまけ:今管理している自作RISC-Vシミュレータ。C++で記述しておりLinuxもブートできるよ!

github.com

オープンソースRISC-VコアHummingBirdについて調査(3. Verilator用にビルドを追加できないか?)

RISC-Vの実装であるHummingBirdを調査していくことにした。

https://camo.githubusercontent.com/f393ca23a6df9f640c1e4a43b78f2a503fdc47e8/68747470733a2f2f66647661643032316173666438712e6f73732d636e2d68616e677a686f752e616c6979756e63732e636f6d2f4c696368656554616e672f626f6f6b7069632e6a7067

Verilator用のビルドを追加したい試行。VerilatorはRISC-VのRocket-Chipなどで間接的に使用したことはあるし、gtkwaveで生成した波形をデバッグしたことはあるけど、自分でテストベンチを書いた事は無いので四苦八苦している。なるほど、ChiselのIOTesterと同じノリで書いていけばいいのか、ということが分かってきた。

あとは、VerilatorはテストベンチはC++で記述するという仕様上、通常のテストベンチをそのまま使ってはいけないということに気がついた。 Verilatorは遅延演算子(#10みたいなやつ)をサポートしていないので、テストベンチをそのまま食わせると一瞬でテストが終了してなにも実行されない。

#1000000;  // こういうテストベンチの記述はVerilatorではすべて無視されて、一瞬で終了する。
$finish

というか昔Verilatorの特徴をまとめていたのだった。自分で書いていて忘れていた。

msyksphinz.hatenablog.com

頑張ってC++のテストコードを書いている。これらを合わせてコンパイルを通らせるようにした。テストベンチは最低限の機能を残して削っている。これは後でブランチで公開する。

#include "Vtb_top.h"
#include "verilated.h"
#include "verilated_vcd_c.h"

static uint64_t trace_count = 0;

double sc_time_stamp()
{
  return trace_count;
}

int main(int argc, char **argv, char **env) {
  Verilated::commandArgs(argc, argv);
  Vtb_top* top = new Vtb_top;

  Verilated::traceEverOn(true);
  VerilatedVcdC* tfp = new VerilatedVcdC;

  vluint64_t sim_time = 100000;

  top->trace(tfp, 99);  // Trace 99 levels of hierarchy
  tfp->open("simx.vcd");

  top->clk = 0;
  top->lfextclk = 0;
  top->rst_n = 0;

  while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) {
    if (trace_count >= 10) {
      top->rst_n = 1;
    }
    if ((trace_count % 5) == 0) {
      top->clk = !top->clk;
    }
    if ((trace_count % 15) == 0) {
      top->lfextclk = !top->lfextclk;
    }

    trace_count ++;
    top->eval();
    tfp->dump(trace_count);
    if ((trace_count % 1000) == 0) {
      printf ("Count = %d\n", trace_count);
    }
  }
  tfp->close();

  delete top;
  exit(0);
}

とりあえず波形は出るようになってきたが、まだフェッチが出ているかどうか怪しいなあ? っていうか$readmemhは効いているのだろうか?

次は波形を見ながらHummingBirdのデバッグをしてみる。

f:id:msyksphinz:20191031015450p:plain

FIRRTLに入門する (1. ビルドとVerilog生成テスト実行)

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

FIRRTLというのはハードウェア記述言語Chiselのバックエンドに相当する部分で、Chisel→FIRRTL→Verilogという段階を取り、FIRRTL→VerilogというのはLLVMで言うバックエンドに相当する部分であると説明にも書いてある。

FIRRTL自体はScalaで記述されており、動作も少しもっさりしているような印象だが、なにより生成されるVerilogファイルがイマイチな気がしている。 なんでそんなVerilog出すの?なんでこの機能ないの?といったものが散見されるので、自分で実装を理解してみることにした。

まずはダウンロードとビルドだが、現時点で安定版リリースの1.2.0を使用することにする。

git clone git@github.com:freechipsproject/firrtl.git
cd firrtl
sbt assembly # コンパイル

コンパイル後にサンプルプログラムとしてRocketCore.firRocketCore.vに変換してみる。

./utils/bin/firrtl -td regress -i regress/RocketCore.fir -o regress/RocketCore.v -X verilog

これでとりあえず実行は終了。RocketCore.vが生成されている。 このRocketCore.firは6000行近くもあるのだが、流してみると8.5秒くらいかかっている。 もう少し小さな回路で確認しながら、どのような挙動になっているのか見てみたいと思う。

「実践Rust入門」に入門している (2. 電卓作成まで終了)

少し前から、「実践Rust入門」を読み始めている。Rustを使って四則演算の電卓を作るところまで行った。

実践Rust入門[言語仕様から開発手法まで]

実践Rust入門[言語仕様から開発手法まで]

今回の実装はあまりRustのキワキワの機能を使っていないのかな?という印象。というか写経をしただけなので分かっていないだけなのかも。

どちらかというと文字列を受け取ったときにどういうエラー表示をすべきなのか、エラー表示をわかりやすく表示するためにどのような機能が必要なのか、そっちの方向が少し目から鱗。なるほど、こうやってエラー表示は出力するのか。

並行しながら所有権の部分なども読み進めているが、これも実際に手を動かしてモノを作ってみないと完璧に理解できないかも。。。こっちの本を読み進めながら、別プロジェクトを立ち上げて自分のオリジナル実装を作りながら読み進めてもいいかもなと思った。

という短い日記。おわり。

オープンソースRISC-VコアHummingBirdについて調査(2. Verilator用にビルドを追加できないか?)

RISC-Vの実装であるHummingBirdを調査していくことにした。

https://camo.githubusercontent.com/f393ca23a6df9f640c1e4a43b78f2a503fdc47e8/68747470733a2f2f66647661643032316173666438712e6f73732d636e2d68616e677a686f752e616c6979756e63732e636f6d2f4c696368656554616e672f626f6f6b7069632e6a7067

HummingBirdの概要

前回iverilogを使用しようとしてはねられてしまうという悲しい状態で終了したのだが、少し調査すればVerilatorでも流すことができそうな気がする。というかVerilatorで一からテストベンチと環境を立ち上げたことが無いので、これを気に挑戦してみようかと思う。

とりあえずググって、以下のQiitaの記事が出てきたので読んでみた。 なんだ。C言語の記述はこれだけで行けるのかな?それなら私でもできそうだ。

qiita.com

#include "Vour.h"
#include "verilated.h"

int main(int argc, char **argv, char **env) {
  Verilated::commandArgs(argc, argv);
  Vour* top = new Vour;
  while (!Verilated::gotFinish()) { top->eval(); }
  delete top;
  exit(0);
}

あとはRTLとインクルードファイルが含まれているディレクトリをどんどん追加していくことになる。HummingBirdのビルド環境では、make installを実行すると一か所にソースファイルが固まるので、そこを指定すれば良さそうだ。 なぜそんな仕組みになっているのかは分からないが、ほかのファイルを編集中にシミュレーションが間違ってしまうことを恐れたのか。

Makefileを以下のように書き換え、Verilatorでコンパイルできるように変更していく。

diff --git a/vsim/bin/run.makefile b/vsim/bin/run.makefile
index 46a917e..eeafd22 100644
--- a/vsim/bin/run.makefile
+++ b/vsim/bin/run.makefile
@@ -15,11 +15,15 @@ TB_V_FILES          := $(wildcard ${VTB_DIR}/*.v)
 # The following portion is depending on the EDA tools you are using, Please add them by yourself according to your EDA vendors

 SIM_TOOL      := #To-ADD: to add the simulatoin tool
-SIM_TOOL      := iverilog # this is a free solution here to use iverilog to compile the code
+#SIM_TOOL      := iverilog # this is a free solution here to use iverilog to compile the code
+SIM_TOOL      := verilator

 SIM_OPTIONS   := #To-ADD: to add the simulatoin tool options

-SIM_OPTIONS   := -o vvp.exec -I "${VSRC_DIR}/core/" -I "${VSRC_DIR}/perips/" -D DISABLE_SV_ASSERTION=1 -g2005
+# SIM_OPTIONS   := -o vvp.exec -I "${VSRC_DIR}/core/" -I "${VSRC_DIR}/perips/" -D DISABLE_SV_ASSERTION=1 -g2005
+SIM_OPTIONS := --top-module tb_top -Wall +systemverilogext+ext --cc --exe ${RUN_DIR}/../install/tb/sim_main.cpp -I${RUN_DIR}/../install/rtl/core/ -I${RUN_DIR}/../install/rtl/perips
+
+

実際にコンパイルしてみると、3000個以上の警告が出て、その結果バイナリファイルが生成されない。Verilatorというのはすべての警告を消さないとバイナリファイルが生成されないのもなのだろうか?良く分からない。

徹底的に警告を潰していく。潰していく、というかひたすら警告を無視していく。

+SIM_OPTIONS := --top-module tb_top -Wall +systemverilogext+ext --cc --exe ${RUN_DIR}/../install/tb/sim_main.cpp -I${RUN_DIR}/../install/rtl/core/ -I${RUN_DIR}/../install/rtl/perips -Wno-STMTDLY -Wno-lint  -Wno-UNOPTFLAT -Wno-COMBDLY -Wno-INITIALDLY

ここまで進めてようやくバイナリが生成されたようだ。ただし今日はもう時間切れ。

オープンソースRISC-VコアHummingBirdについて調査(1. ダウンロードとビルド)

RISC-Vの実装であるHummingBirdを調査していくことにした。

https://camo.githubusercontent.com/f393ca23a6df9f640c1e4a43b78f2a503fdc47e8/68747470733a2f2f66647661643032316173666438712e6f73732d636e2d68616e677a686f752e616c6979756e63732e636f6d2f4c696368656554616e672f626f6f6b7069632e6a7067

HummingBirdの概要

HummingBirdは中国の大学で開発されたRISC-Vコアで、正式にはE203という名前が付けられている。GitHubのページを参照すると、このHummingBirdを開発した目的はRISC-Vの人気を向上させ、中国でのIoT設計におけるRISC-Vの普及を目指すために開発したと書いてある。

いくつかのアーキテクチャとの違い

これはHummingBirdの解説本(中国語)から引用してみる。

ARM Cortex-M0 ARM Cortex-M0+ ARM Cortex-M3 E201 E203 E205
Dhrystone(DMIPS/MHz) 0.84(標準) / 1.21(最適化時) 0.94 / 1.31 1.25 1.171 1.23 1.355
Coremark (Coremark/MHz) 2.33 2.42 3.32 1.352 2.14 3.327
最小ゲート数 12K 12K 36K 10K 12K 20K
パイプライン長 3 2 3 2 2 2
乗算器 あり あり あり なし あり あり
除算器 なし なし あり なし あり あり
ITCM / DTCM なし なし なし あり あり あり
拡張性 なし なし なし あり あり あり

ダウンロード

git clone https://github.com/SI-RISCV/e200_opensource.git

シミュレーションを実行する

現在HummigBirdのシミュレーションはiverilogが対応していると書いてある。

sudo apt install iverilog
cd vsim
make run TESTCASE=$PWD/../riscv-tools/fpga_test4sim/dhrystone4sim/dhrystone4sim

なんだ実行できない。Makefileを確認する。

  • vsim/bin/run.makefile
#SIM_EXEC      := #To-ADD: to add the simulatoin executable
#SIM_EXEC      := vvp ${RUN_DIR}/vvp.exec -none # The free vvp is tooooo slow...
SIM_EXEC      := echo "Test Result Summary: PASS" # This is a fake run to just ...

なんか良く分からないが、Dummyの実行ファイルが指定されているので、2番目のvvpというファイルを実行するように変更してみる。

#SIM_EXEC      := #To-ADD: to add the simulatoin executable
SIM_EXEC      := vvp ${RUN_DIR}/vvp.exec -none # The free ...
#SIM_EXEC      := echo "Test Result Summary: PASS" # This is a fake run to just direct...

再実行。

cd dhrystone4sim; vvp /home/msyksphinz/work/riscv/e200_opensource/vsim/run/vvp.exec -none  +DUMPWAVE=1 +TESTCASE=/home/msyksphinz/work/riscv/e200_opensource/vsim/../riscv-tools/fpga_test4sim/dhrystone4sim/dhrystone4sim 2>&1 tee dhrystone4sim.log; cd /home/msyksphinz/work/riscv/e200_opensource/vsim/run;
/home/msyksphinz/work/riscv/e200_opensource/vsim/run/../install/tb/tb_top.v:245: Error: $value$plusargs() is a system function, it cannot be called as a task.
/home/msyksphinz/work/riscv/e200_opensource/vsim/run/vvp.exec: Program not runnable, 1 errors.
make[1]: Leaving directory '/home/msyksphinz/work/riscv/e200_opensource/vsim/run'

えええ?iverilogって$value$plusargs()サポートしていないのかしら...?Verilatorとかで流せないかな...あるいはもうシミュレーションは実行せずに、中身だけ見てみようかな...