Найти в Дзене
Универ на диване

Знакомимся с ПЛИС

Здравствуйте, Дорогие друзья! Сегодня мы с Вами продолжим изучать программирование. Как я уже говорил, мы будем двигаться во всех направлениях, то есть начнем с освоения языков и постепенно перейдём к написанию полноценных программ. При этом будут осваиваться ВСЕ языки программирования, которые могут Вам пригодиться в жизни. Сегодня мы запрограммируем «железяку», проще говоря, в этой статье я покажу Вам что такое отладочные платы и как с ними работать. Самыми распространенными являются, конечно же платы типа Ардуино, все Вы о них слышали. Эти платы построены на базе контроллеров Atmega и могут быть использованы для управления всем (ну почти всем): на их базе возможно построит генератор частот, модуль управления, «мозги» для робота и т.д. В общем, штука полезная.

Рисунок 1 – Arduino UNO
Рисунок 1 – Arduino UNO

Конечно же мы поработаем и с этой платой, но не сегодня. Существуют микроконтроллеры, микропроцессоры и многие – многие другие элементы обработки сигналов. К таким элементам также можно отнести ПЛИС (программируемые логические интегральные схемы). Не стоит пугаться такого названия – это всего лишь куча логических элементов в одной микросхеме с программируемыми ключами (переключателями). Сегодня о программировании ПЛИС и поговорим. У меня в наличии имеется отладочная плата на базе ПЛИС EP4CE6Е22C8N производства фирмы Altera.

Рисунок 2 – Отладочная плата на базе ПЛИС
Рисунок 2 – Отладочная плата на базе ПЛИС

Не будем рассматривать характеристики этой схемы, по крайней мере пока что, так как наша сегодняшняя задача – изучить базовые принципы программирования таких схем. Существует несколько языков программирования для ПЛИС, одни из них популярны, другие нет. Мы будем изучать популярные, а именно: Verilog и VHDL. Кто бы что ни говорил, а эти языки друг на друга не похожи. Verilog подойдёт для любителей полного анализа кода, VHDL– для тех, кому нравится работать с библиотеками, как по мне: Verilog намного легче, чем VHDL. Ну да ладно, давайте знакомиться с этими языками. Программировать мы будем в Quartus – специальном компиляторе для ПЛИС. Начнем с самого простого: построим на базе отладочной платы пять логических элементов: буфер, инвертор, элементы И, ИЛИ и XOR. Насколько я помню, с логикой Вас я еще не знакомил. Итак, логические элементы легче всего представлять в виде «черных ящиков», внутрь которых мы не заглядываем, но знаем что при таком-то уровне на входе этих ящиков, на их выходе появляются другие. Эххх... Начнем самую сложную часть программирования, к слову оно может быть аппаратным и программным (тавтология рулит). В первом случае мы ручками запаиваем контакты в микросхеме, а при втором – за нас это делает программа (объяснение не очень верное, но на первое время сгодится). Все мы слышали, что в электронных модулях информация представлена в виде нулей и единиц (про q-биты забудем). И это самое простое, что можно придумать для хранения и передачи информации: участок схемы заряжен - единичка, заряда нет – ноль. Но в дебри пока не лезем. Для нас все компоненты – черные ящики. Рассмотрим те, которые мы будем сегодня моделировать. Буфер. Что это такое? Обычная перемычка. То есть то, что приходит на его вход не изменяясь появляется на выходе. Любой логический элемент можно охарактеризовать таблицей истинности (то есть таблицей, в которой показано, что появляется на выходе элемента в зависимости от значения на его входе). Для буфера эта табличка самая простая: что на входе, то и на выходе:

-3

Переходим к инвертору. Этот элемент инвертирует значение, как понятно из его названия. То есть, если на вход приходит 1, то на выходе он выдает 0; если на вход приходит 0, то на выходе появится 1. Всё просто.

-4

На схеме инвертор показан как буфер с маленьким кружочком на выходе. Также этот элемент можно называть логическим НЕ или логическим отрицанием. Запомнить его таблицу истинности не сложно: у нас всего два значения, 0 и 1. На вход пришёл 0, на выходе НЕ 0, то есть 1; на вход пришёл 1, на выходе НЕ 1, то есть 0.

