22 / 24 · Практика
Итоговая лабораторная работа: FIFO и двухступенчатый потоковый вычислитель
Соедините входную FIFO глубины два, две эластичные ступени и таблицу ожидаемых результатов.
Уроки доступны бесплатно. Запишитесь, чтобы сохранять прогресс.
Совместите хранение, вычисление и выдачу
Итоговый проект имеет входную FIFO на две записи и две эластичные вычислительные ступени. Первая хранит девятибитную сумму a+b; вторая — 3×sum по модулю 256. Метки сопровождают каждое значение. Общая ёмкость — четыре транзакции. Обратное давление распространяется от выхода, пока пустые места могут заполняться. Предположим один такт, регистровую FIFO, одновременные pop/push и отсутствие сквозного прохода выхода.
Вручную сохраните capstone-model.mjs и выполните node capstone-model.mjs. Вычисляйте всю готовность и значения следующей ступени из старого состояния до обновления FIFO и регистров. Массив JavaScript — функциональная очередь, а не синтезируемый HDL.
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');| Метка | a,b | Широкий результат | y |
|---|---|---|---|
| 2 | 250,10 | 780 | 12 |
| 3 | 255,255 | 1530 | 250 |
| 5 | 17,240 | 771 | 3 |
Первый вход входит в FIFO на фронте 0, s1 — на фронте 1, s2 — на фронте 2. Остановки приёмника на фронтах 2,3,4 задерживают выдачу до фронта 5. Задержка без остановок — три фронта; эта транзакция занимает пять. Другие пустые места заполняются, пока s2 удерживает данные.
Эталонная очередь вычисляет ожидаемые значения только при приёме. Сравнивайте метки и результаты при выдаче и проверяйте заполненность на каждом фронте. После фронта 25 опустошите всё. Запуск Node проверяет восемь упорядоченных результатов; файлы SystemVerilog ниже независимо проверяют реальные регистры FIFO и конвейера.
Соберите и симулируйте RTL
Загрузите dp_stream_calculator.sv и tb_dp_stream_calculator.sv в одну папку. Беззнаковый восьмибитный интерфейс a,b,tag принимает по in-valid/in-ready и выдаёт y со своей меткой по out-valid/out-ready. Девятибитные суммы и одиннадцатибитные масштабированные значения сужаются только на выходе. Reset освобождает хранилище и подавляет передачи.
Выполните при установленном Icarus Verilog:
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При авторской проверке эти файлы успешно скомпилированы и симулированы:
RTL PASS: accepted=527 delivered=523 canceled=4 stalls=517 full_replace=64 wraps=262 reset_with_work=1 zero=3Тестовый стенд проверяет арифметику, метки, порядок, остановки, замену при заполненности, циклический переход указателей, отмену четырёх элементов сбросом и опустошение. Измените длительность остановки и сравните задержку с тремя тактами без остановок. Это проверяет симуляцию RTL Icarus, а не синтез, физические временные характеристики или выполнение DDS.
Попробуйте сами
При постоянно готовом приёмнике на каком фронте передаётся tag0? Если глубина FIFO становится один, что происходит с задержкой без остановок и общей ёмкостью?
Прочитать объяснение
Tag0 передаётся на фронте 3 после приёма на 0, s1 на 1 и s2 на 2. Сохранение регистровой FIFO сохраняет задержку в три фронта; ёмкость уменьшается до 1+1+1=3 транзакций. Измените ограничение глубины input-ready и снова проверьте модель.