Altifigence Academy

22 / 24 · Lab

Capstone lab: a FIFO and two-stage streaming calculator

Connect a depth-two input FIFO, two elastic stages, and a scoreboard.

Combine storage, computation, and delivery

The capstone has a two-entry input FIFO and two elastic computation stages. Stage one stores the nine-bit sum a+b; stage two stores 3×sum modulo 256. Tags follow every value. Total capacity is four transactions. Backpressure propagates from the output while empty slots may still fill. Assume one clock, a registered FIFO, simultaneous pop/push, and no output fall-through.

Manually save capstone-model.mjs and run node capstone-model.mjs. Compute all readiness and next-stage values from old state before updating the FIFO and registers. The JavaScript array is a functional queue, not synthesizable HDL.

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');
Taga,bWide resulty
2250,1078012
3255,2551530250
517,2407713

The first input enters the FIFO at edge 0, s1 at edge 1, and s2 at edge 2. Sink stalls at edges 2,3,4 delay its delivery to edge 5. Unstalled latency is three edges; this transaction takes five. Other empty storage fills while s2 holds.

The reference queue computes expected values only on acceptance. Compare tags and results on delivery and check occupancy each edge. After edge 25, drain everything. The Node run checks eight ordered results; the SystemVerilog files below independently exercise actual FIFO and pipeline registers.

Build and simulate the RTL

Download dp_stream_calculator.sv and tb_dp_stream_calculator.sv into one folder. The unsigned eight-bit a,b,tag interface accepts on in-valid/in-ready and delivers y with its tag on out-valid/out-ready. Nine-bit sums and eleven-bit scaled values narrow only at output. Reset empties storage and suppresses transfers.

Run with Icarus Verilog installed:

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

Author verification compiled and simulated these files successfully:

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

The testbench checks arithmetic, tags, order, stalls, full replacement, pointer wrap, four-item reset cancellation, and drain. Change stall duration and compare latency against three unstalled cycles. This verifies Icarus RTL simulation, not synthesis, physical timing, or DDS execution.

Try it yourself

With an always-ready sink, at which edge does tag0 transfer? If FIFO depth becomes one, what happens to unstalled latency and total capacity?

Read the explanation

Tag0 transfers at edge 3 after acceptance at 0, s1 at 1, and s2 at 2. Keeping the registered FIFO preserves three-edge latency; capacity decreases to 1+1+1=3 transactions. Change the input-ready depth limit and recheck the model.

Lesson files

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

Your choice applies to this browser. Change it any time using the footer.