- PVSM.RU - https://www.pvsm.ru -

Я не знал, как работают процессоры, поэтому написал программный симулятор

Я не знал, как работают процессоры, поэтому написал программный симулятор - 1

Несколько месяцев назад меня вдруг поразила мысль, что я понятия не имею о принципах работы компьютерного железа. Я до сих пор не знаю, как работают современные компьютеры.

Я прочитал книгу «Но откуда он знает?» [1] Кларка Скотта с детальным описанием простого 8-битного компьютера: начиная с логических вентилей, ОЗУ, транзисторов процессора, заканчивая арифметико-логическим устройством и операциями ввода-вывода. И мне захотелось реализовать всё это в коде.

Хотя я не настолько интересуюсь физикой микросхем, но книга просто скользит по волнам и красиво объясняет электросхемы и как биты перемещаются по системе — от читателя не требуется знание электротехники. Но мне недостаточно текстового описания. Я должен видеть вещи в действии и учиться на своих неизбежных ошибках. Так я начал реализацию схем в коде. Путь оказался тернист, но поучителен.

Результат моей работы можно посмотреть в репозитории simple-computer [2]: простом вычислителе. Он простой и он вычисляет.

Я не знал, как работают процессоры, поэтому написал программный симулятор - 2

Я не знал, как работают процессоры, поэтому написал программный симулятор - 3

Я не знал, как работают процессоры, поэтому написал программный симулятор - 4
Пример программ

Код процессора реализован как ужасная куча логических вентилей, которые включаются и выключаются [3], но он работает. Я прогнал модульные тесты [4], а все мы знаем, что модульные тесты — неопровержимое доказательство, что программа работает.

Код обрабатывает ввод с клавиатуры [5] и отображает текст на дисплее [6], используя кропотливо созданный набор глифов для профессионального шрифта, который я назвал Daniel Code Pro. Единственный чит: чтобы взять ввод с клавиатуры и вывести результат, мне пришлось подключить каналы через GLFW [7], но в остальном это полностью программная симуляция электросхемы.

Я даже написал грубый ассемблер [8], который на многое открыл глаза, мягко говоря. Он не идеален. На самом деле даже немного дерьмовый, но он показал мне проблемы, которые другие люди уже решили много-много лет назад.

Но зачем ты это делаешь?

«Тринадцатилетние дети собирают процессоры в Minecraft. Позови, когда сможешь сделать настоящий CPU из телеграфных реле»

Моя ментальная модель устройства CPU застряла на уровне учебников по информатике для начинающих. Процессор для эмулятора Gameboy, который я написал в 2013 году [9], на самом деле не похож на современные CPU. Даже если эмулятор — это просто машина состояний, он не описывает состояния на уровне логических вентилей. Почти всё можно реализовать с помощью только оператора switch и сохраняя состояние регистров.

Я хочу лучше разобраться, как всё устроено, потому что не знаю, например, что такое кэш L1/L2 и конвейеризация и я не совсем уверен, что понимаю статьи об уязвимостях Meltdown и Spectre. Кто-то сказал, что они оптимизируют код таким образом, чтобы использовать кэш процессора, но я не знаю, как это проверить, кроме как поверить на слово. Я не совсем уверен, что означают все инструкции x86. Не понимаю, как люди отправляют задачи на GPU или TPU. И вообще, что такое TPU? Я не знаю, как использовать SIMD-инструкции.

Всё это построено на фундаменте, который нужно усвоить в первую очередь. Это значит вернуться к основам и сделать что-то простое. В вышеупомянутой книге Кларка Скотта описан простейший компьютер. Вот почему я начал с него.

Слава Скотту! Он работает!

Компьютер Скотта — это 8-разрядный процессор, подключённый к 256 байтам ОЗУ, все они подключены через 8-разрядную системную шину. У него 4 регистра общего назначения и 17 машинных инструкций [10]. Кто-то сделал визуальный симулятор для веба [11]: это действительно здорово. Страшно подумать, сколько времени потребовалось, чтобы отследить все состояния схемы!

Я не знал, как работают процессоры, поэтому написал программный симулятор - 5 [12]
Схема со всеми компонентам процессора Скотта. Копирайт 2009-2016. Зигберт Фильбингер и Джон Кларк Скотт

Книга сопровождает вас по маршруту от скромных логических вентилей до битов в памяти и регистров, а затем продолжает наслаивать компоненты, пока вы не получите что-то похожее на схему выше. Очень рекомендую прочитать книгу, даже если вы уже знакомы с концепциями. Только не версию Kindle, потому что диаграммы иногда трудно увеличить и разобрать на экране «читалки». По-моему, это многолетняя проблема Kindle.

Мой компьютер отличается от версии Скотта разве что тем, что я обновил его до 16 бит, чтобы увеличить объём доступной памяти, ведь хранение только глифов для таблицы ASCII занимает большую часть 8-битной машины Скотта, оставляя совсем мало места для полезного кода.

Мой процесс разработки

В целом, разработка шла по такой схеме: чтение текста, изучение диаграмм, а затем попытка реализовать их на языке программирования общего назначения и определённо не использовать никаких специализированных инструментов для проектирования интегральных схем. Я написал симулятор на Go просто потому, что немного знаком с этим языком. Скептики могут сказать: «Болван! Неужели ты не мог изучить VHDL [13] или Verilog [14], или LogSim [15], или ещё что-то. Но к тому моменту я уже написал свои биты, байты и логические вентили и погрузился слишком глубоко. Может, в следующий раз я выучу эти языки и пойму, сколько времени потратил впустую, но это мои проблемы.

В большой схеме в компьютере просто передаётся куча булевых значений, поэтому подходит любой язык, который дружит с булевой алгеброй.

Наложение схемы на эти булевы значения помогает нам (программистам) вывести смысл, а самое главное — решить, какой порядок байтов [16] будет использовать система, и убедиться, что все компоненты передают данные по шине в правильном порядке.

Это было очень трудно реализовать. Ради смещения я выбрал представление с обратным порядком байтов, но при тестировании ALU никак не мог понять, почему выходят неправильные цифры. Мой кот услышал много, очень много непечатных выражений.

Разработка шла не быстро: возможно, она заняла около месяца-двух моего свободного времени. Но когда только процессор успешно выполнил операцию $2 + 2=5$, я был на седьмом небе от счастья.

Всё шло своим чередом, пока дело не дошло до ввода-вывода. Книга предлагала дизайн системы с простой клавиатурой и интерфейсом дисплея, чтобы вводить данные в машину и выводить результат. Ну, мы уже зашли так далеко, нет смысла останавливаться на полпути. Я поставил цель реализовать набор на клавиатуре и отображение букв на дисплее.

Периферия

В качестве аппаратного интерфейса между CPU и внешним миром периферийные устройства используют шаблон адаптера [17]. Наверное, несложно догадаться, что этот шаблон позаимствован из проектирования программного обеспечения.

Я не знал, как работают процессоры, поэтому написал программный симулятор - 7 [18]
Как адаптеры ввода-вывода подключаются к окну GLFW

С таким разделением оказалось довольно просто подключить клавиатуру и дисплей к окну под управлением GLFW. На самом деле я просто вытащил большую часть кода из своего эмулятора [19] и немного изменил его, чтобы каналы Go работали как сигналы ввода/вывода.

Запускаем компьютер

Я не знал, как работают процессоры, поэтому написал программный симулятор - 8

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

Самой большой проблемой стало жонглирование четырьмя регистрами, отслеживать их, вытаскивая данные из регистров и временно сохраняя их в памяти. В процессе я вспомнил, что у процессора Gameboy есть регистр указателя стека для удобства выгрузки и загрузки регистров. К сожалению, у этого компьютера нет такой роскоши, поэтому приходилось постоянно вручную перемещать данные в память и обратно.

Я решил потратить время только на одну псевдоинструкцию CALL, чтобы вызвать функцию, а затем вернуться к точке. Без этого доступны вызовы только на один уровень в глубину.

Кроме того, поскольку машина не поддерживает прерывания, пришлось реализовать ужасный код опроса состояния клавиатуры. В книге обсуждаются шаги, необходимые для реализации прерываний, но это серьёзно усложняет схему.

Но хватить ныть, я всё-таки написал четыре программы [20], и большинство из них используют какой-то общий код для рендеринга шрифтов, ввода с клавиатуры и т. д. Это не совсем операционная система, но даёт понимание, что делает простая ОС.

Это было нелегко. Самая сложная часть программы text-writer — правильно рассчитать, когда перейти к новой строке или что происходит, когда вы нажимаете клавишу Enter.

main-getInput:
	CALL ROUTINE-io-pollKeyboard
	CALL ROUTINE-io-drawFontCharacter
	JMP main-getInput

