RISC процессор с интуитивно понятным машинным кодом

Публичный форум для http://www.nedopc.org/nedopc

Moderator: Shaos

User avatar
Paguo-86PK
Maniac
Posts: 267
Joined: 12 Apr 2011 20:43
Location: Tashkent

RISC процессор с интуитивно понятным машинным кодом

Post by Paguo-86PK »

Давнo волновала мысль о реализации процессора с интуитивно прозрачной системой команд.
Так, x80 задумывался мною как нечто среднее между z80 и i8086. Но CISC довольно сложна в освоении реализации и там я наткнулся на множество подводных камней, хотя первые шаги и сделал.

Решил наконец перешагнуть через некоторые свои принципы и заняться RISC всерьёз.
А именно, сделать набросок простого (не простейшего) процессора, но с оригинальной системой команд и интуитивно ясным байт-кодом, чтобы не нужно было зубрить таблицу системы команд.
И я нашёл достаточно оригинальный вариант…

Мой подход максимально прост: Шестнадцатиричный код инструкции и является мнемоникой команды.

Code: Select all

Bytes|Assm | Краткое описание
----- ----- ------------------
00    HALT - Куда без неё?
01…99 1…99 - Шестнадцатиричные цифры используются как десятичные
0A…0F ALU  - 0xA:Add; 0xB:suB; 0xC:Conjunction (AND); 0xD:Disjunction (OR); 0xE:Eor; 0xF:Flags (CMP)
A1…A9 A1…9 - Выбор одного из девяти регистров A
B1…B9 B1…9 - Выбор одного из девяти регистров B
C1…C9 C1…9 - Выбор одного из девяти регистров C
AA…CC R,T  - Выбор пары приёмник-источник (Receiver,Translator pair)
AD    ADDR - Фиксация значений приёмника-транслятора в регистре адреса
BE    BE   - Чтение из буфера памяти (Buffer Extract)
BF    BF   - Запись в буфер памяти (Buffer Flush)
DA…DF DA…F - Skip 1 Instruction by: Amount; Bigger; Carry; Dis-Carry; Equal; False
EB    EB   - Execute Buffer (Jump to Buffer Address)
С помощью ПЗУ объёмом в 256 байтов с асинхронным доступом можно легко прошить всю таблицу дешифратора команд, в которую также входят 99 ячеек перевода двоично-десятичных представлений кодов.

Сначала схему я строил в Atanua, но потом перешёл в Logisim, так как он удобнее в работе с шинами. За неделю мне удалось набросать более-менее приличную схему, если сравнивать с предыдущим безобразием из путаницы проводников и логики по всему экрану. Сейчас выглядит схема более приличнее и многое расположено гораздо ровнее, чем было в первых вариантах.
А главное - это работает.
Самым сложным моментом было разработка узла чтения/записи памяти, так как первый вариант был аналогичным Гарвадскому. Теперь набор вентилей и сдвиговый регистр выполняют нормальный Принстонский функционал, на отладку которого ушло несколько ночей. Думаю, опыт пригодится в моём x80, когда руки дойдут до него.

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

Всего данный процессор способен выполнить пока 140 инструкций.
И на данный момент АЛУ имеет только функцию сумматора под кодом операции 0A, так как пока нет нужды во всех операциях. Хотя, как и в x80, операции будут те же…
(0A:Add, 0B:suB, 0C:Conjunction/AND, 0D:Disjunction/OR, 0E:Eor, 0F:diFference/CMP)

Например:

Code: Select all

0000 BA ; Выбор пары B,A
0001 B9 ; Выбор регистра B9
0002 64 ; Загрузить десятичное 64 (0x40) в регистр B9
0003 CB ; Выбор пары C,B
0004 C8 ; Выбор регистра C8
0005 30 ; Загрузить десятичное 30 (0x1E) в регистр C8
0006 AD ; Поместить в указатель адреса значения C8 и B9: Addr = (C8 << 8) | B9
0007 A1 ; Выбор регистра A1
0008 AB ; Выбор пары AB
0009 BE ; Прочитать память и сохранить данные: A1 = [Addr] или A1 = [0x1E40]
000A 00 ; Останов
Если вдуматься, то реализация процессора получилась почти полной по Тьюрингу.
Реализовать стек программно теоретически возможно уже сейчас.

Code: Select all

