22 / 24 · Travaux pratiques
TP final : FIFO et calculateur en flux à deux étages
Reliez une FIFO d’entrée de profondeur deux, deux étages élastiques et un tableau de suivi.
Les leçons sont gratuites. Inscrivez-vous pour enregistrer vos progrès.
Combiner stockage, calcul et livraison
Le projet final a une FIFO d’entrée de deux éléments et deux étages élastiques de calcul. Le premier stocke la somme a+b sur neuf bits ; le second stocke 3×sum modulo 256. Les étiquettes suivent chaque valeur. La capacité totale est quatre transactions. La contre-pression se propage depuis la sortie tandis que les emplacements vides peuvent encore se remplir. Supposez une horloge unique, une FIFO enregistrée, un retrait/insertion simultané et aucun passage direct de sortie.
Enregistrez manuellement capstone-model.mjs et exécutez node capstone-model.mjs. Calculez toutes les disponibilités et valeurs suivantes des étages à partir de l’état précédent avant de mettre à jour FIFO et registres. Le tableau JavaScript est une file fonctionnelle, et non du HDL synthétisable.
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');| Étiquette | a, b | Résultat large | y |
|---|---|---|---|
| 2 | 250, 10 | 780 | 12 |
| 3 | 255, 255 | 1530 | 250 |
| 5 | 17, 240 | 771 | 3 |
La première entrée pénètre la FIFO au front 0, s1 au front 1 et s2 au front 2. Les blocages du récepteur aux fronts 2, 3, 4 retardent sa livraison au front 5. La latence sans blocage est trois fronts ; cette transaction en prend cinq. D’autres stockages vides se remplissent tandis que s2 conserve sa valeur.
La file de référence calcule les valeurs attendues uniquement à l’acceptation. Comparez étiquettes et résultats à la livraison et vérifiez l’occupation à chaque front. Après le front 25, vidangez tout. L’exécution Node vérifie huit résultats ordonnés ; les fichiers SystemVerilog ci-dessous testent indépendamment les registres réels de FIFO et de pipeline.
Construire et simuler le RTL
Téléchargez dp_stream_calculator.sv et tb_dp_stream_calculator.sv dans un même dossier. L’interface a, b, tag non signée sur huit bits accepte sur in-valid/in-ready et livre y avec son étiquette sur out-valid/out-ready. Les sommes sur neuf bits et les valeurs mises à l’échelle sur onze bits ne sont réduites qu’en sortie. Reset vide le stockage et supprime les transferts.
Exécutez avec Icarus Verilog installé :
iverilog -g2012 -Wall -s tb_dp_stream_calculator -o dp-test.vvp dp_stream_calculator.sv tb_dp_stream_calculator.sv
vvp dp-test.vvpLa vérification de l’auteur a compilé et simulé ces fichiers avec succès :
RTL PASS: accepted=527 delivered=523 canceled=4 stalls=517 full_replace=64 wraps=262 reset_with_work=1 zero=3Le banc de test vérifie arithmétique, étiquettes, ordre, blocages, remplacement à plein, rebouclage des pointeurs, annulation de quatre éléments par reset et vidange. Modifiez la durée du blocage et comparez la latence aux trois cycles sans blocage. Cela vérifie la simulation RTL Icarus, et non la synthèse, la temporisation physique ou l’exécution DDS.
Essayez vous-même
Avec un récepteur toujours prêt, à quel front tag0 est-il transféré ? Si la profondeur FIFO devient un, que deviennent la latence sans blocage et la capacité totale ?
Lire l’explication
Tag0 est transféré au front 3 après acceptation à 0, s1 à 1 et s2 à 2. Conserver la FIFO enregistrée maintient une latence de trois fronts ; la capacité descend à 1+1+1=3 transactions. Modifiez la limite de profondeur d’input-ready et revérifiez le modèle.