Основной цикл программы text-writer

Я не удосужился реализовать клавишу Backspace и клавиши-модификаторы. Зато понял, сколько труда требует разработка текстовых редакторов и насколько это утомительно.

Выводы

Это был весёлый и очень полезный для меня проект. В разгар программирования на ассемблере я почти забыл о логических вентилях, работающих внизу. Я поднялся на верхние уровни абстракции.

Хотя этот процессор очень прост и далёк от CPU в моём ноутбуке, но мне кажется, что проект многому меня научил, в частности:

  • Как биты перемещаются по шине между всеми компонентами.
  • Как работает простой ALU.
  • Как выглядит простой цикл Fetch-Decode-Execute.
  • Что машина без регистра указателя стека и концепции стека — отстой.
  • Что машина без прерываний тоже отстой.
  • Что такое ассемблер и что он делает.
  • Как периферийные устройства взаимодействуют с простым процессором.
  • Как работают простые шрифты и как отображать их на дисплее.
  • Как может выглядеть простая операционная система.

Так что дальше? В книге говорится, что никто не производил таких компьютеров с 1952 года. Это значит, что мне придётся изучить материал за последние 67 лет. Это займёт меня на какое-то время. Я вижу, что руководство по x86 составляет 4800 страниц [21]: вполне достаточно для приятного, лёгкого чтения перед сном.

Может, я немного побалуюсь с операционной системой, языком C, убью вечер с набором для сборки PiDP-11 [22] и паяльником, а потом заброшу это дело. Не знаю, посмотрим.

Если серьёзно, то я думаю исследовать архитектуру RISC, возможно, RISC-V. Вероятно, лучше начать с ранних процессоров RISC, чтобы понять их происхождение. У современных процессоров гораздо больше функций: кэши и прочее, я хочу разобраться в них. Там нужно многое изучить.

Пригодятся ли эти знания на моей основной работе? Возможно, пригодятся, хотя вряд ли. В любом случае, мне это нравится, так что неважно. Спасибо за чтение!

Автор: m1rko

Источник [23]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/assembler/318552

Ссылки в тексте:

[1] «Но откуда он знает?»: http://buthowdoitknow.com/

[2] simple-computer: https://github.com/djhworld/simple-computer

[3] ужасная куча логических вентилей, которые включаются и выключаются: https://github.com/djhworld/simple-computer/blob/master/cpu/cpu.go#L763

[4] прогнал модульные тесты: https://github.com/djhworld/simple-computer/blob/master/cpu/cpu_test.go

[5] ввод с клавиатуры: https://github.com/djhworld/simple-computer/blob/master/io/keyboard.go#L20

[6] на дисплее: https://github.com/djhworld/simple-computer/blob/master/io/display.go#L13

[7] GLFW: https://github.com/djhworld/simple-computer/blob/master/cmd/simulator/glfw_io.go

[8] грубый ассемблер: https://github.com/djhworld/simple-computer/blob/master/asm/assembler.go

[9] эмулятора Gameboy, который я написал в 2013 году: https://github.com/djhworld/gomeboycolor

[10] 17 машинных инструкций: https://github.com/djhworld/simple-computer#instructions

[11] визуальный симулятор для веба: http://www.buthowdoitknow.com/but_how_do_it_know_cpu_model.html

[12] Image: https://habrastorage.org/webt/t_/hm/rp/t_hmrpg_rrprxwh5uiiybl2jf6w.png

[13] VHDL: https://en.wikipedia.org/wiki/VHDL

[14] Verilog: https://en.wikipedia.org/wiki/Verilog

[15] LogSim: http://www.cburch.com/logisim/

[16] порядок байтов: https://en.wikipedia.org/wiki/Endianness

[17] шаблон адаптера: https://en.wikipedia.org/wiki/Adapter_pattern

[18] Image: https://habrastorage.org/webt/dc/ge/e_/dcgee_zl9m4i_wrzlkovzh8pnjc.png

[19] эмулятора: https://github.com/djhworld/gomeboycolor-glfw

[20] четыре программы: https://github.com/djhworld/simple-computer/blob/master/_programs/README.md

[21] руководство по x86 составляет 4800 страниц: https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf

[22] набором для сборки PiDP-11: https://obsolescence.wixsite.com/obsolescence/pidp-11

[23] Источник: https://habr.com/ru/post/453158/?utm_source=habrahabr&utm_medium=rss&utm_campaign=453158