16-bit Serial Homebrew CPU - 2023

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Создание домашнего процессора с нуля требует большого количества логических микросхем. Понятно, что реализация регистров, счетчика программ, АЛУ и других компонентов ЦП в логике ТТЛ или КМОП требует значительного количества микросхем. Но сколько именно?

Я попытался оптимизировать свой домашний процессор для минимально возможного количества логических микросхем и ответить на вопрос:
Сколько микросхем требуется для Тьюринг-полного ЦП без ЦП?

16-bit Serial Homebrew CPU

Мой ответ — 16-битный последовательный процессор всего с 8 микросхемами, включая память и часы. Он имеет SRAM объемом 128 КБ, флэш-память 768 КБ и может работать на частоте до 10 МГц. Он содержит только 1-битное АЛУ, но большинство из 52 его инструкций работают с 16-битными значениями (последовательно). На максимальной скорости он выполняет примерно 12 тыс. инструкций в секунду (0,012MIPS) и, помимо прочего, способен передавать потоковое видео на ЖК-дисплей на базе PCD8544 (Nokia 5110) со скоростью ~ 10 кадров в секунду.

В зависимости от того, где вы проводите границу между конечным автоматом и процессором, моя 16-битная система на самом деле может быть процессором с наименьшим количеством микросхем. Хотя среди других участников - 1-битный компьютер Джеффа Лотона с 1 инструкцией и 1 битом памяти, а также простой процессор Дэниела Торнбурга с 1-байтовой инструкцией перехода и памятью, смоделированной на Raspberry PI.

Аппаратное обеспечение:

Архитектура вдохновлена другими сборками ЦП, такими как JAM-1 Джеймса Шармана, SAP-1 Бена Итера, 4-битный Crazy Small CPU Уоррена, его 8-битная версия и другие. Все они, как и многие другие, используют «управляющую» EEPROM, EPROM или ROM для генерации управляющих сигналов для компонентов ЦП. Поскольку это намного проще, чем генерировать их с помощью одних только логических схем, и поскольку это обеспечивает большую гибкость в будущем, я также решил использовать такую «управляющую» память, а именно EPROM. В отличие от сборок, упомянутых выше, я стремился к минимально возможному количеству микросхем, поэтому я попытался «втиснуть» как можно больше обработки данных в память, чтобы либо снизить требования к другим компонентам ЦП, либо, что еще лучше, устранить их полностью. Вот некоторые ключевые шаги:

     Полностью исключаем ALU и реализуем его как поиск. Поскольку большинство СППЗУ имеют только 8-битный выходной сигнал, а системе также необходимы другие управляющие сигналы, ширина данных АЛУ должна быть резко ограничена. Не волнуйтесь, его можно сократить до одного бита: на самом деле 1-битные вычисления — это все, что нам нужно.
     Чтобы выполнить какое-либо значимое вычисление, необходимо сериализовать выходные данные 1-битного АЛУ. Это идеальный вариант использования последовательной SRAM, который также дает и другие преимущества. Во-первых, это устраняет необходимость в регистрах, поскольку все операции ALU могут выполняться непосредственно с данными в SRAM. Во-вторых, последовательные SRAM также адресуются последовательно, поэтому нет необходимости фиксировать адреса источника и назначения. В-третьих, произвольная ширина обработки данных может быть достигнута простым выбором количества тактов SRAM. Я выбрал 16 бит (16 тактов SRAM на 1 операцию ALU) как хороший компромисс между полезностью и скоростью.
     Требуется как минимум 2 последовательных чипа SRAM, один из них должен обеспечивать последовательный вход для нашего 1-битного АЛУ, и в то же время второй должен хранить результат.
     Для операций ALU с двумя операндами (например, ADD/AND/XOR...) необходимы 2 последовательных входа. Добавление третьей SRAM, безусловно, может быть вариантом (2 для входов ALU, 1 для результатов), но есть лучшее решение. Если вместо SRAM используется последовательная FLASH-память, сохраняются те же преимущества (уже сериализованные данные, сериализованный адрес), но FLASH можно использовать для хранения инструкций/программ, а также для обеспечения входа ALU.
     Нет необходимости добавлять какое-либо оборудование для счетчика программ, поскольку внутри SRAM уже достаточно места, где можно хранить его значение.

Даже несмотря на эти значительные упрощения, по-прежнему требуется некоторое дополнительное оборудование, однако все можно построить всего с использованием всего 8 чипов, следуя схеме ниже:

16-bit Serial Homebrew CPU

Схема построена на основе EPROM M27C1001-15 емкостью 128 КБ, работающего при напряжении 5 В и сочетающего в себе конечный автомат управления и 1-битное АЛУ. Его выходные линии фиксируются микросхемой 74HC574 в каждом такте и управляют двумя последовательными SRAM 23LCV512 емкостью 64 КБ и одной последовательной флэш-памятью W25Q80 емкостью 1 МБ. Недостаточно выходов для индивидуального управления каждой памятью, поэтому они используют общую линию данных и частично также линию выбора микросхемы, только линии тактовой частоты остаются отдельными. Мне не удалось найти последовательную FLASH-память на 5 В, поэтому резисторы R3, R4 и R5 ограничивают ток и образуют мост от 5 В до 3,3 В. Стабилизатор напряжения MCP1703 3,3В я не считаю частью ЦП (считаю его частью блока питания), но вместе с ним мой ЦП содержит 9 микросхем.

Текущая инструкция хранится в буферизованном сдвиговом регистре 74HC595, линии управления которого также частично используются совместно с памятью. Выполнение каждой инструкции занимает пару тактов, поэтому ход выполнения инструкции отслеживается счетчиком «микрокода» 74HC393. После завершения инструкции строка «Counter_reset» сбрасывает счетчик «микрокода» и начинает выполнение следующей инструкции, буферизованной в 74HC595.

74HC574 и «микрокодовый» счетчик 74HC393 используют противоположный фронт тактовой частоты, поэтому тактовый генератор 74HC14 подает инвертированный тактовый сигнал на 74HC393, чтобы сделать их синхронными.

16-bit Serial Homebrew CPU Schematic

Входы и выходы:

Одна вещь, которую мне не удалось реализовать в моем процессоре, — это самопрограммирование флэш-памяти. Таким образом, загрузчик невозможен, и загрузка новой программы в последовательную FLASH должна выполняться извне. Для этой цели я использовал микроконтроллер Attiny13, который слушает набор команд через UART, поэтому для загрузки нового кода достаточно любого адаптера USB-UART. При программировании он отключает вывод 74HC574 через строку «Prog_en» и переходит к непосредственному программированию FLASH-памяти. Микроконтроллер используется только для загрузки новой программы, а процессор прекрасно работает и без нее.

Единственными доступными выходами являются два старших бита регистра сдвига команд 74HC595. Я использовал одну из этих перевернутых линий в качестве выбора микросхемы, которая позволяет процессору подключаться к SPI-подобному устройству. Например, ЖК-дисплей SPI на базе PCD8544 с напряжением 3,3 В (Nokia 5110) можно подключить напрямую, при этом второй старший бит инструкции действует как селектор данных/команд ЖК-дисплея.
Другой вариант — подключить дополнительный сдвиговый регистр 74HC595 вместо ЖК-дисплея, чтобы получить классические линии цифрового вывода.

Единственными доступными входами являются два сигнала данных/входов памяти, подключенные к адресным линиям EPROM (A9, A11). Последовательная память поддерживает высокий импеданс этих сигналов, когда они не используются, поэтому их можно использовать как обычные цифровые входы, когда память простаивает. Важно отметить, что входной сигнал не должен мешать данным памяти, поэтому между входным сигналом и входной линией памяти требуется высокое сопротивление (R6, R7). Примечание: чтение входного сигнала в линиях данных памяти работает только для тактовых частот примерно до 8 МГц. На более высоких частотах выборочные данные кажутся неустойчивыми, и процессор склонен к зависаниям.

16-bit Serial Homebrew CPU Peripherals

Вы уже могли видеть, как мой процессор играет «Bad Apple!!» музыкальное видео на ЖК-дисплее PCD8544 где-то вверху этой страницы. В видео ниже я демонстрирую возможность управления общими цифровыми выходами путем добавления еще одного 74HC595. Эту же схему можно использовать для создания 8-битной музыки со скоростью до 4300 сэмплов в секунду, если вместо светодиодов использовать лестницу R-2R, и это та же самая схема, которую я использовал для создания саундтрека к фильму «Плохое яблоко!! " видео.

Карта памяти:

ЦП не имеет выделенных регистров, но имеет две SRAM, из которых он может читать и записывать. Обратной стороной является то, что каждый раз, когда ЦП хочет получить доступ к данным, ему приходится записывать полный 16-битный адрес в последовательную SRAM. Положительным моментом является то, что, поскольку ему в любом случае приходится записывать полный 16-битный адрес, ЦП (и инструкции в целом) могут получить доступ ко всем 64 КБ SRAM в постоянное время.

Я выбрал одну SRAM (U8/RAM1), которая будет использоваться для хранения данных программы, и все арифметические и логические операции предназначены для выполнения над значениями в этой памяти. Вторая SRAM (U7/RAM2) предназначена для использования в стеке, поэтому только несколько инструкций могут получить доступ к ее содержимому и изменить его.
Первые несколько байтов обеих памяти зарезервированы для хранения внутреннего состояния ЦП, такого как счетчик программ, бит флага, указатель стека, промежуточный результат, адреса источника/назначения и другие внутренние значения. Примерная карта памяти приведена в таблице ниже:

Address:0x00x10x20x30x40x50x60x70x80x90xA0xB0xC0xD0x000E~0xFFFF
RAM1:Flag & InputProgram counter (PC)Program counter reversedStack pointer (SP)Stack value (SPVAL)Registers and user data
RAM2:FlagProgram counter (PC)Destination addressInstruction's resultStack and user data

Отдельно хотелось бы упомянуть способ использования флэш-памяти в качестве второго входа АЛУ. Поскольку FLASH-память довольно велика (1 МБ), в нее можно поместить полную 16-битную таблицу поиска, содержащую 16-битные идентичные значения. С помощью этого поиска в 128 КБ затем можно записать 16-битное значение во FLASH в качестве адреса и прочитать то же 16-битное значение в качестве данных, которые можно использовать в качестве входа ALU.

Небольшое неудобство при использовании последовательной памяти состоит в том, что они адресуются в формате «сначала старший бит», в то время как 1-битное АЛУ естественным образом выполняет вычисления в формате «сначала младший бит». Чтобы получить функциональную адресацию памяти, нам нужно поменять местами биты из формата «сначала младший бит», с которым работает процессор, на формат «сначала старший бит», с которым работает память. Реверсировать биты с помощью 1-битного АЛУ непросто, поэтому я зарезервировал еще 128 КБ флэш-памяти для таблицы поиска «обратных значений», чтобы ускорить операцию. Он работает так же, как и предыдущий поиск: значение записывается во флэш-память как адрес, а его обратное представление считывается обратно как данные.

Эти две 16-битные таблицы поиска являются причиной того, что мой процессор имеет только 768 КБ флэш-памяти и почему счетчик программ (ПК) начинается с адреса 0x040000, а не с нуля.

Набор инструкций:

Существуют некоторые ограничения на набор команд, связанные с ограниченностью аппаратного обеспечения. ЦП способен выполнять только 64 уникальные инструкции/операции, каждая из которых должна соответствовать максимум 256 шагам микроинструкций и должна выполняться при работе только с 1-битным АЛУ и 1 битом флага. Но даже с этими ограничениями, как ни удивительно, можно создать довольно красивый набор команд:

OP codeNameOperandsWidthFlagCyclesTotalDescription
0x00INIT--clear256256Wait for clock to stabilize, then initialize RAM ICs to sequential mode
0x01RESET--clear235235Set program counter PC = 0x040000 and stack pointer SP = 0x000A
0x02----158414Shadow instruction: Fetch
0x03----256414Shadow instruction: Fetch continuation
0x04----129129Shadow instruction: Increment program counter PC = PC + 3
0x05----129129Shadow instruction: Increment program counter PC = PC + 5
0x06----129129Shadow instruction: Increment program counter PC = PC + 7
0x07----129129Shadow instruction: Increment program counter PC = PC + 8
0x08----162291Shadow instruction: Copy 32 bit result
0x09----130259Shadow instruction: Copy 16 bit result
0x0A----113113Shadow instruction: Copy program counter
0x0B----167296Shadow instruction: Store to RAM indirect
0x0C----151280Shadow instruction: Store to RAM indirect
0x0D----173587Shadow instruction: Arithmetic instruction dispatch
0x0ESTF--set132546Set FLAG
0x0FCLF--clear132546Clear FLAG
0x10NOP---132546No operation
0x11MOVaddr16 <- addr1616-231774Move 16 bit value
0x12MOVWaddr16 <- addr1632-146851Move 32 bit value
0x13INCaddr16 <- addr1616overflow231774Increment
0x14DECaddr16 <- addr1616overflow231774Decrement
0x15COMaddr16 <- addr1616zero2317741's complement (NOT)
0x16NEGaddr16 <- addr1616zero2317742's complement
0x17LSLaddr16 <- addr1616overflow233776Left shift (<<)
0x18LSRaddr16 <- addr1616overflow233776Right shift (>>)
0x19ROLaddr16 <- addr1616overflow233776Left shift with carry
0x1ARORaddr16 <- addr1616overflow255798Right shift with carry
0x1BASRaddr16 <- addr1616overflow235778Arithmetic right shift (keeps sign bit)
0x1CREVaddr16 <- addr1616-238781Bit reverse
0x1DADDIaddr16 <- addr16, val1616overflow231774Add immediate
0x1EADCIaddr16 <- addr16, val1616overflow231774Add immediate with carry
0x1FSUBIaddr16 <- addr16, val1616overflow231774Subtract immediate
0x20SBCIaddr16 <- addr16, val1616overflow231774Subtract immediate with carry
0x21ANDIaddr16 <- addr16, val1616zero231774Logical AND with immediate
0x22ORIaddr16 <- addr16, val1616zero231774Logical OR with immediate
0x23XORIaddr16 <- addr16, val1616zero231774Logical XOR with immediate
0x24ADDaddr16 <- addr16, addr1616overflow171887Add register
0x25ADCaddr16 <- addr16, addr1616overflow171887Add register with carry
0x26SUBaddr16 <- addr16, addr1616overflow171887Subtract register
0x27SBCaddr16 <- addr16, addr1616overflow171887Subtract register with carry
0x28ANDaddr16 <- addr16, addr1616zero171887Logical AND with register
0x29ORaddr16 <- addr16, addr1616zero171887Logical OR with register
0x2AXORaddr16 <- addr16, addr1616zero171887Logical XOR with register
0x2BJMPaddr24--197611Jump to address
0x2CCALLaddr2432-221748Copy following instruction's address (PC + 4) and current FLAG to SPVAL, then jump
0x2DRET-32restore138552Move SPVAL to PC & FLAG (effectively returns from CALL and restores previous FLAG)
0x2EBRFSaddr24--160625|574Branch if FLAG set
0x2FBRFCaddr24--160625|574Branch if FLAG cleared
0x30BREQaddr16, addr2416-243708|657Branch if register is zero
0x31BRNEaddr16, addr2416-243708|657Branch if register is not zero
0x32LDIaddr16 <- value1616-81624Load 16 bit immediate
0x33LDIWaddr16 <- value3232-113656Load 32 bit immediate
0x34LDaddr16 <- [addr16]16-238911Indirect load 16 bits from address
0x35LDBaddr16 <- [addr16]8-238911Indirect load 8 bits from address, set upper 8 bits to 0
0x36ST[addr16] <- addr1616-163873Indirect store 16 bits to address
0x37STB[addr16] <- addr168-163857Indirect store 8 bits to address
0x38LD2W[addr16]32-256799Indirect load 32 bits from address in RAM2 to SPVAL register
0x39LD2[addr16]16-224767Indirect load 16 bits from address in RAM2 to SPVAL register
0x3AST2W[addr16]32-256799Indirect store 32 bits from SPVAL register to address in RAM2
0x3BST2[addr16]16-224767Indirect store 16 bits from SPVAL register to address in RAM2
0x3CLPMaddr16 <- [addr16]16-211884Indirect load 16 bits from address in FLASH
0x3DLPBaddr16 <- [addr16]8-211884Indirect load 8 bits from address in FLASH, set upper 8 bits to 0
0x3EOUTaddr168-252795Output 8 bits over SPI
0x3FHALT--clear14428Stop execution

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

Арифметические и логические операции используют один бит флага либо в качестве флага переноса/переполнения, либо в качестве нулевого флага. Как упоминалось выше, доступ ко всему адресному пространству не снижает производительность, поэтому все эти инструкции могут указывать любой адрес источника/назначения в адресном пространстве SRAM размером 64 КБ. Косвенная адресация для арифметических операций не поддерживается напрямую, но должна выполняться инструкциями LD/ST (загрузка/сохранение).

Второй набор инструкций LD2/ST2 обеспечивает доступ ко второму SRAM. Они предназначены для использования в стеке, но могут храниться любые данные. Инструкции PUSH и POP не реализованы, но их можно составить из инструкций LD2/ST2 и INC/DEC.

В среднем инструкция занимает около 800 тактов, включая операцию выборки и приращение счетчика программ. При максимальной тактовой частоте 10 МГц процессор может выполнять около 12 тыс. инструкций в секунду.

Написание кода на ассемблере:

Я использую инструмент Customasm Лоренци для создания двоичных файлов из исходного кода сборки. Затем двоичные файлы можно загрузить с помощью небольшого приложения Python3 в программный микроконтроллер Attiny13, который записывает двоичные файлы во FLASH.

Ниже приведены два примера небольших подпрограмм, написанных на ассемблере для моего процессора. Первая подпрограмма возвращает 32-битный результат умножения двух 16-битных значений. Второй записывает на ЖК-дисплей строку ascii, хранящуюся во флэш-памяти.

Multiply32_16x16LCD_WriteStrF
; Returns FA32 = FA16 * FB16 ; FB is expected to be smaller Multiply32_16x16:     ;PUSH_PC        ; Not necessary     LDIW FC, 0      ; Clear result     LDI FA+2, 0     ; Cast FA16 to FA32 .loop:     ANDI TMP, FB, 1     BRFS .skip_add     ADD FC, FA      ; Add FC32 += FA32     ADC FC+2, FA+2  ; Add FC32 += FA32 .skip_add:     LSL FA          ; Shift FA32 << 1     ROL FA+2        ; Shift FA32 << 1     LSR FB          ; Shift FB16 >> 1     BRNE FB, .loop     MOVW FA, FC     ; Copy result     ;POP_PC         ; Not necessary     RET 
; Write String in Flash ; input: FA32 <- Address of the string in Flash LCD_WriteStrF:     PUSH_PC             ; Save return address     PUSHW RA            ; Save RA 32-bit     MOVW RA, FA .loop:     LPB FA, RA          ; Load character from Flash     BREQ FA, .stop      ; Test "" character     REV FA              ; MSB-first -> LSB-first     ANDI FA, FA+1, 0xFF ; Cast to 8bits     CALL LCD_WriteChar  ; Write character     ADDI RA, 1          ; Increase 32-bit pointer     ADCI RA+2, 0        ; Increase 32-bit pointer     JMP .loop .stop:     POPW RA             ; Restore RA 32-bit     POP_PC              ; Restore return address     RET 

Максимальная частота и критический путь:

Согласно спецификациям, общая задержка распространения по критическому пути составляет:

     12 нс при 74HC14 от «Clock_pos» до «Clock_neg»,
     54 нс на 74HC393 для пульсации до последнего 8-го бита (12+3x5+12+3x5 нс),
     Время доступа 150 нс в СППЗУ M27C1001-15,
     2нс на 74HC574 для стабилизации входов перед фронтом тактовой частоты.

Если собрать все вместе, схема должна работать только на частоте ~4,6 МГц. Однако моя конкретная сборка способна безупречно работать до 10 МГц и становится нестабильной только выше ~10,5 МГц. Для схемы, построенной на макетной плате с большим количеством паразитной емкости, я считаю это весьма впечатляющим. Максимальная тактовая частота могла бы быть даже улучшена, если бы использовался более быстрый двоичный счетчик или более быстрое СППЗУ.

Заключение и ретроспектива:

Я очень доволен готовым процессором. Он имеет приятный и простой в работе набор инструкций, в котором присутствуют все основные инструкции. Он достаточно мощный для потоковой передачи видео на небольшой ЖК-экран, воспроизведения звука (хотя и с использованием внешней «звуковой карты») и в целом выполнения простых вычислительных операций ввода-вывода, для которых он изначально предназначался. Наконец, он успешно демонстрирует, что можно создать функциональный самодельный процессор, используя лишь несколько микросхем.

Однако возможны небольшие улучшения:

     Счетчик пульсаций 74HC393 является существенным узким местом на критическом пути. Замена его сумматором с упреждающим переносом («быстрый сумматор») или буферизованным счетчиком, например 74HC590, увеличит максимальную тактовую частоту.
     То же самое касается и EPROM M27C1001-15. Использование более быстрой памяти, такой как M27C1001-35 EPROM или SST39SF020A-70 FLASH, также позволит повысить тактовую частоту.
     СППЗУ большего размера с более чем 17 адресными строками можно использовать либо для увеличения количества команд, либо для использования дополнительных адресных линий в качестве обычных цифровых входов.
     Добавление некоторых инструкций по стиранию и программированию внутренней флэш-памяти позволило бы создать загрузчик, что сделало бы схему программирования Attiny13 ненужной.
     Система способна выполнять код только из FLASH-памяти. Можно было бы создать эмулятор внутри FLASH и заставить эмулятор выполнять код из SRAM, но чтобы заставить ЦП выполнять код из SRAM изначально, потребовался бы другой процесс выборки инструкций, возможно, включая дублирующий набор инструкций только для SRAM. само исполнение.

Мне еще предстоит выяснить, стоит ли внедрять некоторые из этих улучшений. А пока, если вам нравится проект и вы хотите углубиться в него, вы можете просмотреть исходный код, доступный здесь. Он содержит симулятор, генератор микрокода EPROM, прошивку программатора Attiny13 и все мои ассемблерные коды.

Обновлять:

Я реализовал минималистичный механизм проецирования трехмерных каркасных объектов, используя 16-битную арифметику с фиксированной запятой. Умножение матриц на моем процессоре с производительностью 0,012 MIPS происходит довольно медленно, поэтому 3D-игры, вероятно, не появятся в ближайшее время:

Я также медленно расширяю список оборудования, которое напрямую поддерживает мой процессор. Я добавил буквенно-цифровой ЖК-дисплей SPI, который спас от старого принтера HP:

16-bit Serial Homebrew CPU

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

16-bit Serial Homebrew CPU

Обновление 2:

ЦП теперь поддерживает драйвер ЖК-дисплея PCF8833, хотя для рендеринга одного кадра требуется около 96 секунд.

16-bit Serial Homebrew CPU

Самодельные процессоры WebRing

Обязательно ознакомьтесь с другими замечательными сборками домашних процессоров на сайте Уоррена https://www.homebrewcpuring.org.

Выйти на ринг?

Чтобы присоединиться к кольцу самодельных процессоров, напишите Уоррену (почта запутана, вам придется заменить [at] на @), упомянув URL-адрес вашей страницы. Затем он добавит его в список. Вам нужно будет скопировать этот фрагмент кода на свою страницу (или указать на него ссылку). Примечание. Кольцо предназначено для проектов, включающих самодельный процессор. Он может имитировать коммерческую часть, это нормально. Но на самом деле использование этого коммерческого процессора не имеет значения. Точно так же проект должен быть хотя бы частично реализован: чисто бумажные проекты тоже не оцениваются. Его можно построить с использованием любой технологии, от реле до FPGA.


Источник: www.jiristepanovsky.cz

Комментарии: