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

Автор оригинала: Daniel Harper
  • Перевод

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

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

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

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






Пример программ

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

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

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

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


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

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

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

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

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


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


Схема со всеми компонентам процессора Скотта. Копирайт 2009-2016. Зигберт Фильбингер и Джон Кларк Скотт

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

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

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


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

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

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

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

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

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

Периферия


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


Как адаптеры ввода-вывода подключаются к окну GLFW

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

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




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

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

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

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

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

Это было нелегко. Самая сложная часть программы 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 страниц: вполне достаточно для приятного, лёгкого чтения перед сном.

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

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

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

Комментарии 33

    +19

    Ожидал увидеть тонкости написания симулятора, а вместо этого вода "как я провёл лето".

      +2

      Я знал как работают процессоры, поэтому сделал свой на телеграфных реле.

        +9

        Краткое содержание: "Оказывается, компьютер спроектировали люди, пользующиеся какой-то хренью под названием "логика" и "математика", и её, оказывается, можно даже понять! Я просто охренел!".


        Ждём следующую статью от автора — "оказывается, как работают живые клетки, тоже можно понять!"

          0

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

            +2

            Там ничего непоятного нет, от слова "совершенно" — если не перескакивать уровни абстракции. Всё замечательно цепляется одно за другое; как "транзисторы бибикают" в принципе неважно — скажем, я начинал с двоичной логики ("да & нет = нет" и проч), далее триггеры, далее простейший CPU (скажем, Z80), "далее — везде".

              0

              Кстати да, неплохой подход. У меня одно время были проблемы с пониманием ассемблера и всей этой низкоуровневой вакханалии. В итоге сначала написал свой ассемблероподобный скриптовый движок, итеративно пытаясь уложиться в как можно более мелкие команды, реализовывая более высокие за счёт них внутри скрипта. Затем, с чуть большим пониманием, перекинулся на тот самый Z80, на спектруме, у которого и мнемоника элементарная. И в итоге как-никак допёр :-)

              0
              На хабре есть несколько статей. Например эта: habr.com/ru/post/132486 прям с основ, как они там бибикают.
              +1
              А кто-то понял, как работают живые клетки? Вот это новость!

              Когда ваши ручки потянутся воткнуть мне минус, можете для начала поизучать вот этот сайтец: biochemical-pathways.com

              Так что сравнивать компьютер и клетку несколько преждевременно.
                –1

                Я сказал — "можно понять". Что Вам непонятно на "этом сайтеце"?

              0
              На эту тему достаточно хорошо написано в книге Харрисов — Юрий Панчул активно рассказывает о ней и о возможностях изучения архитектуры MIPS.
                0
                Здравствуйте. Спасибо за статью, аж ностальгия нахлынула. Не так давно тоже решил, что хочу узнать больше о железе и вообще о том как все работает. Для лучшего понимания посмотрел лекции Carnegie Mellon — Computer Architecture. Рекомендую, там рассказывают про работу более актуальных процессоров. Сейчас пытаюсь создать свой процессор на ПЛИС, и рассказываю о процессе создания на youtube. Если интересно, то вот ссылка на канал.
                  0
                  Тоже порекомендую хороший курс на эту тему Build a Modern Computer from First Principles: From Nand to Tetris. Название в принципе говорит само за себя. Может кого заинтересует. И книга по курсу — The Elements of Computing Systems: Building a Modern Computer from First Principles.
                    +2
                    Еще хорошая книга по теме Code: The Hidden Language of Computer Hardware and Software. Мне очень зашла, есть в русском варианте.
                      +1
                      «Код. Тайный язык информатики» — моя вообще любимая книга на эту тему. Интересно, что ее автор Чарльз Петцольд работал в Microsoft (и из-за этого некоторые крутые программисты имеют к ней предубеждение и не хотят читать), но при этом книга просто шедевральна.

                      Везде есть в PDF на русском. Её собираются переиздавать кстати: www.mann-ivanov-ferber.ru/books/kod-tajnyij-yazyik-informatiki
                      –1
                      Рекомендую автору найти книжку Толкиена — Микропроцессоры, курс и упражнения, изданную у нас в 1988 году (именно этого года).
                      Если у вас хорошая наблюдательность и развитая логика, то это как раз для вас книжка. В ней мноо-о-ожество ошибок. Исправляя их вы самосовершенствуетесь и одновременно изучаете тему.)) Без захода в железо. С самопроверкой.
                      Оригинал — R.L.Tokheim — Theory and Problems of Microprocessor. Fundamentals. Mc Graw-Hill, 1983.
                      На основе виртуального intel 8080. Актуально и сейчас для начинающих. )
                        0
                        Рекомендую автору найти книжку Толкиена

                        может быть всё-таки Токхейма? :)

                        0
                        Рекомендую игру MHRD.
                        Как раз про проектирование собственного процессора.
                          0
                          Хочу написать эмулятор Intel 80486, способный запускать Windows 95. Вопрос: стоит ли пробовать реализовывать его, или для начала лучше что-то попроще взять?
                            0
                            Думаю стоит с 8080.
                              0
                              ao486 — готовый 80486 на Verilog, можно погонять на плис/симуляторе, модифицировать или помодульно воспроизводить/дополнять своими силами
                              github.com/alfikpl/ao486
                              0
                              Сколько подобных материалов про устройство процессора на уровне логических единиц, но нет материалов про уровень ещё ниже — уровня технической работы элементов.
                              Транзистор — знаменитый революционный элемент, закон Мура и всё-такое, а для чего он — так и неизвестно.
                              слово «транзистор» можно растолковать, как преобразователь сопротивления. Примерно так же, как в гидравлике изменение потока жидкости регулируется задвижкой. У транзистора такая «задвижка» изменяет количество электрических зарядов, создающих электрический ток. Это изменение есть не что иное, как изменение внутреннего сопротивления полупроводникового прибора.
                              Непонятно, зачем и в каком месте в этой схеме процессора применяется, почему при увеличении его количества процессор работает быстрее…
                              image
                                0
                                Логические элементы создаются из транзисторов. youtu.be/RwSLO953anc?t=225
                                  0
                                  Советую вам поиграть в Minecraft. Принцип работы транзистора вы уже знаете, так что провести аналогию будет не трудно. А вот что решает количество — поймёте на практике, сделав парочку сумматоров.
                                    0
                                    Спасибо, но видео выше помогло понять, что всё было банально просто.

                                    Но раз транзистор выдаёт два состояния, тогда получается, что ноль — это не отсутствие сигнала, а очень слабый сигнал, чтобы его можно было передать?
                                      0
                                      Вообще говоря, вольтаж очень мал, а сопротивление велико, поэтому считается, что сигнал отсутствует и воспринимается это как логический ноль. А физика берёт своё, и ток, пытаясь преодолеть сопротивление, начинает нагревать транзистор.
                                      P.S. оценки стоит принимать относительно размеров. Для больших транзисторов и напряжение больше.
                                        0
                                        Так вот почему чипы нагреваются и приходится охлаждать.
                                        Выходит, что чем больше чип имеет дело с нулями, тем сильнее греется?
                                        Но если будет оперировать больше «единицами», то тогда возрастает энергопотребление?
                                        Питание vs Тепловыделение :)
                                          0
                                          Видео сильно упрощенное и с кучей неточностей. Большинство современных цифровых схем потребляют ток в момент переключения из одного состояния в другое. Именно поэтому при разгоне процессора или видеокарты требуется мощная система охлаждения: чем выше частота, тем больше переключений и тем больше потребляемая мощность.
                                        0
                                        В смысле передавать? Должно быть просто отсутствие тока.
                                          0
                                          Там вроде тактовые генераторы в этом участвуют, да? Надо передать файл из 10 нулей — отсчитал 10 тактов, а там ему и не важно что передаёт — нули или единицы.
                                            0
                                            На уровне транзисторов нет нулей и единиц, если наличие тока и отсутствие. Тактовым генераторам, да, не важно, есть ток или нет.
                                          0
                                          Хочу дополнить видео. Там показаны pnp или npn транзисторы, а в процессорах используют полевые с изолированным затвором.
                                          0
                                          еще круче сыграть в кохктпактоп: www.zachtronics.com/kohctpyktop-engineer-of-the-people
                                        0
                                        Хех, похоже на мой диплом. И ведь пришлось писать микрокод и микроассемблер, ассемблер и демонстрационные программы, монитор. Сам эмулятор. Воспоминания, воспоминания… Только архитектура процессора иная.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое