Уровень 64: Короткий путь

Открыть уровень →

Задание

Цель: собрать блок Fwd32 (сравнение номеров rs с rdM/rdW, приоритет EX/MEM > MEM/WB, исключения x0 и lw из EX/MEM), расширить IdEx32 номерами rs1/rs2 (+10 бит) и поставить мьюксы операндов EX, гейтящиеся fwd_en. Конвейер 62-го исполнял RAW-зависимости честной латентностью: команда читала регистры в ID, а писались они в WB — результат доезжал только через 3 такта, и ближняя зависимость читала мусор. Вы закрываете конфликт коротким путём — форвардингом: результат АЛУ из EX/MEM и значение записи из MEM/WB подаются прямо на входы АЛУ следующей команды, минуя регфайл. Ручной прогон на конвейерном движке покажет разницу: эталон не платит такт за RAW, а «железо с выключенным fwd_en» читает мусор. Приёмка — exact-step против движка + негатив fwd_en=0.

Конвейер 62-го закрывает RAW-зависимости честной латентностью: команда читает регистры в ID, а пишет результат в WB — значение доезжает только на дистанции 4. Ближняя зависимость читает мусор. Вы закрываете конфликт коротким путём — форвардингом: результат АЛУ из EX/MEM и значение записи из MEM/WB подаются прямо на входы АЛУ следующей команды, минуя регфайл.

Уровень аппаратный: редактируемые файлы — pipetop.v, fwd.v, idex.v; межстадийные регистры 62-го и все блоки ядра 58 — read-only. Задания:

  1. Расширьте IdEx32 до 165 бит. К раскладке 62-го добавьте номера rs1N[9:5] и rs2N[4:0] — не значения, а именно номера из Decoder32: их будет сравнивать блок форвардинга.
  2. Соберите Fwd32 (fwd.v). Сравните номера операндов с rdM (EX/MEM) и rdW (MEM/WB): fwdX=01 при regWriteM & rdM!=0 & !memReadM, fwdX=10 при regWriteW & rdW!=0. Приоритет — EX/MEM над MEM/WB: свежее значение старше. Исключения: x0 не форвардится никогда, lw из EX/MEM не форвардится (данных ещё нет — это лечит 65-й).
  3. Поставьте мьюксы операндов EX. По 32 битам, три плеча: payload / alu_y из EX/MEM / wdata из MEM/WB; каждый бит плеча гейтится fwd_en. Используйте форварднутые значения везде: Alu32, восемь I-модулей, alusrc-мьюкс, store_val.
  4. Закройте дистанцию 3. Дистанции 1–2 покрывает форвардинг, 4+ — регфайл; дистанцию 3 закрывает write-first: если rdWb совпадает с читаемым регистром и regWriteW, читайте wdataWb вместо q регфайла (тоже под fwd_en).
  5. Прогоните на эталоне. Ручной прогон идёт на конвейерном движке: RAW-программа идёт без пузырей (15 команд, 19 тактов), а при fwd_en=0 регистры заполняются мусором.

Решение для самопроверки

Сначала соберите сами. Решение ниже — контрольные точки.

Что должно получиться: exact-step против конвейерного эталона проходит: RAW-программа идёт без пузырей — 15 ретайров, 19 тактов, nstall = 0, nflush = 0; негатив fwd_en = 0 оставляет err = 0, но регистры обязаны отличаться от эталона.

Шаг 1 — приёмочные программы. RAW-программа (дистанции 1–3, lw→use на дистанции 2), линейная логика, негатив fwd_en=0 (должен оставить err=0 и изменить регистры — иначе юнит не в тракте).

Шаг 2 — компараторы Fwd32. Каждый — 5 бит xor+not и and-дерево. Плечо EX/MEM: m1 = regWriteM & rdMnz & ~memReadM & eqM; плечо MEM/WB: m2 = regWriteW & rdWnz & eqW; выходы: fwdX[0]=m1, fwdX[1]=m2 & ~m1 (приоритет).

Шаг 3 — мьюксы и bypass. fwa0 = fwdA[0] & fwd_en, fwa1 = fwdA[1] & fwd_en, нулевое плечо ~(fwa0|fwa1); rs1F[b] = rs1E[b]&sel00 | aluYM[b]&fwa0 | wdataWb[b]&fwa1 (и для rs2F). Write-first: wf = eq(rdWb, rs) & weRfWb & fwd_en → в pack payload идёт wdataWb вместо значения регфайла.

Шаг 4 — контрольные числа. RAW-программа: 15 ретайров, 19 тактов (15 + 4 заполнения), пузырей нет: nstall=0, nflush=0. Логика: 12/16. Негатив fwd_en=0: программа всё ещё останавливается штатно на .end, но регистры обязаны отличаться от эталона.

Проверь себя

Почему lw из EX/MEM не форвардится?

На стадии EX/MEM готов только адрес — сами данные выйдут из памяти к концу MEM, форвардить нечего. Поэтому соседа, читающего результат lw слишком рано, спасает пузырь 65-го уровня, а не Fwd32: исключение memReadM прямо запрещает плечо EX/MEM.