Уровень 61: Почему такт длинный

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

Задание

Цель: собрать линейную программу из 9 команд (lui/addi/ori/sw/lw/add), запустить её на эталонном конвейере и предсказать, в какой стадии окажется инструкция №5 на такте 6. Однотактная машина делала всю работу за один длинный такт: выборка, декод, АЛУ, память и запись — по одному разу за такт. Конвейер разрезает этот путь на пять коротких стадий (IF → ID → EX → MEM → WB), и команды движутся по нему, как по конвейерной ленте: пока одна исполняется, следующая уже декодируется. Обратите внимание: тактов больше, чем команд — первые четыре такта конвейер только заполняется, последние четыре — опустошается. Race Track на платформе показывает каждую команду на её стадии.

До сих пор программы исполняло однотактное ядро: одна команда — один такт, потому что весь путь (выборка, декод, АЛУ, память, запись) умещался в один длинный такт. С этого уровня программы исполняет конвейер — пять коротких стадий: IF (выборка) → ID (декод и чтение регистров) → EX (исполнение) → MEM (память) → WB (запись результата). Команды движутся по стадиям, как по конвейерной ленте: пока одна исполняется, следующая уже декодируется.

Схема не собирается — уровень программный, по образцу 54–59. Задания:

  1. Соберите линейную программу из 9 команд (без веток и зависимостей «через такт»): база lui x5, 0x1, данные addi x6, x0, 7 и addi x7, x0, 20, константа ori x8, x0, 0x7F0, запись sw x6, 0(x5), чтение назад lw x9, 0(x5), арифметика add x10, x6, x7, sw x10, 4(x5), add x11, x9, x10.
  2. Предскажите стадию. Перед первой проверкой «Проверить» спросит: инструкция №5 — в какой стадии она окажется на такте 6? Считайте сами: инструкция №5 выбирается на такте 5, и каждый следующий такт двигает её на следующую стадию.
  3. Прогоните на конвейере. Платформа показывает Race Track: пять дорожек — по одной на стадию; жмите «Шаг» и смотрите, как капсулы команд едут вправо. Клик по капсуле — карточка команды.
  4. Объясните числа. 9 команд — а тактов 13: первые четыре такта конвейер только заполняется (fill), последние команды доходят до WB после останова (drain). Останов .end по-прежнему не ретайрится и такта не добавляет.

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

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

Что должно получиться: программа из 9 команд даёт 13 тактов (9 ретайров + 4 заполнения); предикт подтверждён: инструкция №5 на такте 6 — в стадии ID; финальные значения: x11 = 34, память 0x1000 = 7 и 0x1004 = 27.

Шаг 1 — эталонная программа.

lui  x5, 0x1        # 0:  база 0x1000
addi x6, x0, 7      # 4
addi x7, x0, 20     # 8
ori  x8, x0, 0x7F0  # 12
sw   x6, 0(x5)      # 16: DMEM[0x1000] = 7
lw   x9, 0(x5)      # 20
add  x10, x6, x7    # 24: 27
sw   x10, 4(x5)     # 28: DMEM[0x1004] = 27
add  x11, x9, x10   # 32: 34
.end                # 36: останов

Шаг 2 — контрольные числа. Прогон: 9 команд = 9 ретайров, 13 тактов (9 + 4 такта заполнения), останов на PC = 36. Регистры в финале: x5 = 0x1000, x6 = 7, x7 = 20, x8 = 0x7F0, x9 = 7, x10 = 27, x11 = 34; память: 0x1000 = 7, 0x1004 = 27. Пузырей нет: программа линейная, без load-use (использование x9 отстоит от lw на три команды) и без веток.

Шаг 3 — предикт. Инструкция №5 (sw) — такт 5: IF, такт 6: ID (ответ 2), такт 7: EX, такт 8: MEM (запись слова), такт 9: WB.

Шаг 4 — почему не 5×. Короткая программа платит 4 такта заполнения и ничего не экономит на дренаже: 13 > 9. В длинном цикле (например, counter или array_sum на следующих уровнях) заполнение амортизируется на десятках ретайров, и темп приближается к «команда за такт» — при такте в несколько раз короче однотактного.

Проверь себя

Почему конвейер не «в 5 раз быстрее» однотактного ядра?

Короткая программа платит заполнение и опустошение целиком: 13 тактов против 9 ретайров. Выигрыш появляется на длинном потоке команд — в цикле, где 4 такта заполнения амортизируются на десятках инструкций.