31 / 37 · 概念
乗加算パイプラインとデータの整列
演算をレジスタ境界で分割し、データ・valid・補助オペランドが同じトランザクションに属するよう設計する。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
計算式から二つの段へ
前提知識:符号付き算術、ノンブロッキングノンブロッキング代入 順序 RTL で <= と書く代入です。右辺を評価して更新を予約するので、同じエッジのレジスタが更新前の状態に基づいて計算できます。 詳しく見る代入、setupセットアップ 取り込みクロックエッジの前に、入力データが安定していなければならない最小時間です。違反すると記憶結果を保証できません。 詳しく見る 条件、valid 信号。
各入力トランザクションが符号付き 8 ビットの a・b と符号付き 16 ビットの c を含み、結果が次の式になるとする。累積器ではなく、各トランザクションを独立に処理する乗加算である。
8 ビット同士の積の正確な結果には 16 ビットが必要である。そこへ c を加えるとき、16 ビット同士で計算してから幅を増やしても、すでに失った上位ビットは復元できない。両オペランドを先に 17 ビットへ符号拡張してから足す。
第 1 段で積と c を一緒に格納し、第 2 段で同じトランザクションの二つの値を足す。c を遅延しないと、以前のトランザクションの積に現在のトランザクションの c が混ざる。
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 段 | 出力レジスタ |
|---|---|---|---|
| E0 | A | A の積と c | 無効 |
| E1 | B | B の積と c | A の結果、有効 |
| E2 | なし | 無効 | B の結果、有効 |
| E3 | なし | 無効 | 無効 |
A の結果は E1 直後に準備できる。同じクロックを使う次の回路は E2 でそれをサンプリングする。したがって、入力受け付け E0 から出力受信 E2 までは 2 周期である。遅延を説明するときに、出力レジスタが変わる時点と、次の回路が受け取る時点を混同してはならない。
各列は一つの立ち上がりエッジ立上りエッジ クロックが 0 から 1 に変わる瞬間です。CLK=1 の区間全体を指すレベルとは区別します。 詳しく見るの観測記録である。入力はエッジ直前、名前に「更新後」が付く状態は更新直後の値を示す。列の整列はサンプル順を示し、物理的な伝搬遅延伝搬遅延 入力が変わってから、出力が正しい値に安定するまでの時間です。論理式の等価性と時間特性は別の性質です。 詳しく見るを描いたものではない。トランザクションは A=(2,3,10)、B=(4,5,100) である。第 1 段は積と c を一緒に格納し、次のエッジで前段の値同士を足す。ダッシュは無効な区間を表す。
波形データを表示
| 信号 | 波形 | バスの値 |
|---|---|---|
| エッジ | 2345 | E0 → E1 → E2 → E3 |
| 受け付け | 234. | A → B → - |
| 更新後 product1 | 234. | 6 → 20 → - |
| 更新後 c1 | 234. | 10 → 100 → - |
| 更新後 y | 2345 | - → 16 → 120 → - |
| 更新後 out_valid | 01.0 |
パイプラインに分けるとどれだけ速くなるか
乗算の遅延 3.2 ns、加算の遅延 1.1 ns、レジスタのオーバーヘッド 0.2 ns という単純なモデルを考える。これらの値は計算用の仮定である。
最大周波数の上限は約 222 MHz から 294 MHz に変わる。二段だからといって、スループットが必ず 2 倍になるわけではない。最も遅い段とレジスタのコストが周期を決める。実際の結果は合成・配置・制約によって変わる。
トランザクション単位で検証する
入力を受け付けたら、整数参照モデルの a×b+c をキューへ入れる。出力 valid を受信するエッジで、先頭の期待値を取り出して比較する。in_valid=0 の区間も挟み、バブルがデータとともに進むかを確認する。c だけをトランザクションごとに大きく変えるパターンは、遅延の入れ忘れをよく検出する。
自分で考えてみましょう
連続するトランザクション 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 だけを正しく遅延しても、データのオペランドの整列が誤っていれば、この不具合は防げない。