Рисунок 1 – функциональная блок схема «NMR»
Рисунок 1 – функциональная блок схема «NMR»

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

Для себя поставил такое ТЗ:

  • Описание максимально унифицированное, написанное одним файлом и без замудрёных конструкций, дабы легко переезжать с ПЛИСЫ на ПЛИСУ;

  • По возможности минимально занимаемый объем, а если быть точнее: необходимо все впихнуть в такую старенькую микросхемку как EPF10K100;

  • Адаптировать ядро под работу с внешними микросхемами одна для программы вторая в качестве ОЗУ. На их роль выбрал распространенные 16 битные микросхемы SRAM.

  • А главное: полная свобода фантазии и нестандартный подход.

Ладно, не буду много лить воды зачем это и куда, просто прикола ради, и убить время - ночное время. Творилось все это действо по ночам отсюда кстати и название ядра – NIGHTMARE для краткости NMR. Итак, после множества ночных кошмаров появилась такая вот паукообразная структура, показанная на рисунке 1.

Ядро имеет гарвардскую архитектуру и содержит отдельные шину команд, шину данных и шину периферии. Разрядность адреса памяти программ составляет 20 бит, разрядность шины инструкций 16 бит, однако сами инструкции могут иметь различную длину: 16 бит, или 32 бита. Структура командного слова представлена на рисунке 2.

Рисунок 2 – командное слово ядра «NMR»
Рисунок 2 – командное слово ядра «NMR»

Система команд и архитектура «NMR» разработаны специально для работы с внешней памятью программ, организованной в слова по 16 бит. Разделение некоторых команд на две ячейки предусмотрено архитектурой ядра и не отражается на программе, так как система управления процессором по типу команды, автоматически определяет смещение адреса.

Предполагается, что внешняя память программ работает на более низкой частоте, относительно самого ядра. Поэтому в организации системы команд процессора и его внутренней архитектуры   реализован подход, позволяющий начать выполнение команды еще до её полного считывания.  Инструкции выполняется на конвейере параллельно со считыванием частей команд из памяти, что существенно повышает быстродействие процессора, так как память перестает быть узким местом и длительность процессорного такта в среднем соответствует длительности считывания 32-битного командного слова из памяти программ.

Так как объем логических элементов ограничен, а никаких требований о производительности я себе не ставил, было принято решение повсеместно использовать доступную ПЛИСу встроенную ОЗУ.  Так архитектурой ядра предусмотрены два блока регистров общего назначения: REG_A и REG_B, они 32 битные и почти равноправные с точки зрения выполнения инструкций. Каждый такой блок содержит 256 32-х битных регистра. Кроме РОН существует возможность подкрутить к ядру свободную (оставшуюся в том числе от периферии) ОЗУ, которая будет выполнять роль, своего рода, КЕШ (CACHE), т.к. доступ к ней все же быстрее чем ко внешней ИМС ОЗУ. КЕШ находится в общем для ОЗУ адресном пространстве (младшая часть), но имеет расширенный функционал, а так же расширенную шину данных до 20 бит (напоминаю что внешняя ОЗУ выбрана 16 битной). Перечисленные особенности КЕШ делают его удобным местом для хранения адресов перехода и возврата, т.е. местом для работы с адресами памяти программ. Внешняя ИМС ОЗУ предполагается идентичной ИМС для хранения программ и имеет 21-битную шину адреса. Адресное пространство ОЗУ показано на рисунке 3 и содержит 4 области:

  • Младший сектор КЕШ ОЗУ, позволяющий операции: «marckRAM#» и «jampRAM#», данная область максимально оптимизирована для организации СТЕКа подпрограмм и инструкций вызова и перехода.

  • КЕШ ОЗУ, в базовой конфигурации (адаптированной для EPF10K100) содержит менее 1023 слов (включая младшую область). Весь доступный КЕШ имеет разрядность слова 20 бит, что соответствует разрядности шины адреса счетчика инструкций «instruction_counter». Объем КЕШ ОЗУ может быть увеличен (в диапазоне 1К – 32К слов) для других семейств FPGA.

  • Адресация свыше 32767 слова должна быть помечена маркером в 16-м бите адреса ОЗУ. В таком случае будет доступна область внешней ОЗУ в размере 32768 слов по 16 бит (скорость доступа к внешней ОЗУ, определяется параметрами примененной ИМС SRAM).

  • Старшая область шины адреса ОЗУ определяет используемый сектор внешней ИМС ОЗУ, и может принимать значения от 0 (0 - полмолчания) до 31.

