FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

アウトオブオーダのCPUを作ろう - 命令フェッチをAXIバスに対応させる -

自作CPUのデータバスへのアクセス部分をAXIに変更してしまおうと思う。今までは、オリジナルバスを利用していたがら、それだとZynqとかに接続するときにやりにくい。

f:id:msyksphinz:20150606134728p:plain

アウトオブオーダプロセッサで、しかも同時実行できる数が多いプロセッサにとって生命線となるのが、いかに大量の命令を同時にフェッチしてくるか、ということだ。 また、繰替えし時に効率的に命令を供給できるように命令キャッシュの存在もかかせない。 まだ命令キャッシュは存在していないので、ここでは、なるべくAXIのバースト転送を利用して、大量に命令をとってくることを考える。

まず単純に考えると、

  1. AXIに対してARVALIDを上げて、フェッチアドレスをリクエストする。ARREADYが帰ってきたら、アドレスリクエストは完了とする。
  2. RVALIDがバスから上がりはじめたら、IDを見て該当するデータをキューに放り込んでいく。キューがあふれないように、ある程度の上限を制約する処理は必要になる。

このときにやっかいになったのは、 1. 命令アドレスのリクエストが完了する → あとはRチャネルが動作するのを待つだけ 2. Rチャネルからデータを読み出していく

の1.と2.の間にフラッシュ動作などパイプラインをフラッシュする動作が入ってしまい、それより前に出したフェッチリクエストを無効化しなければならないときがある。

このときに、すでにRチャネルからデータを受け取っていれば単純に受取バッファをクリアすればいいのだが、リクエストを出している最中のものについても、リクエストを取り止めれば良い。 ただし、アドレスチャネルのリクエストが完了していて、データリクエストを待っている段階のものは面倒だ。スレーブ側は、CPUのパイプラインフラッシュの状態など露知らないので、普通にデータを返してくる。 このとき、何も考えずにデータを受け取ってしまうと、本来は不要なリードデータをキューに格納し、誤って後段に命令を渡してしまう。

これを防ぐためには、

  • 命令バッファの各エントリには状態を示すステートマシンが入っている。フラッシュが発生したら、初期化する。

ということで、まずは初期化状態であればリードチャネルのデータは受け取らないようにしておけば間違いない。 ところがこれだけでは不十分で、

  1. アドレスチャネルのリクエストが完了する。
  2. フラッシュが発生する→命令バッファの有効フラグを全て落とす
  3. 再度新しいアドレスから命令フェッチをリクエストする
  4. 命令バッファの該当するエントリを、「受取待ち」状態に遷移させる
  5. ここで、フラッシュしたがリードチャネルは放置状態の、1.に対するデータリード値が帰ってくる。命令バッファのエントリは受取状態に入っているので、誤って受け取ってしまう。

これを回避するためには、やはりIDの一致を取ることが必要で、各エントリにはアドレスチャネル側でIDをいくらでリクエストしたかを記録しておき、「フラッシュ前=フラッシュ後-1」 程度にIDをずらす。 こうすると5.で「フラッシュ後-1」のIDでデータがやってきても、該当するデータではないので、読み飛ばして正しいフラッシュ後のIDが来るまで待つことができるようになる。