###仮想アドレスサポート###
今回はRIDECOREに不足している部分なので、読むソースが無い。そのため、設計指針を記述する。
RIDECOREは実記憶マシンであるため、Linuxが動作するためにはMMUが必要となる。MMUはTLBとページテーブル検索(テーブルウォーク)のためのFSMで構成される(と思われる)。RIDECOREにおいてはPCが確定したら命令メモリを次のステージである<IF>
で検索するが、その前に論理アドレスVAから物理アドレスPAへの変換が必要である。このため、アドレス変換ステージの挿入が必要と考える。TLBとキャッシュメモリとの同一サイクル検索は厳しそうであるため。VAとPAの変換ペアを記憶しているのがTLBであり、TLBに存在しない場合にはTLBミスが起きる。
###TLBミス###
TLBミスすると一般にはパイプラインを停止し、テーブルウォーク(後述)を行い、アドレス変換を行ってからTLBに格納し、後続命令のフェッチに戻る。RIDECOREやOoOマシンではもしかしたら投機実行可能なので、パイプラインを停止する必要は無いのかもしれないが、命令アドレスがTLBミスすると、その後が実行可能とも思えないので、パイプラインを止めても性能はあまり変わらないような気がする。一方で、オペランドアドレスの変換の場合は、TLBミスの場合、パイプラインを止めずに後続のレジスタ使用命令を先に実行できる気がする。気がする、が多いのはOoO(Out of Order)マシンの経験が無いためで、ひととおり読み終わったら判明してくることを期待。
経験の範囲で言えば、設計時にバグが出るところは以下であり、ページクロスアクセスの前半と後半でキャッシュミス、TLBミスする場合としない場合の8通りの組み合わせについて、良く考えて実装しないとバグの温床となりそう。
###仮想アドレス空間###
デマンドページングを行う仮想アドレス空間には32bit空間(Sv32)、39bit空間(Sv39)、48bit空間(Sv48)が存在する。
仮想アドレス空間 | Sv32 | Sv39 | Sv48 |
---|---|---|---|
ページサイズ | 4K | 4K | 4K |
ページテーブル | 2段 | 3段 | 4段 |
PTEサイズ | 4 | 8 | 8 |
Sv39とSv48は制御はほぼ同一のため(ページテーブル段数=繰り返し回数のみの相違)両方をサポートするのが望ましいとある。
###Table Walk FSM###
1. a=satp.ppn << 12; i=1;
2. pte=@(a+va.n[i]<<2);
3. if (pte.v==0 || (pte.r == 0 && pte.w==1) page_fault();
4. //ここからPTE valid
if (pte.r==1 || pte.x == 1) goto 6. // leaf
else i=i-1; if (i < 0) page_fault();
a = pte.ppn << 12;
goto 2;
5. // leaf
pte.x, pte.r, ptexw, pte.u 及び statusレジスタのSUM, MXRフィールドを
使用して許可判定, if NG page_fault();
if (i > 0 && pa.ppa[i-1:0] <> 0) page_fault(); // miss alignd super page
if (pte.a == 0 || (store && pte.d == 0) page_fault();
という感じのFSMを記述し、<IF>
ではなく<ID>
にtable_walk FSMを置き、内部命令に置き換えてパイプラインに流せば良いのではないだろうか。~~いわゆるマルチサイクル命令などもこのように実行しているはずで、その処理とステージを合わせるのが良いと思われる。~~が、OoOだとそのような概念も変わるのかもしれない。
###RIDECORE不足部分###
- 仮想アドレスサポート
- キャッシュ
- フローティングポイント
- 除算
- 特権命令(?)
- C(ompressed)
パッと見、これらが思いつくが、Rocket Chipの開発目的はそれ自体で動作するだけでなく、様々なプロセッサのコンポーネントライブラリとして役立つとのことなので、そこから持ってきても良いかもしれない。問題はChizelで記述されていることで、Verilogに変換後のコードを整理して持ってくるか、ブラックボックスで持ってくるか。できればRefactoringしたいところではある。
##マルチサイクル命令##
ざっと全体を眺めてみてもマルチサイクル命令はなさそう。ここでマルチサイクル命令を説明しておくと、一般的なRISCは基本、シングルサイクル実行である。が、例外があり、例外処理、Enter、load multi、TLBミス, 乗除算だとかにおいて、内部的に命令を決まったシーケンスで実行する必要がある。その場合、前段である<IF>
を止め、内部的には<ID>
を複数回実行する実装が考えられる。このことをマルチサイクル命令(処理)と呼んでいる。さてRIDECOREには、(1)RISC-V ISAにはマルチサイクル実行の命令が少なく?、(2)例外処理、仮想記憶をサポートしていない、との両方からマルチサイクルシーケンサは見当たらなかった。
###実装方針###
基本的には<ID>
で命令が内部的に増殖したように見せれば良い。選択枝として、前段の<IF>
を止めるか止めないかがある。OoOであれば止めなくても良いかもしれない。なるべくdecoupleすることがOoOの基本と考えると、ここは止めない方針とする。IDシーケンサは単一のFSMとするが、通常はシングルサイクル実行ステートにいる。例外処理等が起動されると、その原因に応じてマルチサイクルステートに移行する。その一つがテーブルウォークステートである。ステートは階層構造を持ち、テーブルウォークステート内部では上記のシーケンス動作を実行する。
"Modern Processor Design"によれば、TLBミスの解消に2通りあり、ひとつは上記のようにシーケンサで実装する手法と、RISCライクにTLBミストラップから命令で実行する方法がある。命令であれば可変にできるとは言え、TLBミス処理は決まったパターンであるため、ハードウェア実行とする。
読書会(6)はこちら
https://qiita.com/mocapapa/private/d6f2cc2f95ed22de59ac