11 / 24 · 実習
実習:2段とバブルを追跡する
更新前の状態から次状態を計算し、出力のタグ、値、エッジを比較します。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
同時に更新されるレジスタをモデル化する
2段の関数f(a,b)=3(a+b)のサイクルモデルを作ります。各反復が1つの立ち上がりエッジを表します。最初に更新前のs2を消費します。次に更新前のs1から新しい第2段を計算し、その後両段を更新します。s2を計算する前にs1を更新すると、入力が1つのエッジで2つのレジスタを横断する誤った動作になります。
プロジェクトにpipeline-model.mjsを手動で追加して保存し、Node.jsの端末でnode pipeline-model.mjsを実行します。エッジ0から3で4入力を受け入れ、エッジ4と5で排出します。この常時readyの契約ではすべての入力が受け入れられます。Nullは空の段を意味し、数値のゼロではありません。有効なゼロの結果もトランザクションです。
import assert from 'node:assert/strict';
const inputs = [[1,2], [250,10], [100,0], [255,255]];
let s1 = null, s2 = null;
const seen = [];
for (let edge = 0; edge < 6; edge++) {
if (s2 !== null) seen.push({ edge, ...s2 });
const next2 = s1 === null ? null : {
tag: s1.tag, y: (s1.sum * 3) % 256
};
const next1 = edge < inputs.length ? {
tag: edge, sum: inputs[edge][0] + inputs[edge][1]
} : null;
s1 = next1;
s2 = next2;
}
assert.deepEqual(seen, [
{ edge: 2, tag: 0, y: 9 },
{ edge: 3, tag: 1, y: 12 },
{ edge: 4, tag: 2, y: 44 },
{ edge: 5, tag: 3, y: 250 }
]);
assert.equal(s1, null);
assert.equal(s2, null);
console.log('PIPELINE PASS: 4 ordered results, latency=2');| エッジ | 受け入れた入力 | 引き渡した出力 |
|---|---|---|
| 0 | tag0:1+2 | なし |
| 1 | tag1:250+10 | なし |
| 2 | tag2:100+0 | tag0:y9 |
| 3 | tag3:255+255 | tag1:y12 |
| 4 | バブル | tag2:y44 |
| 5 | バブル | tag3:y250 |
2番目のトランザクションは和260を保持し、780を12に縮小します。最後のものは1530を250に縮小します。エッジ、タグ、値を明示して比較すると、算術だけでなくタイミングの誤りも検出できます。排出時のバブルは両段を空にし、古い出力の繰り返しを防ぎます。
入力がない場合を調べるには、選んだ受け入れエッジにnullを挿入し、受け入れたトランザクションに対してのみタグを増やします。出力の空きはバブルに追従し、有効なトランザクションのレイテンシは2エッジのままです。このモデルには出力バックプレッシャーがありません。次の章で弾力的な動作を追加します。Node検査の成功が検証するのはこの状態モデルであり、HDLコンパイルやDDSでのシミュレーション実行ではありません。
自分で考えてみましょう
エッジ1に入力がなく、B,C,Dをエッジ2,3,4で受け入れる場合、タグ0から3の引き渡しエッジは何ですか。
解説を見る
Aはエッジ2で転送されます。B,C,Dはエッジ4,5,6で転送されます。エッジ3は出力バブルです。タグは物理的なエッジ番号ではなく受け入れ順に0,1,2,3と割り当てます。