FPGA開発日記

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

RISC-V SpikeシミュレータでC/C++のprintfを実現する仕組み (9. RISC-Vはデバイスアクセスをどのようにして実現するか)

UCBが開発しているRISC-VのシミュレータSpikeや、Rocket-ChipのRTLデザインは通常はシステムコールを持っていない。 つまり、当然ながらC言語printf("Hello World\n");などと書いても動作しないのだが、そこはコンパイラとフロントエンドサーバfesvr、pk(Proxy Kernel)によって肩代わりすることでこれらのシステムコールを実現している。

Proxy Kernelを使えば、外部のホストインタフェースを使って入出力が可能だが、それでは複数のデバイスがつながっている場合はどうするのだろうか? 例えば、ディスクが接続されているときなど、コマンドをどのように出力して、どのメモリアドレスにリクエストを出せばよいのか。 それらについて調査した。

f:id:msyksphinz:20180802013846p:plain
図. Spikeシミュレータの構造。システムコールやデバイスアクセスはどのようにして実現しているのか。

最新のSpikeシミュレータではディスクアクセスをサポートしていない?

ソースコードを見てみると、以下のような記述があり、+diskが使えないように見える。ちゃんと確かめていないが...

  • riscv-tools/riscv-fesvr/fesvr/htif.cc
      case HTIF_LONG_OPTIONS_OPTIND + 1:
        // [TODO] Remove once disks are supported again
        throw std::invalid_argument("--disk/+disk unsupported (use a ramdisk)");
        dynamic_devices.push_back(new disk_t(optarg));
        break;

バイスリストを使って各種デバイスにアクセスする仕組み

Spikeシミュレータのソースコードを見る前に、まずはFESVR(RISC-Vのフロントエンドサーバ)を見てみよう。 すると、フロントエンドサーバの初期化時にいくつかのデバイスを接続していることが分かる。

以下のソースで接続されているのは、Proxy Kernelで使用されるシステムコールと、あとはbcdと呼ばれるデバイスだ。

  • riscv-tools/riscv-fesvr/fesvr/htif.cc
void htif_t::register_devices()
{
  device_list.register_device(&syscall_proxy);
  device_list.register_device(&bcd);
  for (auto d : dynamic_devices)
    device_list.register_device(d);
}

ここで言うbcdは、簡単なデバイスに相当するらしい。

  • riscv-tools/riscv-fesvr/fesvr/htif.cc
bcd_t::bcd_t()
{
  register_command(0, std::bind(&bcd_t::handle_read, this, _1), "read");
  register_command(1, std::bind(&bcd_t::handle_write, this, _1), "write");
}
...
void bcd_t::tick()
{
  int ch;
  if (!pending_reads.empty() && (ch = canonical_terminal_t::read()) != -1)
  {
    pending_reads.front().respond(0x100 | ch);
    pending_reads.pop();
  }
}

ディスクは現在は接続できないのだが、実装としては以下のようになっている。

  • riscv-tools/riscv-fesvr/fesvr/htif.cc
disk_t::disk_t(const char* fn)
{
  fd = ::open(fn, O_RDWR);
  if (fd < 0)
    throw std::runtime_error("could not open " + std::string(fn));

  register_command(0, std::bind(&disk_t::handle_read, this, _1), "read");
  register_command(1, std::bind(&disk_t::handle_write, this, _1), "write");

  struct stat st;
  if (fstat(fd, &st) < 0)
    throw std::runtime_error("could not stat " + std::string(fn));

  size = st.st_size;
  id = "disk size=" + std::to_string(size);
}

つまり、ディスクデバイスを接続すると、そのデバイスに相当するイメージファイルをOpenし、read/writeできるようにするというわけだ。

void disk_t::handle_read(command_t cmd)
{
  request_t req;
  cmd.htif()->memif().read(cmd.payload(), sizeof(req), &req);

  std::vector<uint8_t> buf(req.size);
  if ((size_t)::pread(fd, &buf[0], buf.size(), req.offset) != req.size)
    throw std::runtime_error("could not read " + id + " @ " + std::to_string(req.offset));

  cmd.htif()->memif().write(req.addr, buf.size(), &buf[0]);
  cmd.respond(req.tag);
}

void disk_t::handle_write(command_t cmd)
{
  request_t req;
  cmd.htif()->memif().read(cmd.payload(), sizeof(req), &req);

  std::vector<uint8_t> buf(req.size);
  cmd.htif()->memif().read(req.addr, buf.size(), &buf[0]);

  if ((size_t)::pwrite(fd, &buf[0], buf.size(), req.offset) != req.size)
    throw std::runtime_error("could not write " + id + " @ " + std::to_string(req.offset));

  cmd.respond(req.tag);
}

そして、システムコールが呼ばれた場合は、以下のようにデバイスリストの中から1つのデバイスが選択される。 つまり、コマンドの番号によって選択されるデバイスが変わるわけだ。 例えば、ディスクをデバイスリストの2番に接続すると、tohostレジスタにコマンドを投げるときに、CMDフィールドを2に設定すればよい。

  • riscv-tools/riscv-fesvr/fesvr/device.cc
void device_list_t::handle_command(command_t cmd)
{
  devices[cmd.device()]->handle_command(cmd);
}

void device_list_t::tick()
{
  for (size_t i = 0; i < num_devices; i++)
    devices[i]->tick();
}

「ゼロから作るディープラーニング②」を読む (3. 第3章 word2vec)

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。

あまり内容を詳細に記録するつもりはないので、読んで纏めていったものをGistに張り付けていく。

  • Jupyter Notebook Viewer: 3章 word2vec

Jupyter Notebook Viewer

word2vecは、第2章で紹介した単語の推論の処理(カウントベースの手法)とは別の推論を使った方法だ。

ニューラルネットワークを使用して、2つの単語(ターゲット)からその中間に入る単語を推論する(CBOW)。

この方法はcontinuous bag-of-words(CBOW)という手法を使用し、簡単に言うと2つのターゲット(その間に入る単語を推測したい)を入力して、中間の単語を推論させるネットワークだ。

f:id:msyksphinz:20180801014149p:plain
図. CBOWのダイアグラム (上記論文より引用)。

CBOWを使い、各単語に対する推論を実行したが、まだサンプル数が少なすぎて良好な結果は得られていない。

f:id:msyksphinz:20180801014418p:plain
図. CBOWニューラルネットワークの学習の様子。

次章では、よりサンプル数を増やすことと、処理の高速化を行ってみる。

f:id:msyksphinz:20180801014558p:plain

「ゼロから作るディープラーニング②」を読む (2. 自然言語と単語の分散表現)

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。

あまり内容を詳細に記録するつもりはないので、読んで纏めていったものをGistに張り付けていく。

  • Jupyter Notebook Viewer: 2章 自然言語と単語の分散表現

Jupyter Notebook Viewer

第2章は、機械学習を使わない単語の分類を行う。文章を入力して、単語の隣接関係を見ながら単語の類似度を調査していく。

今回試行した手法はカウントベースと呼ばれる手法。コーパス(単語をベクトル化したテーブル)を用意し、その単語の周囲の単語の頻度をカウントする。

こうして作成した共起行列をPPMI行列に変換する。PPMI行列は、単語間の相互確率を使用した行列。

さらに、次元を削減するためにSVD(特異値分解)を用いて次元の圧縮を行う。

最後に、PTBデータセットを使って、より実践的なデータでPPMI行列と単語間の相関関係を算出することに挑戦した。

f:id:msyksphinz:20180731003151p:plain
図. "You say goodbye and I say hello."に対して、単語の分散表現を行い、類似度をグラフ化したもの。"goodbye", "hello"が近く、"i"と"you"が近いことが分かる。

RISC-V SpikeシミュレータでC/C++のprintfを実現する仕組み (8. RISC-Vのシステムコールを自作ISS上で実装)

UCBが開発しているRISC-VのシミュレータSpikeや、Rocket-ChipのRTLデザインは通常はシステムコールを持っていない。 つまり、当然ながらC言語printf("Hello World\n");などと書いても動作しないのだが、そこはコンパイラとフロントエンドサーバfesvr、pk(Proxy Kernel)によって肩代わりすることでこれらのシステムコールを実現している。

Proxy KernelというのはRISC-Vのシステムコールにつながる処理をフロントエンドサーバに受け渡すインタフェース、そしてフロントエンドサーバはPKからのシステムコールリクエストを受け取ってRISC-Vコアの代わりに実現する機能を持っている。

これらの機能をRISC-V自作シミュレータに実装して、システムコールを実現した。

f:id:msyksphinz:20180727001523p:plain

例えば、syscall::sys_readシステムコールは、fesvrでは以下のように実装されている。

  • riscv-fesvr/fesvr/syscall.cc
reg_t syscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
  std::vector<char> buf(len);
  ssize_t ret = read(fds.lookup(fd), &buf[0], len);
  reg_t ret_errno = sysret_errno(ret);
  if (ret > 0)
    memif->write(pbuf, ret, &buf[0]);
  return ret_errno;
}

やっていることは単純だ。fds.lookupというのはファイルディスクリプタのテーブルで、stdin, stdout, stderrの扱い方を格納したテーブルだ。そこからread()(これはx86で実装されたホストのread()関数)を呼び出し、ホストのシステム上でread()を実行する。

その結果を、RISC-Vシミュレータ上のメモリ空間に書き込む。それをmemif->write()で実現している。 実行していることは単純なのだ。

これを、自作RISC-Vシミュレータ上で実装しているAPIに置き換える。

reg_t RiscvSyscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
  Byte_t *buf = new Byte_t[len];
  ssize_t ret = read(fds.lookup(fd), &buf[0], len);
  reg_t ret_errno = sysret_errno(ret);
  if (ret > 0) {
    for (unsigned int idx = 0; idx < len; idx++) {
      m_pe_thread->StoreMemoryDebug (pbuf + idx, Size_Byte, &buf[idx]);
    }
  }
  return ret_errno;
}

ホストの実装は同じで、memifインタフェースへのアクセスをオリジナルのインタフェースに変更しただけだ。 バースト転送のAPIを持っていなかったので、1バイトずつ書き込んでいる。

これをすべてのAPIにおいて実装し(というか単純にコピーして関数を置き換えた)、RISC-V自作シミュレータに組み込んで実行した。

printf()fopen()などで構成された以下のプログラムをRISC-V向けのGCCコンパイルし、Proxy Kernelを組み込んでシミュレーションした。

#include <stdio.h>
#include <stdlib.h>

int main ()
{
  printf("Hello World\n");

  FILE *fp;
  if ((fp = fopen("sample_hello.txt", "w")) == NULL) {
    perror ("sample_hello.txt");
    exit(EXIT_FAILURE);
  }
  fprintf (fp, "Hello World\n");
  fclose (fp);

  if ((fp = fopen("sample_hello.txt", "r")) == NULL) {
    perror ("sample_hello.txt");
    exit(EXIT_FAILURE);
  }
  char cin[100];
  fscanf (fp, "%s", cin);
  printf ("%s\n", cin);

  return 0;
}
$ riscv64-unknown-elf-gcc file_access.c -o file_access_riscv
$ riscv_iss --bit-mode 64 --use-pk /home/msyksphinz/riscv64//riscv64-unknown-elf/bin/pk --binfile file_access_riscv --debug --out file_access_riscv.forest --trace-hier --trace-out file_access_riscv.trace --max 500000
...
<Loading section Code .htif 0x0000000000000123>
<Loading section ... .data 0x0000000000000123>
<Loading section ... .data 0x0000000000000123>
<Loading section Code .data 0x0000000000000123>
<Loading section ... .sdata 0x0000000000000123>
<Loading section ... .sdata 0x0000000000000123>
<Loading section Code .sdata 0x0000000000000123>
<Loading section ... .bss 0x0000000000000001>
<Loading section ... .bss 0x0000000000000001>
<Loading section Code .bss 0x0000000000000001>
<Info: Set Entry Point as     1000>
<Info: Set Debug Module>
<Info: Set Debug Module Done>
Hello World
Hello
$ cat sample_hello.txt
Hello World

やった!成功だ。Hello Worldが正しく実行され、またファイルにも正しく書き込まれている。すばらしい。

