Уровень 66: Призраки неверного пути
Задание
Цель: разрешить ветку в EX: до сих пор ветки в конвейере не работали (PC шёл линейно), а в EX уже готовы zero (АЛУ) и форвардинг операндов: Branch32 даёт taken = isBr & (zero ^ f30) (beq берётся при zero, bne при ~zero), target = pc + imm (B-imm уже в payload от ImmGen32). Взятая ветка сбрасывает двух «призраков» — команды, выбранные по старому пути: clr IfId и IdEx (цена 2 такта), next-PC мьюкс переключается на target. Всё под гейтом flush_en; приоритет над stall. Идёмпотентность .end сохраняется: jal-слово ловится fetch-hold'ом ещё в IF. Приёмка — exact-step на циклах counter и mixed (ветка + load-use одновременно) + негатив flush_en=0.
До сих пор ветки в конвейере не работали: PC шёл линейно. Вы разрешаете ветку в EX — там уже готовы zero (АЛУ) и форвардинг операндов. Взятая ветка сбрасывает двух «призраков» — команды, успевшие зайти в IF/ID и ID/EX по старому пути: их эффекты ещё не случились, clr безопасен.
Редактируемые файлы — pipetop.v, branch.v, idex.v (теперь 167 бит); hazard.v и fwd.v — read-only. Задания:
- Расширьте IdEx32 до 167 бит. Добавьте
isBrиf30(funct3[0]) — ветка разрешается в EX по payload'у. - Соберите Branch32 (branch.v).
taken = isBr & (zero ^ f30): zero — выход Alu32 (ветка исполняется как sub), beq (f3=000) берётся при zero, bne (f3=001) — при ~zero. - Посчитайте target.
Add32(pcE, immE): B-imm уже в payload от ImmGen32 (bimm.v из 53-го внутри) — отдельный декод в EX не нужен. Третий Add32 в схеме. - Проведите flush. Гейт
flush_en, приоритет над stall, слабее freeze:clrBase = stallG | takenF,IfId.en &= ~takenF, next-PC мьюкс:nextpc = takenF ? target : (hold ? pcw : pc4). Цена taken = 2 такта, not-taken бесплатен. - Проверьте .end. Стоп-слово
32'h0000006F— jal, не isBr: fetch-hold держит PC как на 62-м, flush его не трогает. Идёмпотентность останова сохраняется. - Прогоните циклы. counter: 9 взятых переходов × 2 = 18 сброшенных слотов (nflush 18), 22 такта; смешанная программа гонит ветку и load-use одновременно — 17/28 (stall 3, flush 4). Негатив flush_en=0 обязан исказить результат без ошибок.
Решение для самопроверки
Сначала соберите сами. Решение ниже — контрольные точки.
Что должно получиться: counter идёт за 22 такта (10 ретайров + 4 заполнения + 18 flush-тактов, nflush = 18, nstall = 0); смешанная программа — 17/28 (stall 3, flush 4); негатив flush_en = 0 уводит PC мимо цикла без ошибок, но регистры обязаны отличаться от эталона.
Шаг 1 — Branch32 и target. cond = zero ^ f30; taken = isBr & cond. В топе: Branch32 brz(takenRaw, isbrE, zeroE, f30E), Add32 brtg(brTgt, pcE, immE), takenF = takenRaw & flush_en, takenFr = takenF & nfreeze.
Шаг 2 — приоритеты. clrBase = stallG | takenFr; stallClr = clrBase & nfreeze; IfId.en = (nfreeze & ~stallG) & ~takenFr; hold = freeze | isEnd | (stallG & ~takenFr); nextpc = takenFr ? brTgt : (hold ? pcw : pc4).
Шаг 3 — контрольные числа. counter: 10 ретайров, 22 такта (10 + 4 + 9×2 flush), nflush=18, nstall=0. Смешанная (lw + ветка): 17/28 (17 + 4 + 4 flush + 3 stall), nstall=3, nflush=4. Негатив flush_en=0: переходы не берутся, pc уходит мимо цикла, .end ловит останов — err=0, x5 обязан отличаться.
Проверь себя
Почему сброс «призраков» безопасен, хотя команды уже в конвейере?
Команды по старому пути зашли только в IF/ID и ID/EX — их эффекты (записи в регистры и память) ещё не случились, clr стирает их без следа. Дорогой это всё же обходится: 2 такта за каждый взятый переход, поэтому «не взят» — разумный дефолт.