Идём дальше: элемент И. У этого элемента много названий. Для нас он должен ассоциироваться с умножением. У этого элемента два входа (в самом простом случае) и один выход. То, что переходит на входы элемента. Внутри у него перемножается, на выходе мы получаем результат этого перемножения. Смотрим таблицу истинности элемента И.

-5

Мы видим, что пока хотя бы на одном из входов элемента будет 0, на выходе тоже будет 0. Единица на выходе появится только в том случае, когда на обоих входах будет единица. Как запомнить таблицу истинности?

0*0=0

0*1=0

1*0=0

1*1=1

Запомнили? Переходим к элементу ИЛИ. У него также много названий. Но нам нужно запомнить его как сумму. Если на одном из входов этого элемента будет единица, то и на выходе будет единица, то есть:

0+0=0

0+1=1

1+0=1

1+1=1

Соответственно и таблица истинности:

-6

Ну а теперь самое интересное: элемент XORили сумматор по модулю 2. Представьте, что элемент состоит из нескольких элементов и операция суммирования по модулю два протекает в два этапа: переменная с одного из входов инвертируется и перемножается с переменной со второго входа, одновременно с этим, инвертируется переменная со второго входа и перемножается с переменной с первого входа. Полученные сигналы подаются на входы элемента ИЛИ. Давайте рассмотрим схему, которая реализует элемент XOR.

-7

Теперь будет понятнее (восприниматься информация станет легче). Итак, пусть на оба входа элемента XOR пришли нули. Пройдём по шагам:

1. при инверсии первого значения получится единица

2. при перемножении инвертированного первого числа и второго числа получится ноль

3. при инверсии второго числа получится единица

4. при перемножении инвертированного второго числа и неинвертированного первого получится ноль

5. на выходе элемента ИЛИ получится ноль

-8

То есть при нулях на обоих входах элемента XOR, на его выходе будет также ноль.

Пусть теперь на первый вход элемента придет единица, а на второй ноль. Тогда:

1. при инвертировании первого числа получим ноль

2. при перемножении инвертированного первого числа со вторым получим ноль

3. при инвертировании второго числа получим единицу

4. при перемножении инвертированного второго числа с неинвертированным первым получим единицу

5. на выходе элемента ИЛИ получим единицу

То есть если на одном из входов элемента XOR1, а на другом 0, то на выходе 1:

-9
-10

Теперь рассмотрим случай, когда на обоих входах элемента единицы:

1. при инверсии первого числа получаем ноль

2. при перемножении инвертированного первого числа на второе получаем ноль

3. при инвертировании второго числа получаем ноль

4. при перемножении инвертированного второго числа с неинвертированным первым получаем ноль

5. на выходе элемента ИЛИ получаем ноль

-11

Таким образом, таблица истинности для XOR выглядит следующим образом:

-12

Ну а теперь, собственно, после такого краткого введения можем перейти к Verilog. Запускаем Quartus:

Рисунок 3 – Запуск Quartus
Рисунок 3 – Запуск Quartus

И перед нами открывается стартовая страница:

Рисунок 4 – Стартовая страница
Рисунок 4 – Стартовая страница

Смело закрываем это окошко и заходим в меню File:

Рисунок 5 – Заходим в меню Файл
Рисунок 5 – Заходим в меню Файл

Выбираем New. Откроется следующее окошко, в котором выбираем New Quartus II Project и нажимаем ОК:

Рисунок 6 – Выбираем что создать
Рисунок 6 – Выбираем что создать

Этим действием мы создаем новый проект. В открывшемся окне нажимаем Next:

Рисунок 7 – Окно Introduction
Рисунок 7 – Окно Introduction

В открывшемся окне вводим название директории (папки, в которой будет храниться наша программа) и название файла:

Рисунок 8 – Называем наш файл
Рисунок 8 – Называем наш файл

Нажимаем Next. И еще раз Next:

Рисунок 9 – Еще раз жмем Next
Рисунок 9 – Еще раз жмем Next

Откроется вот такое окно:

Рисунок 10 – Окно выбора микросхемы отладочной платы
Рисунок 10 – Окно выбора микросхемы отладочной платы

В этом окне в поле Family выбираем наше семейство (циклон). У моей платы это . В поле Name filter вводим название нашей микросхемы. Выбираем в поисковой строке нашу микросхему и жмем Next:

Рисунок 11 – Выбираем нашу микросхему
Рисунок 11 – Выбираем нашу микросхему

И ещё раз жмём Next:

Рисунок 12 – Последнее окно подтверждения
Рисунок 12 – Последнее окно подтверждения

Ииии... Финиш:

Рисунок 13 – А нет... не последнее
Рисунок 13 – А нет... не последнее

Затем снова нажимаем File -> New.

Рисунок 14 – Создаем новый файл
Рисунок 14 – Создаем новый файл

Выбираем Verilog HDL File и нажимаем ОК:

Рисунок 15 – Выбираем тип файла
Рисунок 15 – Выбираем тип файла

В результате откроется созданный нами файл для написания Verilog-кода:

Рисунок 16 – Созданный файл
Рисунок 16 – Созданный файл

Перейдём непосредственно к написанию программы.

Любая программа в Verilog начинается с обозначения блока. В поле программы пишем module Имя блока (переменные).

-27

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

-28

Теперь нужно написать код для каждого элемента. Для того чтобы долго не мудрить над простой схемой воспользуемся ключевым словом assign (присвоить). С помощью этого ключевого слова мы создадим условие для портов выхода. Для того, чтобы написать код нам нужно знать, что в Verilog мы можем обозначать логические операции как функцией (or, and и т.д.), так и символами. Для наших операций: = означает обычное присвоение результата, ~ будет означать инверсию, | означает ИЛИ, а & означает операцию И, элементу XOR будет соответствовать знак ^. Пишем наш код:

-29

А теперь важная вещь: помните, как в одной из статей мы с Вами говорили о дескрипторах html? Здесь похожая история: некоторые ключевые слова требуют заключающих ключевых слов. Так ключевое слово module подразумевает наличие в конце модуля ключевого слова endmodule. То есть нам нужно показать, что наш модуль закончился.

-30

Теперь нужно скомпилировать наш код, то есть перевести с языка, понятного нам на язык, понятный машине. Для этого нажимаем кнопку Start Compilation на панели инструментов.

-31

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

-32

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

-33

Выбираем пункт Pin Planner. Откроется окно, в котором будет отображаться наша микросхема, а также табличка соответствия портов в микросхеме и портов в программе:

-34

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

-35

Указываем соответствующие порты в табличке:

-36

Теперь нужно назначить выходы. В качестве выходов мы будем использовать светодиоды. На нашей плате они присутствуют. Выберем диоды D3, D4, D5, D6 и D14.

-37
-38

Теперь закрываем окно Pin Plannerи еще раз запускаем компиляцию. После успешного завершения компиляции подключаем нашу плату к компьютеру. Для связи с компьютером мы будем использовать стандартный USB-бластер от Altera.

-39

Запитывать нашу плату мы будем с помощью такого блока:

-40

Я взял его из комплекта от неисправного роутера. Итак, БП в DC– разъем, бластер – в Jtag– порт. Затем, вставляем USB в порт компьютера, ну а БП соответственно в розетку. И наша плата тут же оживает:

-41

Теперь нужно прошить нашу микросхему. Нажимаем кнопку Programmer на панели инструментов:

-42

В открывшемся окне мы видим, что программа автоматически «подхватила» нашу отладочную плату:

-43

И более того, нужный для загрузки скомпилированный файл уже готов к загрузке. Нажимаем Start. Окно Progressпоказало, что плата прошита:

-44

А на самой плате загорелись три диода: один системный и два периферийных:

-45

Это соответственно диоды, назначенные выходами элементов НЕ и XOR. Проверим, так ли это? Горят светодиоды D3 и D6. Им соответствуют порты inv_out и xor_out. Понятно, почему на выходе инвертора горит «единица», но почему она горит на выходе XOR? Это можно считать парадоксом, но многие производители делают так, что значения на выходе этого элемента инвертированы относительно его таблицы истинности, то есть низкому уровню соответствует единица, а высокому – ноль. Давайте проверим, корректно ли работает наша программа.

Как видим, всё работает правильно.

На этом, пожалуй всё. Сегодня я вкратце ознакомил Вас с некоторыми логическими элементами. Также мы начали изучение очень интересного раздела программирования, а именно работу с ПЛИС. Надеюсь было интересно и этой статьей я чем-нибудь Вам да помог. Спасибо, что читаете! Если Вы еще не подписаны на мой канал – обязательно это исправьте. Дальше будет еще интереснее. Удачи в учебе и труде! Жду в гости на моем канале!