Уровень 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. Задания:
- Расширьте IdEx32 до 165 бит. К раскладке 62-го добавьте номера
rs1N[9:5]иrs2N[4:0]— не значения, а именно номера из Decoder32: их будет сравнивать блок форвардинга. - Соберите 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-й). - Поставьте мьюксы операндов EX. По 32 битам, три плеча: payload /
alu_yиз EX/MEM /wdataиз MEM/WB; каждый бит плеча гейтитсяfwd_en. Используйте форварднутые значения везде: Alu32, восемь I-модулей, alusrc-мьюкс, store_val. - Закройте дистанцию 3. Дистанции 1–2 покрывает форвардинг, 4+ — регфайл; дистанцию 3 закрывает write-first: если
rdWbсовпадает с читаемым регистром иregWriteW, читайтеwdataWbвместо q регфайла (тоже подfwd_en). - Прогоните на эталоне. Ручной прогон идёт на конвейерном движке: 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.