f:id:msyksphinz:20180730012618p:plain

図. RISC-V自作シミュレータを実行した様子。最後にHello Worldと、ファイルから読み取ったHelloが表示されている。

「ゼロから作るディープラーニング②」を購入 (1. ニューラルネットワークの復習)

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

「ゼロから作るディープラーニング②」を購入した。第1巻は一応完走した、つもり、、、なので第2巻に挑戦する。

あまり内容を詳細に記録するつもりはないので、Jupyter Notebookに書いたものをスクリーンキャプチャして貼り付けて記録とする。

第1章はニューラルネットワークの復習。スパイラルデータを使ってクラス分けを行う。

@7shi さんに教えていただき、Jupyter Notebookの実行結果はGistに乗せていくことにしました。 なるほど、これは便利。

Jupyter Notebook Viewer

f:id:msyksphinz:20180729172548p:plain

こんなグラフを書けるのが面白い。これはニューラルネットワークによりスパイラルデータをクラスタリングした結果。

f:id:msyksphinz:20180729173055p:plain

NVDLAv2のインストール試行

http://nvdla.org/_images/ig_system_interfaces1.png

いまさらながら、NVDLAv2のインストールを試行した。

開発環境はUbuntu 18.04LTSだ。使用したリビジョンは7cf6ad5a6e75f01aac1b0041288612e63a5999acを使用した。

github.com

commit 7cf6ad5a6e75f01aac1b0041288612e63a5999ac (HEAD -> master, origin/master)
Author: Dan Smith <dsmith@nvidia.com>
Date:   Thu Jul 26 14:05:20 2018 -0700

    Add RAM model

環境設定

NVDLAv1と比較して、いくつかPythonのインストールする必要があった。

$ sudo apt install libyaml-tiny-perl
$ sudo apt install -y libcapture-tiny-perl

ビルド作業

テストベンチはnv_smallしかサポートしていないので、nv_smallを使用する。 いろいろ環境設定をしなければならないのだが、systemcなど入れていないし、VCSなんて持っていないのでどうすればよいのか...

$ cd nvdla/hw/
$ make
Creating tree.make to setup your working environment and projects
Enter project names      (Press ENTER if use: nv_small nv_small_256 nv_small_256_full nv_medium_512 nv_medium_1024_full nv_large):nv_small
Using designware or not [1 for use/0 for not use] (Press ENTER if use: 1):0
Enter design ware path (Press ENTER if use: /home/tools/synopsys/syn_2011.09/dw/sim_ver):
Enter c pre-processor path (Press ENTER if use: /home/utils/gcc-4.8.2/bin/cpp):/usr/bin/cpp
Enter gcc path             (Press ENTER if use: /home/utils/gcc-4.8.2/bin/gcc):/usr/bin/gcc
Enter g++ path             (Press ENTER if use: /home/utils/gcc-4.8.2/bin/g++):/usr/bin/g++
Enter perl path            (Press ENTER if use: /home/utils/perl-5.10/5.10.0-threads-64/bin/perl):/usr/bin/perl
Enter java path            (Press ENTER if use: /home/utils/java/jdk1.8.0_131/bin/java):/usr/bin/java
Enter systemc path         (Press ENTER if use: /home/ip/shared/inf/SystemC/1.0/20151112/systemc-2.3.0/GCC472_64_DBG):
Enter python path          (Press ENTER if use: /home/tools/continuum/Anaconda3-5.0.1/bin/python):/usr/bin/python
Enter vcs_home path        (Press ENTER if use: /home/tools/vcs/mx-2016.06-SP2-4):
Enter novas_home path      (Press ENTER if use: /home/tools/debussy/verdi3_2016.06-SP2-9):
Enter verdi_home path      (Press ENTER if use: /home/tools/debussy/verdi3_2016.06-SP2-9):
OPTIONAL: Enter verilator path (Press ENTER to use: verilator):/usr/bin/verilator
OPTIONAL: Enter clang path     (Press ENTER to use: /home/utils/llvm-4.0.1/bin/clang):/home/msyksphinz/llvm/bin//clang
=====================================================================
tree.make is created successfully, and you can edit tree.make manually if necessary
=====================================================================

