Урок 4. Своя память в процессоре — и первая ошибка

В картинках

В машине процессорного набора вся память — один примитив ram на 256 слов. В этом уроке на его место встаёт ваша RAM256x4. Игра запустится… и начнёт ошибаться: часть кнопок сработает не так, кристаллы будут гаснуть, не докатившись. Это не ошибка сборки — так и задумано. Причину найдём встроенным осциллографом, а починим в уроке 5.


4.1. Что стоит в машине сейчас и чем наша память отличается

Внутри GW-CPU память подключена так: адрес — младшие 8 бит команды ir0–ir7, данные для записи — аккумулятор acc0–acc3, прочитанное mq0–mq3 идёт в АЛУ, «пиши» WE = oST (команда ST), такт CPUCLK.

Примитив ram пишет в момент фронта такта, как триггер. У нашей памяти такта нет: она пишет, пока WE = 1 — это окно, а не мгновение. Значит, на WE нужно подать окно, когда адрес и данные уже спокойны. Сразу после фронта CPUCLK 0→1 процессор переходит к следующей команде — адрес меняется. Ко второй половине такта всё устоялось. Окно:

  • weRAM = oST · НЕ CPUCLK — «сейчас команда ST, и идёт вторая половина такта».
командаir0–ir7аккумуляторacc0–acc3RAM256x4своя памятьадресданныеmq0–mq3АЛУLD, ADD…oST · НЕ CPUCLKweRAM«команда ST,вторая половина такта»
Замена в процессоре. Вместо примитива ram — своя RAM256x4 на тех же метках: адрес — младшие 8 бит команды, данные — аккумулятор, прочитанное — в АЛУ. Нового только окно записи: weRAM = oST · НЕ CPUCLK — «команда ST, вторая половина такта».

4.2. Память при включении — «что попало»

Примитив ram при включении содержит нули. Защёлки — нет: каждая встаёт как придётся (в REAL — «ничего», оранжевое; в Fast — единицы). Так ведёт себя и настоящая память: компьютер при старте сам приводит в порядок то, что собирается читать.

Программа игры процессорного набора читает две ячейки раньше, чем записывает: LASTB и LASTK — какие кнопки были нажаты в прошлый раз. Остальные она сначала пишет. Поэтому в начало программы добавлены две команды:

reset:        LD #0        ST LASTB        ; своя память: ячейки, которые читаются до записи, — обнулить        ST LASTK

Программа целиком — файл game-myram.rom.json (311 команд).

4.3. Сборка

Шаг 1. Перенести чип в проект с машиной

Уроки 1–3 вы собирали в отдельном проекте. Чип переезжает файлом:

  1. В проекте уроков 1–3: в библиотеке в строке RAM256x4 — значок ⬇ (сохранить чип в файл). В файл попадут и все его детали: RAM16x4, WORD4, DLatch и вентили.
  2. Открыть проект с машиной процессорного набора.
  3. В шапке библиотеки — значок ⬆ (загрузить чип) → выбрать файл.
  4. Если появится окно «built differently» — у вас эти вентили уже есть, только собраны иначе. Оставить предложенное Use mine → Import.

Шаг 2. Убрать примитив памяти

Внутри GW-CPU («Редактировать»):

ЭлементДействие
Memory (RAM) RAM, выход которого = mq0удалить

Метки на его ногах (ir0–ir7, acc0–acc3, mq0–mq3) удалять не нужно — на них встанет новая память.

Шаг 3. Своя память и окно записи

Что собираем. Инвертор такта, окно weRAM и RAM256x4 на тех же метках.

ЭлементНогаНаправлениеМетка
NOTI0входCPUCLK
NOTO0выходnCLK
ANDI0входoST
ANDI1входnCLK
ANDO0выходweRAM
RAM256x4A0входir0
RAM256x4A1входir1
RAM256x4A2входir2
RAM256x4A3входir3
RAM256x4A4входir4
RAM256x4A5входir5
RAM256x4A6входir6
RAM256x4A7входir7
RAM256x4D0входacc0
RAM256x4D1входacc1
RAM256x4D2входacc2
RAM256x4D3входacc3
RAM256x4WEвходweRAM
RAM256x4Q0выходmq0
RAM256x4Q1выходmq1
RAM256x4Q2выходmq2
RAM256x4Q3выходmq3

