Posts Issued on November 5, 2026

posted by sakurai on November 5, 2026 #1143

段間データの構造体化

これまでのPCパイプラインでは、段間FIFOを流れるのは Maybe#(int) のPCだけでした。実際のプロセッサでは、段が進むにつれて流す情報が増えます。IFでは命令コード、IDではデコード結果とレジスタ値、EXでは演算結果が加わります。

そこで、段間ごとに構造体を定義し、それをMaybeで包んでFIFOに流します。

typedef Bit#(32) Addr;
typedef Bit#(32) Data;
typedef Bit#(32) Inst;
typedef Bit#(5)  RIndx;

typedef struct { Addr pc; Bool epoch; }            PC2IF deriving (Bits, Eq);
typedef struct { Addr pc; Bool epoch; Inst inst; } IF2ID deriving (Bits, Eq);
typedef struct { Addr pc; Bool epoch; DecInst dInst;
                 Data rVal1; Data rVal2; }         ID2EX deriving (Bits, Eq);
typedef struct { Addr pc; DecInst dInst; Data result; } EX2MA deriving (Bits, Eq);
typedef struct { Addr pc; Maybe#(RIndx) dst; Data data; } MA2WB deriving (Bits, Eq);

DecInstはデコーダが返す構造体で、別の回で説明します。epochは分岐での取り消し用のタグです。

段間FIFOは次のようになります。

import SpecialFIFOs::*;

FIFO#(Maybe#(PC2IF)) ifs <- mkPipelineFIFO;
FIFO#(Maybe#(IF2ID)) ids <- mkPipelineFIFO;
FIFO#(Maybe#(ID2EX)) exs <- mkPipelineFIFO;
FIFO#(Maybe#(EX2MA)) mas <- mkPipelineFIFO;
FIFO#(Maybe#(MA2WB)) wbs <- mkPipelineFIFO;

EX段の例

waitの扱いはこれまでと同じです。wait中は上流をdeqせずに止め、下流にはinvalid(パイプラインバブル)を流します。

rule ex_stage;
   let m = exs.first;                       // Maybe#(ID2EX)
   if (!ex_wait) begin
      exs.deq;
      Maybe#(EX2MA) out = tagged Invalid;
      if (m matches tagged Valid .x)
         out = tagged Valid EX2MA { pc: x.pc, dInst: x.dInst,
                                    result: alu(x.dInst.aluOp, x.rVal1, x.rVal2) };
      mas.enq(out);
   end
   else
      mas.enq(tagged Invalid);
endrule

上流からinvalidが来た場合は、そのままinvalidを次段へ渡します。

この形にする利点

  • 段ごとに型が違うので、段を取り違えた接続はコンパイルエラーになる。
  • 項目を足すときは構造体に1行加えるだけで、FIFOの幅は自動で追従する。
  • valid bitはMaybeがMSBに1ビット付ける。記事(26)で見た33ビットのMaybe#(int)と同じ構造で、GTKWaveでもMSBを見ればバブルを判別できる。
  • 記事(23)で区別したデータパイプラインと制御パイプラインが、1つの型の中に同居する。valid bitが制御パイプライン、構造体の中身がデータパイプラインである。

Invalidのときのデータ部はbscが不定値として扱うので、データ部にはvalidによる選択回路が作られません。bsc 2025.07で生成したVerilogでは、<EX>→<MA>の段間データは次のようになりました。

assign mas_rv$port1__write_1 =
     { 1'd1,                                   // FIFOの内部フラグ
       exs_rv[134] && exs_rv[101] == exEpoch,  // Maybeのvalid bit
       exs_rv[133:102],                        // pc(素通し)
       exs_rv[100:64],                         // dInst(素通し)
       x__h2450 } ;                            // ALU結果(素通し)

計算されているのはvalid bitの1ビットだけで、pc、dInst、演算結果は上流の値がそのまま入っています。合成ツールの最適化を待たずに、bscの段階で「valid bitだけが制御パイプライン、中身はデータパイプライン」という構造になっています。


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