Собираем 8-битный компьютер

8-битный компьютер, выполняющий программу8-битный компьютер, выполняющий программу

Привет! Я всегда хотел собрать свой компьютер — не только в теории понять как «бегают» биты, складываются числа, работают прерывания, как программный код превращается в нули и единицы. У меня получилось и я хотел бы поделиться своим опытом. Это заняло у меня 140 часов и $400 на все компоненты и их доставку. Если вам интересно узнать о проекте, спускайтесь под кат.

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

Содержание статьи:

Видеоиллюстрация

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

Вы можете посмотреть первые 10 секунд видео, в котором демонстрируется выполнение программы, вернуться на статью и дочитать ее, а потом с бóльшим контекстом досмотреть видео.

Архитектура

Компьютер построен на архитектуре SAP-1 simpleaspossible. SAP-1 — это архитектура для начинающих, главная цель — понять базовые идеи и концепции построения компьютера без углубления в детали. Дизайн специально разработан для академических целей.

Большинство деталей в проекте — это 7400 серия интегральных микросхем от Texas Instruments, американской компании-производителя полупроводниковых изделий.

Компоненты

Компьютер состоит из следующих компонентов:

  • Тактовый генератор.

  • Оперативная память.

  • Регистр адреса оперативной памяти.

  • Буферные регистры A и B.

  • Арифметико-логическое устройство.

  • Регистр ввода-вывода и дисплей.

  • Счётчик команд.

  • Регистр инструкций.

  • Шина для адреса и данных.

  • Устройство управления.

Схема

Схема расположения компонентов выглядит следующим образом:

7336d5bf49e9258c92e0e4e39abece10.png

Компоненты

Тактовый генератор

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

В основе тактового генератора лежит чип LM555CN — это таймер, устройство для формирования повторяющихся импульсов тактовыхсигналов. С помощью резисторов и конденсатора можно контролировать частоту импульсов. Так, например, у Intel Core i9–7980XE базовая тактовая частота — 2.60 GHz. Это значит, что за одну секунду выдается 2.6 миллиарда импульсов.

26dc13bb28c491a0f046005cdd2b181b.gif

Частота импульса складывается из времени наличия напряжения и его отсутствия как проиллюстрировано на рисунке ниже. По формуле ниже, она из документации к таймеру, при резисторе А — 100 Ом, резисторе B — 100K Ом, конденсаторе С — 2 микрофарад, получается, что один такт занимает — 0.693×201000 * 0.000002 = 0.278 секунды. За одну секунду получится — 1 / 0.278 = 3.59 такта.

T=0.693(R_a+2R_b)Ca2d6bba9aaa089ad87285a58b1b1eea4.jpeg

Пример использования тактового генератора — внизу на картинке на макетной плате находится чип SN74LS173, это 4-битный D flip-flop — он нужен для того, чтобы хранить 4 битовых значения. Таким образом можно хранить 16 комбинаций значений, от 0000 до 1111. У чипа 16 ножек с помощью которых он вставляется в плату. Каждая из которых отвечает за свою часть работы. Чтобы не вдаваться в подробности, если на M и N разрешениеназапись, и 1D подать напряжение, мы ожидаем, что чип сохранит значение какнапряжение и отобразит это в 1Q, выход которого ведет к диоду краснаялампочка —, но ничего не произойдет. Для сохранения значения нам нужно также подать напряжение на вход CLK clock signal — тактовый сигнал, который исходит из тактового генератора.

9fce5da9bea6ad2c4f7269ab00461d7a.png

В проекте тактовый генератор чуть-чуть сложнее:

  • Вместо резистора на 100К Ом там находится потенциометр, это «резистор с крутилкой», его можно поворачивать за и против часовой стрелки и динамически изменять сопротивление от 0 ОМ до 1М Ом. Таким образом можно увеличить количество тактов в секунду и компьютер будет работать быстрее, и наоборот.

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

Оперативная память

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

Знакомый нам чип SN74LS173 может сохранить 4 бита информации, чтобы сохранить 8 бит информации — нужно взять два SN74LS173. Таким образом, мы можем хранить значения от 0000 0000 до 1111 1111 256 возможных комбинаций, 2 в степени 8.

На схеме ниже к двум SN74LS173 подключен DIP-переключатель на 8 переключателей, которыми можно задавать 8 бит информации. Так как переключатели подсоединены к питанию, если переключить один из них, он выдаст напряжение. При подаче сигнала от тактового генератора, это значение сохранится в чипе и соответствующий диод загорится.

7fe15ce99723f13b14ef4f9ef5b619d2.png

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

Для этого мы соединяем входы M и N с кнопкой. Кнопка подключена к напряжению, если на нее нажать, она передаст напряжение по перемычке. Нажав на кнопку и дождавшись тактового сигнала, мы получим запись значения.

1f7f7f654869cf8d325b4c6e90648185.png

Таким образом, схематически, можно выразить масштабируемость оперативной памяти как наличие одной кнопки, которая контролирует запись 8 бит. Если мы хотим иметь 128 бит оперативной памяти, а именно столько памяти в проекте, нам нужно 16 кнопок, каждая из которых отвечает за свои 8 ячеек оперативной памяти 16×8 = 128.

Если бы мы горизонтально подключили все ячейки между собой все первые ячейки каждой колонки, все вторые, третьи и так далее, соединив с одним переключателем на 8 переключателей, мы могли бы контролировать в какую именно колонку записать переданное через переключатели значение нажатием кнопки. Нажали на 16-ю кнопку — значение записалось только в последнюю колонку ячеек.

Кнопки получились бы репрезентацией адресов оперативной памяти. Но это сложно масштабировать, легче масштабировать бинарное представление 16 кнопок. То есть 4 бита, от 0000 до 1111 — в сумме 16 комбинаций, что равно количеству кнопок и, соответственно, колонок ячеек. С этим поможет DIP-переключатель на 4 переключателя.

Если значение переключателей будет 0000 — выбираем первый ряд, если 0001 — второй ряд, 0011 — четвертый ряд, и так далее до 1111 — 16 ряд. Раз кнопки превратились в переключатели, а переключатели превратились в перенаправление на определенную колонку ячеек, мы потеряли кнопку на запись — которую тоже надо добавить.

Таким образом, мы изобретаем декодер адресов. На вход декодера подается 4 сигнала, отвечающих за адрес в памяти, и 1 сигнал, отвечающий за запись.

203e9c26a06c74e0b95e59f1e6ab900e.png

Мы не будем разбирать устройство декодера. Внутри декодера находится комбинационная логика — логические вентили И AND и инверторы NOT. Иллюстрация работы в коротком видео здесь.

0a4828c69a45e4155d573a3559b47f04.png

Таким образом, мы имеем 4 переключателя для адресов, 8 переключателей для значений ячеек, 1 кнопка на запись значений.

Арифметико-логическое устройство

Арифметико-логическое устройство АЛУ — компонент, который выполняет арифметические и логические операции. Например, АЛУ в проекте умеет суммировать и вычитать два числа, каждое из которых представлено 8 битами. Вид операции зависит от положения тумблера: замкнутый тумблер даст сигнал АЛУ сложить числа, разомкнутый вычесть одно число из другого.

efd070d8467e36f792067dace39781c2.png

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

00000011 + 00001100 = 00001111 \\ (3 + 12 = 15)

Но на самом деле, АЛУ не принимает произвольные значения из переключателей. Каждое значение хранится в отдельном регистре — A и B. Эти регистры являются буферными регистрами. Буферные регистры предназначены для временного хранения данных и напрямую подключены ко входам АЛУ.

Регистры A и B почти идентичны по строению 4 знакомых нам чипа SN74LS173, но у них разные задачи. Регистр A призван сохранять промежуточный результат вычислений — один операнд, а регистр B призван хранить другой операнд.

Ниже в коде чуть более наглядно о задачах регистров на примере счётчика с инкрементом. Изначально, мы инициализируем переменную a и регистр А значением 0, переменную b регистр B значением 2. Cуммируем a и b, сохраняя в переменную sum регистр суммы в АЛУ. Значение из sum перезаписывается в a. Повторяем в цикле пока a меньше 255.

def increment():
    a = 0
    b = 2
    
    while a < 255:
        sum = a + b
        a = sum
    
    return a

Схема архитектуры, которую вы уже видели, и пример задачи показывает, что:

  • Регистр суммы в АЛУ нужен для сохранения результата операции между регистрами A и B — значение можно передать в другие компоненты через шину данных. Например, в регистр A и решить этим задачу с инкрементом.

  • Регистр B нужен для хранения вспомогательных значений — в него можно только записать через шину данных.

  • Регистр A нужен для временного хранения значений — его можно передать в другие компоненты через шину данных.

8313d8d6e5d8371f63033d9863f1ade3.png

Также АЛУ не подключен к тактовому генератору, это видно на схеме выше, — это ассинхронный компонент. Это значит, что он отрабатывает сразу как только меняются значения в A и/или B. Это достигается за счет того, что в состав АЛУ включены только комбинационные схемы, как у декодера адресов.

Схема SN74LS181— 4-битного АЛУСхема SN74LS181— 4-битного АЛУ

Регистр ввода-вывода и дисплей

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

Чтобы отобразить букву F, нужно подать питание на 1, 2, 4 и 6 ножки слева направо, сверху вниз. Чтобы отобразить цифру 1, нужно подать питание на 5 и 9 ножки. Вместо порядкового номера ножки, можно использовать буквы на схеме — для цифры 1 это B и C.

a28067ea48548a1966ed1321e7a75daa.jpg

Если мы хотим отобразить число, состоящее из нескольких цифр, мы можем использовать несколько индикаторов.

f1a51483c26d02188850918b44491972.jpeg

В проекте таких индикаторов 3 — они используются для отображения чисел в диапазоне от 0 00000000 до 255 11111111, один индикатор на одно число 0 отображается как 000, 1 как 001. Также в индикаторе есть ножка десятичного знака DP на схеме на случай, если нужно отображать числа с дробной частью например 17.3 —, но такое функциональности в проекте нет, поэтому эта ножка не используется. Как вы поняли, шестнадцатеричная система счисления в проекте не используется, вместо F 15, используется два дисплея с 1 и 5.

Снизу проиллюстрированы все возможные варианты отображения одного десятичного числа на дисплее.

16f5810ba0ee561cab672f2f76ef6c2b.png

Теперь нужно понять как «соединить» 4-битное значение в диапазоне от 0000 0 до 1111 9 с входами дисплея от A до G. Например, если значение 0011, то на B и C нужно подать напряжение, а на A, D, E, F, G не нужно. С этим поможет таблица истинности ниже.

5ff51dd639b27e3c54fa66b90730636b.png

© Habrahabr.ru