Уровень 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. Задания:

  1. Расширьте IdEx32 до 167 бит. Добавьте isBr и f30 (funct3[0]) — ветка разрешается в EX по payload'у.
  2. Соберите Branch32 (branch.v). taken = isBr & (zero ^ f30): zero — выход Alu32 (ветка исполняется как sub), beq (f3=000) берётся при zero, bne (f3=001) — при ~zero.
  3. Посчитайте target. Add32(pcE, immE): B-imm уже в payload от ImmGen32 (bimm.v из 53-го внутри) — отдельный декод в EX не нужен. Третий Add32 в схеме.
  4. Проведите flush. Гейт flush_en, приоритет над stall, слабее freeze: clrBase = stallG | takenF, IfId.en &= ~takenF, next-PC мьюкс: nextpc = takenF ? target : (hold ? pcw : pc4). Цена taken = 2 такта, not-taken бесплатен.
  5. Проверьте .end. Стоп-слово 32'h0000006F — jal, не isBr: fetch-hold держит PC как на 62-м, flush его не трогает. Идёмпотентность останова сохраняется.
  6. Прогоните циклы. 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 такта за каждый взятый переход, поэтому «не взят» — разумный дефолт.