FPGA開発日記

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

FPGA開発日記 カテゴリ別インデックス

RISC-VにおけるRVWMOの仕様について読み直す

続きを読む

自作CPUのSpikeモデルシミュレータ環境の変更 (4. LiteX BIOSのブート)

自作CPUのSpikeシミュレータをかなり久しぶりにアップデートすると、いろんな関数が変わっていてかなり戸惑ってしまった。

LiteXとSpikeの設定を変更できるようにして、とりあえずLiteXモードで動作するようにした。 Spike側のBootROMを削除する必要がある。

diff --git a/riscv/sim.cc b/riscv/sim.cc
index f4919c91..5714549a 100644
--- a/riscv/sim.cc
+++ b/riscv/sim.cc
@@ -69,7 +69,7 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
   for (auto& x : mems)
     bus.add_device(x.first, x.second);
 
-  bus.add_device(DEBUG_START, &debug_module);
+  // bus.add_device(DEBUG_START, &debug_module);
 
   socketif = NULL;
 #ifdef HAVE_BOOST_ASIO
@@ -376,8 +376,8 @@ void sim_t::set_rom()
   const int align = 0x1000;
   rom.resize((rom.size() + align - 1) / align * align);
 
-  std::shared_ptr<rom_device_t> boot_rom(new rom_device_t(rom));
-  add_device(DEFAULT_RSTVEC, boot_rom);
+  // std::shared_ptr<rom_device_t> boot_rom(new rom_device_t(rom));
+  // add_device(DEFAULT_RSTVEC, boot_rom);
 }

とりあえずこれで動き始めた。今度はmain()に到達したところで落ちる。要解析...

MW4(0x000000000c200000)=>0000000000000000
GPR[00](0) <= 0000000000000000
GPR[10](86) <= 0000000000001000
GPR[10](83) <= 0000000000000880
33526 : L1D Evict       : c200000(00000) : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
33526 : EVict ISS Check : c200000        : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
8385 : RTL(43,1) Exception Cause = CSR Update Flush(27) PC=000000000168, Inst=30451073, csrw    mie, a0
GPR[01](52) <= 0000000000000170
8418 : RTL(12,1) Exception Cause = Illegal Instruction(2) PC=00000000174c, Inst=00000000, c.unimp
==========================================
8418 : Exception Happened(12,1) : Cause = Illegal Instruction(2)
==========================================
==========================================
Wrong PC: RTL = 0000000000000020, ISS = 000000000000174c
==========================================
===============================
SIMULATION FINISH : FAIL (CODE=100)
RUNNING TIME : 8442
===============================

自作CPUのSpikeモデルシミュレータ環境の変更 (3. Spikeの設定変更)

自作CPUのSpikeシミュレータをかなり久しぶりにアップデートすると、いろんな関数が変わっていてかなり戸惑ってしまった。

前回、モデルシミュレータが途中でハングしてしまっていたのだが、一度全部Cleanしてやり直すと多分ちゃんと動いている。

LiteXとSpikの設定を変更できるようにして、とりあえずLiteXモードで動作するようにした。

いきなりエラーとなってはいるが、一応正しいELFが読めているようなので、まずは第1段階クリアかな。

==========================================
Wrong INSN: RTL = 0b00006f, ISS = 00000297
            RTL = j       pc + 0xb0
            ISS = auipc   t0, 0x0
==========================================
===============================
SIMULATION FINISH : FAIL (CODE=100)
RUNNING TIME : 6191
===============================

正しいのはRTLっぽい。ISSの設定が何か間違えているな。

0000000000000000 <_ftext>:
       0:       0b00006f                j       b0 <crt_init>
       4:       00000013                nop
       8:       00000013                nop
       c:       00000013                nop
      10:       00000013                nop
      14:       00000013                nop
      18:       00000013                nop
      1c:       00000013                nop

0000000000000020 <trap_entry>:
      20:       fe113c23                sd      ra,-8(sp)
      24:       fe513823                sd      t0,-16(sp)
      28:       fe613423                sd      t1,-24(sp)
      2c:       fe713023                sd      t2,-32(sp)
      30:       fca13c23                sd      a0,-40(sp)

RISC-VのRVA/RVBプロファイルについての調査

RISC-Vのプロファイルについて、RVAに続いてRVBという謎のプロファイルが追加されていたので調査することにした。

RVAというのはRISC-Vのプロファイルのうちアプリケーション・プロセッサ向けのものである。 一方で、RVBというのもアプリケーション・プロセッサ向けのプロファイルであり、どの拡張を挿入すべきかで違いはあるのだが、その目的が良く分からない。

以下のブログも参考にしている:

fprox.substack.com

まず、RVA (RVA23U64) プロファイルで必要となる命令拡張についてリストアップしてみよう。

  • Mandatory : M / A / F / D / C / Zicsr / Zicntr / Zihpm / Ziccif / Ziccrse / Ziccamoa / Zicclsm / Za64rs / Zihintpause / Zba / Zbb / Zbs / Zic64b / Zicbom / Zicbop / Zicboz / Zfhmin / Zkt / V / Zvfmin / Zvbb / Zvkt / Zihintntl / Zicond / Zimop / Zcmop / Zcb / Zfa / Zawrs / Supm
  • Optional : Zvkng / Zvksg / Zabha / ZZacas / Ziccamoc / Zvbc / Zama16b / Zfh / Zbc / Zvfh / Zfbfmin / Zvfbfmin / Zvfbfwma

RVA23S64で必要となる拡張命令をリストアップしてみる。

  • Mandatory : Zifencei / Ss1p13 / Svbare / Sv39 / Svade / Ssccptr / Sstvecd / Sstvala / Sscouuterenw / Svpbmt / Svinval / Svnapot / Sstc / Sscofpmf / Ssnpm / Ssu64xl / H / Ssstateen / Shcounterenw / Shcounterenw / Shvstavala / Shtvala / Shvstvecd / Shvsatpa / Shgatpa
  • Optional : Sv48 / Sv57 / Svadu / Zkr / Sdext / Ssstrict / Svvptc / Sspm

もう訳が分からん。各命令拡張の詳細については、それぞれの仕様書を確認してほしい。

次に、RVB (RVB23U64) プロファイルについてリストアップしてみる。

  • Mandatory : M / A / F / D / C / Zicsr / Zicntr / Zihpm / Ziccif / Ziccrse / Ziccamoa / Zicclsm / Za64rs / Zihintpause / Zba / Zbb / Zbs / Zic64b / Zicbom / Zicbop / Zicboz / Zkt / Zihintntl / Zicond / Zimop / Zcmop / Zcb / Zfa / Zawrs
  • Optional :
    • RVA23U64と同様 : Zvbc / Zvkng / Zvksg
    • RVA23U64には登場しない : Zvkg / Zvknc / Zvksc / Zkn / Zks
    • RVB24U64ではMandatoryになる予定 : Zabha / ZZacas / Ziccamoc / Zama16b
    • RVA23U64ではMandatoryのもの : Zfhmin / V / Zvfmin / Zvbb / Zvkt / Supm
    • RVA23U64でもexpansion optionのもの : Zfh / Zbc / Zvfh / Zfbfmin / Zvfbfmin / Zvfbfwma
    • RVA23U64からRVA24U64でMandatoryになる予定だが、RVBではその予定ではないもの : Zvbc

RVB23S64で必要となる拡張命令をリストアップしてみる。

  • Mandatory : Zifencei / Ss1p13 / Svbare / Sv39 / Svade / Ssccptr / Sstvecd / Sstvala / Sscouuterenw / Svpbmt / Svinval / Svnapot / Sstc / Sscofpmf / Ssu64xl
  • Optional : Ssnpm / Sspm / H / Ssstateen / Shcounterenw / Shcounterenw / Shvstavala / Shtvala / Shvstvecd / Shvsatpa / Shgatpa
    • RVA23S64でも同様 : Sv48 / Sv57 / Svadu / Zkr / Sdext / Ssstrict / Svvptc

ここまでリストアップしてみて、RVBの目的というのは何なのかというのを考えてみたが、良く分からない。

上記のブログでは、

RVB purpose: giving more flexibility to companies that are ready to develop custom software for their RISC-V implementation

これはRVBの目的である、RISC-V実装用のカスタムソフトウェアを開発する準備ができている企業により柔軟性を与えるという目的

これがどのような意味を持つのかが不明瞭で、ではRVAとRVBをどのように使い分ければよいのかも記載されていない。 せっかくプロファイルで仕様をまとめたのに、また分断化しそうで不安な点もありそうだ。

追記:プロファイルの全体をマップしたGoogle Spreadsheetがあるそうだ。

docs.google.com

自作CPUのSpikeモデルシミュレータ環境の変更 (2. Spikeの設定変更)

自作CPUのSpikeシミュレータをかなり久しぶりにアップデートすると、いろんな関数が変わっていてかなり戸惑ってしまった。

前回、モデルシミュレータが途中でハングしてしまっていたのだが、一度全部Cleanしてやり直すと多分ちゃんと動いている。

ただ、それでも通常のベンチマーク動作用のモードとLiteX環境用のモードでいろいろ変更することが多い。 特に、SpikeはスタートアドレスがDefineで決め打ちされているのがきつい。

  • riscv-isa-sim/riscv/platform.h
#define DEFAULT_RSTVEC     0x00000000
#define CLINT_BASE         0x02000000
#define CLINT_SIZE         0x000c0000
#define PLIC_BASE          0x0c000000

このDEFAULT_RSTVECで最初のpcが決まってしまう。--pc=オプションで決定できるのBootROMを動かした後のとび先であり、BootROMも0x1000に固定してあるのでこれもメモリマップを移すのが面倒くさい。 これらも変更できるようにいろいろ改造するとなると、かなり面倒なのでやはり独自のシミュレーション環境を構築することになり、面倒だな...

例えば、--pc=0x0で実行すると、以下のようにBootROMのロード値がエラーとなる。

Set ToHost Addr 80001000
initial_spike opening ../../../tests/riscv-tests-elf/dhrystone.riscv ...
spike iss done
GPR[05](32) <= 0000000000001000
GPR[11](64) <= 0000000000001020
5175 : RTL(1,1) Exception Cause = CSR Update Flush(27) PC=000000001008, Inst=f1402573, csrr    a0, mhartid
GPR[10](33) <= 0000000000000000
MR8(0x0000000000001018)=>0000000000000000
==========================================
Wrong GPR[05](35): RTL = 0000000080000000, ISS = 0000000000000000
==========================================
===============================
SIMULATION FINISH : FAIL (CODE=100)
RUNNING TIME : 5207
===============================

自作CPUのSpikeモデルシミュレータアップデート

自作CPUのSpikeシミュレータをかなり久しぶりにアップデートすると、いろんな関数が変わっていてかなり戸惑ってしまった。

一応すべてのコンパイルエラーを直して動かしてみたが、どうも途中でエラーが発生する。

argv[7] = --log-commits
argv[8] = --dtb=../../../dts/rv64imafdc.dtb
argv[9] = --kernel=../../../tests/linux/Image
argv[10] = --initrd=../../../tests/linux/spike_rootfs.cpio
argv[11] = ../../../tests/litex_soc/software/bios/bios.elf
warning: tohost and fromhost symbols not in ELF; can't communicate with target

Program received signal SIGSEGV, Segmentation fault.
mstatus_csr_t::read (this=0x0) at ./riscv/csrs.h:258
258         return val;
(gdb) bt
#0  mstatus_csr_t::read (this=0x0) at ./riscv/csrs.h:258
#1  0x0000555559e97353 in step_spike ()
#2  0x0000555556ac881e in Vscariv_tb___024root___sequent__TOP__22(Vscariv_tb___024root*) ()
#3  0x00005555558d25a7 in Vscariv_tb___024root___eval(Vscariv_tb___024root*) ()
#4  0x00005555558cee4c in Vscariv_tb::eval_step() ()
#5  0x00005555558aec43 in main ()

LiteXのBIOSソフトウェアを独自に構築する方法調査 (2. SpikeのCLINTの動作確認)

  • CLINT : 0x02000000
  • PLIC : 0x0c000000

MIEを設定すると、タイマ割り込みがかかる。

core   0: 3 0x0000000000001760 (0x09313c23) mem 0x0000000010001fd8 0x0000000000000000
core   0: 0x0000000000001764 (0x0c0027b7) lui     a5, 0xc002
core   0: 3 0x0000000000001764 (0x0c0027b7) x15 0x000000000c002000
core   0: 0x0000000000001768 (0x0007a023) sw      zero, 0(a5)
core   0: 3 0x0000000000001768 (0x0007a023) mem 0x000000000c002000 0x00000000
core   0: 0x000000000000176c (0x30046073) csrsi   mstatus, 8
core   0: 3 0x000000000000176c (0x30046073) c768_mstatus 0x0000000a00000008
core   0: exception interrupt #7, epc 0x0000000000001770
core   0: >>>>  trap_entry
core   0: 0x0000000000000020 (0xfe113c23) sd      ra, -8(sp)
core   0: 3 0x0000000000000020 (0xfe113c23) mem 0x0000000010001f38 0x0000000000000170
core   0: 0x0000000000000024 (0xfe513823) sd      t0, -16(sp)
core   0: 3 0x0000000000000024 (0xfe513823) mem 0x0000000010001f30 0x0000000010000b48
core   0: 0x0000000000000028 (0xfe613423) sd      t1, -24(sp)
core   0: 3 0x0000000000000028 (0xfe613423) mem 0x0000000010001f28 0x0000000010000b48
core   0: 0x000000000000002c (0xfe713023) sd      t2, -32(sp)
core   0: 3 0x000000000000002c (0xfe713023) mem 0x0000000010001f20 0x0000000000005d70
core   0: 0x0000000000000030 (0xfca13c23) sd      a0, -40(sp)
core   0: 3 0x0000000000000030 (0xfca13c23) mem 0x0000000010001f18 0x0000000000000880
core   0: 0x0000000000000034 (0xfcb13823) sd      a1, -48(sp)
core   0: 3 0x0000000000000034 (0xfcb13823) mem 0x0000000010001f10 0x0000000020000020
core   0: 0x0000000000000038 (0xfcc13423) sd      a2, -56(sp)
core   0: 3 0x0000000000000038 (0xfcc13423) mem 0x0000000010001f08 0x0000000000000000
core   0: 0x000000000000003c (0xfcd13023) sd      a3, -64(sp)
core   0: 3 0x000000000000003c (0xfcd13023) mem 0x0000000010001f00 0x0000000000000000
core   0: 0x0000000000000040 (0xfae13c23) sd      a4, -72(sp)
core   0: 3 0x0000000000000040 (0xfae13c23) mem 0x0000000010001ef8 0x00000000000001fe
core   0: 0x0000000000000044 (0xfaf13823) sd      a5, -80(sp)
core   0: 3 0x0000000000000044 (0xfaf13823) mem 0x0000000010001ef0 0x000000000c002000

これは誰がタイマを挿入しているのか?PLICかな? riscv-isa-simの実装を確認してみる。

最初に誰かが挿入しているようだ:

mip_csr_t::backdoor_write_with_mask(origin=00000000, mask=00000080, val=00000080).
mip_csr_t::backdoor_write_with_mask. val = 00000080
warning: tohost and fromhost symbols not in ELF; can't communicate with target
core   0: 0x0000000020000000 (0x00000297) auipc   t0, 0x0
core   0: 3 0x0000000020000000 (0x00000297) x5  0x0000000020000000
core   0: 0x0000000020000004 (0x02028593) addi    a1, t0, 32
core   0: 3 0x0000000020000004 (0x02028593) x11 0x0000000020000020
  • csrs.cc
void mip_csr_t::backdoor_write_with_mask(const reg_t mask, const reg_t val) noexcept {
  fprintf(stderr, "mip_csr_t::backdoor_write_with_mask(origin=%08lx, mask=%08lx, val=%08lx).\n",
          this->val, mask, val);
  this->val = (this->val & ~mask) | (val & mask);
  fprintf(stderr, "mip_csr_t::backdoor_write_with_mask. val = %08lx\n", this->val);
}

うーん、CLINTが最初にコンストラクタで書きに行っている。

Breakpoint 1, mip_csr_t::backdoor_write_with_mask (this=this@entry=0x555555e4f3a0, mask=mask@entry=128, val=128) at ./riscv/csrs.cc:746
746     void mip_csr_t::backdoor_write_with_mask(const reg_t mask, const reg_t val) noexcept {
(gdb) bt
#0  mip_csr_t::backdoor_write_with_mask (this=this@entry=0x555555e4f3a0, mask=mask@entry=128, val=128) at ./riscv/csrs.cc:746
#1  0x00005555558be236 in clint_t::tick (this=this@entry=0x555555e54c00, rtc_ticks=rtc_ticks@entry=0) at ./riscv/clint.cc:115
#2  0x00005555558be354 in clint_t::clint_t (this=0x555555e54c00, sim=<optimized out>, freq_hz=<optimized out>, real_time=<optimized out>) at ./riscv/clint.cc:18
#3  0x00005555558be3f0 in clint_parse_from_fdt (fdt=<optimized out>, sim=0x7fffffffc380, base=0x7fffffffb870, sargs=...) at ./riscv/clint.cc:124
#4  0x00005555558a4fcd in sim_t::sim_t (this=0x7fffffffc380, cfg=<optimized out>, halted=<optimized out>, mems=..., plugin_device_factories=..., args=..., dm_config=..., log_path=0x0, dtb_enabled=true, dtb_file=0x7fffffffd475 "../dts/rv64imafdc.dtb", socket_enabled=false, cmd_file=0x0) at ./riscv/sim.cc:165
#5  0x000055555583daef in main (argc=<optimized out>, argv=<optimized out>) at ./spike_main/spike.cc:528
    clint@2000000 {
      compatible = "riscv,clint0";
      interrupts-extended = <&CPU0_intc 3 &CPU0_intc 7 >;
      reg = <0x0 0x2000000 0x0 0xc0000>;
    };

なるほど、mtimemtimecmpが両方ともゼロで、書き込みが割り込みの挿入が行われているのか。

  • clint.cc
void clint_t::tick(reg_t rtc_ticks)
{
  if (real_time) {
   struct timeval now;
   uint64_t diff_usecs;

   gettimeofday(&now, NULL);
   diff_usecs = ((now.tv_sec - real_time_ref_secs) * 1000000) + (now.tv_usec - real_time_ref_usecs);
   mtime = diff_usecs * freq_hz / 1000000;
  } else {
    mtime += rtc_ticks;
  }

  for (const auto& [hart_id, hart] : sim->get_harts()) {
    hart->state.time->sync(mtime);
    hart->state.mip->backdoor_write_with_mask(MIP_MTIP, mtime >= mtimecmp[hart_id] ? MIP_MTIP : 0);
  }
}

とすると、CLINTのデバイスを除去すると割り込みが掛からなくなるのか?

    /*
    clint@2000000 {
      compatible = "riscv,clint0";
      interrupts-extended = <&CPU0_intc 3 &CPU0_intc 7 >;
      reg = <0x0 0x2000000 0x0 0xc0000>;
    };
    */

こうすると、一応割り込みはかからなくなった。次は、LiteXのベンチマークと自作CPUの協調動作シミュレーションで、何が違うのかを確認していこうと思う。