Posts Tagged with "Pipeline processor"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.
posted by sakurai on October 20, 2022 #533

「ハードウェアインタプリター」に食わせる機械語列が必要です。そこで、この記事を参考に、Fibonacciプログラムをコンパイルし機械語化しました。試みにFibonacciが通るための「インタプリター」を書いていきます。前述のとおりこの「インタプリター」は実行ステージが逆アセンブル相当の表示をするだけのものです。

入力するFibonacciのソースは以下のように短いプログラムです。

fibo.c:

int fib(int n) {
  if(n <= 1) return 1;
  return fib(n-1) + fib(n-2);
}
int main() {
  fib(10);
  for(;;) {}
  return 0;
}

これをクロスコンパイルしてRISC-Vの命令を作成し、BSVの入力とします。シーケンサの自動生成を利用して1サイクル毎に、命令デコーダに命令を供給します。

Stmt main = seq
    instr <= 32'h074000ef;
    instr <= 32'hfe010113;
    instr <= 32'h00112e23;
    instr <= 32'h00812c23;
    instr <= 32'h00912a23;
    instr <= 32'h02010413;
    instr <= 32'hfea42623;
    instr <= 32'hfec42703;
    instr <= 32'h00100793;
    instr <= 32'h00e7c663;
    instr <= 32'h00100793;
    instr <= 32'h0300006f;
    instr <= 32'hfec42783;
    instr <= 32'hfff78793;
    instr <= 32'h00078513;
    instr <= 32'hfc9ff0ef;
    instr <= 32'h00050493;
    instr <= 32'hfec42783;
    instr <= 32'hffe78793;
    instr <= 32'h00078513;
    instr <= 32'hfb5ff0ef;
    instr <= 32'h00050793;
    instr <= 32'h00f487b3;
    instr <= 32'h00078513;
    instr <= 32'h01c12083;
    instr <= 32'h01812403;
    instr <= 32'h01412483;
    instr <= 32'h02010113;
    instr <= 32'h00008067;
    instr <= 32'hff010113;
    instr <= 32'h00112623;
    instr <= 32'h00812423;
    instr <= 32'h01010413;
    instr <= 32'h00a00513;
    instr <= 32'hf7dff0ef;
    instr <= 32'h0000006f;
endseq;

今回はまだデコーダのテストだけなので、PCは実装していません。


左矢前のブログ 次のブログ右矢

posted by sakurai on October 18, 2022 #532

図532.1にBSV版のBitpatのREADMEに掲載されていた使用例を示します。

図%%.1
図532.1 BSVのBitpat関数

挙げられた例はちょうどRISC-Vの命令パターンに一致しており、add命令とaddi命令のデコード部分を示したものです。この要領で、次々に他の命令を実装していくことができます。

本ライブラリの動作は以下の2ステップとなっています。

  1. whenの中のパターンマッチでは可変部をvで表し、固定部をnとビットパターンで記述します。vの幅を指定しないで良いのは使いやすそうです。whenの最後に識別した機能を解釈するための関数名を記述します。
  2. マッチした後に呼ばれる関数では、可変部のみを変数で受け(固定部は捨てる)、処理を実行します。結局vの幅は意識しなければなりません。

このように最初に固定部、次に可変部という考え方に慣れる必要があります。最初は使いにくいと感じましたが、慣れれば気にならないのかもしれません。

プロセッサ設計と言ってもパイプラインでなければ、見方を変えれば、RISC-V機械語のインタプリターをHDLで作成するだけなので、それほど難しいことではありません。ひとつずつデコードし、対応する処理を実装していくだけです。この「ハードウェアインタプリター」の1段目はデコードステップで、2段目は実行ステップになります。


左矢前のブログ 次のブログ右矢

RISC-Vプロセッサの設計

posted by sakurai on October 17, 2022 #531

「はじめてのCPU自作」という本を購入したので、これを参考にRISC-Vプロセッサを設計します。ただし、この本ではChiselベースとなっていますが、本稿ではBSVベースとします。またパイプラインプロセッサの経験があるため、最初からパイプラインプロセッサを設計します。

