RISC-VのアウトオブオーダコアであるBOOM (Berkely Out-of-Order Machine) について勉強を進めている。以下のドキュメントを日本語に訳しながら読んでいくことにした。
リオーダバッファ(ROB)とディスパッチステージ
リオーダバッファ は、パイプライン内のすべてのインフライト命令の状態を追跡します。 ROBの役割は、プログラマに自分のプログラムがインオーダーで実行されているように見せることです。 命令がデコードされ、リネームされると、その命令はROBと 命令キュー に ディスパッチ され、 busy とマークされます。 命令の実行が終了すると、ROBに通知され、 not busy とマークされます。 ROBの”先頭”がビジー状態でなくなると、その命令は “コミット “され、アーキテクチャの状態が見えるようになります。 例外が発生し、例外命令がROBの先頭にある場合は、パイプラインがフラッシュされ、例外命令の後に発生したアーキテクチャの変更は表示されません。 そして、ROBはPCを適切な例外ハンドラにリダイレクトします。
ROBの構成
Fig. 16 3命令発行の2ワイドBOOMのリオーダバッファです。ディスパッチされたUops(dis uops)はROBの下側(rob tail)に書き込まれ、 コミットされたUops(com uops)は上側(rob head)からコミットされ、Renameの状態を更新します。 実行を終えたUops(wb uops)は、ビジービットをクリアします。 注:ディスパッチされたUOPは、同じROBの行にまとめて書き込まれ、 メモリ上で連続して配置されるため、1台のPCで行全体を表現することができます。
ROBは、概念的には、すべてのインフライト命令を順に追跡するリングバッファです。 最も古い命令は commit head で指し示され、最新の命令は rob tail で追加されます。
スーパースカラの ディスパッチ と コミット を容易にするために、ROBは W
バンクを持つ円形バッファとして実装されています (ここで W
はマシンの dispatch と commit の幅です 1 )。 この構成を Fig. 16 に示します。
ディスパッチ では、最大で W
個の命令が フェッチパケット から ROB の行に書き込まれ、 各命令は行間の異なるバンクに書き込まれます。 1つの フェッチパケット 内の命令はすべてメモリ内で連続している(アラインされている)ため、 これにより1つのPCを フェッチパケット 全体に関連付けることができます (また、 フェッチパケット 内の命令の位置は、自身のPCに低次ビットを提供します)。 これは、分岐コードがROB内にバブルを残すことを意味しますが、 高価なコストが各ROB行で償却されるため、ROBへの命令の追加が非常に安くなります。
ROBの状態
各ROBのエントリには小さなステートが含まれます:
- エントリが有効か?
- エントリがBusy状態か?
- エントリで例外が発生しているか?
- 分岐マスク(エントリ中のどの分岐命令がまだ投機中であるか?)
- リネーム状態(論理書き込みアドレスと古い書き込み物理レジスタ)
- 浮動小数点ステータスアップデート
- 他の様々なデータ(例えば統計のトラッキングに有益なもの)
PCと分岐予測の情報は、行ごとに保存されます( PC Storage 参照)。 例外状態 は、最も古い既知の例外命令のみを追跡します( Exception State 参照)。
例外の状態
ROBは、最も古い例外命令を追跡します。この命令がROBの先頭に到達すると、例外が発生します。
各ROBエントリには、その命令に例外的な動作が発生したかどうかを示す1ビットのマークが付けられますが、 追加の例外状態(不良仮想アドレスや例外原因など)は、既知の最古の例外命令についてのみ追跡されます。 これは、エントリごとに保存しないことで、かなりのステートを節約できます。
PCの保存
ROBはすべてのインフライト命令のPCを知っていなければなりません。 この情報は以下のような状況で使用されます。
- どのような命令でも例外が発生する可能性があり、その場合は”例外PC”(epc)を知る必要があります。
- 分岐命令やジャンプ命令は、対象となる計算のために自分のPCを知る必要があります。
- ジャンプレジスタ命令は、自分自身のPCと、 プログラム中の次の命令 のPCの両方を知っていなければなりません。
この情報を保存するには非常にコストがかかります。 分岐命令やジャンプ命令は、PCをパイプラインに渡す代わりに、 レジスタリード の段階でROBの”PCファイル”にアクセスし、 分岐ユニット で使用します。 2つの最適化が使われています。
- 単一のPCのみがROBの行に保存されます。
- PCファイルは2つのバンクに分かれており、1つのリードポートで連続した2つのエントリーを同時に読み取ることができます(JR命令で使用)。
コミットステージ
コミットヘッド にある命令がビジーでなくなると(そしてそれは例外ではない)、 その命令は コミット されるかもしれません。 スーパースカラのコミットでは、ROBの行全体がnot busy命令について分析されます(そのため、1サイクルでROBの行全体までコミットされる可能性があります)。 ROBは、できるだけ早くリソースを解放するために、行ごとにできるだけ多くの命令を貪欲にコミットします。 しかし、ROBは複数の行をまたいでコミット可能な命令を探すことは(現在は)ありません。
ストアがコミットされて初めて、そのストアをメモリに送ることができます。 スーパースカラでストアをコミットする場合、 Load/Store Unit (LSU) に「いくつのストアをコミットしてよいか」を伝えます。 LSUはコミットされたストアを必要に応じてメモリに排出します。
(レジスタに書き込む)命令がコミットすると、 古くなった物理レジスタ を解放します。 その後、 stale pdst は自由になり、新しい命令に再割り当てされます。
例外とフラッシュ
例外は、 commit head にある命令が除くときに処理されます。 パイプラインはフラッシュされ、ROBは空になります。 リネームマップテーブル をリセットして、真の、非投機的な committed 状態を表す必要があります。 そして、 フロントエンド は適切なPCに指示されます。 アーキテクチャ上の例外であれば、例外となる命令のPC( 例外ベクトル と呼ばれます)がControl/Status Register(CSR)ファイルに送られます。 マイクロアーキテクチャ的な例外(ロード/ストア順序の誤記など)の場合は、失敗した命令がリフェッチされ、 新たに実行を開始することができます。
パラメータ:ロールバックと1サイクルリセット
リネームマップテーブルをリセットする際の動作はパラメータ化可能です。 最初のオプションは、ROBをサイクルごとに1行ずつロールバックしてリネーム状態を解除することです(これはMIPS R10kの動作です)。 各命令に対して、 stale物理的デスティネーション レジスタは、その 論理的デスティネーション 指定のためにマップテーブルに書き戻されます。
より高速なシングルサイクルのリセットが可能です。 これは、リネームテーブルの committed 状態を追跡する別のリネームスナップショットを使用することで実現されます。 この コミットマップテーブル は命令がコミットすると更新されます。2
例外要因
The RV64G ISA provides relatively few exception sources:
Load/Store Unit
page faults
misaligned fetches
Decode Stage
all other exceptions and interrupts can be handled before the instruction is dispatched to the ROB
なお、メモリ順序の推測エラーもLoad/Storeユニットから発生し、 BOOMパイプラインでは例外として扱われます(実際にはパイプラインの「リトライ」が発生するだけです)。
Point of No Return (PNR)
Point-of-no-returnヘッドは、ROBコミットヘッドの前を走り、 誤判定や例外が発生する可能性のある次の命令をマークします。 これには、未解決の分岐や翻訳されていないメモリ操作などが含まれます。 このように、コミットヘッドの 前 とPNRヘッドの 後 にある命令は、 たとえまだ書き戻されていなくても、 非仕様 であることが保証されます。
現在、PNRはRoCC命令にのみ使用されています。 RoCCコプロセッサは、通常、命令をインオーダーで実行することを期待しており、 投機ミスを許容しません。 そのため、コプロセッサへの命令発行は、その命令がPNRヘッドを通過して、推測可能でなくなってから行うことになります。