FPGA開発日記

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

RISC-V命令セットシミュレータの途中状態を保存する(セッション保存)機能の実装(メモリ内容の保存と回復)

f:id:msyksphinz:20160520003556j:plain

msyksphinz.hatenablog.com

前回で、CPUの汎用レジスタやシステムレジスタなどの部分において情報を保存する機能は実装した。 次に、メモリの情報について保存する機能について考えていく。

基本的には前回の記事でも書いたように、メモリのストアされた部分だけが情報として残っているので、それをダンプしていくだけでよい。 また、リストアするときはそのダンプした結果を読み込み、メモリに逐次書き込んでいくだけで良い。

github.com

自作ISSのメモリの構造について

本自作ISSでは、メモリはある固まったブロックを複数マップとして持つ構造になっており、 マップの先頭はベースアドレス、内容はメモリブロックそのもの、としている。 メモリブロックは0x1000バイトのメモリとなっており、そのに書き込みを行ったり、読み込みを行ったりする。 またストアしたい場所にメモリブロックが定義されていなければ、洗たに0x1000バイトのメモリブロックを定義し、書き込みを行う。 ロードしたい場所にメモリブロックが存在しなければ、それは不定領域からのロードということになる。

map
 - <Base=0x0000> : 00, 01, 00, 00, AC ...  // 0x1000バイト分
 - <Base=0xA000> : EF, FF, A5, 53, 91 ...
 - <Base=0x0200> : 00, 09, 8C, 3D, 11 ...
...

メモリのセーブ

github.com

メモリ内容をセーブするためには、以下のように、このmapの内容を全てファイルに書き出しておけば良い。

void Memory::DumpMemory (std::ofstream *ofs)
{
  std::map<Addr_t, std::unique_ptr<MemoryBlock>>::iterator it = m_memory_vec.begin ();
  while (it != m_memory_vec.end()) {
    (*ofs) << std::hex << (*it).first << '\n';
    for (Addr_t addr = 0; addr < ((*it).second)->GetBlockSize (); addr++) {
      (*ofs) << std::setw(2) << static_cast<int>(((*it).second)->ReadByte(addr)) << ' ';
      if ((addr % 16) == (16-1)) {
        (*ofs) << '\n';
      }
    }
    it++;
  }
}

メモリのリストア

メモリの内容をリストアするためには、上記の書き込んだファイルを呼び込み、ベースアドレスから算出したアドレスに対してデータをストアして行けば良い。

void Memory::RestoreMemory (std::ifstream *ifs)
{
  while (!ifs->eof()) {
    Addr_t base_memory_addr;
    (*ifs) >> std::hex >> base_memory_addr;
    for (Addr_t i = 0; i < 0x1000ULL; i++) {
      int mem_data;
      (*ifs) >> std::hex >> mem_data;

      uint8_t u_data = static_cast<uint8_t>(mem_data);
      StoreMemByte (base_memory_addr, &u_data);
      base_memory_addr++;
    }
  }

  return;
}

検証

Coremarkを動作させ、65535命令目で実行を止めてセーブした。

$ cat init.lua
riscv = make_core ("risc-v")
-- set_pcbreak (riscv, get_addr(riscv, "main"))
skip_hier (riscv, "ee_printf")
skip_hier (riscv, "cmp_complex")

run (riscv)

save_session (riscv, "session.log")

$ ./swimmer_riscv --binfile ../benchmarks/releases/coremark_v1.0_riscv_gcc49_O2/coremark.bin --script init.lua --debug --out coremark.init.log

次にsession.logに保存されたセッションを復元し、再びCoremarkの実行を再開した。

$ cat restore.lua
riscv = make_core ("risc-v")
-- set_pcbreak (riscv, get_addr(riscv, "main"))

restore_session (riscv, "session.log")

run (riscv)

$ ./swimmer_riscv --binfile ../benchmarks/releases/coremark_v1.0_riscv_gcc49_O2/coremark.bin --script restore.lua --debug --out coremark.restore.log --max 100000 --reg_abi soft

最後までCoremarkが完走したことを確認できた。実装は成功だ。