Можно ли самому сделать микроконтроллер?
«марка аппаратно-программных средств для построения простых систем автоматики и робототехники, ориентированная на непрофессиональных пользователей. Программная часть состоит из бесплатной программной оболочки (IDE) для написания программ, их компиляции и программирования аппаратуры. Аппаратная часть представляет собой набор смонтированных печатных плат, продающихся как официальным производителем, так и сторонними производителями. Полностью открытая архитектура системы позволяет свободно копировать или дополнять линейку продукции Ардуино.
Arduino может использоваться как для создания автономных объектов автоматики, так и подключаться к программному обеспечению на компьютере через стандартные проводные и беспроводные интерфейсы. «
Микроконтроллер своими руками
Пытаясь освоить контроллеры и уже владея навыками программирования ПЛИС, мне пришла в голову дурная мысль. Пришла, постучала и вошла. Всем тем, к кому приходят дурные мысли, и кому интересно как с этим явлением справляются другие, посвящается.
Возникла идея нарисовать свой контроллер, не ограниченный по количеству периферии, ОЗУ и другим параметрам, кроме ёмкости ПЛИС. Скажем контроллер содержит 5 UARTов, а позарез нужен шестой, придётся изворачиваться. А зачем, если можно просто щёлкнуть мышкой и добавить необходимое? Или наоборот, задачка хорошо решается на пяти контроллерах с разрядностью 5, 32, 20, 32 и 20 с непредсказуемым количеством линий связи между ними. Жалко использовать пять 32 разрядников, ресурс всегда жалко, а совмещать две подзадачи на одно ядро – некрасиво, что ли.
Время отклика на прерывание в контроллерах достаточно велико. Да, контроллер прерываний мониторит входы (почему-то ограниченные по количеству) независимо от ядра. Но прежде чем начать выполнение кода программы по обработке прерывания, скажем, читать порт, требуется сохранить регистры общего назначения, а затем их ещё и восстановить. А это не один такт на пустое с точки зрения логики программы переписывание регистров в стек и возврат этих же значений обратно. Оно конечно вписывается в понятие реального времени, но ведь можно же делать это мгновенно: выполнялся код с низким приоритетом, на следующем же такте по причине резкой необходимости начинает выполнятся высокоприоритетное прерывание по таймеру, а через такт выполняется код по обработке внешнего потока данных, ибо там совсем всё быстро надо.
Конечно, все эти проблемы надуманы и решаются и легко и просто. Но дурная мысль не ищет таких решений, она просто бродит в переполненном пространстве сознания и подталкивает сделать нестандарт. А, если у молодого инженера отпуск, то прости жена и дети, но папа не «опять сидит за компьютером», а профессионально растёт. Тем более иногда хочется выполнять не кем-то придуманные задачки, а свои, полностью свои.
Рис. 1. Таймеры. Добавляй сколько хочешь. Обратите внимание на грамматическую ошибку. Стыдно, но не хочется Builder устанавливать для перекомпиляции.
Разрешите представить вам контроллер, архитектура которого, не то чтобы отличается от всех прочих, скорее она не принята на вооружение. Уже давно забыт список прочитанной перед началом работ литературы, использованы мысли многих людей из разных компьютерных эпох, простите меня мои забытые учителя. Зато вам не придётся от меня открещиваться. Да и денег это не пронесло, поэтому и делиться нечем.
Сразу скажу, что контроллер состоялся, прошит в Спартаны всех поколений и успешно трудится на просторах СНГ и одной стране Прибалтики. Сейчас бы многое было сделано по другому, но любые изменения мне уже ленивы и, что случилось, то случилось. А случилось вот что.
— Разрядность контроллера от 1 до 32, со знаком или без оного. (Не помню проверял ли я работу на малых разрядностях). Используется дополнительный код.
— Количество портов ввода/вывода ни чем не ограничена, разрядность ограничена разрядностью ядра.
— Количество таймеров так же не ограничено. На каждый таймер можно установить свой обработчик прерывания.
— Обработчик прерываний с бесконечным количеством входов, каждый вход имеет приоритет от 0 до 99 (ограничение по причине «и так большого числа»). Возможность запрещать все прерывания или низкоприоритетные только.
— Количество последовательных портов типа UART неограниченно, разрядность ограничена разрядностью ядра.
— Делитель частоты сделан плохо и неправильно, но исправно выдаёт любую частоту для любой периферии или просто наружу контроллера.
— Сопроцессор. О нём ниже.
То есть мы имеем контроллер, поддерживающий основные операции арифметики и прочего изменения битов в словах (плюс, минус, сдвиг, побитовая логика. ). Так же он обращается к внутренней памяти и хранит код программы так же внутри себя. Память используется блочная, именно она является платформозависимой и в настройках программы обязательно нужно указать какая микросхема будет носителем контроллера. Все операции по любому направлению действий выполняются за один такт, это может и не всегда хорошо, но упростило проектирование архитектуры. Исключением является целочисленный делитель, функции которого реализованы в сопроцессоре, деление выполняется медленно, но уверенно. Алгоритм деления был выбран самый простой – побитный.
Хотелось бы рассказать про код команд, но сам не помню из чего он состоит. Исходя из принципа «не использовать то, что не используется» даже длинна кода команды является величиной не константной, тем более его содержимое. Скажем, если в компилируемой программе встречается 14 команд, то каждая команда кодируется 4 битами, если используется 18 команд – выделяется пятый бит. Плюс к каждой команде добавляется бит длинны, если он равен 0, то команда одинарная, если 1 – двойная, или наоборот, это не важно. Двойная команда нужна для операций, содержащих адрес ОЗУ или ПЗУ. Добавим к этому то, что шины адреса так же имеют не константные длины, и получим полный бардак в коде команд, рассматривать его никаким дизассемблером смысла нет, как собственно и ассемблер в этом контроллере.
Форт – вот тот язык, который позволил мне реализовать эти странные идеи. Форт прост и низкоуровневый. Писать компилятор данного языка – одно удовольствие. Ну, конечно, все конструкции языка не поддерживаются, только основные по пересылке данных и их изменении.
Рис. 2. Руководство достаточно скромное. Нескромное так и не написано.
Рис. 3. Вторая часть руководства. Не знаю почему, но окно расширить нельзя. Есть ещё и третья часть, но уж совсем скучная.
И, естественно, мы имеем стековый контроллер. То есть Форт функционирует в своей естественно среде – в стеке, в аппаратном стеке, а не медленном программном. «Всё за такт!» — второй девиз проекта. Аппаратный стек позволил при переходе в функции или вызове прерывания сразу приступать к выполнению команд обработки данных, без необходимости сохранять контент прерванного процесса. «Новые» данные просто помещаются в стек сверху, «старые» уходят в глубь и прекрасно там сохраняются. Затем «новые» данные, поучаствовав в разных операциях, благополучно переходят в ОЗУ или другие места, а «старые» выталкиваются на вершину. При возврате в прерванный участок кода никто ничего не заметит. Адрес прерванного кода сохраняется в другом стеке, и максимальное количество прерванных процессов зависит только ото глубины Стека Возвратов. Это величина настраивается при компиляции, хоть бы и «очень большое число». Параметры в функции передаются так же через стек.
Stackcpu – именно так и называется контроллер, почему-то латиницей, хотя писать код можно и кириллицей. Что я и делаю, приведу пример функции «меняем_биты_в_слове»:
Где: «читаем_из_нужного_порта», «меняем_прочитанное_из_нужного_порта», «записываем_в_требуемую_ячейку_памяти» — функции, выполняющие определённые действия.
Ну не псих ли я красиво ли? Приведу ещё доказательства, программа управления светофором:
Ну что, псих красиво? Третий принцип проекта: «Литературный язык в управление электроникой!» Хотя, как показала практика, писать так программы довольно утомительно. Для знатоков Форт сообщу, что точку мой компилятор воспринимает как разделитель, и запятую тоже. Конечно, лучше использовать язык С, но компилятор С мне не по зубам, а использовать сторонние компиляторы архитектура не позволяет.
Рис. 4. Среда разработки с отчётом компилятора и линковщика.
Сколько ёмкости FPGA занимает контроллер? Да кто ж его знает. Всё зависит от разрядности числа, выбранной периферии, ещё чего-то, и от текста программы. В контроллере не будет умножителя, если он не встречается в программном коде, или не будет делителя, если его не использует программист. Под программистом я подразумеваю себя, так как других программистов данного контроллера не существует (принцип «не использовать тем, кто не использует»). По блочной памяти для ОЗУ данных и ПЗУ команд: минимум два блока, максимум вся блочная память кристалла.
Максимальная частота работы высчитывается только опытным путём, под конкретную архитектуру. Один из моих 24 разрядных контроллеров не работал на частоте 48 Mhz в Spartan2, на частоте 40Mhz заработал. В Spartan6 на сотне Mhz работал 32 разрядный. А может и на паре сотен заработает, ничего в нём такого сложного нет.
Рис. 5. Вот такой код увидит процессор. Обратите внимание, одна команда FORTH – это одна команда ядра процессора – один системный такт.
Первый PS: упомяну одну возможность сопроцессора – фильтр. Обычный БИХ фильтр. Но вот, что необычного в нём, так это то, что он использует плавающую точку. Не то что б в этом был какой-то смысл, просто прочитал какую-то книжку про форму представления чисел, и решил: ерунда интересна эта ваша плавающая точка, сделаем.
Рис. 6. Моделирование результатов работы фильтра. Высчитывается логика кода VHDL с точностью до битика.
Так же были планы по превращения проекта в систему на кристалле: добавление различных, писанных на VHDL блоков в периферию контроллера. И вроде не так уж и сложно, но запал иссяк. Дурные мысли меня покинули, и бродят где-то между программистами и электронщиками. И зовут их теперь Стартапами.
Так что, если к вам кто-то придёт и представится Стартапом, подумайте, а не скрывается ли под ним какой-нибудь Времяубиватель.
И ещё одно PS: С другой стороны, за время работы над проектом Stackcpu я неплохо подучил три два языка программирования:
1. FORTH, точнее некое его подмножество, необходимое для проверки работоспособности контроллера.
2. С++, на чистом С было бы тяжело написать среду разработки и компилятор. 3. VHDL, именно буквы этого языка и есть контроллер.
Сделаем простой AVR микроконтроллер
Меня часто спрашивают: «Чем отличается микроконтроллер от ПЛИС?» Ну что тут можно ответить? Это как бы разные вещи. Микропроцессор последовательно выполняет команды, описанные в его программе. Работа ПЛИС в конечном счете определяется принципиальной электрической схемой, реализованной внутри чипа. Архитектура микроконтроллера, то есть тип процессора, количество портов ввода вывода, интерфейсы, определяется производителем. Микросхема микроконтроллера изготовлена на заводе и изменить ее нельзя. Можно только написать программу, которую он будет исполнять. ПЛИС — это свобода для творчества. Архитектура реализуемого устройства может быть почти любая, лишь бы поместилась вся логика в чип. В ПЛИС можно, например, попробовать реализовать даже и микроконтроллер! Попробуем?
Один из самых распространенных микроконтроллеров — это 8-ми разрядные RISС процессоры семейства AVR компании Atmel. В этой статье я расскажу как реализовать «почти» совместимый с AVR микроконтроллер внутри нашей ПЛИС на плате Марсоход.
Прежде, чем начинать делать свою реализацию микроконтроллера, конечно, следует изучить внутренности контроллера AVR. Нужно как минимум знать систему команд микропроцессора AVR. На нашем сайте можно скачать его описание:
Кроме триггеров и логики в нашей ПЛИС имеется последовательная флеш память UFM объемом 512 слов по 16 бит. В этой флеш памяти мы будем хранить программу микроконтроллера. Удобно, что слова, хранимые во флеш, имеют разрядность 16. Все команды процессора AVR также шестнадцатиразрядные. Кое-что про UFM мы уже писали на нашем сайте. У нас был проект для ПЛИС платы Марсоход, который выполнял чтение из UFM памяти.
«Оперативной памяти» в нашей ПЛИС нет. Ну значит не будет памяти у нашего микроконтроллера, жаль но это нас не остановит.
У микроконтроллера AVR имеется 32 восьмиразрядных регистра общего назначения. Нижняя группа регистров r0-r15 может быть использована только в командах с операндами-регистрами. Верхняя группа регистров r16-r31 может использоваться в командах и с непосредственными операндами. Поскольку места внутри нашего чипа на плате Марсоход действительно не много, нам придется реализовать только некоторые регистры. Это довольно существенное ограничение, и его нужно будет учитывать при написании программ для нашего микроконтроллера.
Мы реализуем только 7 регистров: r16-r22:
- Первые 4 регистра r16. r19 — это просто регистры.
- Регистр r20 — это тоже обычный регистр, только его биты мы подключим к 8-ми светодиодам платы Марсоход.
- Регистр r21 — это тоже обычный регистр, но его биты мы подключим к выводам управления шаговых двигателей на плате Марсоход.
- Регистр r22 — только для чтения. К нему подключены входы от 4-х кнопочек платы Марсоход.
Схема нашего микроконтроллера создана в среде Altera QuartusII и выглядит вот так (нажмите на картинку, чтобы увеличить):
Наш микроконтроллер работает по простому алгоритму:
- Считывает из флеш памяти UFM очередную команду.
- Декодирует команду и выбирает для нее нужные операнды из регистров или непосредственно из кода команды.
- Выполняет команду в арифметико-логическом устройстве.
- Запоминает результат исполнения команды в регистре приемнике, определяемом командой.
- Переходит к исполнению следующей команды.
У нас сейчас нет цели сделать высокопроизводительный микроконтроллер, мы не будем делать конвейерную обработку данных. Это объясняется тем, что команды из флеш памяти чипа мы можем считывать только в последовательном формате, то есть на чтение одной команды нужно как минимум 16 тактов. Быстрее здесь сделать нельзя (да нам и не нужно сейчас).
Ход выполнения программы может изменяться в зависимости от результата исполнения команд. Специальные команды переходов позволяют переходить к нужной операции в нужных условиях.
Перечислим команды микроконтроллера AVR, которые мы собираемся реализовать:
ADD 0000 11rd dddd rrrr
SUB 0001 10rd dddd rrrr
AND 0010 00rd dddd rrrr
EOR 0010 01rd dddd rrrr
OR 0010 10rd dddd rrrr
MOV 0010 11rd dddd rrrr
CP 0001 01rd dddd rrrr
LSR 1001 010d dddd 0110
SUBI 0101 KKKK dddd KKKK
ANDI 0111 KKKK dddd KKKK
ORI 0110 KKKK dddd KKKK
CPI 0011 KKKK dddd KKKK
LDI 1110 KKKK dddd KKKK
BREQ 1111 00kk kkkk k001
BRNE 1111 01kk kkkk k001
BRCS 1111 00kk kkkk k000
BRCC 1111 01kk kkkk k000
Слева написаны названия команд, а справа — их бинарное представление (кодирование). Так буква «r» обозначает регистр источник, буква «d» — регистр приемник, «K» — это непосредственно операнд.
Конечно — это только малая часть от «настоящей системы команд», но уже и эти команды позволять писать вполне работающие программы.
У нас будет упрощенное АЛУ (Арифметико-Логическое Устройство). Оно реализует только некоторые, наиболее употребительные команды, а так же всего 2 флага для условных переходов: «Z» и «C».
Флаг «Z» устанавливается, если результат АЛУ это ноль. Если результат из АЛУ не нулевой, то флаг «Z» сбрасывается. Флаг «C» устанавливается при возникновении переноса в арифметических операциях ADD и SUB/SUBI или сравнения CP/CPI. Флаги влияют на исполнение команд условных переходов: флаг «Z» влияет на BREQ, BRNE, а флаг «C» влияет на BRCS, BRCC.
Вообще всеь проект мы уже реализовали и его можно взять здесь:
.
Исходный текст нашего ядра AVR написан на языке Verilog и его можно посмотреть здесь.
Теперь посмотрим, как мы сможем написать программу для нашего микроконтроллера? Для написания программы на языке ассемблер воспользуемся средой разработки компании Atmel AVRStudio4. Эту среду разработки можно скачать прямо с сайта компании Атмел (после регистрации), вот здесь. Или поищите в яндексе — наверняка найдете в свободном доступе.
Создаем проект в AVRStudio4 и пишем простую программу. Программа будет моргать светодиодом на плате Марсоход и опрашивать состояние нажатых кнопочек. Если нажать одну кнопочку, то моргающий светодиод «побежит» в одну сторону, а если нажать другую кнопочку, то светодиод «побежит» в другую сторону. Вот исходный текст на ассемблере для нашего примера:
.include «1200def.inc»
.device AT90S1200
;initial one bit in register
ldi r16,$80
;read port (key status)
mov r17,r22
cpi r17,$0f
;go and blink one LED if no key pressed
breq do_xor
cpi r17,$0e
;go and right shift LEDs if key[0] pressed
breq do_rshift
cpi r17,$0d
;go and left shift LEDs if key[1] pressed
breq do_lshift
;jump to read keys
or r16,r16
brne rd_port
do_rshift:
cpi r16,1
breq set80
lsr r16
mov r20,r16
brne pause
set80:
ldi r16,$80
mov r20,r16
or r16,r16
brne pause
do_lshift:
cpi r16,$80
breq set1
lsl r16
mov r20,r16
brne pause
set1:
ldi r16,$01
mov r20,r16
or r16,r16
brne pause
do_xor:
eor r20,r16
pause:
ldi r18,$10
cycle2:
ldi r19,$FF
cycle1:
or r19,r19
or r19,r19
subi r19,1
brne cycle1
subi r18,1
brne cycle2
or r16,r16
brne rd_port
Видите? Чтение состояния кнопочек — это чтение из регистра r22. Изменение состояния светодиодов — это запись в регистр r20.
Настройте AVRStudio так, что бы выходной формат был «Generic». Это в свойствах проекта, «Assembler Options», настройка «Hex Output Format».
После компиляции программы получается вот такой текстовый файл с кодами программы:
Этот файл нам почти подходит для QuartusII. В нашем проекте для ПЛИС есть файл avr_prog.mif (Memory Initialization File), куда мы и вставляем полученный из AVRStudio код (только нужно добавить точку с запятой в конце каждой строки). Таким образом, после компиляции QuartusII эти коды попадут во флеш UFM нашей ПЛИС.
Теперь можно компилировать и пробовать наш проект в плате Марсоход. Вот видеоролик, демонстрирующий работоспособность нашего процессора:
Все работает так как и задумывалось!
Обратите внимание, что после компиляции, весь проект занимает только 205 логических элемента из 240 имеющихся в нашей ПЛИС. Это значит, что наш микроконтроллер можно и дальше усложнять или добавить какую-то новую логику. Так что проект может быть полезен для создания Ваших устройств.
Как сделать свой микроконтроллер?
Давно хотел сделать свой микроконтроллер. Но не знаю как и с чего начать. Искал в интернете, но ничего не нашёл.
Если кто знает что, помогите. Я изучаю arduino. Уже собирал несколько схем. Подключал различные модули и т. д.
![]()
![]()
Сделать собственный микроконтроллер не так сложно, как кажется. Но для отладки точно потребуется осциллограф.
Возьмите плату ПЛИС, например, производства Terasic, и изучайте Verilog и VHDL.
Возможно, Вам будет небезынтересно ознакомиться с моими собственными статьями по этой теме.
![]()
Именно микроконтроллер у вас навряд ли получится сделать, так как МК — это процессор + периферия. А что бы сделать процессор очень рекомендую почитать Харрис и Харрис «Цифровая схемотехника и архитектура компьютера». Есть в свободном доступе, например, http://easyelectronics.ru/files/Book/digital-design-and-computer-architecture-russian-translation.pdf Книга отличная! Все дается с азов, а заканчивается она разработкой процессора с набором команд MIPS.
Надо взять ассемблер AVR и реализовать все команды в виде машины состояний, а также регистры, указатель стека, память, регистры ввода-вывода (в адресах памяти), контроллер прерываний и переферию (в начале можно без этого).
Для написания и тестирования не нужны даже платы, вся отладка должна идти на симуляторе (ModelSim, QuestaSim), только так можно выявить и исправить все ошибки.
Писать нужно синтезируемые конструкции, т.е. прогонять проект через синтизатор. Тестирование должно заключаться в том, чтобы программа откомпилированная под AVR работала точно также как на микроконтроллере. HDL позволяет её загрузить из файла в память процессора.
Даже самый простой стандартный микроконтроллер займет достаточно много времени для реализации. Но можно следать и свое ALU на минимальное количество команд, тогда придется написать не большой свой компилятор для своего ассемблера или генератор команд в тестбенче.