ビルドを実行する。

$ ./tools/bin/tmake -build vmod
[TMAKE]: building nv_small in spec/defs
[TMAKE]: building nv_small in spec/manual
[TMAKE]: building nv_small in spec/odif
[TMAKE]: building nv_small in vmod/vlibs
[TMAKE]: building nv_small in vmod/include
[TMAKE]: building nv_small in vmod/rams/model
[TMAKE]: building nv_small in vmod/rams/synth
[TMAKE]: building nv_small in vmod/rams/fpga/model
[TMAKE]: building nv_small in vmod/fifos
[TMAKE]: building nv_small in vmod/nvdla/apb2csb
[TMAKE]: building nv_small in vmod/nvdla/cdma
[TMAKE]: building nv_small in vmod/nvdla/cbuf
[TMAKE]: building nv_small in vmod/nvdla/csc
[TMAKE]: building nv_small in vmod/nvdla/cmac
[TMAKE]: building nv_small in vmod/nvdla/cacc
[TMAKE]: building nv_small in vmod/nvdla/sdp
[TMAKE]: building nv_small in vmod/nvdla/pdp
[TMAKE]: building nv_small in vmod/nvdla/cfgrom
[TMAKE]: building nv_small in vmod/nvdla/cdp
[TMAKE]: building nv_small in vmod/nvdla/bdma
[TMAKE]: building nv_small in vmod/nvdla/rubik
[TMAKE]: building nv_small in vmod/nvdla/car
[TMAKE]: building nv_small in vmod/nvdla/glb
[TMAKE]: building nv_small in vmod/nvdla/csb_master
[TMAKE]: building nv_small in vmod/nvdla/nocif
[TMAKE]: building nv_small in vmod/nvdla/retiming
[TMAKE]: building nv_small in vmod/nvdla/top
[TMAKE]: Done nv_small
[TMAKE]: nv_small: PASS

テストの準備を実行する。なぜかFailしてしまった。

$ ./tools/bin/tmake -build ready_for_test
[TMAKE]: building nv_small in spec/defs
[TMAKE]: building nv_small in spec/manual
[TMAKE]: building nv_small in verif/testbench/trace_generator
Traceback (most recent call last):
  File "../../../tools/bin/epython", line 162, in <module>
    process_file.exe_input()
  File "../../../tools/bin/epython", line 117, in exe_input
    self.codes_gen(tmp_file_hdl, scripts)
  File "../../../tools/bin/epython", line 146, in codes_gen
    codes = self.exe_python(scripts)
  File "../../../tools/bin/epython", line 154, in exe_python
    exec(scripts)
  File "<string>", line 4, in <module>
  File "../../.././verif/tools/spec2constrain.py", line 103, in enum_gen
    print('%-50s = \'h%0x' % (str0,keys[idx]))
TypeError: unicode argument expected, got 'str'
make: *** [../../../outdir/nv_small/verif/testbench/trace_generator/resources/nvdla_cdp_resource.sv] Error 1
[TMAKE]: DIE when building nv_small verif/testbench/trace_generator at ./tools/

上記の設定でPythonのPATHをPython3にするとうまく動作した。が、VCSなんて入ってないよう!

$ ./tools/bin/tmake -build ready_for_test
[TMAKE]: building nv_small in spec/defs
[TMAKE]: building nv_small in spec/manual
[TMAKE]: building nv_small in verif/testbench/trace_generator
make: /home/tools/vcs/mx-2016.06-SP2-4/bin/vcs: Command not found
make: *** [vcs_build] Error 127
[TMAKE]: DIE when building nv_small verif/testbench/trace_generator at ./tools/bin/tmake line 278.
[TMAKE]: nv_small: FAIL

Verilatorで実行できるかしら。

