ここまでは、命令のデコードを行うと、各部のマルチプレクサなどが適当にコントロールされて、パイプラインの各段で必要な動作が行える仕組みが存在すると仮定してきたが、それをどのようにして実現するかを考えてみよう。
パイプラインが正しく動作するためには、各種のハザードを避けるようにストールサイクルを挿入してやる必要がある。しかし、1つしかない資源(演算器など)を2つの命令が使用する状況をその直前に検出して、一方の命令の処理をストールさせれば良いというわけには行かない。まあ、ここで例題としている程度の簡単なパイプラインであれば、途中で止めることも可能であるが、パイプラインが長くなりあちこちで資源競合が発生したり、複数のパイプラインがあったりすると、止めるべきところを全部、同時に止めるのは中々難しい。
パイプラインであるので、処理すべき命令が次々と流れてくる。ここで一つの命令の実行を止めると、ベルトコンベヤーの流れ作業で処理が滞ったように、後続の命令が山積みになってあふれてしまう。そして流れ作業の場合は、山積みになってもモノは無くならないが、パイプラインの場合は、次々と異なる命令の処理指令が流れてきて上書きされてしまうので、処理の滞った命令はなくなってしまい、後で処理するというわけにはいかない。
読者の諸氏は、予定表でスケジュールを管理しておられると思うが、会議をスケジュールしようとすると、出席者の都合を調整することに加えて、共通資源である会議室を予約しないと、会議を予定表には書き込めない。パイプライン実行も同じであり、出席者の都合はオペランドがいつ使用可能になるかに相当し、会議室は命令の実行に必要な資源に相当する。このように考えて、その命令の実行に必要な全ての資源が予約できないと、命令の実行を開始しないというやり方でパイプラインを制御する。
データ依存性のチェック
出席者が揃わないと会議が開けないように、オペランドが揃わないと命令の実行はできない。従って、パイプラインで命令を処理しようとするには、まず、オペランドの状態をチェックする必要がある。
一般には、オペランドはレジスタに格納されているデータであり、読み出せばすぐに使えるのであるが、前に述べたように、先行する実行中の命令の結果をオペランドとして使用するデータ依存性がある場合には、すぐにオペランドが揃わない可能性がある。
この関係を正しく認識して処理しないと、前のLD命令の結果を使ってメモリからレジスタに読み込んだデータを使ってADDをするべきところが、LD前のレジスタの値に基づいてADDを行ってしまうことになってしまい、正しい結果が得られない。
図4.5 Validビットを持つレジスタ構造 |
このデータ依存性を検出するためには、図4.5のように、レジスタの各エントリにバリッド(Valid:正当)ビットを設けて管理する。例えば、R1レジスタに結果を書き込む命令がデコードされて実行を開始すると、現在のR1の内容はその命令で書き換えられてしまうので、後続の命令では使用できない。これ表現するため、R1レジスタのValidビットを"0"(Invalid)とする。そして、その命令の実行が終わり、処理結果がR1レジスタに書き込まれると、Validビットを"1"(Valid)にする。
そして、ある命令のデコードサイクルに、使用する全てのオペランドレジスタがバリッドであれば実行を開始し、インバリッドのオペランドがある場合は、未完の先行命令の結果に依存するオペランドであるので、この先行命令の処理の終了を待ち合わせるという制御を行う。
この様子をもう少し詳しく説明すると、次の図のようになる。まず、全てのレジスタには、以前の命令で何かに値が書き込まれているとする。従って、図4.5のように、各レジスタエントリのValidビットは"1"になっている。
この状態で、LD [R0]→R1 (R0の内容で指されるメモリの内容を読んでR1に書き込む)という命令を実行するとR1の値が更新される。つまり、現在のR1の内容は古い値であり、以降の命令でその値を使ってはならないので、LD命令をデコードして実行サイクルに入る時にバリッドビットを"0"クリアして図4.6の(1)の状態とする。
図4.6 LD命令とADD命令の実行状況 |
そして、その次の命令が、ADD R0+R1→R2と、前のLD命令で読まれるR1の値に依存する命令とすると、ADD命令のデコード時に、ADD命令のオペランドであるR0とR1レジスタのValidビットをチェックする。両方のオペランドがValidであれば演算を実行することが出来るが、この場合は、(1)のようにR1のValidビットは"0"であるので、ADD命令の実行は開始できず、ストールすることになる。
そして、LD命令によるメモリからのデータ読み込みが終わりR1にデータが格納されると、R1のValidビットが"1"にセットされ(2)のようになる。これで、両方のオペランドがValidとなりADD命令の実行が開始される。
このADD命令はR2レジスタに結果を書き込むので、ADD命令の開始時点でR2レジスタの内容はValidでは無くなり、(3)に示すように、R2レジスタのValidビットが"0"クリアされる。そして、ADD命令が終わり結果がR2レジスタに書き込まれる時点で、(4)のようにR2レジスタのValidビットが"1"にセットされる。
このようにして各レジスタエントリの状態を管理し、オペランドがValidになるのを待って実行を開始することによりデータ依存性を正しく処理することができる。ここではオペランドが得られるかどうかのチェックについて述べたが、命令の実行を開始するには、次に述べる、演算器などの実行資源が使用できるかどうかのチェックも必要となる。