0000 A9 ;     A9  ; Чтобы организовать цикл, нужно настроить указатель
0001 B9 ;     B9  ; Выбираем регистры A9 B9
0002 AA ;     A,A ; Эти аргументы помогут очистить регистр A9
0003 0E ; EOR     ; (EOR A9,A9) посредством Исключающего ИЛИ
0004 BA ;     B,A ; Теперь загрузим адрес единственной метки
0005 16 ;      16 ; Сейчас B9,A9 имеют значения 0x10,0x00
0006 AB ;     A,B ; Формируем корректный адрес: 0x00,0x10
0007 AD ; ADDRESS ; Загружаем в указатель буфера 0x0010 из A9,B9
0008 A8 ;     A8  ; Теперь настроим регистры для циклического счёта
0009 B8 ;     B8  ; A8 будет счётчиком, а B8 будет хранить предел
000A AA ;     A,A ; Очистим будущий счётчик
000B 0E ; EOR     ; (EOR A8,A8) традиционным способом
000C BA ;     B,A ; Чтобы загрузить B8, нужно выбрать аргументы
000D 99 ;      99 ; (MOV B8,99) Теперь B8 == 0x63
000E B1 ;     B1  ; Здесь будет храниться шаг инкремента
000F 01 ;      1  ; (MOV B1,1)
0010 ;;;;         ; Этот адрес мы указали в начале программы - прошло 32 такта
0010 AB ;     A,B ; Указываем порядок аргументов
0011 A8 ;     A8  ; Чтобы обеспечить инкремент
0012 B1 ;     B1  ; как A8 += B1
0013 0A ; ADD     ; (ADD A8,B1)
0014 B8 ;     B8  ; Регистр предела
0015 0F ; CMP     ; (CMP A8,B8) Операция сравнения
0016 DC ; DC      ; (Do if Carry) Операция проверки флага переноса
0017 EB ;  EB     ; Если переноса нет, эта команда (Execute Buffer) будет пропущена - 16 тактов + 2
0018 00 ; HLT     ; Останов - всегда 2 такта
В этой программе на каждую инструкцию уходит по 2 такта (4 полупериода ручным кликом), за исключением условной DC, которая требует 4 такта для холостого инкремента указателя инструкций при завершении цикла. Тем самым, на всю программу потребуется 2*16+99*(2*8)+2+2
You do not have the required permissions to view the files attached to this post.
Last edited by Paguo-86PK on 25 May 2020 09:27, edited 3 times in total.
Alekcandr
Doomed
Posts: 665
Joined: 01 Oct 2007 10:30
Location: Ukraine

Re: RISC эксперимент

Post by Alekcandr »

Ничего не понял (надо думать). Это вам надо R800 (MSX) курить? на базе Z80(Z280).
Эмулятор OrionEXT:
http://www.orion-ext.narod.ru
User avatar
Shaos
Admin
Posts: 24007
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: RISC эксперимент

Post by Shaos »

Логисим не глючит от такой большой схемы?
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Paguo-86PK
Maniac
Posts: 267
Joined: 12 Apr 2011 20:43
Location: Tashkent

Re: RISC эксперимент

Post by Paguo-86PK »

Первопoст обновил…
Добавил в схему все 6 операций АЛУ, флаговый регистр и инструкции условного исполнения.
Теперь есть операция загрузки указателя инструкций.

Очень любопытно, смогу ли я написать что-нибудь практическое?
Типа, Тетриса или простейшую BIOS-заставку…
Shaos wrote:Логисим не глючит от такой большой схемы?
Когдa пытался сделать большой экран 320x192, симуляция часто подвисала и приходилось LogiSim закрывать через диспетчер задач.
Если развёртку делать последовательно по всем панелям, то период их обновления очень велик.
Потому я на каждую панель выделил отдельное ОЗУ и устал разводить шины плексеров.
Отдельное ПЗУ отвечает за линейный доступ к ячейкам всего дисплея, чтобы все 7680 ячеек не имели всяких фокусов с дешифрацией при доступе к ним.

Но весь труд себя не оправдал - симуляция через какое-то время зависает…
You do not have the required permissions to view the files attached to this post.
User avatar
Paguo-86PK
Maniac
Posts: 267
Joined: 12 Apr 2011 20:43
Location: Tashkent

RISC эксперимент

Post by Paguo-86PK »

Рeшил набросать схему контекстного файла - Logisim сильно тормозить стал.
Да и тактовый вход забыл вывести, как потом заметил…

