4.6 データパスのパイプライン化と制御
前の節でも書いたように、MIPSでパイプライン化を検討する際は、以下のステージ分けを行う
IF: 命令のフェッチステージ ID: 命令のデコードステージ EX: 実行ステージ MEM:メモリアクセスステージ WB: 書き戻しステージ(write back to register) |
パイプライン化されたCPU環境で、命令を実行する際、注意が必要なのは2点。
1.WBステージによって、レジスタの値が書き換わってしまう(EXステージの元ネタが変わってしまう) 2.次のPCの値が、EXステージが完了しないと分からない場合(分岐命令)がある |
前者はデータハザード、後者は制御ハザードのリスクがあるので考慮が必要となってくる。
パイプライン化を行うと、各ステージ間でデータの受け渡しが必要となる。
この受け渡し用のバッファとして、”パイプライン・レジスタ”をCPUに追加する。
パイプラインレジスタの名前は、以下のように受け渡しされるステージ名をつけている。
IF/ID ID/EX EX/MEM MEM/WB |
一番最後の処理であるWBステージは、本来のレジスタファイル(or メモリ or プログラムカウンタ)を直接書き換えればよいので、後続のパイプラインレジスタは不要となる。
また、パイプラインレジスタの値は計算途中の値を保存したワーク変数的な位置づけなので、例外やパイプラインハザードが発生して演算を最初から実行しなおす場合は破棄できるような性質のもの。一方で、レジスタファイルやPCは基本的に保持しておく必要がある(一部,保存しなくて良いレジスタも歩けど…)。
というわけで、各ステージは、それぞれ同時に、原則として1クロックサイクルにおいて、以下の処理を行う
1.前段のパイプラインレジスタからデータを読んで, 2.所定の操作を行い, 3.後段のパイプラインレジスタに書き込む. |
プログラムカウンタは、IF(命令フェッチ)のステージでインクリメントされる。
この値は後続の処理で使用される可能性があるので、パイプラインレジスタにも保存される。
というわけで、EXなどの後ろのステージにとっては、今処理している命令のアドレスはPCに存在しない(インクリメントされて、もっと先に進んでいるので)。
なので、パイプラインレジスタに保存された、”当時のPC値”を元に処理を行う事になる。
同様にレジスタファイルの内容は、IDステージで取得され、原則として、その値のコピーがパイプラインレジスタを経由して順に渡されていく。ここで、”原則として”というのは、前節で説明した”フォワーディングなどが発生しない場合において”という意味。
コピーするのは、レジスタ値だけでなく、”レジスタ番号”もパイプラインレジスタ経由で渡す必要がある。
これは、データハザードが発生していないかの検出に使用する。
(今の命令で演算した結果値を、次の命令で使用してないかをチェックする必要がある。詳細は後述)
また、各ステージを単純に同時実行すると、他の問題も発生してくる。
レジスタファイルを考えると、1クロック内で、WBによるデータ書き込みと、IDによるデータ読み込みが同時に発生してしまう。単一のリソース(レジスタファイル)を同時に2つの目的で使用することは出来ないので、結果として構造ハザードが発生する。この問題は次節で考える事にする。
CPU内の各構成ユニット(ALU,レジスタ,データメモリetc)は、その動作をコントロールするための制御線が必要だった。
パイプライン化に伴い、この制御線のOn/Offの管理も注意が必要になる。
制御線の情報は2番目のIDステージには全て決まるので、ここで決定した上で、制御線の情報をパイプラインレジスタに登録しておくことで対処できる。その後、後続の各ステージでは、自分が必要とする制御線の情報を使用する。後続のステージで必要な情報はそのまま次のパイプラインレジスタにスルーさせておく。
4.7 データ・ハザード:フォワーディングとストール
前節でパイプライン化の基本形が出来上がったので、次はハザード処理のイレギュラーパターンを考える。構造ハザード
まずは、前節で判明している構造ハザードを考える。
再掲すると、構造ハザードは以下の問題だった。
また、各ステージを単純に同時実行すると、他の問題も発生してくる。 レジスタファイルを考えると、1クロック内で、WBによるデータ書き込みと、 IDによるデータ読み込みが同時に発生してしまう。 単一のリソース(レジスタファイル)を同時に2つの目的で使用することは出来ないので、 結果として構造ハザードが発生する。 |
これは、ハードウェア回路的に解消させる事が出来る(らしい…)
レジスタの回路だけちょっと工夫を行い、1クロックの前半でWBステージによるデータの書き込みを行い、後半でIDステージのデータ読み込みを行わせる。(クロックによる同期処理をさらに細分化させる)
※これはおそらく、他のステージ処理に比べ、レジスタの読み書きの方が回路的に遅延が少ないからとれる戦略なんだと思う。
データハザード その1
次にデータ・ハザードを考えてみる。データ・ハザードは下記の状況で発生する。
WBステージで書き込んだレジスタ値を、既にパイプライン実行中のIDステージが必要とする |
これに対して今までは、”回路を工夫してフォワーディング(レジスタを経由せずに結果値を受け取る)という仕組みを利用する。”とだけ言及し、詳細の検討を後回しにしていた。
ここでは、フォワーディングの仕組みをもう少し具体的に考えてみる。
フォワーディングが必要な状況の中でEXステージについて厳密に考えてみる。
(EXステージ以外の場合も同じ考え方なので、そっちの方は省略する)
問題が起きるのは、後続で求めた値を前段が計算元として使用する場合なので、以下の何れか条件が満たされた場合になる。
// EXステージで必要とする元ねたを、前段のEXステージで更新した EX/MEM.RegRd = ID/EX.RegRs EX/MEM.RegRd = ID/EX.RegRt // EXステージで必要とする元ねたを、前々段のEXステージで更新した MEM/WB.RegRd = ID/EX.RegRs MEM/WB.RegRd = ID/EX.RegRt |
条件は、パイプラインレジスタに登録された処理対象レジスタ番号を比較する事で判定できる。
なので、上記条件が満たされたかを比較し、条件に合致すればALUの値をMUX経由で取得するような回路を追加すればよい。この回路の事を”フォワーディングユニット”と呼ぶ事にする。
フォワーディングユニットの設置場所はEXステージになる。これは、命令の実行時にデータを先送りすべきかが決定できるから。(なので、イメージとしては後続の処理がALUからデータを引っ張るというより、EXステージがMEM,WBステージに値をPUSHする型を想像すると分かりやすい)
データハザード その2
上記で検討したデータハザード以外にも、問題が残っている。それは、lw命令でメモリからレジスタに読み込んだ値を、直後の命令で使用する場合に発生する。
この場合、lw命令がMEMステージを実行中に次の命令はパイプライン上でEXステージを行っているが、EXステージで使用するALUの入力値はlw命令のMEMステージがおわらないと、どうがんばっても取得できない。なので、このような場合は後続の命令のEXステージを、絶対に1クロック分待たせる必要がある。(バブルの挿入)
バブルの挿入は2つの作業を行えばよい。
1つ目: パイプラインレジスタを書き込み禁止にする作業になる。 これによって前サイクルの命令が再実行される事になる(演算の元ねたが書き換わらない) 2つ目: 待たせたEXステージの出力を、nop命令に強制的に置き換える。 これによって後続のステージを一回休ませる事が可能で、結果的に"バブル"が入る事になる。 |
これらの作業は、想像できるように電子回路的に実現可能な仕組みになる。
バブルの挿入によるハザードの解消はCPU側による防御機構だけど、当然コンパイラ側もこのような状況が起きないように命令の並び替えを可能な範囲で調整する必要がある。
調整が不十分な場合、計算結果自体はおかしくならないがパフォーマンスが落ちるという問題が発生する。

コンピュータの構成と設計(上)
関連記事
コメントを残す