61 / 63 · 概念
停止可能なパイプラインとバックプレッシャー
段ごとに ready を計算し、停止中や同時移動時のデータの欠落・重複・再計算を防ぐ。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
受信側が毎回は受け取れない場合
前提知識:1 要素の ready/validハンドシェイク 送信側の valid と受信側の ready が同じエッジでともに 1 のときに転送を確定する取り決めです。待機中のデータ保持規則も含みます。 詳しく見る バッファと、パイプラインパイプライン 計算をレジスタで区切った段に分け、複数入力を重ねて処理する構造です。データ・valid・制御信号の段をそろえる必要があります。 詳しく見るでのデータの整列。
固定速度のパイプラインに out_ready だけを加えて出力を止めても不十分である。最終段が満杯で停止したら、前の段もデータを上書きしてはならない。停止要求を入力側へ伝えるバックプレッシャーバックプレッシャー 受信側が一時的に受信できないことを送信側へ伝え、データの流れを止める方式です。停止中は未転送データを保持しなければなりません。 詳しく見るを設計する必要がある。
二つの段を、それぞれデータ 1 項目と valid 1 ビットで表す。第 2 段は空であるか、現在の出力を渡せるときに新しい値を受け取れる。第 1 段にも同じ規則を適用する。
外部の in_ready は r1 である。r2=0 でも第 1 段が空なら、新しい入力を 1 項目受け付けられる。出力が止まるたびにすべての入力を無条件に拒否するより、記憶領域を有効に使える。
各段は移動が許可されたときだけ更新する
次の例は、符号なしunsigned ビット列を非負整数として解釈する規則です。n ビットの範囲は 0 から 2^n−1 で、同じビット列の signed 解釈と異なる場合があります。 詳しく見る 8 ビットの x に対して y=2(x+1) を計算する。第 1 段は x+1 を 9 ビットで格納し、第 2 段は 10 ビットにして 1 ビット左へシフトする。最大入力 255 の結果である 512 まで、正確に表現できる。
module stallable_pipeline (
input logic clk, rst,
input logic in_valid,
output logic in_ready,
input logic [7:0] in_data,
output logic out_valid,
input logic out_ready,
output logic [9:0] out_data
);
logic v1, v2, r1, r2;
logic [8:0] d1;
logic [9:0] d2;
assign r2 = !v2 || out_ready;
assign r1 = !v1 || r2;
assign in_ready = r1;
assign out_valid = v2;
assign out_data = d2;
always_ff @(posedge clk) begin
if (rst) begin
v1 <= 1'b0;
v2 <= 1'b0;
end else begin
if (r2) begin
v2 <= v1;
if (v1) d2 <= {d1, 1'b0};
end
if (r1) begin
v1 <= in_valid;
if (in_valid) d1 <= {1'b0, in_data} + 9'd1;
end
end
end
endmoduleノンブロッキングノンブロッキング代入 順序 RTL で <= と書く代入です。右辺を評価して更新を予約するので、同じエッジのレジスタが更新前の状態に基づいて計算できます。 詳しく見る代入なので、第 2 段は同じエッジで新しく計算した d1 ではなく、以前の d1 を受け取る。r1=0 なら v1・d1 を保持し、r2=0 なら v2・d2 を保持する。valid だけを保持し、データを更新し続ける実装は誤りである。
両段が満杯のときを追う
最初に v2 には A の結果、v1 には B の中間値があり、入力 C が待機しているとする。
| out_ready | in_ready | エッジでの動作 |
|---|---|---|
| 0 | 0 | A・B を保持、C は受け付けない |
| 0 | 0 | A・B を保持、C は受け付けない |
| 1 | 1 | A を渡し、B を第 2 段へ移し、C を受け付ける |
送信側は C が受け付けられるまで、C と in_valid を保持する。最初の停止エッジで C を送ったつもりで次のデータを出すと、C が失われる。
各列は立ち上がりエッジ立上りエッジ クロックが 0 から 1 に変わる瞬間です。CLK=1 の区間全体を指すレベルとは区別します。 詳しく見る直前の値である。初期状態では最終段に A、第 1 段に B があり、C が入力で待機している。E2 で A を渡して C を受け付けた後、E3 直前の出力は B に変わる。
波形データを表示
| 信号 | 波形 | バスの値 |
|---|---|---|
| エッジ | 2345 | E0 → E1 → E2 → E3 |
| out_ready | 0.1. | |
| out_valid | 1... | |
| out_data | 2..3 | A → B |
| in_valid | 1... | |
| in_ready | 0.1. | |
| in_data | 2..3 | C → D |
検証する保存則
reset 後の入力受け付け数を Nin、出力転送数を Nout とすると、格納量は次のようになる。reset エッジでは転送を数えない。
個数だけが合っていても、A を二度出して B を捨てる不具合は見逃せるため、参照キューで値と順序も検査する。入力 valid と出力 ready を独立に変化させ、長時間の停止、連続処理、reset による内容の破棄を含める。
ready 経路もタイミング経路である
段を長く接続すると、r2→r1 のような逆方向の組み合わせ経路も長くなる。レジスタ一つで ready を遅延させると、その遅延中にすでに受け付けを約束した入力を保持する追加領域が必要になる場合がある。記憶領域とプロトコルを考えずに ready にフリップフロップを入れるだけでは、安全なタイミング最適化にはならない。
自分で考えてみましょう
v1=0、v2=1、out_ready=0 とする。in_valid=1 の入力を受け付けられるか。次のサイクルも out_ready=0 なら、追加の入力を続けて受け付けられるか。
解説を見る
最初は r2=0 だが r1=1 なので、1 項目を受け付けて第 1 段を満たせる。エッジ後に v1=v2=1 となると r1=r2=0 になり、それ以上は受け付けられない。この違いが二つの格納場所を実際に活用する条件である。