いろんなニュースがあり、Imperasの検証環境が使えなくなる可能性があるので、オープンソース(そしてほぼRISC-Vシミュレータとしてはデファクトスタンダード)のRISC-V命令セットシミュレータSpikeを使ってどのようにハードウェアを検証するかどうかについて、まとめていきたいと思う。
ハードウェア設計時における検証環境としての命令セットシミュレータ
CPU設計者にとって、命令セットシミュレータ(Instruction Set Simulator: ISS)というものはとても重要です。 CPUの開発は、設計:検証=1:5 (自分の体験談)くらいの感覚で検証が重要視されており、検証のために使用される命令セットシミュレータは取り扱いのしやすさも含めて非常に重要です。
RISC-Vの命令セットシミュレータとしては、riscv-isa-sim (Spike) が最も有名です。
SpikeはRISC-Vのバイナリを読み込んでシミュレーションし、必要に応じてその結果を出力することができます。 ハードウェア設計者は、RTLで記述したRISC-V CPUコアに対してRISC-Vバイナリを流してRTLシミュレーションを行い、同時に命令セットシミュレータに同じバイナリを流して実行することで、実行結果の一致比較を行います。
この一致比較の方式にはいろいろあると思いますが、基本的にはRTLシミュレーションを行っている最中に、RTLで命令が実行されるたびにISSでも命令を実行してその結果をその場で比較する方式が一般的かと思います。 もしRTLとISSの結果が不一致の場合(例えば汎用レジスタの値が異なっている場合)、その時点でRTLシミュレーションを即時ストップさせ、デバッグを開始することができます。
RTLとの一致比較する項目は、主に以下の項目だと思います。
- 実行中のプログラムカウンタ
- 汎用レジスタの値
- メモリアクセスの状態
- 性能
この中で、プログラムカウンタと汎用レジスタの値をチェックするのは結構簡単です。 一方で、メモリアクセスの状態は少しチェックするのが難しい項目かもしれません。メモリアクセスの領域は広大なので、どのように検証するかはいろいろ考えどころです。
もう一つ、今回はSpikeによる機能レベルシミュレータとの一致比較なので無視しますが、性能比較というところも重要です。 性能比較の観点では、サイクル精度シミュレータとのサイクル精度比較、あるいは、内部のマイクロアーキテクチャ項目(分岐予測器の内部状態など)を理想状態のISSの動作と比較する、というものが挙げられます。
このように、CPU設計開発において、命令セットシミュレータの取り扱いは、非常に重要なのです。
Spikeの実装はC++で記述されており、その構造は非常に単純です。
主な構造はprocessor.cc
とexecute.cc
を見ておけば問題ありません。
execute.cc
のstep()
を見ておけば、その動きをだいたい理解できるでしょう。step(1)
で、1命令ずつ実行していきます。
// fetch/decode/execute loop void processor_t::step(size_t n) { if (!state.debug_mode) { if (halt_request == HR_REGULAR) { enter_debug_mode(DCSR_CAUSE_DEBUGINT); } else if (halt_request == HR_GROUP) { enter_debug_mode(DCSR_CAUSE_GROUP); } // !!!The halt bit in DCSR is deprecated. else if (state.dcsr->halt) { enter_debug_mode(DCSR_CAUSE_HALT); } } while (n > 0) { size_t instret = 0; reg_t pc = state.pc; /* ... 途中省略 ... */
また、Spikeは各コアの内部状態を保持するstate_t
に実行ログを保持しています。
processor.h
// architectural state of a RISC-V hart struct state_t { /* ... 途中省略 ... */ commit_log_reg_t log_reg_write; commit_log_mem_t log_mem_read; commit_log_mem_t log_mem_write; reg_t last_inst_priv;
これらの情報を使って、RTLとISSの一致検証を行っていきます。続く。