Altifigence Academy

31 / 37 · 概念

乗加算パイプラインとデータの整列

演算をレジスタ境界で分割し、データ・valid・補助オペランドが同じトランザクションに属するよう設計する。

計算式から二つの段へ

前提知識:符号付き算術、ノンブロッキングノンブロッキング代入 順序 RTL で <= と書く代入です。右辺を評価して更新を予約するので、同じエッジのレジスタが更新前の状態に基づいて計算できます。 詳しく見る代入、setupセットアップ 取り込みクロックエッジの前に、入力データが安定していなければならない最小時間です。違反すると記憶結果を保証できません。 詳しく見る 条件、valid 信号。

各入力トランザクションが符号付き 8 ビットの a・b と符号付き 16 ビットの c を含み、結果が次の式になるとする。累積器ではなく、各トランザクションを独立に処理する乗加算である。

y=a×b+cy=a\times b+c

8 ビット同士の積の正確な結果には 16 ビットが必要である。そこへ c を加えるとき、16 ビット同士で計算してから幅を増やしても、すでに失った上位ビットは復元できない。両オペランドを先に 17 ビットへ符号拡張してから足す。

第 1 段で積と c を一緒に格納し、第 2 段で同じトランザクションの二つの値を足す。c を遅延しないと、以前のトランザクションの積に現在のトランザクションの c が混ざる。

SystemVerilog
module multiply_add_pipeline (
    input  logic               clk, rst, in_valid,
    input  logic signed [7:0]  a, b,
    input  logic signed [15:0] c,
    output logic               out_valid,
    output logic signed [16:0] y
);
    logic v1;
    logic signed [15:0] p1, c1;
    logic signed [16:0] p_ext, c_ext;
    assign p_ext = {p1[15], p1};
    assign c_ext = {c1[15], c1};

    always_ff @(posedge clk) begin
        if (rst) begin
            v1        <= 1'b0;
            out_valid <= 1'b0;
        end else begin
            v1        <= in_valid;
            out_valid <= v1;
            if (in_valid) begin
                p1 <= a * b;
                c1 <= c;
            end
            if (v1) y <= p_ext + c_ext;
        end
    end
endmodule

このインターフェースは毎エッジ入力を受け付けられ、受信側も常に出力を受け取れると仮定する。out_valid=0 の y は比較しない。データレジスタレジスタ 複数ビットの状態を記憶する回路です。このコースの同期式レジスタは、クロックエッジで定められた入力を記憶します。 詳しく見るをリセットリセット 状態を仕様で定めた初期値に戻す制御です。同期式か非同期式か、他の制御より優先されるかを合わせて定めます。 詳しく見るしなくても、valid を初期化すれば使用しないデータを区別できる。

「二段」と観測時点を区別する

下表は、各エッジで入力を受け付け、エッジ直後のレジスタ状態を記録したものである。A と B は異なるトランザクションである。

エッジ受け付け入力第 1 段出力レジスタ
E0AA の積と c無効
E1BB の積と cA の結果、有効
E2なし無効B の結果、有効
E3なし無効無効

A の結果は E1 直後に準備できる。同じクロックを使う次の回路は E2 でそれをサンプリングする。したがって、入力受け付け E0 から出力受信 E2 までは 2 周期である。遅延を説明するときに、出力レジスタが変わる時点と、次の回路が受け取る時点を混同してはならない。

各列は一つの立ち上がりエッジ立上りエッジ クロックが 0 から 1 に変わる瞬間です。CLK=1 の区間全体を指すレベルとは区別します。 詳しく見るの観測記録である。入力はエッジ直前、名前に「更新後」が付く状態は更新直後の値を示す。列の整列はサンプル順を示し、物理的な伝搬遅延伝搬遅延 入力が変わってから、出力が正しい値に安定するまでの時間です。論理式の等価性と時間特性は別の性質です。 詳しく見るを描いたものではない。トランザクションは A=(2,3,10)、B=(4,5,100) である。第 1 段は積と c を一緒に格納し、次のエッジで前段の値同士を足す。ダッシュは無効な区間を表す。

積と c は同じトランザクションで合流する
波形データを表示
波形データ:各文字は一つの区間、ドットは前の状態の保持、p はクロック 1 周期を表します。
信号波形バスの値
エッジ2345E0 → E1 → E2 → E3
受け付け234.A → B → -
更新後 product1234.6 → 20 → -
更新後 c1234.10 → 100 → -
更新後 y2345- → 16 → 120 → -
更新後 out_valid01.0

パイプラインに分けるとどれだけ速くなるか

乗算の遅延 3.2 ns、加算の遅延 1.1 ns、レジスタのオーバーヘッド 0.2 ns という単純なモデルを考える。これらの値は計算用の仮定である。

Tone≥3.2+1.1+0.2=4.5 nsT_{\mathrm{one}}\ge3.2+1.1+0.2=4.5\ \mathrm{ns}
Tpipe≥max⁡(3.2,1.1)+0.2=3.4 nsT_{\mathrm{pipe}}\ge\max(3.2,1.1)+0.2=3.4\ \mathrm{ns}

最大周波数の上限は約 222 MHz から 294 MHz に変わる。二段だからといって、スループットが必ず 2 倍になるわけではない。最も遅い段とレジスタのコストが周期を決める。実際の結果は合成・配置・制約によって変わる。

トランザクション単位で検証する

入力を受け付けたら、整数参照モデルの a×b+c をキューへ入れる。出力 valid を受信するエッジで、先頭の期待値を取り出して比較する。in_valid=0 の区間も挟み、バブルがデータとともに進むかを確認する。c だけをトランザクションごとに大きく変えるパターンは、遅延の入れ忘れをよく検出する。

参考資料:MIT OpenCourseWare — Performance Measures

自分で考えてみましょう

連続するトランザクション A=(a=3,b=-4,c=10)、B=(a=2,b=5,c=100) を与える。正しい出力順序と、c を遅延せず現在の入力 c を足したときに A で生じ得る誤った結果を求めよ。

解説を見る

正しい結果は A が -12+10=-2、B が 10+100=110 である。A の積が第 2 段にあるとき、現在の c が B の 100 なら 88 になる。valid だけを正しく遅延しても、データのオペランドの整列が誤っていれば、この不具合は防げない。

選択はこのブラウザに適用されます。フッターからいつでも変更できます。