$ ./tools/bin/tmake -build verilator
...
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365: Cannot find file containing module: RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365: Looked in:
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/bdma/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/bdma/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/bdma/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cacc/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cacc/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cacc/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/car/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/car/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/car/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cbuf/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cbuf/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cbuf/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cdma/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cdma/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cdma/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cdp/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cdp/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cdp/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cmac/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cmac/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/cmac/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/csc/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/csc/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/csc/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/glb/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/glb/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/glb/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/nocif/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/nocif/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/nocif/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/pdp/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/pdp/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/pdp/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/retiming/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/retiming/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/retiming/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/rubik/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/rubik/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/rubik/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/sdp/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/sdp/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/sdp/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/top/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/top/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/top/RAMPDP_80X17_GL_M2_D2.sv
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/csb_master/RAMPDP_80X17_GL_M2_D2
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/csb_master/RAMPDP_80X17_GL_M2_D2.v
%Error: ../../outdir/nv_small/vmod/rams/synth/nv_ram_rwsthp_80x17_logic.v:365:       ../../outdir/nv_small/vmod/nvdla/csb_master/RAMPDP_80X17_GL_M2_D2.sv
%Error: Exiting due to too many errors encountered; --error-limit=50
%Error: Command Failed /usr/bin/verilator_bin --compiler clang --output-split 250000000 --cc --exe -f verilator_nv_small.f --Mdir ../../outdir/nv_small/verilator/ nvdla.cpp --compiler clang --output-split 250000000
make: *** [../../outdir/nv_small/verilator/VNV_nvdla.mk] Error 10
[TMAKE]: DIE when building nv_small verif/verilator at ./tools/bin/tmake line 278.
[TMAKE]: nv_small: FAIL

うーんだめだ。Synopsysのツールが入ってないと使えないっぽい?

RISC-V SpikeシミュレータでC/C++のprintfを実現する仕組み (7. RISC-Vのシステムコールとfesvr)

UCBが開発しているRISC-VのシミュレータSpikeや、Rocket-ChipのRTLデザインは通常はシステムコールを持っていない。 つまり、当然ながらC言語printf("Hello World\n");などと書いても動作しないのだが、そこはコンパイラとフロントエンドサーバfesvr、pk(Proxy Kernel)によって肩代わりすることでこれらのシステムコールを実現している。

Proxy KernelというのはRISC-Vのシステムコールにつながる処理をフロントエンドサーバに受け渡すインタフェース、そしてフロントエンドサーバはPKからのシステムコールリクエストを受け取ってRISC-Vコアの代わりに実現する機能を持っている。

f:id:msyksphinz:20180727001523p:plain

例えば、RISC-V Fesvrは以下のシステムコールをサポートしている。

syscall_t::syscall_t(htif_t* htif)
  : htif(htif), memif(&htif->memif()), table(2048)
{
  table[17] = &syscall_t::sys_getcwd;
  table[25] = &syscall_t::sys_fcntl;
  table[34] = &syscall_t::sys_mkdirat;
  table[35] = &syscall_t::sys_unlinkat;
...
  table[2011] = &syscall_t::sys_getmainvars;
}
ID Function
17 getcwd sys_exit ( reg_t code)
25 fcntl sys_fcntl ( reg_t fd, reg_t cmd, reg_t arg)
34 mkdirat sys_mkdirat ( reg_t dirfd, reg_t pname, reg_t len, reg_t mode)
35 unlinkat sys_unlinkat ( reg_t dirfd, reg_t pname, reg_t len, reg_t flags)
37 linkat sys_linkat (reg_t odirfd, reg_t poname, reg_t olen, reg_t ndirfd, reg_t pnname, reg_t nlen, reg_t flags)
38 renameat sys_renameat ( reg_t odirfd, reg_t popath, reg_t olen, reg_t ndirfd, reg_t pnpath, reg_t nlen)
46 ftruncate sys_ftruncate ( reg_t fd, reg_t len)
48 faccessat sys_faccessat ( reg_t dirfd, reg_t pname, reg_t len, reg_t mode)
49 chdir sys_chdir ( reg_t path)
56 openat sys_openat ( reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t mode)
57 close sys_close ( reg_t fd)
62 lseek sys_lseek ( reg_t fd, reg_t ptr, reg_t dir)
63 read sys_read ( reg_t fd, reg_t pbuf, reg_t len)
64 write sys_write ( reg_t fd, reg_t pbuf, reg_t len)
67 pread sys_pread ( reg_t fd, reg_t pbuf, reg_t len, reg_t off)
68 pwrite sys_pwrite ( reg_t fd, reg_t pbuf, reg_t len, reg_t off)
79 fstatat sys_fstatat ( reg_t dirfd, reg_t pname, reg_t len, reg_t pbuf, reg_t flags)
80 fstat sys_fstat ( reg_t fd, reg_t pbuf)
93 exit sys_exit ( reg_t code)
1039 lstat sys_lstat ( reg_t pname, reg_t len, reg_t pbuf)
2011 getmainvars sys_getmainvars ( reg_t pbuf, reg_t limit)

例えば、sys_openatはファイルのオープンを処理しているものと思われるが、実装は以下のようになっている。AT_SYSCALLはフロントエンド(シミュレータの場合はx86上で動くLinux)のopenatシステムコールを呼び出して肩代わりする。 ファイル名は、RISC-Vシミュレータ上のメモリの特定の場所に格納されているため、memif->read()で呼び出して実際のファイル名をシステムコールに渡している。

reg_t syscall_t::sys_openat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t mode, reg_t a5, reg_t a6)
{
  std::vector<char> name(len);
  memif->read(pname, len, &name[0]);
  int fd = sysret_errno(AT_SYSCALL(openat, dirfd, &name[0], flags, mode));
  if (fd < 0)
    return sysret_errno(-1);
  return fds.alloc(fd);
}

システムコールはどのようにして呼ばれるのか?

これは、プログラムのコンパイル結果を見ていったほうが早い。printf()などと記述すると、それはlibraryを経由してputc()の塊となり、最終的にシステムコールとなりProxy Kernelがホストにシステムコールとして伝える。

file_t* file_open(const char* fn, int flags, int mode)
{
  return file_openat(AT_FDCWD, fn, flags, mode);
}

file_t* file_openat(int dirfd, const char* fn, int flags, int mode)
{
  file_t* f = file_get_free();
  if (f == NULL)
    return ERR_PTR(-ENOMEM);

  size_t fn_size = strlen(fn)+1;
  long ret = frontend_syscall(SYS_openat, dirfd, va2pa(fn), fn_size, flags, mode, 0, 0);
  if (ret >= 0)
  {
    f->kfd = ret;
    return f;
  }
  else
  {
    file_decref(f);
    return ERR_PTR(ret);
  }
}
long frontend_syscall(long n, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
  static volatile uint64_t magic_mem[8];

  static spinlock_t lock = SPINLOCK_INIT;
  spinlock_lock(&lock);

  magic_mem[0] = n;
  magic_mem[1] = a0;
  magic_mem[2] = a1;
  magic_mem[3] = a2;
  magic_mem[4] = a3;
  magic_mem[5] = a4;
  magic_mem[6] = a5;
  magic_mem[7] = a6;

  htif_syscall((uintptr_t)magic_mem);

  long ret = magic_mem[0];

  spinlock_unlock(&lock);
  return ret;
}

最終的に、HTIF(Host Interface)によりホストにこのシステムコールは渡される。

  • riscv-pk/machine/htif.c
void htif_syscall(uintptr_t arg)
{
  do_tohost_fromhost(0, 0, arg);
}

static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
  spinlock_lock(&htif_lock);
    __set_tohost(dev, cmd, data);

    while (1) {
      uint64_t fh = fromhost;
      if (fh) {
        if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
          fromhost = 0;
          break;
        }
        __check_fromhost();
      }
    }
  spinlock_unlock(&htif_lock);
}

HTIF経由で渡されたコマンドはriscv-fesvrに渡され、コマンドに応じて処理される。

  • riscv-fesvr/fesvr/syscall.cc (ホストのx86のコードとしてコンパイルされる)
void syscall_t::handle_syscall(command_t cmd)
{
  if (cmd.payload() & 1) // test pass/fail
  {
    htif->exitcode = cmd.payload();
    if (htif->exit_code())
      std::cerr << "*** FAILED *** (tohost = " << htif->exit_code() << ")" << std::endl;
    return;
  }
  else // proxied system call
    dispatch(cmd.payload());

  // cmd.respond(1);
}