FPGA開発日記

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

サイクルベースシミュレータSniperについて調査する (SniperのRISC-V対応を最新のSpikeに対応させるための確認)

サイクル精度シミュレータSniperRISC-Vバイナリの実行に対して基本的な対応が行われているが、Spikeのバージョンが古くてベクトル命令が実行できなかったりと問題がある。

そこで、Sniperに付属しているspikeシミュレータをアップデートして、どのような変更が必要野なのかを確認した。

基本的な考え方として、Spikeの命令実行時にSIFTと呼ばれるSniper向けのトレースファイルを出力するようにしておけばよい。メモリアクセスが発生したときにそれを記録するためのコードを追加しておく。

例えば、ロード命令に対してLOG_ADDR()と呼ばれるマクロを追加して、これがSniperのSIFTトレーサのためのログが追加されることになる。

#ifdef RISCV_ENABLE_SIFT
  # define LOG_ADDR(addr) ({ \
      if (proc && proc->get_state()) { \
        proc->get_state()->log_addr = addr; \
        proc->get_state()->log_addr_valid = true; \
      } \
    })
  #else
  # define LOG_ADDR(addr) do {} while (false)
  #endif

  // template for functions that load an aligned value from memory
  #define load_func(type, prefix, xlate_flags) \
    type##_t ALWAYS_INLINE prefix##_##type(reg_t addr, bool require_alignment = false) { \
      LOG_ADDR(addr); \
      if (unlikely(addr & (sizeof(type##_t)-1))) { \
        if (require_alignment) load_reserved_address_misaligned(addr); \
        else return misaligned_load(addr, sizeof(type##_t), xlate_flags); \
      } \
      reg_t vpn = addr >> PGSHIFT; \
      size_t size = sizeof(type##_t); \
      if ((xlate_flags) == 0 && likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) { \
        if (proc) READ_MEM(addr, size); \
...

LOAG_ADDRは、実行したログ情報を記録しているわけだ。ただしこれだけでは、ログ情報は1つしか記録できないのでこれはちょっと問題かもしれない。

ストア命令も同様。

// template for functions that store an aligned value to memory
  #define store_func(type, prefix, xlate_flags) \
    void ALWAYS_INLINE prefix##_##type(reg_t addr, type##_t val, bool actually_store=true, bool require_alignment=false) { \
      LOG_ADDR(addr); \
      if (unlikely(addr & (sizeof(type##_t)-1))) { \
        if (require_alignment) store_conditional_address_misaligned(addr); \
        else return misaligned_store(addr, val, sizeof(type##_t), xlate_flags, actually_store); \
      } \
      reg_t vpn = addr >> PGSHIFT; \
      size_t size = sizeof(type##_t); \
      if ((xlate_flags) == 0 && likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) { \
        if (actually_store) { \
          if (proc) WRITE_MEM(addr, val, size); \
...

execute.ccで、log_print_sift_traceを使ってログを書き出せばよかろう。

// This is expected to be inlined by the compiler so each use of execute_insn
// includes a duplicated body of the function to get separate fetch.func
// function calls.
static inline reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
{
#ifdef RISCV_ENABLE_COMMITLOG
  commit_log_reset(p);
  commit_log_stash_privilege(p);
#endif

  reg_t npc;

  try {
    npc = fetch.func(p, fetch.insn, pc);
    if (npc != PC_SERIALIZE_BEFORE) {

#ifdef RISCV_ENABLE_COMMITLOG
      if (p->get_log_commits_enabled()) {
        commit_log_print_insn(p, pc, fetch.insn);
      }
#endif

      log_print_sift_trace(p->get_state(), pc, fetch.insn);

    }
#ifdef RISCV_ENABLE_COMMITLOG
  } catch (wait_for_interrupt_t &t) {
      if (p->get_log_commits_enabled()) {
        commit_log_print_insn(p, pc, fetch.insn);
        log_print_sift_trace(p->get_state(), pc, fetch.insn);
      }
      throw;
  } catch(mem_trap_t& t) {
      //handle segfault in midlle of vector load/store
      if (p->get_log_commits_enabled()) {
        for (auto item : p->get_state()->log_reg_write) {
          if ((item.first & 3) == 3) {
            commit_log_print_insn(p, pc, fetch.insn);
            log_print_sift_trace(p->get_state(), pc, fetch.insn);
            break;
          }
        }
      }
      throw;
#endif
  } catch(...) {
    throw;
  }
  p->update_histogram(pc);

  return npc;
}

log_print_sift_trace()はこんな感じ。これでトレースファイルを出力できる。

static void log_print_sift_trace(state_t* state, reg_t pc, insn_t insn)
{
#ifdef RISCV_ENABLE_SIFT
  uint64_t addr = pc;
  uint64_t size = insn.length();
  uint8_t  num_addresses = state->log_addr_valid; // true -> 1, false -> 0
  uint64_t addresses[1] = {state->log_addr};
  bool     is_branch = state->log_is_branch;
  bool     taken = state->log_is_branch_taken;
  state->log_writer->Instruction(addr, size, num_addresses, addresses, is_branch, taken, 0 /*is_predicate*/, 1 /*executed*/);
  state->log_addr = 0;
  state->log_addr_valid = false;
  state->log_is_branch = false;
  state->log_is_branch_taken = false;
#endif
}

これでSIFTファイルが生成できていることは確認できた。

$ ../../sniper/sift/siftdump memcpy.sift > memcpy.sift.dmp
0000000080002854                                            07 80 05 02
                 -- addr 80002e1f
0000000080002858                                            96 95
000000008000285a                                            33 06 56 40
000000008000285e                                            27 80 06 02
                 -- addr 8000325f
0000000080002862                                            96 96
0000000080002864                                            75 f6
                 -- taken
0000000080002850                                            d7 72 06 00
0000000080002854                                            07 80 05 02
                 -- addr 80002e2f
0000000080002858                                            96 95
000000008000285a                                            33 06 56 40
000000008000285e                                            27 80 06 02
                 -- addr 8000326f
0000000080002862                                            96 96
0000000080002864                                            75 f6
                 -- taken
0000000080002850                                            d7 72 06 00
0000000080002854                                            07 80 05 02
                 -- addr 80002e35

もんだいは、Sniper側の実行にもrv8が必要のようで、こちらにはベクトル命令がサポートされていない様子。これはどうすればいいんだ?

PC: 800027b0 Size: 4 num_addresses=0 is_branch=0
PC: 800027b4 Size: 4 num_addresses=0 is_branch=0
PC: 800027b8 Size: 4 num_addresses=0 is_branch=0
PC: 8000284e Size: 2 num_addresses=0 is_branch=0
PC: 80002850 Size: 4 num_addresses=0 is_branch=0
0          [22893]  [ 0]  [core_model_boom_v1.c:  54]  *ERROR* Invalid instruction type 0