Выйти из редактирования чипа с сохранением.

Шаг 4. Программа

ПЗУ (снаружи GW-CPU): ⚙ → ⬆ → файл game-myram.rom.json.

4.4. Запуск — и ошибка

Движок Fast, такт CPUCLK — MAX.

Что делаешьЧто видишь
включитьчасы 12:00 идут — процессор работает на своей памяти
держать A, потом Pнижняя левая рука, верхняя правая — верно
держать Q✗ нижняя левая рука вместо верхней левой
держать L✗ верхняя правая вместо нижней правой
START✗ кристалл появляется в начале трубы и гаснет

Так и должно быть в этом уроке. Память собрана правильно, окно записи по смыслу правильное — а запись иногда уходит не в ту ячейку. Q портит ячейку позы, кристаллы — ячейки труб.

4.5. Ищем причину осциллографом

Осциллограф показывает сигналы по времени. Открыть — клавиша w.

  1. В поле add ввести и нажать Enter после каждого: CPUCLK, ir0-7 (шина адреса одной строкой), GW-CPU.oST, GW-CPU.weRAM.
  2. В поле trig — GW-CPU.weRAM=1, Enter. Как только окно записи откроется, картинка замрёт (FROZEN), событие — посередине.
  3. Снова ловить — LIVE.
CPUCLKir0-70f1413020doSTweRAMодин срез: новый адрес И окноследующий срез: адрес 13 → 02, а oST гаснет не сразу
Осциллограф, урок 4. Срез — одно вычисление симулятора на фронте такта. В подсвеченном срезе адрес только что сменился (13) и в этот же срез открылось окно weRAM. Всё, что попало в один срез, для симулятора происходит одновременно — и на следующем фронте, пока дешифратор гасит oST, а адрес уже новый, запись уходит не туда.

Как читать. Один столбик — один срез: одно вычисление симулятора на фронте такта. Это самая маленькая единица времени, какая у симулятора есть: всё, что попало в один срез, для него происходит одновременно, без «раньше» и «позже».

В подсвеченном срезе адрес только что сменился (13) — и в этот же срез открылось окно weRAM. В настоящем железе между этими событиями было бы мгновение: сначала новая команда устаканивается, потом открывается окно. В симуляторе они совпали. А на следующем фронте адрес уже перескакивает на следующую команду (02), и пока дешифратор гасит oST, окно ещё не закрыто — значение успевает записаться и туда.

Вывод: окно записи не должно открываться и закрываться на том же фронте, где меняется адрес. Как этого добиться, не замедляя процессор до бесконечности, — урок 5.

Вставка урока 4 (Cmd+I)

В режиме редактирования GW-CPU, после шагов 1–2.

# Урок 4 — своя память в процессоре (простое окно записи).# Вставка: Cmd+I В РЕЖИМЕ РЕДАКТИРОВАНИЯ чипа GW-CPU, после того как удалён примитив ram RAM (256×4).# Адрес — младшие 8 бит команды, данные — аккумулятор, прочитанное — в АЛУ, как у прежней памяти. # ---------- окно записи: команда ST, вторая половина такта ----------NOT(CPUCLK, nCLK)AND(oST, nCLK, weRAM) # ---------- память ----------RAM256x4(A0=ir0, A1=ir1, A2=ir2, A3=ir3, A4=ir4, A5=ir5, A6=ir6, A7=ir7, D0=acc0, D1=acc1, D2=acc2, D3=acc3, WE=weRAM, Q0=mq0, Q1=mq1, Q2=mq2, Q3=mq3)

Не получается? Что-то не работает →