Уровень 59: Knight Rider RISC

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

Задание

Цель: запустить финал однотактной линии — программу «Knight Rider»: бегущий огонёк на LED через MMIO. Новых инструкций и схем нет: адрес LED 0xFFFF_F000 строится как lui x10, 0xFFFFF, каждая sw x11, 0(x10) зажигает биты огонька и ложится строкой в MMIO-трейс. Последовательность за период — 1,2,4,8,16,32,64,128,64,32,16,8,4,2,1 — повторить дважды и остановиться на LED = 1. LED write-only (lw с него — ERR_WRITEONLY_READ), switches read-only (sw — ERR_READONLY_WRITE). После зачёта ваше ядро 58 уедет на плату Tang Nano 9K ZIP-кнопкой.

Финал однотактной линии: огонёк бежит по восьми светодиодам туда и обратно, как в сериале «Рыцарь дорог». Никаких новых команд и схем — это уровень чистого программирования, и его программа уедет на настоящую плату.

Вывод идёт через MMIO (memory-mapped I/O): периферия живёт в адресном пространстве памяти. LED — адрес 0xFFFF_F000, write-only: запись обычным sw зажигает биты огонька, но чтение с этого адреса запрещено (ERR_WRITEONLY_READ). Тумблеры — 0xFFFF_F004, read-only: запись туда даёт ERR_READONLY_WRITE. Каждая sw в LED ложится строкой в MMIO-трейс — проверка сверяет его попарно.

Задания уровня:

  1. Соберите адрес LED. 0xFFFF_F000 не влезает в 12-битное immediate — верхняя половина строится через lui x10, 0xFFFFF.
  2. Заложите шапку. Граница x13 = 128 (стоп-маска 0x80) и счётчик периодов x14 = 2.
  3. Напишите период. Метка period: огонёк x11 = 1; вправо (right) — sw, slli, bne до 0x80; отдельная запись 0x80; влево (left) — srli до нуля. Последовательность за период: 1,2,4,8,16,32,64,128,64,32,16,8,4,2,1.
  4. Повторите период дважды и остановитесь на .end — LED должен остаться 1.
  5. Прогоните и сверьте. «Проверить» сначала спросит число записей MMIO-трейса — посчитайте сами: 15 за период × 2. После зачёта ZIP-кнопка соберёт ваше ядро 58 в проект Gowin для Tang Nano 9K.

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

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

Что должно получиться: Knight Rider завершается штатно: 115 тактов, 30 записей MMIO-трейса (15 за период × 2), финальный LED = 1, регистры x10 = 0xFFFF_F000, x11 = 1, x13 = 128, x14 = 0; после зачёта ядро уезжает ZIP-кнопкой на Tang Nano 9K.

Шаг 1 — адрес через lui. lui x10, 0xFFFFF: imm20 = 0xFFFFF попадает в биты [31:12], младшие — нули. Получается ровно 0xFFFF_F000. Частая ошибка — пытаться собрать адрес addi-ями: 12-битное знаковое immediate такое константы не покрывает.

Шаг 2 — эталонная программа. Два периода на счётчике x14; внутри периода огонёк x11 идёт вправо сдвигами влево до границы 0x80 и обратно сдвигами вправо до нуля:

        lui  x10, 0xFFFFF   # 0:  база LED 0xFFFFF000
        addi x13, x0, 128   # 4:  0x80, стоп-граница
        addi x14, x0, 2     # 8:  счётчик периодов
period: addi x11, x0, 1     # 12
        beq  x14, x0, halt  # 16
        addi x14, x14, -1   # 20
right:  sw   x11, 0(x10)    # 24
        slli x11, x11, 1    # 28
        bne  x11, x13, right# 32 → 24
        sw   x11, 0(x10)    # 36: 0x80
left:   srli x11, x11, 1    # 40
        beq  x11, x0, period# 44 → 12
        sw   x11, 0(x10)    # 48
        beq  x0, x0, left   # 52 → 40
halt:   .end                # 56

Шаг 3 — контрольные числа. Прогон: 115 команд = 115 тактов, останов .end на PC = 56 такта не добавляет. MMIO-трейс — 30 записей: за период 15 (восемь вправо, запись 0x80, шесть влево), периодов два; финальный LED = 1. Регистры в финале: x10 = 0xFFFF_F000, x11 = 1, x13 = 128, x14 = 0. Задержки (пауза между шагами) — ученический вариант поверх эталона: внутренний счётный цикл. Эталон без задержек — для детерминированной длины трейса.

Шаг 4 — контракт MMIO. Пробы на эталонном runner: lw с 0xFFFF_F000 — ERR_WRITEONLY_READ (err 7), состояние замораживается; sw в 0xFFFF_F004 — ERR_READONLY_WRITE (err 6); невыровненный sw по 0xFFFF_F002 — ERR_DATA_ADDRESS_MISALIGNED (err 1), кратность проверяется раньше MMIO. Чтение тумблеров работает: lw x6, 4(x10) при тумблерах 0x01 вернёт 1 (байт в младших битах, старшие — нули).

Шаг 5 — ZIP. После зачёта кнопка экспорта проверяет сохранённое ядро 58 (parse, структурные проверки, сборку схемы) и собирает архив: 28 .v-файлов ядра, программа в rtl/ROM.v, делитель тактов до ≈3 Гц, обёртка с портами clk/top_sw/top_led, CST-пины и проект Gowin. Битстрим в архив не входит — соберите его в Gowin EDA сами. После прошивки тот же код исполняет физический чип.

Проверь себя

Почему sw по 0xFFFF_F002 — это ошибка выравнивания, а не запись в LED?

Проверка кратности 4 стоит раньше MMIO-декода: невыровненный адрес ловится ERR_DATA_ADDRESS_MISALIGNED до того, как схема вообще смотрит, на какую периферию он указывает. «Половинчатой» записи не существует.