コンピュータの構成と設計 4.8~4.10節

パタヘネ4.8~4.10です。前回はパイプラインレジスタの導入によってデータパスを5ステージに分けて動作させ、パイプライン処理ができるようになることを見ました。今回はデータハザードと制御ハザードの回避方法に関して見ていきます。

4.8

以下のような並び順の命令を実行することを考える。

sub $2,$1,$3
and $12,$2,$5
or $13,$6,$2
add $14,$2,$2
sw $15,100($2)

このとき、$2が引き続く命令で使われているので、工夫なしにパイプライン実行しているとデータハザードが起こる。具体的には、orで$2が必要になるEXステージに到達している頃、subはWBステージにおり、まだレジスタに結果が書き込まれていない。これを回避するためには、データパスのレジスタ書き込みを待たず、次の命令の実行に必要な結果をひきわたせばよい。subのEXステージの結果はパイプラインレジスタに入っているので、そこから値を取ってきて、andやorの実行に使う。より一般的には、データハザードが起きる条件は以下のように書くことができる。

EX/MEM.RegisterRd=ID/EX.RegisterRs
EX/MEM.RegisterRd=ID/EX.RegisterRt
MEM/WB.RegisterRd=ID/EX.RegisterRs
MEM/WB.RegisterRd=ID/EX.RegisterRt

Rd,Rs,RtはR形式のオペランドとなっているレジスタのことを意味しており、Rdは書き込み先レジスタ、Rs,Rtは計算に使用するレジスタである。例えば最初の条件は、前の命令でRdとなっているレジスタが次の命令でRsに指定されており、前の命令の現在のステージがMEMステージで次の命令がEXステージであることを意味する。これら条件が成立しており、かつ前の命令がレジスタに値を書き込むタイプの命令ならパイプラインレジスタからフォワーディングを行う。フォワーディングは例えばEX/MEMパイプラインレジスタからID/EXパイプラインレジスタに線を引き、マルチプレクサでレジスタファイルから読み込んだ値とパイプラインレジスタから引き込んだ値を選択するようにすれば良い。

 3命令間でデータハザードが発生することがある。例えば、
add $1,$1,$2
add $1,$1,$3
add $1,$1,$4
のようになっている場合は、3番目のaddはMEMステージから$1をフォワーディングする。最初のaddのあと2番目のaddがフォワーディングで最新値を計算しているので、それを使うためである。

 計算などの場合でデータハザードが発生するのはフォワーディングで回避できるが、ロード命令でデータハザードが起こるとメモリからデータを取るまでは値が手にはいらないのでフォワーディングが使えない。この場合はパイプラインをストールさせる必要がある。そのためにはまずロード命令でデータハザードが起きていることを検知する回路が必要である。次にストールさせる方法だが、適当な値をデータパスに流すと不正な値がレジスタやメモリに書き込まれる恐れがあり、流す値を考えなければならない。結論としては、全ての制御線に0を入れるnop命令を流せばレジスタに不正な値が書き込まれることはなく安全にパイプラインを止めることができる。nopを挿入した回数だけ命令の実行ステージがずれる事となり、データハザードが解消されるまで待つことができる。

4.9

 分岐命令がある場合に、分岐結果が出る前に分岐後の命令を正確に知ってパイプライン実行することはできない。この問題を制御ハザードという。制御ハザードへの対処は分岐予測を用いることである。分岐が不成立であると予想し、PCを書き換えることなく分岐命令の次の命令を取ってきて実行することが分岐予測の最も単純な例である。分岐が成立していると分かったタイミングで、レジスタやメモリに不正な値が書き込まれないよう、パイプライン上を流れている間違えて実行されている命令は消してやる必要がある。このことをフラッシュという。データハザードのときのパイプラインストールはたんにnopを挿入すれば、制御値0がステージ間を勝手に伝播してくれたが、分岐予測を間違えた場合は、既に複数のステージのパイプラインレジスタが本来実行すべきでない命令の結果を持っているので、それぞれを消すための制御線が必要となる。

 分岐予測を間違えた場合のペナルティは、パイプライン上で既に間違って実行されている命令の個数に比例する。間違って実行されている命令の数を減らすためには、分岐予測を間違えたと判定するステージをより早い段階にすれば良い。これまでMEMステージで分岐先PCの値を取り上げていたが、これをIDステージまで前倒しすることで高速化が見込める。これは実際回路的に可能である。なぜなら、多くの比較命令はALUを使うまでもない単純な回路で実現できるからである。問題となるのは、分岐判定に使うレジスタもフォワーディングが必要となってくるということである。従って、IDステージにフォワーディング論理回路を新たに設置し、その回路はEX/MEMもしくはMEM/WBから値を取ってくることを判断しなければならない。また、ストールが必要となる事がある。直前の算術命令の結果を使って分岐するならその結果が出るまで、ロード命令を使った分岐なら2ステージストールする。

 分岐予測の形態として、動的分岐予測というものもある。分岐予測バッファに過去どのようなアドレスの命令で分岐結果がどうであったかをためておく。アドレスは全ビット保持するのではなく、アドレスの下位ビットだけを保持する。そのため、下位ビットが等しい別命令の分岐結果を使って予測してしまう事がある。ただ、そうであっても分岐先を間違えるのは取り返しがつかない失敗ではないので、大きな問題ではない。

 バッファ内に最新の分岐結果だけ保持する場合、例えばループを実行する場合には、必ず2回分岐予測を外してしまう。なぜなら、ループを何回か実行してからループを出るとき、直前まで分岐が成立していたので分岐成立で予測するが、ループをでなければならないので予測が外れる。また、再度ループに入ったら前回は分岐不成立だったので今回も分岐不成立で予測するが、実際はループを実行しなければならないので分岐成立となり予測が外れる。この問題に対処するためには、分岐予測バッファに保持するビットを2ビットとし、2連続で同じ分岐結果だった場合は、次回以降の分岐予測をその結果に合わせるようにする。

4.10

 例外・割り込みはプロセッサ設計において大きな課題である。MIPSには未定義命令の実行と算術オーバーフローの2種類の例外が存在する。例外が発生した場合、MIPSでは例外プログラムカウンタ(EPC)に次の命令のアドレスをいれる。次に特定アドレスのルーチンを実行する。特定アドレスにはOSのプログラムが入っており、OSはユーザープログラムを止めたり、プログラムの実行を継続したりする。例外が発生したとき、OSに例外発生理由を教える必要がある。そのやり方は2種類あり、一つはcauseレジスタに発生理由の値を入れる。もう一つは例外発生時に実行されるルーチンを発生理由によって変更する方法であり、これをベクタ割り込みという。割り込みベクタは8命令ごとに区切られており、OSは8命令で何かしら例外に応じた適切な処理を行わなければならない。

 パイプライン方式で例外が発生した場合、制御ハザードの1種として考える。例外が発生したら既にパイプラインで実行されている命令をフラッシュする必要がある。EXステージの結果がWBステージに到達すると、例外発生時のレジスタの内容が書き換わってしまうので、EXステージの内容もフラッシュが必要である。また、例外ルーチンに飛ぶため、PCを例外ルーチンのアドレス80000180に置き換える。最後に、EPCを例外発生PCの次のアドレスに書き換える。

 パイプライン実行中は複数の命令が同時に実行されているのでどの命令で例外が発生したか対応づけることが必要である。また、例外が複数同時に発生することがある。例外に優先順位をつけて対処の順番を決める必要がある。これらは例外処理ソフトウェアがCauseレジスタを見ながら行う。また、割り込みの場合は例外と異なり、いつパイプラインに割り込みをかけるか設計自由度がある。

コメント

タイトルとURLをコピーしました