Altifigence Academy

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.

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.

javascript
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');
Etiquetaa,bResultado anchoy
2250,1078012
3255,2551530250
517,2407713

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:

text
iverilog -g2012 -Wall -s tb_dp_stream_calculator -o dp-test.vvp dp_stream_calculator.sv tb_dp_stream_calculator.sv
vvp dp-test.vvp

La verificación del autor compiló y simuló estos archivos correctamente:

text
RTL PASS: accepted=527 delivered=523 canceled=4 stalls=517 full_replace=64 wraps=262 reset_with_work=1 zero=3

El 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.

Lesson files

capstone-model.mjs ↓dp_stream_calculator.sv ↓tb_dp_stream_calculator.sv ↓

Tu elección se aplica a este navegador. Puedes cambiarla en el pie de página.