さて、この本を読んでいたらChisel(だかScalaだか)にはBitpatという便利な機能があるようです。

図%%.1
図531.1 ChiselのBitpat関数

命令デコーダを書くのに便利そうなので、BSVにもないのか調べたら、GithubにBitpatという似たようなものがありました。これはAlexandre Joannouさんが作成されたものであり、Readmeには以下のように書いています。

BitPat BitPat is a bit-string pattern matching library for Bluespec, inspired by Morten Rhiger's "Type-Safe Pattern Combinators".

BSVにおいてのパターンマッチライブラリとのことです。便利そうなのでRISC-Vの命令デコーダに採用することにします。


左矢前のブログ 次のブログ右矢

Pipeline processorの設計 (19)

posted by sakurai on March 1, 2022 #469

パイプライン制御の一般化

前項までの議論を一般化すれば、ステージSにおいての前段へのウエイト信号$\mathrm{W_{S-}}$と後段への有効信号$\mathrm{V_{S+}}$は

$$ \begin{eqnarray} \mathrm{W_{S-}}&=&\mathrm{W_S }\cup\mathrm{W_{S+}}\\ \mathrm{V_{S+}}&=&N(\mathrm{!W_S }\cup\mathrm{W_{S+}})\ \cap\ !\mathrm{C_{S+}} \end{eqnarray} \tag{469.1} $$ ただし、 $$ \mathrm{W_{S+}}: 下位ステージS+からSへのウエイト信号\\ \mathrm{C_{S+}}: 下位ステージにおけるキャンセル信号\\ N(): 時相論理、次のクロックサイクルの値 $$ とする。

何事も分かってしまえば簡単なのですが、パイプライン制御の秘密は、この論理469.1にあります。


左矢前のブログ 次のブログ右矢

posted by sakurai on February 28, 2022 #468

パイプライン制御の無効化論理

図%%.1
図468.1 CV32E40Pパイプライン図(再掲)

一方、後段にバブルを流すinvalidate信号は、EXWBレジスタのC(lear)信号ですが、やや冗長になっています。EXWB.C信号に(467.1)を代入し、ドモルガンの定理を用いて整理すれば、 $$ \require{cancel} \begin{eqnarray} \text{EXWB.C}&=&\text{wb_ready }\cap\text{!ex_valid}\\ &=&\text{wb_ready }\cap\text{(!granted }\cup\text{!wb_ready)}\\ &=&\text{(wb_ready }\cap\text{!granted) }\cup\bcancel{\text{(wb_ready }\cap\text{!wb_ready)}}\\ &=&\text{wb_ready }\cap\text{!granted} \end{eqnarray} \tag{468.1} $$ 使用ゲートは同じで、配線を繋ぎ変えるだけで1段論理になるので、2段通すのは若干無駄な論理のように見えます。論理合成を用いれば上記のように最適化されるでしょうけど。

図468.2に修正後の回路を示します。

図%%.2
図468.2 論理修正後パイプライン制御論理

前稿等で検討したように、パイプラインバブルは後段へ流すものですから、当該ステージ、この場合は<EX>にウエイト要因があり($=\text{!granted}$)、かつ後段である<WB>からウエイトが来ていない($=\text{wb_ready}$)ときに限り、後段を無効化する論理となり、(468.1)は正しいです。そして、この無効化信号は、パイプラインストリームのキャンセルにも用いられます。

パイプライン制御の有効論理

EXWB.Cの反転論理である<EX>有効信号を新たに$ex\_valid$とすれば、 $$ \require{cancel} \begin{eqnarray} ex\_valid&=&\text{!wb_ready }\cup\text{granted }\\ &=&wb\_wait\text{ }\cup\text{ }!exs\_wait \end{eqnarray} \tag{468.2} $$ となります。


左矢前のブログ 次のブログ右矢

posted by sakurai on February 25, 2022 #467

既存RISC-Vの研究

このページでパイプライン制御の図を見つけたので、以下に示します。これは4ステージのインオーダーパイプラインプロセッサです。