Короче, должна быть схема памяти с четырьмя портами.
Но для Logisim это очень тяжело, оказывается…
Нужно что-то иначе делать…
You do not have the required permissions to view the files attached to this post.
User avatar
Paguo-86PK
Maniac
Posts: 267
Joined: 12 Apr 2011 20:43
Location: Tashkent

RISC процессор с интуитивно понятным машинным кодом

Post by Paguo-86PK »

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

Упростил процессор до предела в рамках выбранной концепции, упразднив некоторые инструкции и упростив доступ к памяти. Появилась возможность расширения системы команд программными процедурами по адресам E000…FF00.
Теперь основных групп команд сократилось до девяти, включая группу АЛУ. Тем самым, если операции АЛУ вынести напрямую к дешифратору, то потребуется всего один типа ИД3…

Вся схема выполнена в строгом синхронном стиле и легко должна ровно лечь в Verilog и FPGA.

Регистровый файл расширен с трёх до четырёх групп регистров общего назначения.
Четвёртая D-группа представляется внешними устройствами - Devices, что позволяет обращаться к устройствам типа клавиатура или джойстик непосредственно и в скором порядке, как кэш-периферия первого уровня с мгновенным доступом. Более медленные устройства требуется проецировать в адресное пространство памяти.

Тем самым, процессор теоретически пригоден для построения любого автомата, типа «Ну, Погоди!», «Тетриса» или «Лунохода».

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

Code: Select all

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

------------------------------------------------------------------------------
|Код команд / диапазоны|Группа    |Описания, замечания, комментарий
-----------------------|----------|-------------------------------------------
00                     |HLT       |Прекращение счёта указателем команд IP
01-99                  |ADD Ri,BCD|Двоично-десятичное накопление в регистр
0A-9F                  |ALU Ri,Tk |Операция АЛУ с комбинацией аргументов
A0-A9 B0-B9 C0-C9 D0-D9|REG Rn    |Установка индекса активного регистра группы
AA-AD BA-BD CA-CD DA-DD|ARG Ri,Tj |Выбор комбинации аргументов операциям АЛУ
AE    BE    CE    DE   |EXT Ri    |Экстракция данных из внешнего ОЗУ в регистр
AF    BF    CF    DF   |FIX Ri    |Фиксация значения регистра во внешнее ОЗУ
E0-E7                  |EX0-7     |Переход на адрес 0xE000-0xE700
E8-EF                  |Ecnd      |Выполнение следующей команды по условию
F0                     |FIN       |Завершение с переходом на адрес Базы:Счёта
F1-FF                  |FN1-15    |Переход на адрес 0xF000-0xFF00
------------------------------------------------------------------------------
(n - устанавливаемый индекс, указанный битами команды)
(i или j - индекс, установленный раннее)
(k - индекс регистра временно берётся из старшего ниббла кода команды)

Группы регистров A0-A9, B0-B9 и C0-C9 составляют внутренний регистровый файл с
безусловным доступом к ним на программном уровне. Регистры B0-B9 хранят "Базу"
для обращения к памяти блоком в 256 байт, а регистры C0-C9 - счёт байта блока.
Инструкция с кодом F0 помещает текущий адрес указателя инструкций из IP в B0 и
C0, а значения из Bi и Cj помещает в IP, производя переход на адрес Bi:Cj. Тем
самым, можно обращаться к коду подпрограмм и знать адрес возврата через B0:C0.

Группу регистров D0-D9 составляют внешние Device-устройства с непосредственным
использованием в качестве операндов. Не рекомендуется активно использовать эту
группу в любых вычислительных действиях, так как нет гарантии работы программ.
Тем самым, как любитель Hex-кода, разработал вполне работоспособную архитектуру, которая не требует даже ассемблера и позволяет в голом дампе набивать машинный код, визуально прозрачный к пониманию.

Есть соблазн упразднить ПЗУ разметки карты команд и заменить его на комбинаторику. Правда, не уверен, оправдает ли это себя…

P.S.: Пожалуй, это - самый удачный из всех моих проектов данного класса.
You do not have the required permissions to view the files attached to this post.
User avatar
Paguo-86PK
Maniac
Posts: 267
Joined: 12 Apr 2011 20:43
Location: Tashkent

RISC процессор с интуитивно понятным машинным кодом

Post by Paguo-86PK »

