22 / 24 · Übung
Abschlusspraktikum: FIFO und zweistufiger Rechner für Datenströme
Verbinden Sie einen Eingabe-FIFO mit Tiefe zwei, zwei elastische Stufen und ein Scoreboard.
Lektionen sind frei lesbar. Schreibe dich ein, um deinen Fortschritt zu speichern.
Speicherung, Berechnung und Ausgabe kombinieren
Das Abschlussprojekt hat einen Eingabe-FIFO mit zwei Einträgen und zwei elastische Rechenstufen. Stufe eins speichert die Neun-Bit-Summe a+b; Stufe zwei speichert 3×sum modulo 256. Tags folgen jedem Wert. Die Gesamtkapazität beträgt vier Transaktionen. Rückstau breitet sich vom Ausgang aus, während leere Plätze weiter gefüllt werden können. Nehmen Sie einen Takt, einen registrierten FIFO, gleichzeitiges Entnehmen/Einfügen und keinen Ausgabedurchgriff an.
Speichern Sie capstone-model.mjs manuell und führen Sie node capstone-model.mjs aus. Berechnen Sie alle Bereitschaftssignale und Folgestufenwerte aus dem alten Zustand, bevor Sie FIFO und Register aktualisieren. Das JavaScript-Array ist eine funktionale Warteschlange, kein synthetisierbares HDL.
import assert from 'node:assert/strict';
const input = [[0,0],[1,2],[250,10],[255,255],
[128,128],[17,240],[90,90],[4,8]];
const q = [], expected = [], seen = [];
let s1 = null, s2 = null, issued = 0, delivered = 0;
for (let edge = 0; edge < 50; edge++) {
const ready = edge >= 25 || ![2,3,4].includes(edge % 7);
const old1 = s1, old2 = s2;
const r2 = old2 === null || ready;
const r1 = old1 === null || r2;
const pop = q.length > 0 && r1;
const inReady = q.length < 2 || pop;
if (old2 !== null && ready) {
assert.deepEqual(old2, expected.shift());
seen.push(old2.y);
delivered++;
}
const n2 = r2 ? (old1 === null ? null :
{ tag: old1.tag, y: old1.sum * 3 % 256 }) : old2;
const n1 = r1 ? (pop ?
{ tag: q[0].tag, sum: q[0].a + q[0].b } : null) : old1;
if (pop) q.shift();
if (issued < input.length && inReady) {
const [a,b] = input[issued];
q.push({ tag: issued, a, b });
expected.push({ tag: issued, y: (3*a + 3*b) % 256 });
issued++;
}
s1 = n1; s2 = n2;
if (old2 !== null && !ready) assert.deepEqual(s2, old2);
assert.ok(q.length <= 2);
assert.equal(issued - delivered,
q.length + Number(s1 !== null) + Number(s2 !== null));
}
assert.equal(issued, input.length);
assert.equal(delivered, input.length);
assert.deepEqual(seen, [0,9,12,250,0,3,28,36]);
assert.equal(expected.length + q.length, 0);
assert.equal(s1, null); assert.equal(s2, null);
console.log('CAPSTONE PASS: 8 ordered outputs, drained');| Tag | a,b | Breites Ergebnis | y |
|---|---|---|---|
| 2 | 250,10 | 780 | 12 |
| 3 | 255,255 | 1530 | 250 |
| 5 | 17,240 | 771 | 3 |
Die erste Eingabe tritt an Flanke 0 in den FIFO, an Flanke 1 in s1 und an Flanke 2 in s2 ein. Empfängerwartezeiten an Flanke 2,3,4 verschieben ihre Ausgabe auf Flanke 5. Die Latenz ohne Wartezyklen beträgt drei Flanken; diese Transaktion benötigt fünf. Andere leere Speicherplätze werden gefüllt, während s2 hält.
Die Referenzwarteschlange berechnet erwartete Werte nur bei Übernahme. Vergleichen Sie Tags und Ergebnisse bei Ausgabe und prüfen Sie die Belegung an jeder Flanke. Leeren Sie nach Flanke 25 alles. Der Node-Lauf prüft acht geordnete Ergebnisse; die folgenden SystemVerilog-Dateien prüfen unabhängig tatsächliche FIFO- und Pipelineregister.
RTL bauen und simulieren
Laden Sie dp_stream_calculator.sv und tb_dp_stream_calculator.sv in denselben Ordner herunter. Die Schnittstelle mit vorzeichenlosen Acht-Bit-Werten a,b,tag übernimmt bei in-valid/in-ready und gibt y mit seinem Tag bei out-valid/out-ready aus. Neun-Bit-Summen und skalierte Elf-Bit-Werte werden erst am Ausgang gekürzt. Reset leert Speicher und unterdrückt Übertragungen.
Führen Sie bei installiertem Icarus Verilog aus:
iverilog -g2012 -Wall -s tb_dp_stream_calculator -o dp-test.vvp dp_stream_calculator.sv tb_dp_stream_calculator.sv
vvp dp-test.vvpDie Verifikation der Autoren hat diese Dateien erfolgreich kompiliert und simuliert:
RTL PASS: accepted=527 delivered=523 canceled=4 stalls=517 full_replace=64 wraps=262 reset_with_work=1 zero=3Die Testbench prüft Arithmetik, Tags, Reihenfolge, Wartezyklen, Ersatz bei voller Belegung, Zeigerumlauf, Reset-Abbruch von vier Elementen und Auslaufen. Ändern Sie die Wartedauer und vergleichen Sie Latenz mit drei Takten ohne Wartezyklen. Dies verifiziert Icarus-RTL-Simulation, keine Synthese, kein physikalisches Timing und keine DDS-Ausführung.
Selbst ausprobieren
An welcher Flanke wird tag0 bei stets bereitem Empfänger übertragen? Was passiert mit Latenz ohne Wartezyklen und Gesamtkapazität, wenn die FIFO-Tiefe eins wird?
Erklärung lesen
Tag0 wird an Flanke 3 übertragen, nach Übernahme an 0, s1 an 1 und s2 an 2. Der beibehaltene registrierte FIFO erhält die Latenz von drei Flanken; die Kapazität sinkt auf 1+1+1=3 Transaktionen. Ändern Sie die Tiefengrenze für input-ready und prüfen Sie das Modell erneut.