図%%.1
図467.1 CV32E40Pパイプライン図

パイプライン制御の論理を追ってみます。EXWBは<EX>と<WB>を分離するレジスタです。通常は<EX>の結果を保持するのでEXパイプラインレジスタと呼びますが、もちろん<WB>の入力でもあるため、この設計ではそれをわかりやすいようにEXWBとしているようです。

パイプライン制御のイネーブル論理

まずイネーブル論理を見てみると、 $$ \begin{eqnarray} \text{EXWB.E}&=&\text{ex_valid}\\ &=&\text{ex_ready}\\ &=&\text{granted }\cap\text{wb_ready} \end{eqnarray} \tag{467.1} $$ これは

  • <EX>が正当であるという、後段に対する有効論理、かつ
  • <EX>以降のステージが受け入れ可能という、上段に対するイネーブル(ウエイトの否定)

を同時に意味します。

上段に対するウエイト信号を新たに$ex\_wait(=\text{!ex_ready})$とし、<EX>の許可信号である$\text{granted}$の逆論理を、<EX>のステージウエイトとして新たに$exs\_wait(=\text{!granted})$と名付ければ、

$$ \begin{eqnarray} ex\_wait&=&\text{!ex_ready}\\ &=&\text{!(granted }\cap\text{wb_ready)}\\ &=&\text{!granted }\cup\text{!wb_ready}\\ &=&exs\_wait \cup wb\_wait \end{eqnarray} \tag{467.2} $$ ただし、$wb\_wait=!\text{wb_ready}$

前述のように、ウエイトは、当該段のウエイトに下段のウェイト信号のORを取りながら、上段へパイプラインとは逆向きに同一サイクル中に送るので、これは正しいです。


左矢前のブログ 次のブログ右矢

posted by sakurai on February 24, 2022 #466

EIT処理

EIT処理は

  • <MA>においてEITの優先度判定し、最弱のEITを識別
  • EITスタックにEITの分岐先(EITハンドラ先頭アドレス)をストア
  • EIT原因を無効化

以上を繰り返し、最後のひとつに対してはスタックに格納せずにEIT種別による分岐先をPCに格納します。

これはメモリアドレス計算やメモリアクセスを伴うので<ID>のFSMにより実行します。後続の命令はキャンセルされます。以下に具体例を示します。

  • 1の命令の<MA>でそのパイプラインストリームで集められたEIT要因を判定します。前後のストリームは見ません。
  • 後続命令ストリームをキャンセルします。具体的には2, 3, 4, 5までがパイプラインに入っているので全てキャンセルします。
  • <MA>から<ID1>に乗り換えることから、<ID>より前の<IF>と<PC>を持つ、後続の4, 5の命令はキャンセルされるだけでなく、ウエイトされます。
  • <ID1>のFSMが起動され、弱いEIT先行してEIT分岐先をスタックに格納します。この場合はE1が最弱でありスタックに格納すると同時にEIT要因を無効化します。同時にSPを+4します。
  • 次に<ID2>のFSMが起動され、同様にEIT分岐先をスタックに格納します。SPは前パイプライン<EX>からバイパスします。同時にEIT要因を無効化し、SPを+4します。
  • 最後に<ID3>のFSMが起動され、残った最強のEIT分岐先を<EX>で計算します。
  • そのサイクルは分岐先の<PC>と同一であり、6のEITハンドラが起動されます。

図%%.1
図466.1 パイプラインハザード

図%%.2
図466.2 パイプラインハザード

左矢前のブログ 次のブログ右矢

posted by sakurai on February 23, 2022 #465

EIT処理

通常の命令処理以外の処理、例外(Exception)、割り込み(Interrupt)、トラップ(割り出し、Trap)を総称してEIT処理と称します。 パイプラインの各ステージで検出されるEITを以下に示します。

  • <PC>: 奇数命令アドレスジャンプ例外
  • <IF>: 命令フェッチアドレス例外、デバッグトラップ
  • <ID>: 無効命令例外、ゼロ除算例外(命令が除算かつdivisorソースレジスタの内容がゼロ)
  • <EX>: 無し
  • <MA>: メモリアクセスアドレス例外、デバッグトラップ
  • <WB>: 無し