Таким образом, организован доступ к 2-Мбайт ОЗУ, с 2,5КБ высокоскоростной КЕШ и остальной областью внешней SRAM, разделенной на сектора, что позволяет организовывать защищенный режим для 32 потоков.

Рисунок 3 – Организация адресного пространства ОЗУ «NMR»
Рисунок 3 – Организация адресного пространства ОЗУ «NMR»

Архитектурой предусмотрен простенький механизм прерывания, для этого в ядро входит 10 битная шина от источника прерывания или контроллера. Контроллер прерываний решено сделать внешним модулем, ибо его функционал от проекта к проекту может очень сильно изменятся.  Ядро на первой стадии процессорного такта проверяет наличие запроса на прерывание, и наличие флага о нахождении в прерывании, сохраняет адрес возврата в системный регистр и считывает из памяти программ по вектору из шины прерываний адрес перехода на подпрограмму соответствующего прерывания. Таким образом младшая область памяти программ 1-1023 слова содержит адреса подпрограмм соответствующих прерываний.

Периферия подключается к ядру по выделенной шине. Шина адреса периферии является однонаправленной: от ядра к устройствам. Шина данных периферии является двунаправленной с Z состоянием по умолчанию. Состояние шины данных задается выставленным адресом на шине адреса периферии, либо во время передачи от ядра в периферию, т.е. всегда контролируется ядром. Организация периферии и логика использования обшей шины данных показаны на рисунке 4.

Рисунок 4 – Организация периферии и логика доступа к двунаправленной шине данных
Рисунок 4 – Организация периферии и логика доступа к двунаправленной шине данных

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

Ядро «NMR» универсально и было испытано на разных ИМС ПЛИС, полученные характеристики ядра сведены в таблицу 1.

Таблица 1 – характеристики ядра «NMR_V 32I» в FPGA разных поколений
Таблица 1 – характеристики ядра «NMR_V 32I» в FPGA разных поколений

Для испытаний и отладки «NMR» по мере его развития разрабатывались разные платы и встраиваемые модули актуальная версия такого модуля Flexible Microcontroller Unit «FMU» специально разработанная под ядро «NMR», структура модуля показана на рисунке 5.

Рисунок 5 - Отладочная система на ядре «NMR» с использованием Cyclone3
Рисунок 5 - Отладочная система на ядре «NMR» с использованием Cyclone3

Далее на рисунках 6-9 представлена архитектура системы команд:

Рисунок 6 - Архитектура системы команд «NMR» для работы с системными регистрами и реализации косвенной адресации
Рисунок 6 - Архитектура системы команд «NMR» для работы с системными регистрами и реализации косвенной адресации
Рисунок 7 - Архитектура системы команд «NMR» для простого перемещения данных (системные регистры адреса задаются системой управления автоматически)
Рисунок 7 - Архитектура системы команд «NMR» для простого перемещения данных (системные регистры адреса задаются системой управления автоматически)
Рисунок 8 - Архитектура системы команд «NMR» для реализации переходов и задания режимов
Рисунок 8 - Архитектура системы команд «NMR» для реализации переходов и задания режимов
Рисунок 9 - Архитектура системы команд «NMR» для реализации логических и математических операций
Рисунок 9 - Архитектура системы команд «NMR» для реализации логических и математических операций

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

Рисунок 10 - Файл верхнего уровня минимальной обвязки «NMR» ядра из Quartus II
Рисунок 10 - Файл верхнего уровня минимальной обвязки «NMR» ядра из Quartus II

Это первая статья тут (как в принципе, так и из запланированного цикла), возможно есть куча недочетов, плюс не нашел как прикреплять файлы - например с HDL описанием самого ядра, если что проект открытый и буду рад поделиться со всеми наработками для его потенциального развития.

Комментарии (15)


  1. jpegqs
    25.10.2023 14:39
    +5

    не нашел как прикреплять файлы

    Создайте репозиторий/гист на гитхабе и укажите ссылку. Или хоть на Яндекс.Диск директорию.



  1. yamifa_1234
    25.10.2023 14:39
    +1

    На первый взгляд возникает вопрос "а зачем?"... Но потом приходит осознание что автору нравится этим заниматься, а это главное) а теперь к конкретике: как я понял система команд реализована собственная и компиляторов разумеется нет? Если компилятора нет, то есть планы реализовать?

    P.s. на первой картинке схема больше похожа на сердце)


    1. Refridgerator
      25.10.2023 14:39
      +4

      Такое?)


    1. fpga500
      25.10.2023 14:39

      Поддерживаю вопрос про компилятор. Тоже когда то давно делал свое процессорное ядро на FPGA. Но основная проблема, что без компилятора его невозможно использовать. Нужен хотя бы какой-нибудь удобный ассемблер, не говоря уже про Си


  1. Sukhachev_KIR Автор
    25.10.2023 14:39
    +2

    Ассемблер использовался для отладки, а над СИшным компилятором ведется активная работа (компилятор пишет напарник, и да, в комментариях угадали - я больше по аппаратной части). Горизонт думаю пару +- месяцев.

    Важно, что ядро разрабатывалось без претензий на ПК, это контроллерный проект)


    1. wesker_96
      25.10.2023 14:39

      Очень ждем-с. Создайте гитхаб репозиторий. Конкретно меня, например, очень интересует компилятор. Или быть может он уже есть?..


  1. LAutour
    25.10.2023 14:39

    А почему без преддекрементной адресации? По особенностям Гарвардовской архитиектуры: доступ к массиву констант как?


    1. Sukhachev_KIR Автор
      25.10.2023 14:39
      +1

      Почти все варианты адресации можно получить, работая с выходными (относительно ядра) регистрами адреса (первый блок ISA). Т.е. перед любым обращением куда-либо можно предварительно обработать и обновить регистр адреса шины, по которой последует обращение. Для этого и сделаны доступными из программы все «системные» регистры. Отчасти такой подход усложняет компилятор, но сильно расширяет возможности, иногда экономит время выполнения, а главное позволяет выполнить главное условие Т.З. уместиться куда надо. Второй блок, содержащий команды перемещения — это скорее частный случай первого для облегчения процедур единичных обращений.

      Доступ к массиву констант? Т.е. получить массив данных из памяти программ?

      Если да, то предполагается, что перед началом выполнения основной программы константы загружаются в РОН А (A255<=16'd65535 команда 29), можно блоками по 256 значений (255 – это максимальный адрес РОН блока А: доступные адреса от 0 до 255). А из РОН блока А данные можно отправить куда угодно, например в ОЗУ с предустановкой начальной области ОЗУ (ATR<=16'd65535 команда 3 / расширение 1), регистра адреса блока РОН (ATA<=8'd255 команда 1) и последующим копирование с инкрементами адресов (RAM+<=A+ команда 6 / расширение 3). Таким образом массив констант можно записать и в разные сектора ОЗУ, предварительно указав целевой сектор 0 - 31 (MEM<=5'd31 команда 111). Из ОЗУ данные можно отправлять в режиме пословно, как с явным, так и с косвенным, или со смешанным указанием адресов, а так же в потоковом режиме (R(65535)=>P команда 13).

      Пример: Загрузили массив констант в Х-сектор ОЗУ, в процессе выполнения основной программы, получили массив данных который сохранился в другой области или секторе ОЗУ, потоково отправили в периферийное устройство оба массива (например сопроцессор, или уникальный модуль для выполнения какой-то конкретной обработки), и дали команду на выполнение, и вернулись к основной программе и ждем флага или прерывания, выполняя основную программу.

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


  1. Teimir
    25.10.2023 14:39
    +1

    Интересная статья. Мы сейчас тоже делаем ядро для микроконтроллера. Только в нашем случае архитектура Неймана с общей памятью команд и данных. Прерывания пока не реализовали, да и в целом до работающего ядра далеко.


    1. Sukhachev_KIR Автор
      25.10.2023 14:39

      Спасибо!!!  Нейман сложнее (м.б. чисто в моем восприятии), а мне хотелось именно уместиться в целевую ИМС.

      П.С.: интересно будет почитать про Ваш проект!

      П.П.С.: В любом случае я планирую довести проект до удобоваримого варианта чтобы можно было отдать на растерзание людям ядро (уже) и компилятор. Скорее всего и ошибки есть и придется еще править ISA (сейчас далеко не первый вариант), но считаю, что правки уже нужно вносить, когда будет компилятор что бы была быстрая отладка


  1. supermaxus
    25.10.2023 14:39

    Создать свой проц - это Мечта творческого технического специалиста. Такая же, как - создать свою операционную систему. И если полететь на Марс ему не грозит даже в самых смелых грезах, то такая мечта выглядит осуществимой.

    Однако, вы уже поняли к чему я веду. Реализовать даже одну мечту одному - очень сложно, об этом -ниже. Реализовать же вторую мечту ему же- практически невозможно. Допустим, вы сделали и отладили проц. Теперь под него нужно сделать операционную систему, компилятор и отладчик. Поскольку писать проги будете не на самом устройстве, нужен еще эмулятор устройства на ПК, совместимый с отладчиком. Ну, понятно, загрузчик программы в устройство, потому как флешка более подходит для финального варианта. а до того будет 100млн итераций отладки.

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

    стандартные битовые операции реализованы не все: нужно и, или, not, xor, битовый сдвиг влево, битовый сдвиг вправо. без этого криптографию - не реализовать. криптография нужна в ssl/tls, что в свою очередь сейчас нужно для всех сайтов в инете, потому как обычный http сейчас считается небезопасным.

    не увидел выхода не то что на видеокарту, даже на обычный мини-дисплей. вы предлагаете под это дело занять все свободные gpio?

    аппаратные прерывания любого современного проца - стандартны: деление на 0, ошибка доступа к памяти, прерывание отладки и др. не увидел поддерживаемого списка таких прерываний.

    не увидел потуг на мультизадачность. зачем вам однопоточный проц? любой arm на 33мгц делает в этой части ваш проц.

    я не убиваю вашу мечту. самому было бы интересно. но так понимаю, вы больше по аппаратной части и над программной составляющей (что надо то?) сильно не задумывались.


    1. Sukhachev_KIR Автор
      25.10.2023 14:39
      +1

      1.       Так если посмотреть мое самое ТЗ я и хотел максимально странно все)) (Шутка)

      2.       Во-первых, Вы совершенно правы, как я и писал память программ и ОЗУ вполне конкретные +-, основной тезис: что доступна шина 16 бит (мало ножек ПЛИС + опять же по доступности ИМС памяти) и память медленная. Старался оптимизировать плотность, и это далеко не первый вариант ISA, и да, скорее всего можно много что улучшить в принципе, но пока вышло вот как вышло… эх

      3.       Это не баг это фича, идея и родилась от того, что бывают случаи, когда можно вообще копошиться в областях РОН (для несложных задач управления чем-либо с небольшой обработкой), а в ОЗУ вообще не лезть. В таких случаях можно отказаться и от внешней ИМС ОЗУ, и освободить внутреннюю память ПЛИСа для нужд периферии.

      П.С.: кстати пытаемся реализовать в компиляторе – подход что он после анализа программы может влиять на ядро, отключая неиспользуемые команды области и тд.

      С Регистрами не совсем так - они выглядят как РОН с точки зрения компилятора и программы, но по природе это сектора интегрированной в ПЛИС ОЗУ, реализовать 512 РОН в рамках целевой ИМС думаю невозможно.

      ВАЖНО в таблицах системы команд, когда есть условная запись «A255 что то там B 254» действие 253, это условная запись необходимая что бы просто отразить числа в столбцах opr1, opr2 т.д.. Т.Е. регистры 253, 254, 255 ничем не особенные. И вообще в таблице индексы числовые просто указывают на максимальный доступный адрес, иногда адрес «типа» меньше на единицу или две, чтобы команду можно было представить в числовом формате. (Возможно непонятно, лучше дам комментарии по конкретной команде, если нужно)

      4.       Регистровый файл разбит именно по той причине, что физически он состоит их двух банков реальной ОЗУ ПЛИС и при операциях использует скрытые реальные регистры А, B и result, ведь все операции только на РОН.

      5.       В точку, спасибо попробуем добавить!

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

      7.       Да! сил тратится не меньше уж точно! Правда на разработчика компилятора свалился не супер-сырой проект железа, не первая итерация, плюс, что мог отлаживал своими силами ассемблером и простыми программками, а также море симуляций. Конечно, ОС кипит, да и пинаю его постоянно – что бы работал)


    1. fpga500
      25.10.2023 14:39

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

      И для моих задач мне хватит простенького 8-и битного процессора. Он должен:
      1. занимать небольшое количество ресурсов

      2. в нем должна быть удобная работа с битовыми операциями и с отдельными битами

      3. удобство программирования этого проца (очень важно!)

      4. возможность не использовать внешнее ОЗУ, т.е. вся оперативная память на BRAM, память программ можно на отдельном внешнем ПЗУ

      5. Аппаратная независимость, т.е. не должно быть никакой привязки к конкретным производителям FPGA, код должен иметь возможно собираться под ПЛИС любых производителей

      Т.е. нужно не какое то мощное ядро, а именно что то простенькое, легко встраиваемое и программируемое


  1. hitomi2500
    25.10.2023 14:39
    +1

    Для максимально простенького ядра некоторые вещи выглядят по меньшей мере странно.

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

    Ко многим командам есть вопросы. Например почему длинный прыжок (up2M) с суммированием адреса? Для короткого это логично, но для длинного очень неудобно, указатели надо будет пересчитывать для каждого вызова.

    Огромный регистровый файл, не очень понятно как компилятор должен его использовать? Только как регистры общего назначения великоват, как куча или стек маловат. Может быть просто как быструю память, но не проще ли тогда сделать малое число регистров и быструю память отдельно? Для регистров 253, 254 и 255 есть специальные инструкции, они прямо просятся в отдельный маленький файл регистров.

    Регистровый файл разбит на 2 части, но не совсем понятно зачем. Они не работают как два контекста вне и внутри прерывания, они непонятно зачем раздувают систему команд, да ещё и не симметричны с её точки зрения.

    Насколько я понял, есть скрытые регистры. Как минимум PC и LR. Добавьте их в регистровый файл, создатель компилятора скажет вам спасибо.

    У вашего ядра наружу выходят целых 4 шины. Какие-то поменьше, какие-то больше 40 ног. Это отсекает огромное количество маленьких и средних ПЛИС с количеством выводов менее 100, для которых обычно такие архитектуры и используются. Если будете мигрировать на маленькие корпуса, попробуйте использовать мультиплексированную шину, либо перейти на QSPI/CSPI RAM или HyperRAM.

    Не владею статистикой, но рискну предположить, что на написание компилятора под новую уникальную архитектуру обычно уходит больше усилий, чем на разработку железа. Будьте готовы поддерживать плотную обратную связь с вашим разработчиком компилятора и идти на архитектурные изменения по его просьбе.