22 / 24 · Práctica
Práctica final: FIFO y calculadora en flujo de dos etapas
Conecta una FIFO de entrada de profundidad dos, dos etapas elásticas y un marcador de referencia.
Las lecciones se pueden leer gratis. Inscríbete para guardar el progreso.
Combina almacenamiento, cálculo y entrega
El proyecto final tiene una FIFO de entrada de dos posiciones y dos etapas elásticas de cálculo. La primera almacena la suma de nueve bits a+b; la segunda almacena 3×sum módulo 256. Las etiquetas siguen a cada valor. La capacidad total es de cuatro transacciones. La contrapresión se propaga desde la salida, mientras que las posiciones vacías pueden seguir llenándose. Supón un reloj, una FIFO registrada, extracción e inserción simultáneas y ausencia de paso directo a la salida.
Guarda manualmente capstone-model.mjs y ejecuta node capstone-model.mjs. Calcula toda la disponibilidad y los valores de las etapas siguientes a partir del estado anterior antes de actualizar la FIFO y los registros. La matriz de JavaScript es una cola funcional, no HDL sintetizable.
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');| Etiqueta | a,b | Resultado ancho | y |
|---|---|---|---|
| 2 | 250,10 | 780 | 12 |
| 3 | 255,255 | 1530 | 250 |
| 5 | 17,240 | 771 | 3 |
La primera entrada entra en la FIFO en el flanco 0, en s1 en el 1 y en s2 en el 2. Las detenciones del receptor en los flancos 2,3,4 retrasan su entrega al flanco 5. La latencia sin detenciones es de tres flancos; esta transacción tarda cinco. Otro almacenamiento vacío se llena mientras s2 conserva su valor.
La cola de referencia calcula los valores esperados solo al aceptar. Compara etiquetas y resultados al entregar y comprueba la ocupación en cada flanco. Después del flanco 25, drénalo todo. La ejecución Node comprueba ocho resultados ordenados; los archivos SystemVerilog siguientes ejercitan de forma independiente los registros reales de FIFO y de segmentación.
Construye y simula el RTL
Descarga dp_stream_calculator.sv y tb_dp_stream_calculator.sv en una carpeta. La interfaz sin signo de ocho bits a,b,tag acepta con in-valid/in-ready y entrega y con su etiqueta mediante out-valid/out-ready. Las sumas de nueve bits y los valores escalados de once bits reducen su anchura solo en la salida. El reinicio vacía el almacenamiento y suprime transferencias.
Ejecuta con Icarus Verilog instalado:
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 verificación del autor compiló y simuló estos archivos correctamente:
RTL PASS: accepted=527 delivered=523 canceled=4 stalls=517 full_replace=64 wraps=262 reset_with_work=1 zero=3El banco de pruebas comprueba aritmética, etiquetas, orden, detenciones, reemplazo con ocupación completa, vuelta de punteros, cancelación de cuatro elementos por reinicio y drenaje. Cambia la duración de la detención y compara la latencia con los tres ciclos sin detenciones. Esto verifica simulación RTL en Icarus, no síntesis, temporización física ni ejecución DDS.
Inténtalo tú
Con un receptor siempre listo, ¿en qué flanco se transfiere tag0? Si la profundidad de FIFO pasa a uno, ¿qué ocurre con la latencia sin detenciones y la capacidad total?
Leer la explicación
Tag0 se transfiere en el flanco 3 después de la aceptación en el 0, s1 en el 1 y s2 en el 2. Mantener la FIFO registrada conserva la latencia de tres flancos; la capacidad baja a 1+1+1=3 transacciones. Cambia el límite de profundidad de input-ready y vuelve a comprobar el modelo.