МАРСОХОД

Open Source Hardware Project

Проекты Altera Quartus II для платы Марсоход

Сделаем простой AVR микроконтроллер

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

Один из самых распространенных микроконтроллеров - это 8-ми разрядные RISС процессоры семейства AVR компании Atmel. В этой статье я расскажу как реализовать "почти" совместимый с AVR микроконтроллер внутри нашей ПЛИС на плате Марсоход.

Прежде, чем начинать делать свою реализацию микроконтроллера, конечно, следует изучить внутренности контроллера AVR. Нужно как минимум знать систему команд микропроцессора AVR. На нашем сайте можно скачать его описание: icon Система команд микроконтроллера AVR (686.82 Кбайт) Мы не будем ставить себе целью полностью повторить поведение чипа Atmel, мы хотим сделать наш микропроцессор лишь частично совместимым. Полностью повторить можно, но нужна ПЛИС гораздо большего объема. У нас на плате Марсоход стоит CPLD EPM240T100C5, значит у нас есть всего-навсего 240 триггеров и логических элементов.

Кроме триггеров и логики в нашей ПЛИС имеется последовательная флеш память 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 и выглядит вот так (нажмите на картинку, чтобы увеличить):

схема ядра микроконтроллера AVR для платы Марсоход


Наш микроконтроллер работает по простому алгоритму:

  1. Считывает из флеш памяти UFM очередную команду.
  2. Декодирует команду и выбирает для нее нужные операнды из регистров или непосредственно из кода команды.
  3. Выполняет команду в арифметико-логическом устройстве.
  4. Запоминает результат исполнения команды в регистре приемнике, определяемом командой.
  5. Переходит к исполнению следующей команды.

У нас сейчас нет цели сделать высокопроизводительный микроконтроллер, мы не будем делать конвейерную обработку данных. Это объясняется тем, что команды из флеш памяти чипа мы можем считывать только в последовательном формате, то есть на чтение одной команды нужно как минимум 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.

Вообще всеь проект мы уже реализовали и его можно взять здесь: icon Ядро микропроцессора Atmel AVR (107.02 Кбайт).
Исходный текст нашего ядра AVR написан на языке Verilog и его можно посмотреть здесь.

Теперь посмотрим, как мы сможем написать программу для нашего микроконтроллера? Для написания программы на языке ассемблер воспользуемся средой разработки компании Atmel AVRStudio4. Эту среду разработки можно скачать прямо с сайта компании Атмел (после регистрации), вот здесь. Или поищите в яндексе - наверняка найдете в свободном доступе.

avrstudio


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


.include "1200def.inc"
.device AT90S1200

.cseg
.org 0

start:

;initial one bit in register
ldi    r16,$80

rd_port:

;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".
После компиляции программы получается вот такой текстовый файл с кодами программы:


000000:e800
000001:2f16
000002:301f
000003:f0c1
000004:301e
000005:f021
000006:301d
000007:f059
000008:2b00
000009:f7b9
00000a:3001
00000b:f019
00000c:9506
00000d:2f40
00000e:f471
00000f:e800
000010:2f40
000011:2b00
000012:f451
000013:3800
000014:f019
000015:0f00
000016:2f40
000017:f429
000018:e001
000019:2f40
00001a:2b00
00001b:f409
00001c:2740
00001d:e120
00001e:ef3f
00001f:2b33
000020:2b33
000021:5031
000022:f7e1
000023:5021
000024:f7c9
000025:2b00
000026:f6d1


Этот файл нам почти подходит для QuartusII. В нашем проекте для ПЛИС есть файл avr_prog.mif (Memory Initialization File), куда мы и вставляем полученный из AVRStudio код (только нужно добавить точку с запятой в конце каждой строки). Таким образом, после компиляции QuartusII эти коды попадут во флеш  UFM нашей ПЛИС.

Теперь можно компилировать и пробовать наш проект в плате Марсоход. Вот видеоролик, демонстрирующий работоспособность нашего процессора:

Все работает так как и задумывалось!
Обратите внимание, что после компиляции, весь проект занимает только 205 логических элемента из 240 имеющихся в нашей ПЛИС. Это значит, что наш микроконтроллер можно и дальше усложнять или добавить какую-то новую логику. Так что проект может быть полезен для создания Ваших устройств.

 

