FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

RISC-Vのトレースフォーマット仕様書が公開されたので読んでみる(2. 分岐トレース)

RISC-V トレースフォーマットの勉強。続き。

github.com

github.com

1章ずつ、翻訳ではなく日本語でサマライズしていく形式で進めていく。

Chapter.2 分岐トレース

命令データトレース、もしくは分岐トレースはとあるアドレスからスタートして命令の実行情報の差分を転送する。実行情報の差分とは、ジャンプ、関数コール、関数リターン、分岐系の命令の実行である。また、割り込みや例外についても差分として分類される。

命令さ文トレースによって命令シーケンスのトレースを効率的にエンコーディングして転送することができるようになる。

この手法はプログラムバイナリのオフラインコピーが必要である。この手法は一般的には動的な(事故書き換えの)プログラムには向かないため、このようなアクセスは禁止されている。

このアプローチを拡張して、デコーダがターゲットから命令メモリを要求するように調整することで、決定的な動的コードの小さなセクションに対処するように拡張できる。

2.1 命令差分トレースの考え方

2.1.1 シーケンシャル命令

RISC-V命令セットは、すべての命令は基本的にシーケンシャルに動作する。シーケンシャルに動作している最中にはトレースは必要ない。分岐命令によりジャンプするときにトレースを出力する。

2.1.2 推測できないプログラムカウンタのジャンプ

プログラムバイナリだけではプログラムカウンタがどちらの方向にジャンプするのか判定できない場合、命令の差分トレースによりジャンプ先のアドレスが示される。

間接ジャンプの例では、次の未例アドレスは現在のレジスタの値によって決定される。このような場合には、ジャンプ命令の次に実行される命令のアドレスをトレースする必要がある。

割り込みと例外も、推測できないプログラムカウンタの種類である;これについては以下で議論する。

2.1.3 分岐

分岐は、レジスタもしくはフラグにより条件付きでジャンプするかを決定する。デコーダはプログラムのフローをトレースすために、トレースは分岐が成立したかどうかを示す必要がある。

直接分岐では、ジャンプ先のアドレスはプログラムのバイナリから計算できる。この場合にはトレース情報は必要ない。RISC-V ISAでは直接分岐のみが定義されている。

2.1.4 割り込みと例外

割り込みは分岐命令とは異なる形の非同期な差分トレースが必要となる。例外も同様であるが、例外の場合は特定の命令に関連付けられる。

デコーダは一般的にどこで割込みが発生したかは判別できないため、トレースでは通常のプログラムのフローが中断される形で非同期的なジャンプ先と例外のタイプが示される。割り込みもしくは例外が発生すると、割り込みが発生するまでのすべての命令がトレースされていなければならない。次の有効な命令(トラップハンドラの最初の命令)が最初にトレースされていなければならない。

2.2 オプションとランタイムコンフィギャラブルトレース

命令トレースエンコーダは複数のトレースモードを持つ。デコーダがパケットを正しく処理するために、現在アクティブなコンフィグレーションを正しく認識する必要がある。コンフィグレーションはエンコーダのコンフィグレーションが変更されたときに、エンコーダによって発行されるパケットに載って送信される。

  • デルタアドレスモード:プログラムカウンタのジャンプは、アドレスの絶対値ではなく差分としてエンコードされる。

    • 関連するパラメータ:なし
  • フルアドレスモード:プログラムカウンタのジャンプはアドレスの絶対値としてエンコードされる。

    • 関連するパラメータ:なし
    • 効率は悪いがデコーダの開発者には有益。
  • 暗黙的例外モード:例外の到達したアドレス(例えば、例外トラップアドレス)はデコーダが認識していると仮定し、エンコーダはトレースしない。

    • 関連するパラメータ:なし
    • デフォルトではtveccauseの値は割り込みと例外が発生すると報告されるが、暗黙的例外モードでは*tvecは報告されない。このモードは、割り込みの要因によりデコーダがトラップハンドラを特定できる場合にのみ有効である。
  • シーケンシャル推測ジャンプモード:インダイレクトジャンプのジャンプターゲットは2命令の複合効果によって推測できると考える。

    • 関連するパラメータ:sijump_p
    • デフォルトではインダイレクトジャンプのジャンプ先は推測不能だが、ジャンプターゲットが定数としてロードされており予測可能であると考えられう場合には、ジャンプターゲットをデコーダに通知しない。
  • 暗黙的リターンモード:関数コールの戻りアドレスはコールスタックによって推測できると考え、エンコーダはトレースしない。

    • 関連するパラメータ:call_counter_size_p, return_stack_size_p, itype_width_p
    • ているコールの場合を除き、関数呼び出しがトレースとして通視された時点で戻りアドレスは推測できるはずである。しかしリターンアドレスを修飾するプログラムに関してはこのモードはサポートできない。
    • エンコーダはりたーなドレススタックを管理することができる。
  • 分岐予測モード:エンコーダ分岐予測器(とデコーダ内の同一コピー)によって正しく予測できた分岐命令はTaken/Not Takenはエンコードされないが、より効率的な分岐カウント数が使用される。

    • 関連するパラメータ:bpred_size_p
    • 分岐予測がない場合、分岐命令の結果は「分岐マップ」と呼ばれる1ビットのベクトルに格納される。分岐マップは書く分岐命令の結果を時系列順に格納する。
    • このエンコーディングでは分岐の結果が1ビットであり有効だとしても、いくつかの場合にはまだトレースパケットのサイズが大きい。例えば:
      • 予測不可能なタイトなループを実行している場合。ループの各イタレーションにおける結果は分岐マップに書き込まれる。
      • 割り込みを待つためのアイドルループ。このループでは非常に大きなトレースが生成される。
      • ブレークポイントではアイドルループが発生する。
    • このような効率の悪いトレースの生成を防ぐために、エンコーダに分岐予測モードを実装する。エンコーダとデコーダを同期させるために、同一の動きを行う分岐予測器をデコーダでソフトウェアにより記述する必要がある。
    • 分岐予測器では2^\text{bpred_size_p}エントリを持つルックアップテーブルを実装する。各インデックスは命令アドレスの\text{bpred_size_p}:1ビットにより参照され、テーブルの各エントリは2ビットで構成される:
      • 00 : Not Taken予測。予測が外れると01に移行する。
      • 01 : Not Taken予測。予測が成功すると00に移行する。そうでなければ11に移行する。
      • 11 : Taken予測。予測に失敗すると10に移行する。
      • 10 : Taken予測。予測に成功すると11に移行する。そうでなければ00に移行する。
    • MSBは予測の結果を示しており、LSBは実際の結果を示している。予測に2回失敗すると予測の値を変更するようになっている。
  • ジャンプターゲットキャッシュモード:予測不可能なジャンプターゲットのアドレスを報告する代わりに、最近のジャンプターゲットをキャッシュし、キャッシュエントリのインデックスを報告することにより効率を上げることができる。

    • 関連するパラメータ:cache_size_p
    • デフォルトではターゲットアドレスの予測不可能なジャンプはトレースで通知されるが、同じ関数が何度も呼ばれる場合には同じターゲットアドレスが何度も通知されることになる。この通知を省略するためにジャンプターゲットキャッシュをエンコーダに追加し、キャッシュ内に存在するジャンプと同じ場合にはそのキャッシュのインデックスをデコーダに通知する。