Всем привет! Продолжаю серию постов про мой компьютер на логических микросхемах. Единственный модуль процессора, оставшийся до сих пор без внимания, – это АЛУ, про него и пойдет сегодня речь.
Требования
АЛУ принимает на вход следующие сигналы:
-
A, B – операнды, каждый по 8 бит.
-
OP – код операции, 4 бита.
-
Cin – старый сохраненный флаг переноса, 1 бит.
-
Inv – флаг перемены операндов местами, 1 бит.
-
OE – активация выходного буфера.
Выходные сигналы:
-
R – результат, 8 бит.
-
Z – флаг нуля, 1 бит.
-
C – флаг переноса, 1 бит.
-
S – флаг знака, 1 бит.
-
O – флаг переполнения, 1 бит.
Никаких тактовых сигналов нет, то есть, АЛУ асинхронно: значения на выходе обновляются сразу, как только изменятся сигналы на входах.
Операции
Четыре бита кода операции дают 16 операций, куда влезает весь "джентельменский набор" арифметики и остается место еще для одной странной операции:
-
MOV
– копирование; -
ADC
,ADD
– сложение (с переносом и без); -
SBB
,SUB
– вычитание (с пересом и без); -
INC
,DEC
– инкремент и декремент; -
NEG
– смена знака; -
NOT
– инверсия бит; -
SHL
,SHR
,SAR
– сдвиги на один бит; -
AND
,OR
,XOR
– побитовые логические операции; -
EXP
– сохраненный флаг переноса распространяется на все биты результата.
Первая версия
Когда я начал делать процессор, я хотел не тратить сильно много времени на АЛУ и скорее получить рабочий вариант, поэтому я сделал первую версию с помощью таблиц в ПЗУ. Простейший вариант мог бы быть таким:
Все входные сигналы подаются на вход ПЗУ, а с выхода снимаются выходные. Нужно всего лишь 222 = 4М слов по 12 бит. Такой микросхемы у меня не было, поэтому надо было что-то придумывать, и я придумал вот что:
Тут используется две микросхемы по 32к слов каждая. Левая отвечает за младшие 4 бита, а правая за старшие. Зеленая и красная линии передают информацию вверх или вниз. Передача вверх (зеленая линия) нужна, например, при сложении, чтобы передать перенос с бита 3 на бит 4, а вниз (красная), соответственно, при вычитании. Чтобы не возникало осцилляций (левая микросхема передала правой, правая левой и так по кругу), эти линии пущены через логическое И со старшим битом операции. Таким образом, операции, передающие информацию "вверх" (сложение, сдвиг влево), могут быть закодированы только кодами от 0 до 7, а передающие "вниз" (вычитание, сдвиг вправо) – от 8 до 15.
Что получилось
Вторая версия
Использовать ПЗУ – это читерство, поэтому надо было делать вторую версию полностью на логических микросхемах. Общая схема АЛУ такая:
Операнды A и B подаются на мультиплексор, который меняет их местами, если на входе Inv
единица. В зависимости от кода операции один из блоков OP1-OPn активируется и подает значение на выходную шину, а остальные держат свои выходы в состоянии высокого сопротивления. В качестве выходного буфера каждого блока используется микросхема 74ACT244.
Реализация простых операций
Самая простая операция – EXP
. Нужно всего лишь подать Cin на все входы соответствующего выходного буфера.
Сдвиги (SHR
, SAR
, SHL
) тоже простые: на входы буферов подаются линии операнда со смешением на один бит в ту или другую сторону.
Для логической инверсии NOT
можно использовать микросхему 74ACT240, которая аналогична 74ACT244, но имеет инвертирующие выходы.
Побитовые логические операции могут быть выполнены на паре микросхем 74AC08 для AND
и 74AC32 для OR
(плюс буфер 74ACT244, конечно же).
Сумматор
Осталось девять операций. Восемь из них могут быть выражены при помощи сложения или вычитания:
-
ADD A, B = A + B
-
ADC A, B = A + B + c
-
SUB A, B = A - B
-
SBB A, B = A - B - c
-
INC A = A + 1
-
DEC A = A - 1
-
NEG A = 0 - A
-
MOV A, B = B + 0
Чтобы реализовать их все, универсальный сумматор должен уметь:
-
складывать и вычитать;
-
добавлять бит переноса ко второму операнду;
-
вместо любого из операндов подставлять ноль (нужно для
NEG
иMOV
); -
подставлять единицу вместо второго операнда (для
INC
иDEC
); -
менять порядок операндов.
Смена порядка операндов уже реализована входным мультиплексором.
Замена операнда на ноль тоже реализована входным мультиплексором: в микросхемах 74AC157, из которых он и состоит, есть вход E
, при высоком уровне на котором на выходах микросхемы будут нули.
Чтобы заменить второй операнд на единицу, можно заменить его на ноль (это мы уже умеем), а на младший бит подать единицу. Это легко сделать, пропустив младший бит через логическое ИЛИ:
Осталось сделать сумматор, который может и складывать, и вычитать. Начнем с классической схемы полного сумматора:
Полный сумматор вычисляет сумму трёх бит A + B + c
. Таблица истинности полного сумматора следующая:
A B c | R c_out
0 0 0 | 0 0
0 1 0 | 1 0
1 0 0 | 1 0
1 1 0 | 0 1
0 0 1 | 1 0
0 1 1 | 0 1
1 0 1 | 0 1
1 1 1 | 1 1
Теперь построим таблицу истинности для вычитания A - B - c
и сравним:
A B c | R c_out
0 0 0 | 0 0
0 1 0 | 1 1
1 0 0 | 1 0
1 1 0 | 0 0
0 0 1 | 1 1
0 1 1 | 0 1
1 0 1 | 0 0
1 1 1 | 1 1
Таблицы отличаются столбцом c_out
. Чтобы переделать сумматор в вычитатель, можно добавить в схему два инвертора:
Но нам нужно, чтобы можно было динамически превращать сумматор в вычитатель. Для этого инверторы надо сделать отключаемыми, а отключаемые инверторы – это XOR:
Теперь, если на входе sub
ноль, на выходе схемы будет сумма, а если единица – то разность. Осталось заметить, что эта схема вычисляет еще и исключающее ИЛИ A
и B
– последнюю нереализованную операцию АЛУ:
Дальше объединяем получившиеся ячейки в цепочку, пустив c_out
предыдущего бита на c
следующего.
Цепочка сумматоров
Итого на сумматор нужно 14 микросхем, не считая мультиплексора, буферов и того элемента ИЛИ, который подает единицу на младший бит второго операнда.
Флаги
Кроме результата арифметической операции, АЛУ должно выдавать еще и флаги, сообщающие о свойствах результата.
Флаг S (знак) – самый простой. Это просто старший бит результата.
Флаг Z (ноль) вычисляется как инверсное логическое ИЛИ всех битов результата.
Схема
Флаг C (перенос) – это выход c_out
старшего разряда сумматора в случае сложения и вычитания или "лишний" бит операнда при сдвигах.
Схема
Флаг O (переполнение) – результат исключающего ИЛИ переносов с двух старших разрядов сумматора. Подробнее почитать про флаги переноса и переполнения можно здесь.
Схема
Результат
Плата получилась не очень. Иногда всё работало хорошо, а иногда возникали какие-то высокочастотные помехи на всех линиях, включая питание, что сбивало работу всего компьютера. Сейчас у меня две версии, почему возникали помехи:
-
во-первых, я не поставил конденсаторы рядом с ножками питания каждой микросхемы, а просто разбросал несколько по плате;
-
во-вторых, серия 74ACT склонна давать наводки из-за своих резких фронтов.
Следующие платы я делал с учетом этой мудрости (на красной плате модуля регистров справа видны конденсаторы у каждой микросхемы), а АЛУ я исправил буквально методом тыка. Я запускал тестовую программу, на которой гарантированно возникали помехи, и прикладывал палец в разные части платы АЛУ. Оказалось, что если держать палец на ножке 1 U36, помехи исчезают. Всем известно, что человек – это просто большой конденсатор, поэтому я припаял между этой ножкой и землёй конденсатор на 10 пФ, что устранило проблему.
Автор:
ynoxinul