CPU N

Печатные платы, программируемая логика, разработка и изготовление аппаратуры

Moderator: Shaos

User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

CPU N

Post by danchandoo »

После некоторого перерыва меня вновь охватила волна недо-энтузиазма.
Видеокарта, к несчастью, меня интересовать перестала, так и лежит не доделанная, даже схему в eagle нарисовать и выложить лень, извините. :(
Зато я всецело поглощен разработкой процессора (все-таки ввязался в это гиблое дело).
Идея отнюдь не утопична. Достаточно посетить этот сайт http://mycpu.thtec.org/www-mycpu-eu/ и потом полистать Homebuilt CPUs WebRing внизу страницы, чтобы узнать, что таких проектов было много, и большинство успешно воплощено в жизнь.
Основной принцип работы большинства этих CPU – управление микро-кодом, записанным в ПЛМ. На входы ПЛМ подается код операции, значения флагов и счетчик микрокоманд (микрокоанда – слово большой разрядности, каждый бит которого управляет некоторой простой отдельной частью ЦП, например, подает сигнал на вход OE\ некоторого регистра, управляя выдачей данных из него на внутреннюю шину и т. д.). Счетчик инкриементируется и таким образом с выхода ПЛМ последовательно подается набор микрокоманд необходимый для выполнения одной макрокоманды (той, чей код операции подан на входы ПЛМ)).
В качестве ПЛМ в этих проектах служит EPROM. Но я решил использовать для этих целей SRAM из-за малого (20 нс) времени доступа по сравнению с EPROM (100 нс), что позволяет добиться большего быстродействия. Сделать самопрограммирующийся при включении ЦП оказалось очень сложно, так что буду просто использовать батарейное питание SRAM хоть это и менее солидно.
АЛУ также строится на ПЛМ (в англ. варианте look-up tables), что гарантирует простоту и высокую скорость.

Это была теория. Сладкая. Теперь правда. Горькая.

Image

Это ПЛМ-ы АЛУ проекта MyCPU (регистры АЛУ и некоторая прочая логика на другой схеме, кому нужно все – зайдите на сайт и скачайте selfbuild guide).
АЛУ состоит из 2-х 8-ми мегабитных микросхем EPROM, в одной результаты бинарных операций, в другой флаги, и еще одной 8-ми килобайтной -- содержатся результаты унарных операций. У этих микросхем по 20 адресных входов. 2 х 8 – операнды, 3 – код бинарной операции, и один вход для флага переноса CF.
Я располагаю микросхемами SRAM xxC256 у которых лишь 14 адресных входов, чего не хватает даже для двух операндов.
Выход в каскадировании. Сделать 8-ми разрядное АЛУ из двух 4-разрядных. На входы первого 4р. АЛУ подаются младшие 4 бита каждого операнда, на входы второго – старшие. Флаги C и Z результата первого АЛУ заводятся во второе. Общая задержка «операнд-стабильный результат» равна суме задержек двух микросхем памяти. Такое АЛУ, к сожалению, не может умножать и делить.

Вот моя схема:

Image

ID_BUS — internal data bus внутренняя шина данных ЦП
CD_BUS – command data bus шина по которой передается операнд содержащийся в коде команды.
ALU_CMD – ALU command код операции АЛУ и управляющие сигналы.
Загрузка 2-го операнда осуществляется через «прозрачную» защелку 1-го, для уменьшения нагрузки на шину.
(Нашел в схеме незначительные ошибки, но перезагружаться в винду и исправлять -- не хочется)

К несчастью эта схема нуждается в переработке, т. к. она не может осуществлять сдвиги вправо. Однако каскадирование позволяет собрать и 16-разрядное АЛУ, чего нельзя сделать просто заведя все операнды и код в одну EPROM как это было сделано в MyCPU. Иначе нужна ROM на 64 ГБ :)

Теперь я думаю, а не сделать ли 16-bit CPU? Это было бы круто! Но к несчастью сопряжено с рядом трудностей.

Сейчас важно понять в каком направлении идти. Вот и хотел бы вас по этому поводу спросить. Кто что думает?

Многие проблемы касательно выравнивания команд я уже решил. Но самые большие заморочки с программной архитектурой. Какой должен быть набор команд?
В 8-ми битных ЦП во всех арифметических командах один неявно заданный операнд – аккумулятор. Это экономит коды команд. А в x86 можно написать

Code: Select all

add	bx, cx
и что вообще страшно

Code: Select all

add	bl, bh
для таких «извращений» нужно мультиплексировать в АЛУ младшие и старшие части шины, или хотя-бы сделать между ними мост.

256 кодов операций для этого не хватит.

Регистры я храню во внутреннем ОЗУ, так что их число ограничивается лишь количеством кодов...

Делать ли регистры делимыми на независимые старшую и младшую часть?
User avatar
Shaos
Admin
Posts: 24055
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

А ты сделай длину команды в 16 бит, а не в 8 - тогда всё влезет :)

P.S. Интересно, что к посетителям данного форума идеи приходят синхронно - я на днях купил себе 100 микросхем 74F00 (это ЛА3 со средним временем срабатывания 3 нс) за 4.5 цента за штучку (clearance) - буду городить 16-битный риск-процессор на мелкой логике :)
Я тут за главного - если что шлите мыло на me собака shaos точка net
d_wanderer
Senior
Posts: 180
Joined: 28 Feb 2006 21:34

Post by d_wanderer »

Хм. Как показывает расчет, для 16ти битной шины нужно этак примерно на порядок больше. Ведь часть уйдет просто на инверторы и реализацию ИЛИ. А большая часть на всякие триггеры для регистров. Видел недавно гдето в инете студенческую поделку - программа переводит схему из помоему Верилога как раз в ИЛИ-НЕ.

P.S. А самое интересное, что действительно это так - у меня тоже появилась нужда в процессоре на рассыпухе. Только для обработки изображений.
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

А ты сделай длину команды в 16 бит, а не в 8 - тогда всё влезет :)
Длинну кода операции или команды ))
65536 кодов... я уже вижу эти рекламные объявления "Very hard, very CISC"
Но у меня 14 входов в SRAM, совершенно некак дешифровать 2-байтные коды. Конечно было бы очень заманчиво сделать коды только 2- или 4-байтными, тогда код операции оказывался бы всегда в младщем байте, и не нужно было бы ничего мультиплексировать, но ведь память расходуется зря, а напрямую процессор сможет адресовать только 64 Кб. Так что лучше иметь и 1-, 2-, 3- и 4...

Так вот и вопрос, делать RISC or CISC?
RISC в этом случае это набор <= 256 инструкций.
Например все арифм. и логические операции только с аккумулятором,
что к тому же быстрее работает, т.к. не нужет 1 такт на сохранение результата, он по умолчанию защелкивается в АЛУ в аккумуляторе.

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

Пример:
1-й байт: код "сложить 2 16-бит регистра"
2-й байт: уточнение каких регистра

Дешифруется только 1-й байт, а затем микрокод "вытягивает" из второго нужную информацию. Работает медленно, т.к. в 1-м такте микрокод "разбирается" что ему делать, вместо того, чтобы уже выполнять работу.

Как вариант можно сделать быстрые однобайтные команды пересылки регистр-регистр, и арифм. с аккумулятором, при этом выравнивать их на ширину 2 байта.
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

А ну его!

"Пусть будут команды длинной только 2 или 4 байта!
Регистрам шестнадцатиразрядность и неделимость на младших и старших!
Регистрам равноправие, никаких аккумуляторов!
Больше рабочих мест -- обещаю увеличить колличество регистров до максимума!
Да, у нас нет умножения, но у нас есть сдвиги!
УРА!!!"
Мое популистское выступление перед рядами интергальных схем.
буду городить 16-битный риск-процессор на мелкой логике
Есть где разгуляться программисту, в плане синтеза и разводки, или планируете использовать сторонние программы для этих целей?
User avatar
Shaos
Admin
Posts: 24055
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

danchandoo wrote:
буду городить 16-битный риск-процессор на мелкой логике
Есть где разгуляться программисту, в плане синтеза и разводки, или планируете использовать сторонние программы для этих целей?
C compiler ;)
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24055
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

danchandoo wrote:
А ты сделай длину команды в 16 бит, а не в 8 - тогда всё влезет :)
Длинну кода операции или команды ))
65536 кодов... я уже вижу эти рекламные объявления "Very hard, very CISC"
Но у меня 14 входов в SRAM, совершенно некак дешифровать 2-байтные коды.
ну операцию непосредственно кодировать несколькими битами, а остальное - номера вовлечённых регистров :)
Так вот и вопрос, делать RISC or CISC?
по моему пониманию RISC будет быстрее работать и проще в реализации
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

Shaos wrote: ну операцию непосредственно кодировать несколькими битами, а остальное - номера вовлечённых регистров
Да я вот тоже об этом подумал когда писал
...добавить постфиксный байт, в код команды, уточняющий где брать операнды.
Этот постфиксный байт и состоит собственно из 2-х номеров регистров.
Однако и правда лучше отвести не байт, а 10 бит, тогда можно использовать
32 регистра! :rotate:
На коды операций останется 6 бит = 64 кода, должно хватить.

Так разрешать ли манипуляции с каждым байтом регистра отдельно?
В класических RISC процессорах все операнды одной разрядности...
User avatar
Shaos
Admin
Posts: 24055
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Сподвигли вы меня на великие размышления: viewtopic.php?t=9182
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

Немного подумал. За последнюю неделю много что изменилось в моих взглядах по поводу этого проекта.


Сверх глобально:
Не нужно бросаться в проработку деталей, если еще не подготовлена концепция задумки вцелом. Вот сделал я схему АЛУ, выложил, а вопрос зачем? В свете последних решений она не очень актуально. Разве-что в eagle попробовал работать...
Я понял, что делать что-то простое и легко воплотимое в жизнь, это "тупая" механическая работа. Это не мой путь, я поступлю иначе:

Глобально:

N16 CPU

Тип: RISC, что-то около берклийской архитектуры
Разрядность 16 бит.
Архитектура: гарвардская, запись данных в область памяти команд осуществляется внешними схемами.
Тактовая частота: ~20 МГц
Тактов на команду: около 2-х (для большинства)
Адресуемое пространство: 64К x 32 код, 64K x 16 данные.
Колличество регистров: 128 для каждой подпрограммы.
Всего видимых регистров 256 (еще 128 от вызывавшей подпрограммы, или той из которой произощел возврат)
Длинна команды: 32 бита, пусть будет. Из них:
- 1-й байт: код операции
- 2-й байт: регистр-источник1/приемник операнда из кода команды
- 3-й байт: регистр-источник2/младший байт операнда в кода команды
- 4-й байт: регистр-приемник/старший байт операнда в коде команды

Стек: встроенный 256 2-байтовых адресов возврата (может быть хоть 32 К адресов, но все ограничивается счетчиком-указателем стека -- он 8-ми разрядный).

АЛУ 16 разрядов. Поддерживаемые операции:
- бинарные: add, adc, sbb, sbc, and, or, xor;
- унарные: sal, rcl, not, neg, inc, dec. Возможно еще и: sar, rcr, ror, rol.

Все вышеописанное не "взято с потолка", а является продуктом моих реалистичных рассуждений.
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

Основное направление -- повышение скорости, из-за чего вся логика по прежнему помещена в SRAM, однако никакой загрузки микрокода во время рестарта нет. SRAM используется как ROM, для чего процессор снабжен 4-мя батарейками АА (кто-нибудь знает сколько энергии, например, в mAh, может выработать батарейка? (нужно для рассчета как долго процессор протянет без внешнего питания)).

Одна команда за 2 такта:
1) макро-уровень:
- 1-й такт -- считывание (защелкивание команды в конце такта)
- 2-й такт -- декодирование, инкремент PC с одновременной выдачей
адресса следующей команды на шину.

2) микро-уровень. Для АЛУ все операции выполняются за 2 такта, что достигается конвееризацией:

Code: Select all

|...|SL1|SH1|SL2|SH2|
|LL1|LH1|LL2|LH2|...|
Где:
LL1, LL2, LLN -- загрузить одновременно младшие 8 бит 1-го, 2-го, N-го операнда
SL1, SL2, SLN -- сохранить младшие 8 бит 1-го, 2-го, N-го результата
LHN, SHN -- загрузить и сохранить старшие 8 бит N-го операнда или результата

Это достигается тем, что мое 16-бит АЛУ строется из четырех 4-битных, объедененных в два 8-битных. Эти два 8-битных АЛУ и действуют соответственно над младшим и старшим байтами операндов.

Для одновременной подячи младших или старших частей операндов в АЛУ, число микросхем под регистры удваивается с 2 до 4-х. В тоже время запись ведется одновременно в оба блока регистров.

Есть проблема с логическими операциями действующими в "неприродном" направлении справа налево, например, со здвигами вправо. Это приводит к необходимости передачи значения флага переноса в 2-х направлениях между всеми 4-разрядными АЛУ. Кроме того необходимо сначала загрурать старшие байты операндов, что останавливает конвеер.
Кроме того есть "замечательные" циклические сдвиги без учета флага переноса. Чтобы их осуществить операнды должны присутствовать в АЛУ полностью, а не по частям. Например при rol загружаем сначала младшую часть, нужно делать сдвиг при котором младший бит старшего байта перейдет в старший бит загруженного младшего байта. Но где взять значение этого бита, если старшая часть не загружена в АЛУ? Вот из-за этого приходится загружать оба операнда целиком. Технически это возможно, но опять останавливает конвеер, а кроме того -- усложняет АЛУ.

Может и вовсе отказаться от вращений без флага переноса... Где они применяются? И хорошо бы отказаться от всех сдвигов вправо, но, боюсь, они все же нужны. Что думаете?
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

Ну и кратко о регистрах, стеке и процедурах.

Вызов подпрограммы приводит к тому что регистры вызывающе программы переходят в область 128-255, а доступные ей регистры располагаются от 0 до 127. Все параметры таки образом передаются через регистры. В стек ничего кроме адресов возврата поместить нельзя. Но думаю 128 2 байтных слов для параметров должно хватить.
При возврате все также. От 0 до 127 расположены регистры программы куда произошел возврат, от 128 до 255 регистры подпрограммы из которой произошел возврат, содержащие результат работы.
Один из регистров 0-127 нужно зарезервировать для сохранения флагов при прерывании (в стек значения флагов поместить нельзя).

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

Саможе переключение "окон" осуществляется крайне просто. Есть счетчик, который может как инкрементировать так и декрементировать свое значение -- указатель стека, и есть защелка, данные в которой фиксируются по сигналу инкремента/декремента счетчика. Значение в этой зашелке это значение указателя стека в n-1 такте. Тоесть при вызове это указатель на адресс вызывающей процедуры, а при возврате -- той из которой произошел возврат.
Старший бит номера используемого регистра как раз и выбирает, откуда брать адрес блока регистров: из этой защелки или из счетчика.
Вот и получается что там где старший бит = 1 (128--255) обращение идет к банку регистров функции чей адрес был в защелке, а там где он = 0 к банку, адрес которого сейчас на счетчике т.е. к банку регистров текущей, исполняемой процедуры.

Немного запутано написал. Пояснение. Регистры у меня хранятся в статической памяти. В коде команды на код регистра отводится 8 бит. Из них 7 подаются прямо на адресные входы памяти, а старший 8-й выбирает, откуда брать номер банка регистров -- еще 8 бит, которые нужно подать на адресные входы. Одновременно же в памяти под регистры хранятся банки текущей подпрограммы, и всех подпрограмм ранее вызванных, в которые не произошел возврат.
User avatar
Shaos
Admin
Posts: 24055
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

А зачем блоками по 4(8) бит вычислять? Почему не сразу все 16?

И 2 такта на команду - выглядит черезчур оптимистично...

P.S. А вообще получилось очень похоже на мой дизайн - может нам объединить усилия? ;)
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
HardWareMan
Banned
Posts: 2139
Joined: 20 Mar 2005 13:41
Location: От туда

Post by HardWareMan »

Любите вы все усложнять. Вот вам выдежржка из мегакнижки всех времен и народов (кликабельно):
Image
Image
Image
User avatar
danchandoo
Fanat
Posts: 63
Joined: 24 Sep 2007 12:15
Location: Украина

Post by danchandoo »

И чем я все усложнил?
Нету у меня К155ИП3. Вместо него микросхема памяти, которая выполняет точно теже функции. На ее адресные входы подаются 4-разрядные операнды, код операции и используемые флаги, 14 входов 32 К памяти на это хватает, а на выходе 4 бита результата и 4 новых значения флагов.
Сделать сразу 16-разрядное АЛУ из микросхем памяти нельзя! Входов не хватает:
2 операнда * 16 разрядов + 4 бита код операции + 1 флаг С = 37.

Единственным выходом является соединение 4 таких АЛУ в одно.

При этом флаг переноса из регистра флагов заходит в 1-е АЛУ, в каждое последующее заходит флаг переноса предыдущего (можно и флаг ноля тоже), с выходов 4-го АЛУ значения флагов считываются по окончанию операции.
Но так НЕЛЬЗЯ ДЕЛАТЬ СДВИГИ ВПРАВО. Чтобы это стало возможным нужно завести флаг С еще и в 4-е АЛУ, завести флаг С 4-го в 3-е и т.д. и сделать возможным его считывание из 1-го АЛУ, в общем проблема...
А зачем блоками по 8 бит вычислять? Почему не сразу все 16?
Зачем 2 АЛУ относительно свободно работающих? Дело в том что задержка общая АЛУ будет равна суме задержек составляющих его 4-разрядных АЛУ т.е. около 15*4 = 60 нс. Если АЛУ "монолитное" то для операции при Т = 50нс потребуется 3 такта:
1) одновременная загрузка двух и операндов начало обработки
2) обработка
3) сохранение результата и флагов
Для 8-ми разрядных АЛУ задержка равна 2*15 = 30 нс, что позволяет осуществить конвеерную обработку, как показано на ранее приведеной диаграмме. В момент сохранения результата операции над младшими байтами, флаг этого результата защелкивается, и в следующем такте влияет на операцию над старшими, вто время как "младшее" АЛУ уже обрабатывает новые операнды.

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