Недавно мы занимались восстановлением бортового управляющего компьютера «Аполлона» – компьютера, занимавшегося управлением, навигацией и контролем на борту миссий «Аполлон», летавших к Луне. Этот исторический компьютер одним из первых начал использовать интегральные схемы (ИС), а его процессор был полностью построен на вентилях NOR (в памяти компьютера использовался второй тип ИС, усилитель считывания). В данной статье я опишу архитектуру и схему CPU.
Архитектура управляющего компьютера «Аполлона»
Разделённые лотки управляющего компьютера «Аполлона». В левом лотке содержится логика на основе NOR-вентилей. В правом – память и вспомогательные компоненты.
Управляющий компьютер «Аполлона» (Apollo Guidance Computer, AGC) разработали в 1960-х для обеспечения полёта миссий «Аполлон» на Луну. В то время, когда большая часть компьютеров занимала объём от полноразмерного холодильника до целой комнаты, AGC был чем-то уникальным – он был достаточно маленьким, чтобы уместиться на борту космического корабля «Аполлон», весил 32 кг и занимал не более 0,03 м3.
Компьютер AGC – 15-битный. Странно встречать размер слова, не являющийся степенью двойки, но в 1960-х, ещё до того, как байты стали популярными, в компьютерах использовали разнообразные размеры слов. 15 бит обеспечивали достаточную точность для посадки на луну (и использовали данные с удвоенной и утроенной точностью при необходимости), поэтому 16 бит просто увеличили бы размер и вес компьютера без необходимости.
Инструкция AGC вмещалась в 15-битное слово, и состояла из 3 бит, обозначавших код операции, и 12 бит, обозначавших адрес в памяти. К сожалению, этих объёмов всё равно не хватало, поэтому в компьютере использовались многочисленные хитрости и обходные пути, и архитектура получилась довольно неуклюжей. 12-битный адрес памяти мог обращаться только к 4 К слов. При этом у AGC было 2К слов в основной RAM и 36К слов в памяти на сердечниках. Чтобы обращаться ко всей памяти, AGC использовал сложную систему переключения банков памяти и множество регистров. Иначе говоря, к памяти можно было обращаться только по кускам в 256 слов, а к ROM – по кускам несколько большего размера.
3 бит для кода операции не хватало, чтобы напрямую обозначить 34 возможные инструкции, поэтому AGC использовал трюки с расширением значения инструкций и с тем, что некоторые инструкции имело смысл исполнять только с определёнными ячейками памяти. Кроме того, использовались такие трюки, как «волшебные» адреса в памяти – к примеру, запись в ячейку «регистра сдвига вправо» осуществляла побитовый сдвиг, исключая таким образом необходимость в отдельной инструкции «сдвиг вправо». Были также инструкции, сочетающие несколько действий сразу.
Архитектура AGC была довольно простой, даже по стандартам 1960-х. Хотя его создавали в эпоху сложных и мощных мейнфреймов, возможности AGC были весьма ограничены; по мощности и архитектуре он сравним с ранними микропроцессорами. Его сильными сторонами были компактный размер и большие возможности по обеспечению ввода и вывода данных в реальном времени.
На архитектурной диаграмме ниже показаны основные компоненты AGC. Я выделил цветом те части, на которых остановлюсь подробнее. У AGC был небольшой набор регистров, и простой арифметический модуль, занимавшийся только сложением. У него была ROM всего на 36К слов и RAM на 2К слов. «Шина записи» была основным путём передачи данных между компонентами. Декодирование инструкций и генератор последовательностей выдавали управляющие импульсы для AGC.
Блок-схема AGC
Примерно половину диаграммы занимает память, что отражает тот факт, что во многих аспектах архитектуру AGC разрабатывали вокруг его памяти. Как большая часть компьютеров в 1960-х, AGC использовал память на сердечниках, храня каждый бит в крохотном ферритовом кольце (сердечнике), нанизанном на сетку проводов. Поскольку для каждого бита требовался отдельный физический сердечник, объём такой памяти был кардинально меньше, чем у современной полупроводниковой. Отличительным свойством памяти на сердечниках было то, что прочтение слова из памяти удаляло его, поэтому после каждого доступа это значение приходилось записывать заново. Также у AGC была фиксированная память ROM, знаменитые прошитые сердечники – они использовались для хранения программ, и были физически прошиты проводами (см. ниже).
Память на прошитых сердечниках крупным планом
NOR-вентили
AGC был одним из первых компьютеров, использовавших ИС. Возможности этих первых ИС были весьма ограниченными; на чипах AGC (ниже) находилось всего по шесть транзисторов и восемь резисторов, и вместе они реализовывали NOR-вентиль с тремя входами.
Двойной NOR-вентиль с тремя входами из AGC. Десять проводов снаружи кристалла соединены с внешними контактами ИС.
Ниже показано схематическое обозначение NOR-вентиля. Это простейший логический вентиль: если все входы равны нулю, то выход равен единице. Вас может удивить, но одних NOR-вентилей достаточно для создания компьютера. NOR – вентиль универсальный: на его основе можно сделать любой другой логический вентиль. К примеру, при объединении всех входов NOR мы получаем инвертер. Расположив на выходе NOR инвертер, мы получим OR-вентиль. Расположив инвертеры на входах NOR-вентиля, мы получим AND-вентиль. А из этих вентилей можно строить более сложную логику: триггеры, сумматоры и счётчики.
NAND-вентиль обладает той же универсальностью. В современных схемах по техническим причинам NAND используются чаще, чем NOR. В популярном курсе "От NAND до Тетриса" описано, как создать вычислительную машину из NAND-вентилей, вплоть до реализации игры «Тетрис». Сначала из NAND строится набор логических вентилей (NOT, AND, OR, XOR, мультиплексор, демультиплексор). Затем из них создаются более крупные строительные блоки (триггер, сумматор, счётчик, ALU, регистр), а уже из них – компьютер.
NOR-вентиль выдаёт 1, если на всех входах у него 0. Если хотя бы на одном из входов 1, то NOR выдаёт 0.
Очень часто в AGC попадается такой компонент, как RS-триггер (set-reset, установить/сбросить). Эта схема делается из двух NOR-вентилей и хранит один бит данных. На входе set хранится бит 1, а на входе reset – бит 0. То есть, импульс 1, поданный на вход set, выключает верхний вентиль и включает нижний, поэтому на выходе получается 1. Импульс 1, поданный на вход reset, делает всё наоборот. Если на оба входа подать 0, триггер запоминает своё предыдущее состояние, играя роль накопителя. В следующем разделе мы покажем, как из триггера делаются регистры.
RS-триггер из двух NOR-вентилей. Один вентиль при включении выключает другой. Черта над одним из выходов обозначает, что он дополняет другой.
Регистры
У AGC есть небольшой набор регистров для временного хранения значений вне основной памяти. Основной регистр – накопитель (A), используемый во многих арифметических операциях. Также у него есть регистр счётчика Z, регистры арифметического блока X и Y, буфер B, адрес возврата Q и некоторые другие (в современных компьютерах для вызова подпрограмм и возврата из них используется стек, но в ту эпоху программистам для рекурсии необходимо было писать стек самим). Для доступа к памяти есть регистр адреса памяти S, а для данных — регистр буфера памяти G. Также у AGC есть регистры в основной памяти – к примеру, счётчики ввода/вывода.
На диаграмме ниже приведена схема регистров AGC, упрощённая для случая с одним битом и двумя регистрами. У каждого бита регистра есть триггер, использующий описанную ранее схему (синий и фиолетовый). Данные передаются в регистры и из них по шине записи (красный). Для записи в регистр триггер сбрасывается сигналом очистки (CQG или CZG, зелёный). Затем сигнал «запись» (WQG или WZG, оранжевый) позволяет данным, идущим по шине записи, установить соответствующий триггер регистра. Для чтения регистра сигнал «чтение» (RQG или RZG, голубой) проходит выход триггера через усилитель записи на шину записи, и используется в других частях AGC. Полная схема регистров более сложная, в ней есть несколько 16-битных регистров, но базовая схема такая.
Упрощённая схема работы регистров AGC
Диаграмма регистров иллюстрирует три ключевых момента. Во-первых, схема регистров строится из NOR-вентилей. Во-вторых, движение данных построено вокруг шины записи. Наконец, действия регистров зависят от определённых управляющих сигналов, приходящих в нужное время.
Арифметический модуль
У большинства компьютеров есть арифметико-логическое устройство, выполняющее арифметические и булевские операции. По сравнению с современными компьютерами, арифметический модуль у AGC весьма ограничен: он выполняет только сложение 16-битных величин, поэтому называется арифметическим модулем, а не арифметико-логическим (остальные операции выполняются через различные трюки; к примеру, вычитание выполняется через сложение, перед которым у одного из аргументов биты обращаются на противоположные, и т.п.).
На схеме ниже показан один бит арифметического модуля AGC. Полный сумматор (красный) вычисляет сумму двух битов и перенос. Перенос передаётся в следующий сумматор – таким образом их можно комбинировать для сложения более длинных слов (чтобы ускорить передачу переноса в случаях типа 111111111111111 + 1, AGC использует сумматор с пропуском переноса).
Регистры X и Y (фиолетовый и зелёный) обеспечивают два входящих бита в сумматор. Они реализованы при помощи уже описанных триггеров на NOR-вентилях. Синий контур записывает значения в регистры X и Y в соответствии с управляющими сигналами. Схема довольно сложна, ведь она позволяет хранить в регистрах константы и значения со сдвигом, но я не буду углубляться в эту тему. Обратите внимание на управляющий сигнал A2X, передающий значение регистра A в регистр X; мы вернёмся к нему позже.
На фото ниже показана физическая реализация схемы AGC. Этот модуль реализует четыре бита для регистров и арифметический модуль. Чёрные прямоугольники – это плоские ИС; в каждом модуле есть две платы по 60 чипов, и всего получается 240 NOR-вентилей. Арифметический модуль и регистры собраны из четырёх идентичных модулей, каждый из которых обрабатывает по четыре бита; это похоже на работу микропроцессорной секции.
Арифметический модуль и регистры собраны из четырёх идентичных модулей. Модули установлены в слоты от A8 до A11.
Выполнение инструкции
В данном разделе описывается последовательность операций, которые проделывает AGC для выполнения инструкции. В частности, я покажу, как работает инструкция сложения ADS (add to storage). Эта инструкция читает значение из памяти, прибавляет его к накопителю (регистр А), и сохраняет сумму как в сумматоре, так и в памяти. Это единичная инструкция, но для её выполнения AGC проделывает несколько шагов и много значений перемещаются туда и сюда.
Таймер инструкций реализуется за счёт подсистемы памяти на магнитных сердечниках. В частности, чтение значения из памяти стирает хранимое значение, поэтому после каждого чтения значение необходимо записать обратно. Также при доступе к памяти существует задержка между обозначением адреса и поступлением данных. В итоге каждый такт памяти тратит по 12 шагов на чтение и на последующую запись. Каждый интервал времени (от T1 до T12) длится чуть менее микросекунды, а весь такт длится 11,7 мкс, и называется временем такта памяти (MCT).
Модуль стираемой памяти на магнитных сердечниках от AGC. Хранит 2 килослова, каждый бит хранится при помощи отдельного крохотного ферритового колечка.
MCT является базовой единицей памяти для исполнения инструкций. Типичной инструкции требуется два такта памяти: один для извлечения инструкции из памяти, второй для выполнения операции. Поэтому на типичную инструкцию уходит два MCT (23,4 мкс), что даёт нам 43 000 инструкции в секунду (по сравнению с современными процессорами и их миллиардами инструкций в секунду это чрезвычайно медленно).
AGC обрабатывает инструкции, разбивая их на подкоманды, каждая из которых отнимает один такт памяти. К примеру, инструкция ADS состоит из двух подкоманд: ADS0 (сложение) и STD2 (вызов следующей инструкции). На диаграмме ниже показано движение данных внутри AGC для выполнения инструкции ADS0. 12 тактов идут слева направо.
Важнейшие шаги следующие:
T1: адрес операнда копируется из регистра инструкции B в регистр адреса памяти S, чтобы начать чтение из памяти.
T4: Операнд читается из памяти в регистр данных памяти G.
T5: Операнд копируется из G в сумматор Y. Значение накопителя A копируется в сумматор X.
T6: Сумматор вычисляет сумму U, и копирует её в регистр данных памяти G.
T8: Счётчик программы Z копируется в регистр адреса памяти S для подготовки к получению следующей инструкции из памяти.
T10: Сумма из регистра данных памяти G записывается обратно в память.
T11: Сумма U копируется в накопитель А.
Хотя это простая инструкция суммирования, в течение 12 временных интервалов туда и сюда передаётся множество данных. И с каждым из этих действий связан определённый управляющий сигнал; к примеру, сигнал A2X на интервале T5 копирует значение из накопителя А в регистр Х. Для копирования регистра G в регистр Y требуется два управляющих импульса: RG (прочесть G) и WY (записать Y). В следующем разделе я объясню, как управляющий модуль AGC генерирует нужные управляющие сигналы для каждой инструкции.
Управляющий модуль
Как у большинства компьютеров, управляющий модуль AGC декодирует каждую инструкцию и генерирует управляющие сигналы, сообщающие остальной части процессора, что ему нужно делать. Для генерации сигналов AGC использует заранее запрограммированный управляющий модуль, состоящий из NOR-вентилей. AGC не использует микрокод; у него нет микроинструкций и управляющей памяти, поскольку это заняло бы слишком много физического места.
Сердце управляющего модуля AGC называется генератором пересечений [crosspoint generator]. Он берёт подкоманду и один из временных отрезков и генерирует управляющие сигналы для этой комбинации. Его можно представить себе в виде решётки, на которой в одном направлении идут подкоманды, а в другом – временные отрезки, и каждой из точек пересечения назначен свой управляющий сигнал.
Генератор пересечений требует множества компонентов и разделён на три модуля; это модуль А6. Обратите внимание на добавленные провода, изменяющие схему. Это ранняя версия модуля для тестирования на земле; у полётных модулей уже никаких проводов не было.
Для эффективности итоговый управляющий модуль крайне оптимизирован. Инструкции со схожим поведением скомбинированы и обрабатываются генератором пересечений совместно, что уменьшает размер необходимой схемы. К примеру, у AGC есть инструкция «добавление к накопителю с двойной точностью» (DAS). Поскольку она примерно эквивалентна двум сложениям одинарных слов, у подкоманд DAS1 и ADS0 в генераторе пересечений общая логика. На схеме ниже показана схема генератора пересечений для временного отрезка Т5, и выделена логика подкоманды ADS0 (использующая сигнал DAS1). К примеру, сигнал 5К генерируется из комбинации DAS1 и Т5.
Но что собой представляют сигналы 5К и 5L? Это ещё одна оптимизация. Многие управляющие импульсы часто подаются вместе, поэтому вместо того, чтобы генерировать их напрямую, генератор пересечений генерирует промежуточные сигналы для пересечений. К примеру, 5K генерирует управляющие импульсы A2X и RG, а 5L генерирует управляющий импульс WY. На диаграмме ниже показано, как генерируется сигнал A2X: любой из 8 различных сигналов (включая 5K) генерирует A2X. Похожие схемы генерируют другие управляющие сигналы. Эти оптимизации позволили уменьшить размер генератора пересечений, но он всё равно остался крупным, и разросся на целых три модуля.
Подытоживая, можно сказать, что управляющий модуль отвечает за то, что сообщает CPU, что ему делать для выполнения инструкции. Сначала инструкции разбиваются на подкоманды. Генератор пересечений создаёт нужные управляющие импульсы для каждого временного интервала и подкоманды, сообщая регистрам, арифметическому модулю и памяти, что им нужно делать.
Обычно инструкции состояли из двух подкоманд, но были и исключения. Некоторые из инструкций, например, умножение или деление, требовали использования множества подкоманд, поскольку состояли из многих шагов. И наоборот, инструкция перехода по адресу TC использовала одну подкоманду, поскольку ей нужно было только вызвать следующую инструкцию.
Другие процессоры использовали другие подходы к генерации управляющих сигналов. 6502 и многие другие ранние микропроцессоры декодировали инструкции при помощи программируемой логической матрицы (PLA), реализующей логику AND/OR через память только для чтения.
Микропроцессор 6502.
Заключение
Это была захватывающая экскурсия по бортовому управляющему компьютеру «Аполлона». Чтобы сильно не растягивать её, я сконцентрировался на инструкции сложения ADS и некоторых управляющих импульсах (A2X, RG и WY). Надеюсь, вы получили представление о том, как можно собрать компьютер из таких примитивных элементов, как NOR-вентили.
Самая наглядная часть архитектуры – тракт данных: арифметический модуль, регистры и шина данных. Регистры AGC созданы на основе простых триггеров из NOR-вентилей. И хотя арифметический модуль AGC умеет делать только сложение, компьютер всё равно справляется со всем набором операций, включая умножение, деление и булевские операции.
Однако тракт данных – это лишь часть компьютера. Среди других критически важных компонентов есть управляющий модуль, сообщающий компонентам, что им нужно делать. Подход, используемый в AGC, основан на генераторе пересечений, использующем сильно оптимизированную и жёстко прописанную логику для генерации правильных управляющих импульсов для определённых подкоманд и временных интервалов.
Используя эти возможности, AGC обеспечивал руководство, навигацию и управление на борту миссий «Аполло», и сделал возможным посадки на Луну. Также он подстегнул раннюю индустрию интегральных схем, использовав 60% выпускавшихся в США ИС в 1963. Поэтому современные компьютеры многим обязаны AGC и его простым NOR-компонентам.
AGC работает в лаборатории, подключённый к винтажному осциллографу Tektronix
Автор: Вячеслав Голованов