Пo мере своих возможностей, перерабатываю схему процессора.
Теперь она более чётче разбита на отдельные блоки.
Если пытаться собирать вариант схемы с буферами в регистровом файле, потребуется ≈250 корпусов.
Если же рассматривать вариант с мультиплексорами в регистровом файле, потребуется ≈110 корпусов.
Если, однако, использовать две штуки К1533ИР39, то число микросхем сократится до ≈30 корпусов…

Если ориентироваться на Gigatron, Pico-8 или MegaProcessor, то получилась простая и открытая архитектура с байт-кодом, который можно без ассемблера кодировать подобными переключателями, если их добыть 512 штук и собрать в нормальный электромеханический дамп на 256 байт…

Набросок графика работы машинных циклов

Перевёл в Verilog и попытался синтезировать в Quartus.

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

P.S.: Никакой отладочной платы не имею и приходится всё программно симулировать…
You do not have the required permissions to view the files attached to this post.
User avatar
Paguo-86PK
Maniac
Posts: 267
Joined: 12 Apr 2011 20:43
Location: Tashkent

RISC процессор с интуитивно понятным машинным кодом

Post by Paguo-86PK »

K моему великому огорчению, хотя я и брался сделать RISC, но получился CISC.
С одной стороны, он действительно за 1 такт выполняет 1 операцию. Но большинство операций не выполняет каких-либо вычислительных действий и просто изменяют архитектурное состояние.

Если в i8080/z80 команда АЛУ «ADC A,B» сама выбирает регистр B в качестве второго операнда, то у меня аналогичное действие производится в три инструкции:

Code: Select all

1234 A1      |REG A1    ; Активируем регистр A1 в группе A₀…₉
1235    AB   |ARG A,B   ; Выбираем группу A₀…₉ за аккумуляторы, а группу B₀…₉ вторым операндом
1236       3E|EOR A1,B3 ; Здесь A1 выбран в REG и указан ARG, а B3 выбирается по старшей тетраде
1237       4E|EOR A1,B4 ; Здесь A1 выбран, а индексы B₀…₉ выбирается старшей тетрадью кода команды
1238    AC   |ARG A,C   ; Выбираем группу A₀…₉ за аккумуляторы, а группу C₀…₉ вторым операндом
1239       5E|EOR A1,C5 ; Здесь A1 выбран, а индексы C₀…₉ выбирается старшей тетрадью кода команды
123A A2      |REG A2    ; Активируем регистр A2 в группе A₀…₉
123B       6E|EOR A2,C6 ; Теперь приёмником результата стал регистр A2
123C    87 ++|BCD 87    ; BCD - это Byte-Code-Data: Префикс для расширения действия команды
123D    ++ 6E|EOR A2,C6+BC8+7 ; Из-за префикса данные берутся не из C₆, а из ОЗУ по вектору [B₈C₈+C₆+7]
Здесь действительно на одну инструкцию затрачивается 1 байт и 1 такт, но эффективности заметно меньше и код слишком жидкий.
Если же код представить более плотно и всегда заканчивать строку именно эффективной инструкцией, получится примерно следующее:

Code: Select all

1234 A1 AB 3E|EOR A1,B3
1237       4E|EOR A1,B4
1238    AC 5E|EOR A1,C5
123A A2    6E|EOR A2,C6
123C    87 6E|EOR A2,C6+BC8+7
Откуда и получается, что от лёгкой MISC/RISC технологии мало чего осталось и проявляется вновь CISC с разной длиной инструкций.
(Сейчас я работаю над схемой в Logisim/Verilog, которая читает слово на все 4 байта и разбирает его по столбикам, как я специально и указал. Тем самым, за 1 такт обрабатывается ровно одна инструкция из 1…4 байтов. Сложно и требует 4 дешифратора команд, но это - работает.)

Естественно, ссылку на Verilog-модель не дам пока, так как она работает крайне медленно, используя до 8 машинных циклов и до 24 тактов на команду!
Вот графики:
cpu_graph.png
Эмулятор
Однако, предлагаю Вашему вниманию эмулятор, который разрабатываю вторую неделю и добился более-менее стабильного результата.
Emulator.zip
Эмулятор написан для процессоров i8080/z80 и запуститься должен практически на всех машинах:
  • 3700…37C6: Оболочка («РАДИО-86РК¹» / «СПЕЦИАЛИСТ²» / «ОРИОН-128³»)
  • 3838…3863: Сам псевдо-код моего процессора для эмулятора
  • 3870…38FF: Поддержка совместимости между разными ПК (¹-²-³ и «ZX-Spectrum» 48Kb) для печати символа и ввода с клавиатуры
  • 3900…3A99: Собственно, код самой эмуляции (поддерживаются практически все инструкции)
  • 3ADA…3C7F: Дизассемблер (декодирует практически все команды)
  • 3C80…3CFF: Заглушка портов УВВ (вывод сообщений о попытках доступа к портам)
Оболочка в первую очередь разрабатывалась под РАДИО-86РК, где и отлаживалось всё. Гарантировать не буду, но «Специалист» и «Орион» тоже должны нормально работать под её управлением.

С «ZX-Spectrum» всё несколько сложнее.
В его области ПЗУ в пространстве 386E…3CFF имеются резервные ячейки с кодом FF. Именно поэтому я весь код эмулятора/дизассемблера (3900…3C7F) расположил именно так (чтобы включив ZX «Ленинград-48» физически, наряду с Бейсиком сразу иметь свой эмулятор без возни с загрузкой)…
В общем, можно обойтись лишь эмулятором (3900…3A99).
Но, так как «РАДИО-86РК» имеет регион непосредственно отображаемой текстом памяти, крайне удобно было отлаживать дизассемблер, так как он напрямую всё пишет в экранную память без всяких подпрограмм.
В случае же с ПК «Специалист», «Орион» или «ZX-Spectrum» требуется просто после вызова дизассемблера напечатать строку с адреса 0x76D0.
(Как инструментальная ЭВМ в комфортных условиях XXI века именно эмулятор «РАДИО-86РК» предоставляет наибольшие удобства.)

Естественно, бо́льшего от данного эмулятора ожидать нечего: Он просто исполняет код.
Так как хоть для каких-то эффектов (звука/графики) требуется ещё наработать виртуальную периферию…

Периферия / УВВ
Первоначальный эскиз процессора в Logisim разрабатывался как прикол, где командой «MOV D0,D1» можно было данные из порта D₁ напрямую отправить в порт D₀ - из клавиатуры на терминал (эхо).
Однако, служившую для этого инструкцию «ARG D,D» с машинным кодом DD₁₆ я на днях исключил как вредную. И теперь 0xDD работает как отдельная инструкция «DBG» для отладки: Просто как «CALL 0xDD00»…

В добавок, искусственно внеслись новые ограничения, где нельзя регистры УВВ D₀…₉ использовать в АЛУ-операциях - только «MOV».
Из-за чего вместо «ADD»/«SUB»/«AND»/«OR»/«XOR» образовались резервные «EXA»/«EXB»/«EXC»/«EXD»/«EXE», логику работы которых ещё предстоит придумать (допустим, «SHR» или даже «MUL»).
Если раньше можно было кодировать так, используя порт УВВ в качестве смещения в адресации ячейки:

Code: Select all

1234 A1 AD 23 4E|EOR A1,[BC2+3+D4]
То сейчас всё намного строже и формально так делать нельзя, так как это будет работать как:

Code: Select all

1234 A5 AD 87 0E|EOR A5,BC8+7
1238       87 1E|EOR A5,BC8+7+1+ ++D9
123A       87 2E|EOR A5,BC8+7+ ++D9
123C       87 3E|EOR A5,BC8+7+ D9++
123E       87 4E|EOR A5,BC8+7-1+ D9++
1240       87 5E|EOR A5,BC8+7-2+ --D9
1242       87 6E|EOR A5,BC8+7-1+ --D9
1244       87 7E|EOR A5,BC8+7+ --D9
1246       87 8E|EOR A5,BC8+7+ D9--
1248       87 9E|EOR A5,BC8+7+D9
Довольно сложно и не стабильно (концептуально), хотя в эмуляторе - работает.

Карта периферии будет примерно такой:

Code: Select all

switch(D9) { // D₉ - Селектор страницы УВВ
    case 0x53:      // D0…D3 - i8253
    case 0x55:      // D0…D3 - i8255
    case 0x57:      // D0…D8 - i8257
    case 0x75:      // D0…D1 - i8275
    ... ... ...
}
P.S.: Многое ещё предстоит придумывать…
Так как саму мечту детства (придумать свой процессор) со скрипом, но только начал практически нарабатывать.
А вот вторую мечту детства (придумать свой компьютер на своём процессоре) ещё толком в эскизах не имею…
You do not have the required permissions to view the files attached to this post.