FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

ブラウザ上でRISC-Vプロセッサの動作を学習できるGUIパイプラインシミュレータWebRISC-V

RISC-Vメーリングリストでアナウンスがあった。ウェブブラウザ上で動作するRISC-VパイプラインシミュレータWebRISC-Vが面白い。

論文も公開されている。正直何が新規要素なのかはよく分からないが、しっかりまとまっていることはいいことなので時間を取って読んでみたい。

https://dl.acm.org/doi/10.1145/3338698.3338894

リンクをクリックすると、RISC-Vのパイプライン画面が登場する。ここにプログラムをロードして動作を確認することができる仕組みだ。これがブラウザ上で動作するというのはとても面白い。

x.dii.unisi.it

f:id:msyksphinz:20200515010349p:plain

さっそく使ってみる。" Load Program"からサンプルプログラム"Factorial"を選択して"Load into Memory"をクリックする。すると、バイナリエンコーディングが左側に表示された。

f:id:msyksphinz:20200515010648p:plain

早速、"Step Forward"で命令を実行して行くが、ぬぬ?どうもパイプラインの動きか良く分からんなあ。 もう少しアニメーションで表示してくれると分かりやすいのだが、せっかく面白いアイデアなのに少しもったいない。

そういえば高校1年生の授業で、計算機アーキテクチャ実習でCASL II/COMET IIのGUIシミュレータを使って授業をしていたことを思い出した。

それがとても面白くて、冬休みに自分でVisual Basicを使ってGUIシミュレータの模倣品を作ったことを思い出した。今思い出すとそれがコンピュータアーキテクチャに興味を持った最初だった気がするので、こういう分かりやすいシミュレータが学生のモチベーションになってくれると嬉しい。

VerilatorでDPIを使ってC++側からVerilogのFunctionやTaskを呼ぶための方法調査

これまでVerilog側からDPIを経由してC++の関数を呼び出したりする方法について調査した。 しかしよく考えたらDPIを経由してC++側の実装からVerilogのFunctionやTaskを呼び出す方法についてはあまり調べたことが無かったのでやってみることにする。

前回同様counter_4bitのデザインを使うが、今回はC++側から適当なVerilogのFunctionを呼ぶのであまりモジュール自体は関係ない。

以下のようにしてcounter_4bit.vの中にdpi_verilog_taskを定義した。 単純に$display()で文字を出力するためのタスクだ。

  • counter_4bit.v
logic [3:0] cnt
   );

int                   return_value;

task dpi_verilog_task(input int in);
  $display("dpi_verilog_task is called. in = %d", in);

endtask // dpi_verilog_task

// Function from Verilog Task
export "DPI-C" task dpi_verilog_task;

always_ff @(posedge clk, negedge reset_n) begin
  if (!reset_n) begin
    cnt <= 4'h0;
  end else begin
    if (en) begin
      cnt <= cnt + 4'h1;
    end
  end
end

endmodule // counter_4bit

次はこれを呼び出すためのC++コードを実装する。tb_counter_4bit.cppに以下のようにコードを追加した。

  • tb_counter_4bit.cpp
