公開日:2026/03/07
title.png

38th Season


#38-5 Nihoniumパイプライン実装(Its)

 Cmod A7ボード(FPGA)を使って開発しているオリジナル32ビットCPUコアNihoniumの実装方式として、パイプライン方式を採用してみたいと思います。

 前回、パイプラインハザードの1つである分岐ハザードについての検討を実施しました。今回も引き続きパイプラインハザードの事例研究を進めたいと思います。

多サイクル実行命令

 ARMの教科書に記載されていた3段パイプライン(IF/ID/EX)が、ほとんどの複雑な動作の命令でEXステージが複数サイクルとなり、「ステートマシン方式と変わらない」と、指摘しました。
 ほとんど全ての命令で、その様な事態になることは否定的なのですが、逆に、ほんの数命令しか存在しない、複雑な動作をする命令のためだけにパイプラインの段数を増やすことも、同様に否定的です。本節では、EXが複数サイクルに渡る複雑な命令への対処について考察して行きます。

多サイクル演算命令

 演算に複数サイクル掛かる、乗算命令/除算命令/浮動小数演算命令などは、EXステージを必要数延長しながら実行します。結果、同時並列に実行されるIF/ID等のステージも、EXステージが空くまで先へ進めないため、ストールして待機する事になります(下図)。

a385_pipe_mexe.png

 これは、EXステージの資源を、1つの大きな演算器としてとらえた結果、演算時間が複数サイクルに渡る場合に生じるハザードです。

 1つのアイディアとしては、例えば以下の様に演算器を細分化して、

 EXステージに到達した命令が、空いている演算器に投入されることを許容する手法があります。
 ただ、この手法は、演算器毎に演算終了までの時間(サイクル数)が異なり、投入順番と終了順番が一般的に一致しません(下図)。

a385_pipe_ooo.png

 アウトオブオーダ(OoO;Out of Order)的な動作となるので、制御が複雑となります。

 制御としては一番簡易なので、最初に示したパイプライン実行図の通り、複数サイクルのEXステージが終了するまで他のステージがストールして待機する方式を選択します。まずは、容易で簡易な方式を選択して進める、と言う方針で良いでしょう。

多サイクルメモリアクセス命令

 CPUからアクセスするメモリ空間の中には、アクセスに数サイクルを要する空間が存在します(例えばSRAMC512K空間など)。CPUに比べて、遅いメモリが接続されていた場合です。
 Nihoniumのメモリバスは、brdy信号がネゲートされている間、前のバスアクセスが実行中のため待機が必要です(下図およびリンク参照)。

a154_mem_bus.png

 上図の様なメモリバスプロトコルをパイプラインのステージに読み替えると、

 つまり、MAステージとWBステージでbrdy待ちが発生して、複数サイクル必要になるケースがあります。アクセスに3サイクルを要する空間へアクセスを実行する命令のパイプライン実行図を以下に例示します。

a385_pipe_mema.png

 ldl命令のMAステージおよびWBステージにて、brdy=0の期間を待機するため、ストールが発生している様子です。2サイクルのbrdy=0期間ストールして、3サイクル目にbrdy=1となった事で、ステージが進みます。

 この様なケースも、アウトオブオーダ的に「先に進められるステージがあれば、どんどん進めてしまえ」という考え方により、パフォーマンスの向上が期待できます。
 ある意味「腕の見せ所」では、あるのだけど、一気に処理が複雑となります。工夫したことで、何処に、どのような影響が波及するか、慎重に検討しなくてはなりません。よって一旦、不採用とします。この場合も、他のステージがストールしてタイミングを合わせ、同期動作する事とします(上図の通り)。

 まず、パイプライン方式にてNihoniumを実装して、全体動作させることを優先して行きましょう。

連続メモリアクセス

 ここで、パイプラインハザードから離れて、パイプライン方式を適応したことにより享受できる長所についても触れておきたいと思います。下図はpopl命令で複数レジスタをスタックから読み出して回復する場合のパイプライン実行図です。

a385_pipe_memc.png

 パイプライン方式を採用した場合(上図上半分)と、現行のステートマシン方式(上図下半分)との実行状態を比較できるように図示して見ました。

 命令の実行時間に着目すると、パイプライン方式のpopl命令×4個の実行時間は正味4サイクルです。一方のステートマシン方式は、と言うと、プリフェッチ1段しか並列実行できないので、正味8サイクル掛かっています。実に、パイプライン方式の方が×2倍速⏩️と圧倒的に高速です。

 今回のパイプライン方式による実装の効果が、一番はっきりと観測できる例でした。

複雑な動作を実行する命令

 NihoniumのISAをパイプライン方式で実装するに当たり、最適な段数を考察していた時、一旦「見て見ぬふり」で通り過ぎてきたldwsp命令について考察しておきます(下図)。

a385_pipe_ldwsp.png

 IF/ID/EX/MA/WBの、5段パイプラインを採用していたら、パイプラインハザードは発生しませんでした。今回採用した4段パイプラインでは、EAステージがEX+MAの2サイクルに延長されるため、その間、後続命令のIF/IDステージがストールして待機する事になります(上図の通り)。

 この系統の命令(以下4個)だけなので、パイプラインハザード発生を許容します。

16ビット/32ビット即値命令

 Nihoniumには、16ビット/32ビット即値を取る、多ワード長命令が2個存在します。

 これらの命令は、オペランドアクセス(メモリバス経由)ではなく、フェッチアクセス(フェッチバス経由)にて、即値を読み込みます。ldli命令のパイプライン実行図を以下に示します。

a385_pipe_ldli.png

 1個目と2個目のEXステージが、後続のIDステージを乗っ取って(ID’)、フェッチデータを拡張命令レジスタeirに取り込みます。最後のEXステージにて、宛先レジスタr0=Eirのデータ転送を実行します。
 パイプラインハザードは発生しませんが、32ビット即値をフェッチするため、3サイクルを要します。この実行サイクル数は、ステートマシン方式Nihoniumと同じです。


 これにて、一通りの検討が済んだと思います。次回、全体を組み上げてみたいと思います。


btn_arduino.png

btn_prev.png btn_next.png

home