Комментарии  

0 #33 nckm 09.01.2017 05:58
Цитирую Nenad:
Можно ли получить этот проект сделано в max10 FPGA?

можно попробовать собрать его для max10
0 #32 Nenad 06.01.2017 00:44
Можно ли получить этот проект сделано в max10 FPGA?
-1 #31 Artur 24.12.2013 10:42
можно попробовать заменить (posedge clk) на (posedge clk or negedge clk) в правильных местах. Это иногда помогает ускорить работу плиски
0 #30 nckm 19.09.2013 13:05
Цитирую Колян:
А реализация на Parallel вообще возможна? При наличии не внутренней последовательной, а внешней параллельной памяти.

надо пробовать, хватит ли места в ПЛИС платы Марсоход?
0 #29 Колян 18.09.2013 16:38
А реализация на Parallel вообще возможна? При наличии не внутренней последовательно й, а внешней параллельной памяти.
0 #28 user 06.02.2013 23:58
убого сделаны каменты. по 10 раз повторяются.
0 #27 nckm_ 11.01.2012 05:07
Цитирую Александр:
Можно узнать, почему в случае перехода счетчик команд изменяется так:ip < = ip + {opcode[9],opcode[9],opcode[9:3]}; а не так ip < = ip + {opcode[9:3]};

вероятно это распространение знака - смещение может быть положительное (переход вперед) и отрицательное (переход назад)
0 #26 Александр 09.01.2012 22:43
Можно узнать, почему в случае перехода счетчик команд изменяется так:ip < = ip + {opcode[9],opco de[9],opcode[9: 3]}; а не так ip < = ip + {opcode[9:3]};
0 #25 Александр 09.01.2012 22:42
Можно узнать, почему в случае перехода счетчик команд изменяется так:
ip
0 #24 Александр 09.01.2012 22:39
Можно узнать, почему в случае перехода счетчик команд изменяется так:ip
0 #23 Александр 09.01.2012 22:37
Можно узнать, почему в случае перехода счетчик команд изменяется так:
ip
0 #22 Александр 01.01.2012 15:17
С новым годом!
Я немного доделал проект, как мне кажется процессор правильно переходит по инструкциям прерывания, но светодиодик в итоге так и не мигает... :-|
http://narod.ru/disk/36192334001/DE1_CPU.rar.html
0 #21 nckm 12.12.2011 05:18
Цитирую Александр:
Я использую ROM из megawizard, тут проект целиком.
http://narod.ru/disk/33962772001/DE1_CPU.rar.html

я постараюсь на днях посмотреть
0 #20 Александр 11.12.2011 09:06
Я использую ROM из megawizard, тут проект целиком.
http://narod.ru/disk/33962772001/DE1_CPU.rar.html
0 #19 Александр 10.12.2011 11:17
У меня cyclone II на плате DE1)
0 #18 Александр 10.12.2011 11:16
У меня cyclone II на плате DE1
0 #17 nckm 10.12.2011 04:23
Цитирую Александр:
Добрый день.
А можете помочь переделать модуль для работы с "обычной" памятью имеющей порты addr[8..0] data[15..0]. пробовал самостоятельно но пока не получилось, запутался в сигналах opcode_ready и fix_result, я приравнивал opcode_ready=1, а fix_result = need_jump; но наверное нужно где то поставить задержку.. :sad:

http://narod.ru/disk/33810203001/rAVR.v.html

А вы где это пробовать собираетесь? Какаю микросхему памяти выбрали? Или это будет использовано в фпга с внутренними блоками памяти?
0 #16 Александр 09.12.2011 11:32
Добрый день.
А можете помочь переделать модуль для работы с "обычной" памятью имеющей порты addr[8..0] data[15..0]. пробовал самостоятельно но пока не получилось, запутался в сигналах opcode_ready и fix_result, я приравнивал opcode_ready=1, а fix_result = need_jump; но наверное нужно где то поставить задержку.. :sad:

http://narod.ru/disk/33810203001/rAVR.v.html
0 #15 nckm 08.12.2011 07:21
Цитирую Александр:
прошил плату горит 1 светодиод) у вас проект точно рабочий?))
сегодня проверил проект - что-то странное.. при компилировании его 10м Quartus II проект работает, если 11м - нет. Мистика какая-то. Попробую разобраться.
0 #14 nckm 07.12.2011 18:34
Цитирую Александр:
прошил плату горит 1 светодиод) у вас проект точно рабочий?))

хм.. был точно рабочий! ну завтра еще раз скачаю прошью попробую..
0 #13 Александр 07.12.2011 16:13
прошил плату горит 1 светодиод) у вас проект точно рабочий?))
0 #12 nckm 25.12.2010 19:24
Цитирую pomazan-all.ru:
Странно сделано как то!

А чего странно-то?
0 #11 pomazan-all.ru 25.12.2010 19:11
Странно сделано как то!
0 #10 nckm 23.12.2010 14:40
Цитирую Gudd-Head:
Эммм... А зачем такой изврат?

ну это дело принципа :-)
-1 #9 Gudd-Head 23.12.2010 10:10
Эммм... А зачем такой изврат?
+2 #8 Henolala 21.11.2010 22:47
Дико суперский сайт и пост. Однозначно сайт в закладки
0 #7 mark 03.10.2010 20:54
:cry: хотел бы я так же умничать как Вы 8)
0 #6 Николай 30.05.2010 16:12
Цитирую Arrest:
Цитирую Николай:
Цитирую Arrest:
Зачем вы использовали UFM с interface: none? если бы вы выбрали Parallel, то тогда можно было бы упростить дурацкий ввод инструкций и не тратить по шестнадцать тактов только на то, чтобы вытащить команду из памяти.

хм.. действительно можно было бы использовать Parallel, только это не улучшит ситуацию. Все равно внутреннее представление UFM - serial flash и использование Parallel только "замаскировало" бы этот факт, все равно доступ быстрее 16 тактов не сделать

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

С этой точки зрения наверное Вы правы
0 #5 Arrest 30.05.2010 13:03
Цитирую Николай:
Цитирую Arrest:
Зачем вы использовали UFM с interface: none? если бы вы выбрали Parallel, то тогда можно было бы упростить дурацкий ввод инструкций и не тратить по шестнадцать тактов только на то, чтобы вытащить команду из памяти.

хм.. действительно можно было бы использовать Parallel, только это не улучшит ситуацию. Все равно внутреннее представление UFM - serial flash и использование Parallel только "замаскировало" бы этот факт, все равно доступ быстрее 16 тактов не сделать

Ну хотя бы не было этого крайне странного процесса считывания данных из памяти, и то хлеб.
0 #4 Николай 30.05.2010 11:42
Цитирую Arrest:
Зачем вы использовали UFM с interface: none? если бы вы выбрали Parallel, то тогда можно было бы упростить дурацкий ввод инструкций и не тратить по шестнадцать тактов только на то, чтобы вытащить команду из памяти.

хм.. действительно можно было бы использовать Parallel, только это не улучшит ситуацию. Все равно внутреннее представление UFM - serial flash и использование Parallel только "замаскировало" бы этот факт, все равно доступ быстрее 16 тактов не сделать
0 #3 Arrest 29.05.2010 16:14
Зачем вы использовали UFM с interface: none? если бы вы выбрали Parallel, то тогда можно было бы упростить дурацкий ввод инструкций и не тратить по шестнадцать тактов только на то, чтобы вытащить команду из памяти.
+2 #2 Николай 26.05.2010 18:38
Цитирую qweqwe:
Что-то странное у вас написано. Команда LSL в процессоре не реализована, а в тексте программы используется.
Как так?

Ах это... Ну все правильно: команда LSL это та же команда ADD, если оба регистра и источник и приемник один и тот же. Компилятор генерирует ADD, которая реализована.
0 #1 qweqwe 26.05.2010 17:07
Что-то странное у вас написано. Команда LSL в процессоре не реализована, а в тексте программы используется.
Как так?

Добавить комментарий


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Проекты Проект Марсоход Сделаем простой AVR микроконтроллер