#include <iostream>
#include <verilated.h>
#include <verilated_fst_c.h>
#include "svdpi.h"
#include "Vcounter_4bit.h"
#include "Vcounter_4bit__Dpi.h"
...
int main(int argc, char** argv) {

...
  // Instantiate DUT
  Vcounter_4bit *dut = new Vcounter_4bit();
...
  svSetScope(svGetScopeFromName("counter_4bit"));
...
  int cycle = 0;
  while (time_counter < 500 && !Verilated::gotFinish()) {
    if ((time_counter % 5) == 0) {
      dut->clk = !dut->clk; // Toggle clock
...
    if (cycle % 5 == 0) {
      dut->en = 1;   // Assert En
      dut->dpi_verilog_task(cycle);
      printf("dpi_verilog_task is called. verilog_task_value");
...

これでVerilog側のタスクを呼び出せるはずだ。コンパイルして実行してみよう。

verilator --cc --exe  --trace-fst --trace-params --trace-structs --trace-underscore -CFLAGS "-g" counter_4bit.v -exe tb_counter_4bit.cpp dpi_counter.cpp
./obj_dir/Vcounter_4bit

すると以下のエラーメッセージが表示されて実行が停止してしまった。

%Error: unknown:0: Testbench C called 'dpi_verilog_task' but scope wasn't set, perhaps due to dpi import call without 'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.
Aborting...

ネット上でいろいろ情報を調査したがイマイチ解決方法が分からない。 もう少し調査すべきか。。。あるいはソースコードを読むべきか。

GDBを挿入して調べた結果、

./obj_dir/Vcounter_4bit
run
bt
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007fffff1f2535 in __GI_abort () at abort.c:79
#2  0x0000000008005e66 in vl_fatal(char const*, int, char const*, char const*) ()
#3  0x0000000008005f76 in VL_FATAL_MT(char const*, int, char const*, char const*) ()
#4  0x0000000008010df0 in VerilatedScope::exportFindNullError(int) ()
#5  0x000000000803e47c in VerilatedScope::exportFind (scopep=0x0, funcnum=0) at /usr/local/share/verilator/include/verilated.h:334
#6  0x000000000803e136 in Vcounter_4bit::dpi_verilog_task (in=5) at Vcounter_4bit.cpp:110
#7  0x0000000008004dd7 in main (argc=1, argv=0x7ffffffed778) at ../tb_counter_4bit.cpp:53

ウームやはりdpi_verilog_taskの呼び出し部分で落ちていることは分かる。

  • Vcounter_4bit.cpp
void Vcounter_4bit::dpi_verilog_task(int in) {
    VL_DEBUG_IF(VL_DBG_MSGF("+    Vcounter_4bit::dpi_verilog_task\n"); );
    // Variables
    IData/*31:0*/ in__Vcvt;
    // Body
    static int __Vfuncnum = -1;
    if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum("dpi_verilog_task"); }
    const VerilatedScope* __Vscopep = Verilated::dpiScope();
    Vcounter_4bit__Vcb_dpi_verilog_task_t __Vcb = (Vcounter_4bit__Vcb_dpi_verilog_task_t)(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));
    in__Vcvt = in;
    (*__Vcb)((Vcounter_4bit__Syms*)(__Vscopep->symsp()), in__Vcvt);
}

さらにverilated.hの中身をのぞいてみる。

  • /usr/local/share/verilator/include/verilated.h
    static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
        if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
        if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
            // m_callbacksp must be declared, as Max'es are > 0
            return scopep->m_callbacksp[funcnum];
        } else {  // LCOV_EXCL_LINE
            return scopep->exportFindError(funcnum);  // LCOV_EXCL_LINE
        }
    }

呼ばれ方からして、scopepがNULLになっているのが原因で落ちているようだ。 戻ってみると、__VscopepVerilated::dpiScope()から呼び出されている。 これが設定されていないのが問題なのか?

    if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum("dpi_verilog_task"); }
    const VerilatedScope* __Vscopep = Verilated::dpiScope();
    Vcounter_4bit__Vcb_dpi_verilog_task_t __Vcb = (Vcounter_4bit__Vcb_dpi_verilog_task_t)(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));

うーん、これはdpiContext()で設定するのだろうか。

    static void dpiContext(const VerilatedScope* scopep, const char* filenamep, int lineno) VL_MT_SAFE {
        t_s.t_dpiScopep = scopep; t_s.t_dpiFilename = filenamep; t_s.t_dpiLineno = lineno; }

昇降デスクは値段が高いので机上タイプのスタンディングデスクを導入した

リモートワークとは直接関係ないのだけれども、仕事をしていたり、ブログを書いたりするときにずっと机の前に座っているとお尻が痛くなる。

イケイケのベンチャー企業では、昇降デスクを導入するのが流行っているらしい。これはすごく魅力的で、電動の昇降デスクはすごく便利そうだし、デザインもおしゃれなものが多いと思う。ただし問題は値段だ。

安いものでも3万円から、高いものだと10万円以上もする。毎日ブログを書いたり趣味のために長時間使用するならまだしも、普段会社に出社して昼間は使用しないし、夜だけしか使用しないのであれば投資額としては少しもったいない。 しかもデスクなのでリモートワークが解除されたときにデスクごと会社に持って行くわけにもいかず結局家に置きっぱなしになってしまう。これは問題だ。

そこで、既存の机を使用したまま昇降デスクと同じ機能を活用できる机上タイプのスタンディングデスクを導入することにした。

机上タイプのメリットは、まず安い。安いものだと1万円から、高くても3万円出せばおつりがくる。

そして既存の机を再利用できる。今ある机を捨てることなく、昇降式デスクと同じ役割を持たせることができる。

これはつまり、やろうと思えばリモートワークが解除されたときに、このスタンディングデスクだけ会社に送り付ければ、オフィスの机でも再利用することができるという訳だ。これはメリットしかない。さっそく導入しよう。

(注:現在当モデルはかなり高額になってしまっている。別モデルならば1万円とちょっとで購入できるようだ。)

私が購入したのは上記の製品。実は1か月以上使用しているのだが、特に問題もなく快適に使えている。

ガスによる昇降システムになっており、ハンドルを使って机全体を押し上げるとあまり力を入れずに持ち上げることができる。

通常モードは以下。ずっと立ちっぱなしはつらいので、作業中の大半はこの状態になっている。引きで写すのが嫌なのでなるべく近づいたらなんだか良く分からなくなってしまった。

f:id:msyksphinz:20200513002847p:plain

スタンディングモードは以下。写真を撮るときについでにディスプレイ台にあるスマホスタンドにiPadを置いてみた。普段はiPadなんて邪魔なものは置かずに、スマホを置いている。

f:id:msyksphinz:20200513002814p:plain

もう1か月以上使っているが、かなり快適だ。最初はスタンディングした状態でキーボードをたたくと意外とグラグラするかな?と思ったがこれはもともと私が使っている机の問題な気がする。まあ土台の机も3000円程度で買ったものなので、あまり文句は言えない。現在は特に不満に思った事は無い。

一つ注意点としては、机を降ろすときに下に何かを置いて挟んでしまわないように注意すること。私は一回だけ鉛筆を挟んでしまい折ってしまった。

という訳で、自動・手動昇降デスクがコスト的に厳しいと思っている方は、既存の机を活用してこのような選択肢もあるよ、ということで導入を検討してはいかがだろうか。

GCC 10.1がリリースされたのでビルドしてみる

GCC 10.1がリリースされた。GCCのバージョン自体はあまり真剣に追いかけていないので詳細にチェックしていないのだが、まずは自分の環境にインストールして動作を確認しておきたい。

  • x86向けにビルドを試す

いつも通りtar.gzをダウンロードしてインストールした。

curl -L http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-10.1.0/gcc-10.1.0.tar.gz | tar xz
cd gcc-10.1.0
./contrib/download_prerequisites
mkdir build
cd build
../configure --prefix=/home/msyksphinz/compiler/gcc-10.1.0
make -j$(nproc)
make install

これで問題なくビルドが完了した。/home/msyksphinz/compiler/gcc-10.1.0に生成されたバイナリを実行して確認しておく。

/home/msyksphinz/compiler/gcc-10.1.0/bin/gcc --version
gcc (GCC) 10.1.0
Copyright (C) 2020 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.

問題無いようだ。

  • RISC-V向けのビルドを試す

RISC-V向けにビルドできるだろうか?一応確認しておこう。

cd gcc-10.1.0
./contrib/download_prerequisites
mkdir build-riscv64
cd build-riscv64
../configure --target=riscv64-unknown-elf --prefix=/home/msyksphinz/compiler/riscv64-gcc-10.1.0 --enable-multilib --enable-languages=c --without-headers
make -j$(nproc)
make install

しばらく待っていたが、以下で落ちてしまった。

libtool: compile:  /home/msyksphinz/work/compiler/gcc-10.1.0/build-riscv64/./gcc/xgcc -B/home/msyksphinz/work/compiler/gcc-10.1.0/build-riscv64/./gcc/ -B/home/msyksphinz/compiler/riscv64-gcc-10.1.0/riscv6
4-unknown-elf/bin/ -B/home/msyksphinz/compiler/riscv64-gcc-10.1.0/riscv64-unknown-elf/lib/ -isystem /home/msyksphinz/compiler/riscv64-gcc-10.1.0/riscv64-unknown-elf/include -isystem /home/msyksphinz/compi
ler/riscv64-gcc-10.1.0/riscv64-unknown-elf/sys-include -DHAVE_CONFIG_H -I. -I../../../libssp -Wall -g -O2 -MT ssp.lo -MD -MP -MF .deps/ssp.Tpo -c ../../../libssp/ssp.c -o ssp.o
../../../libssp/memcpy-chk.c:45:15: error: unknown type name 'size_t'
   45 |               size_t len, size_t slen)
      |               ^~~~~~
../../../libssp/memcpy-chk.c:37:1: note: 'size_t' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?
   36 | #include <ssp/ssp.h>
  +++ |+#include <stddef.h>
   37 | #ifdef HAVE_STRING_H
../../../libssp/memcpy-chk.c:45:27: error: unknown type name 'size_t'
   45 |               size_t len, size_t slen)
      |                           ^~~~~~
../../../libssp/memcpy-chk.c:45:27: note: 'size_t' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?
../../../libssp/gets-chk.c:62:22: error: unknown type name 'size_t'
   62 | __gets_chk (char *s, size_t slen)
      |                      ^~~~~~
../../../libssp/gets-chk.c:38:1: note: 'size_t' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?
   37 | #include <stdarg.h>
  +++ |+#include <stddef.h>
   38 | #ifdef HAVE_STDLIB_H
../../../libssp/ssp.c: In function '__guard_setup':
../../../libssp/ssp.c:93:12: warning: implicit declaration of function 'open' [-Wimplicit-function-declaration]
   93 |   int fd = open ("/dev/urandom", O_RDONLY);
      |            ^~~~
../../../libssp/ssp.c:93:34: error: 'O_RDONLY' undeclared (first use in this function)
   93 |   int fd = open ("/dev/urandom", O_RDONLY);
      |                                  ^~~~~~~~
../../../libssp/ssp.c:93:34: note: each undeclared identifier is reported only once for each function it appears in

うーん何かやり方が違うのかしら。Binutils-2.34をいったんビルドしてから再試行したが変化なし。 GCCのリリースノートにもRISC-Vの言及があるので動くと思うのだが...

gcc.gnu.org

もう少し試してみよう。

RISC-V Getting Started GuideのLinuxビルドを試す(3. Spike / 自作RISC-V ISSで試す)

f:id:msyksphinz:20200503214338p:plain

前回の続き。前回はQEMUで動作させることができるようになったので、Spikeと自作RISC-V ISSで動作を確認していく。 あらかじめ断っておくがSpikeは今回のビルド環境に対するサポートはしていないので動作しないことは予想通りなのだが、一応確認していこう。

これまでの経験上Spikeで動かすためには以下のようにする。

spike riscv-pk/build/bbl
bbl loader
              vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                  vvvvvvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrr       vvvvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrr      vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrr      vvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrr       vvvvvvvvvvvvvvvvvvvvvv
rr                vvvvvvvvvvvvvvvvvvvvvv
rr            vvvvvvvvvvvvvvvvvvvvvvvv      rr
rrrr      vvvvvvvvvvvvvvvvvvvvvvvvvv      rrrr
rrrrrr      vvvvvvvvvvvvvvvvvvvvvv      rrrrrr
rrrrrrrr      vvvvvvvvvvvvvvvvvv      rrrrrrrr
rrrrrrrrrr      vvvvvvvvvvvvvv      rrrrrrrrrr
rrrrrrrrrrrr      vvvvvvvvvv      rrrrrrrrrrrr
rrrrrrrrrrrrrr      vvvvvv      rrrrrrrrrrrrrr
rrrrrrrrrrrrrrrr      vv      rrrrrrrrrrrrrrrr
rrrrrrrrrrrrrrrrrr          rrrrrrrrrrrrrrrrrr
rrrrrrrrrrrrrrrrrrrr      rrrrrrrrrrrrrrrrrrrr
rrrrrrrrrrrrrrrrrrrrrr  rrrrrrrrrrrrrrrrrrrrrr

       INSTRUCTION SETS WANT TO BE FREE
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 4.19.0-rc3 (msyksphinz@msyksphinz-ubuntu) (gcc version 8.3.0 (crosstool-NG 1.24.0)) #1 SMP Sun May 3 23:41:29 JST 2020
[    0.000000] bootconsole [early0] enabled
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (512 bytes)
[    0.000000] Zone ranges:
[    0.000000]   DMA32    empty
[    0.000000]   Normal   [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] software IO TLB: mapped [mem 0xfa3fe000-0xfe3fe000] (64MB)
[    0.000000] elf_hwcap is 0x112d
[    0.000000] percpu: Embedded 16 pages/cpu @(____ptrval____) s25496 r8192 d31848 u65536
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 516615
[    0.000000] Kernel command line:
[    0.000000] Dentry cache hash table entries: 262144 (order: 9, 2097152 bytes)
[    0.000000] Inode-cache hash table entries: 131072 (order: 8, 1048576 bytes)
[    0.000000] Sorting __ex_table...
[    0.000000] Memory: 1992024K/2095104K available (3226K kernel code, 214K rwdata, 935K rodata, 152K init, 762K bss, 103080K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[    0.000000] Console: colour dummy device 80x25
[    0.000000] console [tty0] enabled
[    0.000000] bootconsole [early0] disabled

ここで停止する。これは予想通り。次に自作RISC-V命令セットシミュレータで確認する。とりあえずこれで行けるはず。

$ swimmer_riscv --binfile riscv-pk/build/bbl
bbl loader
              vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                  vvvvvvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrr       vvvvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrr      vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrrrrr      vvvvvvvvvvvvvvvvvvvvvv
rrrrrrrrrrrrr       vvvvvvvvvvvvvvvvvvvvvv
rr                vvvvvvvvvvvvvvvvvvvvvv
rr            vvvvvvvvvvvvvvvvvvvvvvvv      rr
rrrr      vvvvvvvvvvvvvvvvvvvvvvvvvv      rrrr
rrrrrr      vvvvvvvvvvvvvvvvvvvvvv      rrrrrr
rrrrrrrr      vvvvvvvvvvvvvvvvvv      rrrrrrrr
rrrrrrrrrr      vvvvvvvvvvvvvv      rrrrrrrrrr
rrrrrrrrrrrr      vvvvvvvvvv      rrrrrrrrrrrr
rrrrrrrrrrrrrr      vvvvvv      rrrrrrrrrrrrrr
rrrrrrrrrrrrrrrr      vv      rrrrrrrrrrrrrrrr
rrrrrrrrrrrrrrrrrr          rrrrrrrrrrrrrrrrrr
rrrrrrrrrrrrrrrrrrrr      rrrrrrrrrrrrrrrrrrrr
rrrrrrrrrrrrrrrrrrrrrr  rrrrrrrrrrrrrrrrrrrrrr

       INSTRUCTION SETS WANT TO BE FREE
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 4.19.0-rc3 (msyksphinz@msyksphinz-ubuntu) (gcc version 8.3.0 (crosstool-NG 1.24.0)) #1 SMP Sun May 3 23:41:29 JST 2020
[    0.000000] bootconsole [early0] enabled
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (512 bytes)
[    0.000000] Zone ranges:
[    0.000000]   DMA32    empty
[    0.000000]   Normal   [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] software IO TLB: mapped [mem 0xfa3fe000-0xfe3fe000] (64MB)
[    0.000000] elf_hwcap is 0x112d
[    0.000000] percpu: Embedded 16 pages/cpu @(____ptrval____) s25496 r8192 d31848 u65536
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 516615
[    0.000000] Kernel command line:
[    0.000000] Dentry cache hash table entries: 262144 (order: 9, 2097152 bytes)
[    0.000000] Inode-cache hash table entries: 131072 (order: 8, 1048576 bytes)
[    0.000000] Sorting __ex_table...
[    0.000000] Memory: 1992024K/2095104K available (3226K kernel code, 214K rwdata, 935K rodata, 152K init, 762K bss, 103080K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[    0.000000] Console: colour dummy device 80x25
[    0.000000] console [tty0] enabled
[    0.000000] bootconsole [early0] disabled

同じようにここで止まった。QEMUを見ながら、修正していこうかな。これまでの経験上freedom-u-sdkではSpikeで動かすときはconfigCONFIG_HVC_RISCV_SBI=yを有効にしなければならなかった。

  • .config
# CONFIG_SERIAL_FSL_LPUART is not set
# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set
# CONFIG_SERIAL_DEV_BUS is not set
# CONFIG_TTY_PRINTK is not set
CONFIG_HVC_DRIVER=y
CONFIG_HVC_RISCV_SBI=y
# CONFIG_VIRTIO_CONSOLE is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_R3964 is not set

あれ、おかしいな有効化されている。。。トレースを取って確かめてみようか。。。

RISC-V Getting Started GuideのLinuxビルドを試す(2. VirtualBox Ubuntu 16.04 LTSで試す)

f:id:msyksphinz:20200503214338p:plain

RISC-V Getting Started Guideというページがある。目的ベースで、QEMULinuxなどのオペレーティングシステムを動かすための方法や、様々なツールキットを動かすための情報がまとめられている。

前回WSLでビルドを試行して上手く行かなかったので、次はVirtualBox上でUbuntuを立ち上げてそちらでビルドしてみよう。使用したのは16.04.6 LTS (Xenial Xerus)だ。

色々試行した結果、どうもやはりctng-linuxでは上手く行かなようで、riscv-gnu-toolchainをダウンロードし直してビルドした。 その際、さらにriscv-gnu-toolchainのリビジョンもタグを打って調整しないと動作しなかった。tag: v20180629 でビルドした。

$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
$ git clone https://github.com/qemu/qemu
$ git clone https://github.com/torvalds/linux
$ git clone https://github.com/riscv/riscv-pk
$ git clone https://github.com/michaeljclark/busybear-linux --recurse-submodules

これでフロー通りにビルドを行うと正しく構成できたようだ。

$ qemu-system-riscv64 -nographic -machine virt -kernel riscv-pk/build/bbl -append "root=/dev/vda ro console=ttyS0" -drive file=busybear-linux/busybear.bin,format=raw,id=hd0 -device virtio-blk-device,drive=hd0

f:id:msyksphinz:20200504212907g:plain

Verilatorの使い方(4. DPI-Cを使ったC/C++との連携)

Verilog-HDLを使った検証では、C/C++のモデルとVerilogのモデルを接続した協調検証が頻繁に行われる。Verilatorでも同様の機能が搭載されており、DPI-Cを使ったC/C++Verilogとの通信が実現可能だ。

f:id:msyksphinz:20200504211549p:plain

ここでは前回作成したcounter_4bit.svに対してDPI-Cを経由してCコードを接続してみる。例えば、カウンタがカウントアップするとC言語の関数を呼び出す。

  1. まず、接続するC言語の関数をimportで宣言する。ここではdpi_c_func()を宣言する。
// Function from C-code
import "DPI-C" context task dpi_c_func(input int in, output int out);

dpi_c_funcは関数名、input intC言語実装側にとってのinputで、output intC言語実装側にとってのoutput(つまりポインタ)となる。

int                   return_value;

always_ff @(posedge clk, negedge reset_n) begin
  if (!reset_n) begin
    cnt <= 4'h0;
  end else begin
    if (en) begin
      cnt <= cnt + 4'h1;
      /* verilator lint_off WIDTH */
      dpi_c_func (cnt, return_value);
      $display("return_value = %d", return_value);
    end
  end
end
endmodule // counter_4bit

上記のように、Verilogの関数のように呼び出すことができる。

  1. C++の実装側。ここではdpi_counter_4bit.cppを用意する。
#include <verilated.h>
#include "Vcounter_4bit.h"
#include "stdio.h"

extern "C" {
  int dpi_c_func (int i, int* o);
}

int dpi_c_func (int i, int* o)
{
  printf("Hello from dpi_c_func(%d)\n", i);

  *o = 100 + i;

  return(0);
}

実装についても特に言及すべきところは無いと思う。extern "C"によってC++のデマングルを禁止している。入力側を値渡しの引数として、出力側はポインタ渡しで記述する。

ビルド時にdpi_counter.cppを追加すればよい。

$ verilator --cc --exe  --trace-fst --trace-params --trace-structs --trace-underscore counter_4bit.v -exe tb_counter_4bit.cpp dpi_counter.cpp
$ make -C obj_dir -f Vcounter_4bit.mk

実行してみよう。

$ ./obj_dir/Vcounter_4bit
Hello from c_task(0)
return_value =         100
Hello from c_task(1)
return_value =         101
Hello from c_task(2)
return_value =         102
Hello from c_task(3)
return_value =         103
Hello from c_task(4)
return_value =         104
Hello from c_task(5)
return_value =         105
Hello from c_task(6)
return_value =         106
Hello from c_task(7)
return_value =         107
Final Counter Value = 8

正しく動作していることが確認できた。