割り込みはいずれのパイプラインステージで検出されるか、保留します。 さて、各ステージでEITが検出されると、前後の命令最大4命令でEITが検出されることになり、後続命令のEITが時間的に先に検出されることになります。これを処理すると命令の前後関係が逆転するために、EITを検出するパイプラインステージを揃えることを考えます。

この中で最も時間的に遅いのが、<MA>であるため、割り込みもここで検出することにします。パイプラインストリーム中に検出されたEITは全てパイプラインレジスタで<MA>まで保留し、ここで優先度を判定し、弱い順にEIT要因をスタックに格納します。従って、EITハンドラは優先度の高い順から実行し、弱い順にハンドラ実行することになります。

例外とトラップ、割り込みの違い

EITはいずれも命令ストリームの途中で起きる例外事象ですが、以下のような違いがあります。

  • E(xception): 当該命令は取り消される。具体的には<MA>で起こる場合はメモリに対する書き込みを無効化する。その他の場合は<WB>において書き込みを無効化(パイプラインキャンセル)する。
  • I(nterrupt): 当該命令の後に割り込みハンドラに分岐するので、当該命令は実行する。
  • T(rap): 当該命令の後にトラップハンドラに分岐するので、当該命令は実行する。

左矢前のブログ 次のブログ右矢

posted by sakurai on February 22, 2022 #464

分岐命令

一般的な5段パイプラインを解説してきましたが、具体的なプロセッサを対象にして検討します。対象は何でも良いので、ここではRISC-Vとします。その理由は命令セットがオープンであり、誰でも自由に使用することができるためです。

さて、前述の分岐命令は一般的にフラグを見て分岐判定を先行して行うRISCプロセッサでした。前の命令の<EX>で演算後にフラグを立て、その結果で次の分岐命令のデコードである<ID>終了時には分岐命令であり、かつ分岐条件が成立しているため、バブルは1$\tau$となります。

しかしながら、RISC-VのISAからの引用の図464.1を見ると、RISC-Vの条件分岐命令はレジスタをテストして、その結果で分岐するため、バブルは2$\tau$となります。そのため、前稿のフラグベースのアーキテクチャよりもこのアーキテクチャのほうが分岐レイテンシが長くなり、性能が悪いことになります。

図%%.1
図464.1 beq命令

図%%.2
図464.2 パイプライン図

それにもかかわらず、RISC-Vにおいて条件フラグを廃したのは、スーパスカラ化を考えると条件フラグの資源競合が起きやすくなるためだと思われます。


左矢前のブログ 次のブログ右矢

posted by sakurai on February 21, 2022 #463

キャンセル

パイプラインウエイトと並んで重要な制御がパイプラインキャンセルです。パイプライン中に例外的な事象が起き、そのパイプラインストリームを無効化します。これは前稿で述べたバブルと同様に、あるステージを無効化(Valid信号=false)することで実現します。パイプライン中をinvalidが流れ、最後に<WB>においてレジスタに書き込まないことで実現します。<MA>においてはinvalidである場合にはメモリアクセスFSMは起動しません。

分岐キャンセル

分岐の場合の具体例を示します。分岐先及び非分岐先(+4)を投機的に計算しておくことで、相対分岐の高速化を図ります。これは分岐命令をデコードした時点で既に前の命令で演算フラグが確定しているものとしています。

図%%.1
図463.1 パイプライン図

1の命令が相対分岐であったとき、<ID>において命令をデコードと並行して相対分岐だと思って分岐先を計算します。タイミングとしては3の命令の<PC>と同一です。その命令の最後に相対分岐かどうかが確定し、かつ条件分岐の成立が確定するので、PC選択のマルチプレクサにおいて、分岐先を選択します。同時に後続命令である2のパイプラインストリームにinvalidを流し、パイプラインキャンセルを実行します。

図%%.2
図463.2 パイプライン図

左矢前のブログ 次のブログ右矢


ページ: