サイクル精度シミュレータSniperはRISC-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