23 / 24 · Travaux pratiques
Projet final : petite chaîne d’inférence et rapport
Conservez K=9, quatre sorties et un cycle supplémentaire par sortie, mais utilisez trois PE. Quels sont les cycles modélisés totaux et l’utilisation des seuls créneaux MAC ?
Les leçons sont gratuites. Inscrivez-vous pour enregistrer vos progrès.
Objectif et critères d’acceptation
Conservez model.mjs du TP précédent, créez capstone-lab.mjs à côté et exécutez node capstone-lab.mjs. Reliez une couche de convolution, biais, requantification, ReLU et regroupement maximal. Rapportez les valeurs séparément du coût. Ce n’est ni un classificateur complet ni un moteur d’entraînement. Vous pouvez examiner manuellement les fichiers via un parcours pris en charge de Digital Design Studio installé ; l’exécution dans DDS n’est pas établie.
Terminez lorsque les accumulateurs de convolution directe et de fenêtres-produits scalaires correspondent, que les jeux intermédiaires/de sortie correspondent, et que le rapport inclut dimensions, MAC, cycles supposés, utilisation des créneaux et octets uniques des tenseurs. Ajoutez un jeu à entrée modifiée calculé indépendamment à la main. Conservez commande, version du fichier et hypothèses avec le journal. Analysez une comparaison échouée au lieu de seulement modifier sa valeur attendue.
Référence détaillée
L’entrée est 4×4 avec les valeurs 1 à 16. Le noyau [1, 0, −1;1, 0, −1;1, 0, −1] soustrait dans chaque ligne l’échantillon droit du gauche. Chaque ligne donne −2 : chaque fenêtre totalise donc −6. Le biais huit donne quatre accumulateurs égaux à deux. Le multiplicateur0.5 et le point zéro zéro produisent le code un. ReLU le conserve et le regroupement 2×2 donne [1]. Conservez aussi des jeux asymétriques aux limites ; des sorties égales peuvent masquer les erreurs d’ordre.
Les dimensions [M, Ho, Wo] sont [1, 2, 2]. Comptez 36 MAC, y compris les termes à poids nul. Avec deux PE à un MAC partageant la réduction de chaque sortie, supposez cinq cycles MAC plus un cycle de combinaison/écriture par sortie, sans chevauchement ni blocage. Le total modélisé est 24 cycles. L’utilisation des seuls créneaux MAC est 36/(4×5×2)=90%. Ce n’est pas une latence de dispositif mesurée.
Les tenseurs int8 uniques utilisent entrée 16, poids 9 et sorties de convolution4 octets, plus un biais de quatre octets. La sortie regroupée utilise un octet. Le trafic réel peut inclure rechargements et sommes partielles. Les échelles sx=sw=1, sy=2 expliquent le multiplicateur0.5.
Passer le biais à zéro prédit accumulateur−6, code −3, zéro activé et zéro regroupé. Modifier seulement la première entrée de un à deux donne un premier accumulateur de trois, l’égalité 1.5 arrondie à deux, trois sorties restantes de un et un résultat regroupé de deux. Testez les changements séparément. Davantage de PE ou sauter les poids nuls nécessite un nouvel ordonnancement explicite tout en conservant les valeurs.
Fichier exécutable
import assert from 'node:assert/strict';
import {conv2d, im2col, dot, requantize, maxPool2x2} from './model.mjs';
const x = [Array.from({length: 4}, (_, r) => Array.from({length: 4}, (_, c) => r * 4 + c + 1))];
const kernel = [[1, 0, -1], [1, 0, -1], [1, 0, -1]];
const weights = [[kernel]], bias = [8];
const reference = conv2d(x, weights, {bias});
const flattenedWeights = kernel.flat();
const rows = im2col(x, 3, 3);
const accumulators = rows.map(row => dot(row, flattenedWeights) + bias[0]);
assert.deepEqual(accumulators, reference[0].flat());
assert.deepEqual(accumulators, [2, 2, 2, 2]);
const activated = reference[0].map(row => row.map(v => Math.max(0, requantize(v, 0.5))));
const pooled = maxPool2x2(activated);
assert.deepEqual(activated, [[1, 1], [1, 1]]);
assert.deepEqual(pooled, [[1]]);
// Cycle estimate with explicit assumptions: all MAC operands local, no stalls,
// one output at a time, K=9, two single-MAC PEs, no overlap with output write.
const macs = rows.length * flattenedWeights.length;
const peCount = 2, macCyclesPerOutput = Math.ceil(flattenedWeights.length / peCount);
const modelCycles = rows.length * (macCyclesPerOutput + 1);
const usefulSlots = macs, scheduledSlots = rows.length * macCyclesPerOutput * peCount;
assert.equal(macs, 36);
assert.equal(modelCycles, 24);
assert.equal(usefulSlots / scheduledSlots, 0.9);
const report = {shape:[1, 2, 2], accumulators, activated, pooled, macs, peCount,
modelCycles, macSlotUtilization:usefulSlots / scheduledSlots,
uniqueInputBytes:16, weightBytes:9, outputBytes:4, biasBytes:4,
scope:'Software reference and schedule estimate only; no synthesis or DDS run'};
console.log(JSON.stringify(report, null, 2));Projet final RTL : convolution entière
Les pièces jointes cnn_window3x3.sv et cnn_conv3x3.sv relient les fenêtres à neuf produits signés 8×8, une somme/un biais signé sur 24 bits, une division par deux arrondie au plus proche vers le pair, une saturation int8, ReLU et un maximum 2×2. La géométrie est le contrat précédent 4×4, monocanal, à noyau 3×3 sans remplissage. Les échelles d’entrée/poids valent un, l’échelle de sortie deux et tous les points zéro zéro. Le noyau par défaut est [1, 0, −1;1, 0, −1;1, 0, −1], le biais huit. Les paramètres de noyau peuvent changer ; revérifiez la borne de somme sur 24 bits.
Comparez séparément out_acc, out_code et out_activation, avec out_row/out_col et les transferts de sortie acceptés. done monte après l’acceptation du quatrième résultat ; pooled est alors valide. Seize entrées acceptées n’impliquent pas la fin. Ce RTL utilise neuf produits parallèles et deux étages enregistrés, au lieu de l’estimation précédente à deux PE et 24 cycles. Fréquence/puissance nécessitent des preuves de synthèse ou de dispositif.
Exécutez le banc commun à vérification automatique :
iverilog -g2012 -s tb_cnn_stream -o cnn-sim cnn_window3x3.sv cnn_conv3x3.sv tb_cnn_stream.sv
vvp cnn-simLes accumulateurs par défaut valent deux, les codes/activations un et la sortie regroupée un. Les vérifications incluent limites signées, saturation, égalités positives/négatives, noyau signé dense, pauses et reset. Vérifiez PASS frames=6 windows=24 outputs=24 kernels=2 dans votre journal réel. Passez la première entrée à deux : la première somme trois s’arrondit au code deux et pooled devient deux. Choisissez ensuite un autre noyau signé, calculez sa première fenêtre à la main, mettez à jour la fonction indépendante de coefficients et relancez tous les jeux. Ne copiez pas les résultats du dispositif testé dans le modèle de référence.
Essayez vous-même
Conservez K=9, quatre sorties et un cycle supplémentaire par sortie, mais utilisez trois PE. Quels sont les cycles modélisés totaux et l’utilisation des seuls créneaux MAC ?
Lire l’explication
Chaque sortie prend trois cycles MAC plus un supplémentaire : le total est donc seize. Les 36 créneaux MAC sont tous utiles, soit 100%. Fréquence identique, alimentation et coût de combinaison nécessitent toujours des preuves d’implémentation distinctes.