自作RISC-V CPUの検証をするため、メモリアクセスをSpikeで検証することを考える。 L1Dのアップデートのタイミングで、現状のメモリの状態を確認することを考えてみよう。
いくつかのチェックポイントがあるはずだ。
- L1Dからデータを外部に吐き出すタイミング
- L1Dにデータをロードするタイミング
- ストア命令がL1Dをアップデートするタイミング
まずは一番簡単そうな、L1Dの外部吐き出しのタイミングでSpikeのチェックを実行することを考える。 基本的な方針としては、メモリサブシステムがキャッシュラインの吐き出しをするタイミングでDPI-Cを呼び出し、Spikeのメモリ状態と比較する。
always_ff @ (negedge i_clk, negedge i_reset_n) begin if (i_reset_n) begin if (l1d_ext_wr_req.valid & l1d_ext_wr_req.ready) begin /* verilator lint_off WIDTH */ record_l1d_evict ($time, l1d_ext_wr_req.payload.addr, l1d_ext_wr_req.payload.addr[$clog2(DCACHE_DATA_B_W) +: DCACHE_TAG_LOW], l1d_array, DCACHE_DATA_B_W);
このタイミングで、Spike経由でメモリの内容を参照する。
bool diff_found = false; fprintf(compare_log_fp, "%lld : EVict ISS Check : %llx : ", rtl_time, paddr); try { for (int i = size/8-1; i >= 0; i--) { uint64_t iss_ld_data; spike_core->read_mem(paddr + i * 8, 8, &iss_ld_data); fprintf(compare_log_fp, "%08x_%08x", iss_ld_data >> 32 & 0xffffffff, iss_ld_data & 0xffffffff); if ((iss_ld_data >> 32 & 0xffffffff) != l1d_data[i*2+1] | (iss_ld_data & 0xffffffff) != l1d_data[i*2+0]) { diff_found = true; } if (i != 0) { fprintf(compare_log_fp, "_"); } } fprintf(compare_log_fp, "\n"); } catch (trap_t &t) { fprintf (compare_log_fp, "Catch exception at record_l1d_evict : PA = %08llx, %s\n", paddr, t.name()); } if (diff_found) { fprintf (compare_log_fp, "Eviction Data Compare Error\n"); stop_sim (102); }
spike_core->read_mem()
でマッピングしているけれど、実体はsim_t
のread_chunk()
を呼び出している。
void read_mem(addr_t taddr, size_t len, void* dst) { read_chunk (taddr, len, dst); }
いろいろ調べてみると、Spikeの環境には2種類のMMUが用意されていて、
という感じがしているので、グローバルなMMUを使用して正確な物理アドレスを使用するのがポイント。
この仕様は、Spikeのインタラクティブモードで物理アドレスを使うことができるので、どこかにそういう機能があるはずだと探して見つかった。
To see the contents of a memory location (physical address in hex): : mem 2020 To see the contents of memory with a virtual address (0 for core 0): : mem 0 2020