今回はパイプラインの動作を妨げるハザードとその対処法をやります。
前回紹介した構造ハザードは、資源の競合により起こるハザードで回避は簡単(とい うか複製しか手がない)でした。今回はハザードの中のハザード、データハザードを 紹介します。
パイプライン処理では、直前の命令の結果がレジスタファイルに書き込まれないうち に、後続の命令が読み出しを行うため、この命令間にデータの依存性があると、誤っ て更新前の値を読み出してしまいます。これを書き込む前に読んでしまうことから RAW(Read After Write)ハザードと呼ばれ、最も一般的なハザードです。他にも WARやWAWがあるのですが、MIPSではパイプラインの最後に結果を書き込むの でこれらは生じません。 RAWハザードを解決するには、命令間の間隔を保ってやれば良いのですが、これ は本質的に性能を落とすことになります。もう一つ、最新の結果を横流しすることで、 データハザードのロスを軽減することができます。 3
データハザードの範囲を検討しましょう。Wステージで書き込みを行うので、②、③で はこの値が読めず、これ以前の値を読み出すことになります。④も書き込んだデータ を読めるように工夫しなければ同様に以前の値を読んでしまうことになります。ここで、 ④は比較的容易に対処が可能です。レジスタファイルに書き込んだ値をそのまま読 めれば良いので、書いた値をスルーして読めるようにするか、サイクルの前半で書い て、後半で読み出すようにするかを行います。 4
この記述は後者のアプローチで、クロックが立ち下がった時にデータが格納されるよ うにします。この方法でクロックの前半で書き込み、後半で読み出しが行われます。 後半の時間がクリティカルパスになり勝ちです。
⑤は回避できたので、それ以前の命令のデータハザードを回避するために、命令間 の距離を取る方法を検討しましょう。この場合、二つNOPを入れれば回避できること が分かります。しかし、これはかなりの性能低下をもたらします。より現実的な方法は 来週検討しましょう。
さらに積極的にフォワーディングをするにはどうすれば良いでしょう?この例では① の命令の結果はEステージの終わりでは計算済です。これを次の命令のEステージ の最初に送れば、計算可能になります。また、この命令がMステージを出た所で、次 の次の命令のEステージに送ってやれば、③の命令も計算可能になります。
ここでは、データの入れ替えは基本的にEステージのALUの直前で行います。これ は、先行命令の結果を書き込むレジスタ(rdかrt)がEステージの命令のrt(rs)
と一致することが必要で、かつ先行命令がレジスタファイルに書き込みを行う命令で あることが必要です。
このためにALUの入力にフォワーディング用のマルチプレクサを付けます。
このマルチプレクサに対して条件が成立した場合の計算結果をフィードバックします。 これは、命令①から命令②へのフィードバックです。
同様のフォワーディングはWステージからも行います。両方からのフィードバックが必 要な場合、Mステージを優先します。
12 通常の計算データはこの方法でフォワーディング可能です。しかし、Load系 の命令lw, lb, lbuではこれだけでは十分でないです。この命令では、答がM ステージの終了後でなければ得られないためです。このため次の命令でこ の結果を利用する場合、どうしても1サイクル分のバブルを入れてパイプライ ンを待たせてやる必要があります。
この待たせる操作をパイプラインインターロックと呼びます。これを実現するにはまず、 Dステージでチェックをし、EステージのLoad命令の読んできた結果が、Dステージで 利用される場合、MとWは実行を続け、F,D,Eは実行を停止します。これをパイプライ ンインターロックと呼びます。
パイプラインインターロックは命令コードの実行順を入れ替えることで対処できます。 例えば、例題のコードを実行する場合、普通にプログラミングすると2か所ストールし てしまいます。
しかし、処理の順番を入れ替えることで、ストールは0にすることができます。これを コードスケジュールと呼びます。
ではフォワーディングのVerilog記述を紹介します。ALUのA,Bそれぞれのマルチプ レクサを拡張します。図と対応させて理解しましょう。
やや拡大した図です。Verilog記述と対応させてください。
次にパイプラインインターロックのVerilog記述を紹介します。Dステージで判定を行 い、Fステージはこの信号lwstallでパイプラインを止めます。
Dステージも同様にしてパイプラインを止めます。一方、Eステージ以降はこのような インターロックをさせません。
最後のハザードがコントロール(制御)ハザードです。これは分岐命令が原因で次に 実行する命令の確定ができないことから生じます。
ALUで分岐先を計算させるとしましょう。Eステージの後のMステージでPCが更新さ れ、次のクロックからそれに従ってフェッチされます。これだ3クロック分次の命令の始 まりが遅れ、パイプラインの性能計算の式に基づくと、分岐系の命令が合わせて 25%と仮定すると、CPI=1が1.75になってしまいます。これはちょっとダメージが大き いです。 21
Fステージではそもそも命令をまだ取って来てないので、最速で分岐先を計算するの は、Dステージで計算および判断をやって、次のステージに分岐後の命令を取ってく ることです。この方法ではALUが使えないので、専用の加算器が必要ですがダメー ジが1サイクルになります。分岐命令と分かったら次に命令を取ってくるのを止めて、 1クロック待って(バブルが入る)、次のクロックに正しい命令を取ってきます。この場 合、1クロックのダメージがあるので、分岐命令の確率を25%とすると、CPIは1から 1.25になります。 22
では、このための仕組みを考えます。Dステージに飛び先計算と、飛ぶかどうかを判 定するハードウェアを入れてやります。飛び先の計算は加算器に入れる前にシフトが 必要です。分岐の判定はレジスタ同士が等しいかどうかをしれべれば良いので簡単 です。
問題は、分岐の判定を早い時期に持ってきたことで、判定するレジスタに対してデー タハザードが生じてしまうことです。これはMステージからとEステージからの二つを考 慮する必要があります。両方ともレジスタ番号が一致して先行命令がレジスタに書き 込む命令で、後続命令が分岐命令の時フォワーディングが必要になりますが、直前 からフォワーディングをすると、クリティカルパスが延びてしまうので、ここではインター ロックをすることにします。 24
また、lw命令は結果が使えるのはMステージの後なので、これもインターロックの必 要があります。
Mステージからのフォワーディングを行うためにマルチプレクサをレジスタファイルの 出力に付けてやります。
それではVerilogコードを見てやりましょう。パイプラインハザードの対処はステージ間 をまたがるので、慎重に考えて信号名を間違えないようにしましょう。ストールしない 場合で、分岐が成立すれば、pcに飛び先をセットし、そうでなければpc+4をpcにセッ トします。これとは別にpc+4は次のステージに送ってやる必要があります。
ではDステージでの処理です。分岐命令が成立するかどうかはフォワーディングのマ ルチプレクサを含めての記述です。条件が少しややっこしいです。分岐の飛び先は FステージからのPC+4に飛び先をシフトした値を足します。ここで専用の加算器を使 います。 ここで使うレジスタには、Mステージからのフォワーディングを行う必要があります。 28
次はパイプラインインターロックの説明です。lw命令の次の命令がそれを使う時、こ れがデータハザードによるインターロックでlwstallという信号名を使っています。分岐 命令の方はbranchstallという名前になっていて、Eステージの命令の結果が次の分 岐命令の判断に使う時、Mステージのレジスタを分岐命令で使う時に、パイプライン を止めています。これらのインターロックは、命令スケジューリングによって回避できま す。 29
このパイプラインでは、分岐命令の次の命令はフェッチしてきても捨てなければなら ず、1クロックのストールが必ず生じます。これを低減するための簡単な方法を二つ紹 介します。
一つは、Predict Not Takenという方法で、「分岐命令が常に分岐しない」と予想する 一種の分岐予測です。予測がはずれて分岐が成立すると分岐命令をNOPに変更し てパイプラインに流します。これはバブルとなってダメージとなりますが、分岐が不成 立ならば、フェッチしてきた命令をそのまま使うことができてロスが生じません。この方 法は簡単な付加ハードウェアで性能が向上しますが、不幸なことに分岐命令は成立 する場合の方が多いので、思ったより効果が得られません。 もう一つの方法は、遅延分岐(Delayed Branch)といって、ハードウェアは何も変更 せずに、取ってきた命令をパイプラインに流してしまいます。そして「この分岐命令は 一命令分効き目が遅いんだ」と解釈します。このパイプラインに流してしまう命令の場 所を遅延スロットと呼びます。 30
この図はPredict Not Takenを示しています。成立の場合のみ命令をフェッチしなお します。
遅延分岐は、分岐命令の次の命令をパイプラインに入れてしまい、必ず実行する方 法です。すなわち分岐命令の効き目が遅いと考えるのです。パイプラインスケジュー ルによって、有効な命令を入れてやることができれば、この命令は無駄にはならない です。どうしても有効な命令が入れられない場合、NOP命令を入れておきます。これ はロスになってしまいます。 32
mult.asmの例を考えましょう。この分岐は遅延分岐で、NOPが入って正常に動いて います。では、このNOPを有効な命令で埋めるにはどうすれば良いでしょうか?
add命令を持ってきた例です。このコードは一見ものすごく変に見えますが、bneが 遅延分岐ならばちゃんと動きます。
もう一つ、制御変数の$1をカウントダウンする命令を使う方法もあります。この場合は、 インターロックを減らす効力もあります。しかし、命令の実行順は変わらないため、あ らかじめ一つ引いて置く工夫が必要になります。
では、インフォ丸にMIPS5段パイプラインをまとめてもらいましょう。実際、このパイプ ラインは良くできていて、単純な32ビットプロセッサはおおむねこれに類似した5段パ イプラインを持っています。