自作CPUの環境に、モデルシミュレータのサイクル精度との比較環境を構築した。RTLもモデルシミュレータも波形ビューアで表示可能なログを表示することができ、パイプラインの挙動を比較することができる。
このためには、大まかに言って以下の作業が必要だ。
- RTLによりパイプライン・ビューアにより可視化可能なフォーマットを出力する(対応済み)
- RTLシミュレーション時に同時動作するSpikeが、Sniper(サイクル精度モデリングツール)に入力可能なトレースファイルを出力する
- Sniperにより、トレースファイルを読み込みサイクル計算を行う。
- Sniperにより、パイプライン・ビューアにより可視化可能なフォーマットを出力する
ということになる。まずは、Spikeを改造し、Sniperが取り扱い可能なSIFTトレースフォーマットを生成するように変更する必要がある。
まず、Spikeのriscv.ac
を書き換えて、--with-sift=${SNIPER_ROOT}
オプションを追加する。この時、RISCV_ENABLE_SIFT
のdefineマクロを追加する(この辺のconfigureの作法が全く分からずにかなり迷った…)
riscv-isa-sim/riscv/riscv.ac
+AC_ARG_WITH(sift, + [AS_HELP_STRING([--with-sift=PATH], + [Sets Sniper root directory for SIFT])], + [AC_DEFINE([DEFAULT_SIFT], "$withval", [Default sniper location]) + CPPFLAGS="-I$withval/sift $CPPFLAGS" + LDFLAGS="-L$withval/sift $LDFLAGS" + LIBS="$LIBS -lsift -lz"], + + AC_DEFINE([DEFAULT_SIFT], ["default path"], [Default sniper location])) +AC_DEFINE([RISCV_ENABLE_SIFT], [], [Sniper SIFT enable])
基本的には、Spikeに対して命令動作のたびにトレースファイルを出力するようにする。
riscv-isa-sim/riscv/execute.cc
+static void log_print_sift_trace(processor_t* p, reg_t pc, insn_t insn) +{ +#ifdef RISCV_ENABLE_SIFT + + FILE *log_file = p->get_log_file(); + fprintf(log_file, "log_print_sift_trace called, %08lx\n", pc); + + uint64_t addr = pc; + uint64_t size = insn.length(); + uint64_t num_addresses = p->get_state()->log_addr_valid; + uint64_t *addresses = p->get_state()->log_addr; + reg_t *wr_regs = p->get_state()->log_reg_addr; + bool is_branch = p->get_state()->log_is_branch; + bool taken = p->get_state()->log_is_branch_taken; + + auto& reg = p->get_state()->log_reg_write; + p->get_state()->log_writer->Instruction(addr, size, num_addresses, addresses, is_branch, taken, 0 /*is_predicate*/, 1 /*executed*/); + if (sift_executed_insn == 0x00100013) { // ADDI + p->get_state()->log_writer->Magic (1, 0, 0); // SIM_ROI_START = 1 at sim_api.h + } + if (sift_executed_insn == 0x00200013) { // ADDI + p->get_state()->log_writer->Magic (2, 0, 0); // SIM_ROI_END = 2 at sim_api.h + } + + p->get_state()->log_addr_valid = 0; + p->get_state()->log_is_branch = false; + p->get_state()->log_is_branch_taken = false; +#endif +}
@@ -184,22 +237,29 @@ static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch) if (npc != PC_SERIALIZE_BEFORE) { #ifdef RISCV_ENABLE_COMMITLOG + record_executed_insn (fetch.insn.bits()); if (p->get_log_commits_enabled()) { commit_log_print_insn(p, pc, fetch.insn); } #endif + log_print_sift_trace(p, pc, fetch.insn); + } #ifdef RISCV_ENABLE_COMMITLOG } catch (wait_for_interrupt_t &t) { + record_executed_insn (fetch.insn.bits()); commit_log_print_insn(p, pc, fetch.insn); + log_print_sift_trace(p, 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) { + record_executed_insn (fetch.insn.bits()); commit_log_print_insn(p, pc, fetch.insn); + log_print_sift_trace(p, pc, fetch.insn); break; } diff --git a/riscv/mmu.h b/riscv/mmu.h index 060552eb..8aac973b 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -93,9 +93,22 @@ public: #define RISCV_XLATE_VIRT (1U << 0) #define RISCV_XLATE_VIRT_MXR (1U << 1) +#ifdef RISCV_ENABLE_SIFT +# define LOG_ADDR(addr, reg_addr) ({ \ + if (proc && proc->get_state()) { \ + proc->get_state()->log_addr[proc->get_state()->log_addr_valid] = addr; \ + proc->get_state()->log_reg_addr[proc->get_state()->log_addr_valid] = reg_addr; \ + proc->get_state()->log_addr_valid++; \ + } \ + }) +#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) \ inline type##_t prefix##_##type(reg_t addr, bool require_alignment = false) { \ + LOG_ADDR(addr, 0); \ if ((xlate_flags) != 0) \ flush_tlb(); \ if (unlikely(addr & (sizeof(type##_t)-1))) { \ @@ -162,6 +175,7 @@ public: // template for functions that store an aligned value to memory #define store_func(type, prefix, xlate_flags) \ void prefix##_##type(reg_t addr, type##_t val) { \ + LOG_ADDR(addr, 0); \ if ((xlate_flags) != 0) \ flush_tlb(); \ if (unlikely(addr & (sizeof(type##_t)-1))) \
ついでに、ちゃんとRTLシミュレーションが終わった後にSpikeを終了させないと、SIFTフォーマットは圧縮されて出力されるので、ちゃんと吐き出し切ってくれなくなるので注意。
diff --git a/cpp/tb_mycpu.cpp b/cpp/tb_mycpu.cpp index 6cbb9f9..464e654 100644 --- a/cpp/tb_mycpu.cpp +++ b/cpp/tb_mycpu.cpp @@ -21,6 +21,7 @@ extern "C" { void initial_spike (const char *filename, int rv_xlen, int rv_flen, const char *ext_isa); void stop_sim(int code, long long rtl_time); void stop_sim_deadlock(long long rtl_time); + void delete_spike (); } extern "C" { @@ -242,6 +243,8 @@ void stop_sim(int code, long long rtl_time) if (dump_fst_enable) tfp->close(); #endif // DUMP_FST + delete_spike(); + exit(!(code == 1)); }
RTLシミュレーションとは別にSniperを駆動すれば、両方のパイプライン・チャートを取得することができるというわけだ。