Vhdl что это такое
Перейти к содержимому

Vhdl что это такое

  • автор:

VHDL для начинающих. Brainfuck

Как известно, VHDL – высокоуровневый язык описания аппаратуры (если это вызывает сомнения, можно посмотреть здесь и здесь). Из всего разнообразия задач мне приглянулся именно brainfuck благодаря лёгкости в реализации с одной стороны и волшебству создания программируемого (пусть и весьма ограниченно) вычислителя с другой.
В рамках данной статьи я не буду углубляться в дебри синтаксиса и настройки среды, сконцентрировавшись на реализации конкретной задачи.
Испытательным стендом будет Altera Cyclone II Starter Kit (EP2C20F484C7)

Любителей мигающих лампочек прошу под кат.

Техническое задание

  1. Память команд — 64 команды, ячейки памяти — 32 ячейки по 8 бит каждая;
  2. Устройство должно поддерживать два режима: занесения программы и выполнения; смена режима должна осуществляться при помощи переключателя SW9;
  3. В режиме занесения программы переключатели SW8 – SW3 определяют адрес в памяти программ, SW2 – SW0 — код команды; запись в память осуществлятся при нажатии кнопки KEY3; содержимое текущей ячейки памяти отображается на свтодиодах LEDR2 – LEDR0;
  4. В режиме выполнения программы значения ячеек памяти должны отображаться на семисегментных индикаторах HEX1 – HEX0; адрес отображаемой ячейки должен задаваться при помощи переключателей SW4 – SW0;
  5. В любом режиме работы по нажатию кнопки KEY3 значения всех ячеек памяти должны обнуляться.

Разметка фронта работ

Проект в Quartus II создан, самое время определиться с набором entity. Я решил не выделяться и, пусть это и не очень красиво, реализовать всё в одной сущности. Для вывода на семисегментные индикаторы понадобится специальный дешифратор, который выделим в отдельный entity.

Реализация

Дешифратор можно реализовать сразу, «в лоб». Он выполняет простейшее преобразование, задаваемое таблично, поэтому портов всего два:

entity dc7x is port( i: in std_logic_vector(3 downto 0); z: out std_logic_vector(6 downto 0) ); end dc7x; 

Модель поведения задаётся просто:

with i select z  

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

Порты

Задействованные внешние устройства ввода и вывода показаны на рисунке:

Как видно, нужен доступ к тумблерам SW, кнопкам KEY, светодиодам LED и семисегментникам HEX. Сигнал синхронизации будет вырабатывать внутренний генератор 50Mhz.

entity brainfuck is port( RUN: in std_logic; SW: in std_logic_vector(8 downto 0); LED: out std_logic_vector(2 downto 0); HEX1: out std_logic_vector(6 downto 0); HEX2: out std_logic_vector(6 downto 0); clk: in std_logic; RESET: in boolean ); end brainfuck; 

RUN — тот самый переключатель режима работы SW9, RESET — кнопка KEY3.

Архитектура

Нам понадобится несколько внутренних элементов: массивы памяти команд и данных, а также указатели на конкретные ячейки в них.
Так как на индикаторах требуется показывать не только вывод программы, но и содержимое каждой конкретной ячейки памяти, использовано два вектора: out_result содержит вывод программы, а final_out_result подключён к дешифраторам семисегментных индикаторов.

type t_memory is array (31 downto 0) of std_logic_vector (7 downto 0); -- command memory signal cell_memory: t_memory := (others => x"00"); type d_memory is array (63 downto 0) of std_logic_vector (2 downto 0); -- cells memory signal comm_memory: d_memory := (others => "000"); signal comm_number: std_logic_vector(6 downto 0) := (others => '0'); signal cell_number: std_logic_vector(5 downto 0) := (others => '0'); signal out_result: std_logic_vector(7 downto 0) := (others => '0'); signal final_out_result: std_logic_vector(7 downto 0) := (others => '0'); 
Process (clk, RESET)

Наконец подобрались к самому главному — модели поведения интерпретатора. Для начала объявим переменную-счётчик открытых скобок.

variable u: integer := 0; 

Для нормальной работы с циклами это должна быть именно переменная, а не сигнал. Главное отличие первого от второго в том, что значение в сигнал записывается по окончании выполнения процесса, а в переменную — непосредственно в момент присваивания.

begin if rising_edge(clk) then if (not RESET) then cell_memory x"00"); out_result '0'); final_out_result 

По сигналу сброса (кнопки в Cyclone II инверсные, поэтому и условие инверсное) обнуляем значения ячеек памяти и выходной вектор, а если при этом ещё и идёт запись программы, заполняем соответствующую ячейку памяти команд.

else if (RUN = '0') then running_led '0'); cell_number '0'); cell_memory x"00");

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

else -- executing running_led '0'); if (SW(5) = '1') then final_out_result  

Выбр вывода: на дешифраторы подаётся либо выход программы, либо значение из текущей ячейки. Выбор осуществляется с помощью тумблера SW5.

 case comm_memory(conv_integer(unsigned(comm_number))) is when "000" => -- next if (u = 0) then cell_number -- [ if ((cell_memory(conv_integer(unsigned(cell_number))) = x"00") or (u /= 0)) then u := u + 1; end if; if (u < 0 )then comm_number -- ] if ((cell_memory(conv_integer(unsigned(cell_number))) /= x"00") or (u /= 0)) then u := u - 1; end if; if (u < 0 )then comm_number -- stop if (u = 0) then null; end if;

Программа на Brainfuck представляется как автомат: есть набор фиксированных состояний, перемещение между которыми осуществляется (за исключением циклов) линейно. Такая модель на VHDL (да и не только) реализуется switch-case конструкцией.
Как уже говорилось, u — счётчик открытых скобок. Команды выполняются только при (u = 0), в остальных случаях происходит поиск парной скобки. В нормальном режиме и при поиске закрывающей скобки указатель команд движется вперёд, иначе — назад. Здесь ясно видно, что если бы u была сигналом, при первой реакции на закрывающую скобку счётчик команд увеличился бы, только на следующем такте указатель пошёл бы назад, наткнулся на закрывающую скобку второй раз (u = -2), а такого количества парных открывающих скобок нет — программа никогда бы не выполнилась.
Условие (u /= 0) сделано для реализации вложенных циклов.

Testbench

Код готов и компилируется,

но перед прошивкой устройства надо протестировать алгоритм на адекватность. Текст тестбенча приводить не буду, он есть в прикреплённых файлах. Отмечу лишь, что тупой последовательный прогон всех значений здесь не подойдёт, поэтому проверяется корректность выполнения конкретной программы. Я использовал сложение двух чисел:
+++>++<[->+.x
В качестве среды моделирования использовалась ModelSim-Altera.

Разводка платы

Последний этап перед прошивкой — задание соответствий сигналов модели реальным портам платы. Координаты выводов есть в приложении «Документация Cyclone II», ну а кому лень — вот готовая распиновка:

Заключение
  • Полностью готовый к прошивке (скомпилированный и разведённый по плате) проект
  • Документация к Altera Cyclone II (с обозначением всех портов на координатной сетке)
P.S.

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

Особенности языков описания архитектуры Verilog и VHDL

Языки VHDL и Verilog (Verilog HDL) относятся, в отличие от языка Argus, к языкам описания аппаратуры. Поэтому их нельзя напрямую сравнивать с Argus - они предназначены не для написания программ для FPGA и др. СБИС, а для проектирования логики самих этих устройств. Эти языки предназначены для моделирования электронных схем на уровнях вентильном, регистровых передач, корпусов микросхем. Поэтому эти языки можно назвать языками сквозного функционально-логического проектирования.

VHDL (Very high speed integrated circuits Hardware Description Language) был разработан в 1983 г. по заказу Пентагона с целью формального описания логических схем для всех этапов разработки электронных систем. Первый стандарт был утверждён в 1987 г., последний из известных - в 2002 г.

Verilog HDL был разработан фирмой Gateway Design Automaton как внутренний язык симуляции. Cadence приобрела Gateway в 1989 г. и открыла Verilog для общественного использования. В 1995 г. был определен стандарт языка - Verilog LRM (Language Reference Manual), IEEE1364-1995, а последний из известных - в 2001 г.

Первоначально VHDL предназначался для моделирования (что и объясняет его большую универсальность), но позднее из него было выделено синтезируемое подмножество. Написание алгоритмической модели на синтезируемом подмножестве гарантирует автоматический синтез по этой модели алгоритмической схемы. Аналогичная поддержка существует и для Verilog.

Основные составляющие языков VHDL и Verilog

Типы данных

В более простом языке Verilog поддерживаются только самые простые типы данных - целые (32-бит со знаком), действительные (с плавающей запятой), а также специфические типы "время" и "событие". В VHDL шире набор базовых типов, и, кроме этого, проектировщик может создавать свои типы данных, а в Verilog этого делать нельзя. Надо отметить, что программируются-то в этих языках как данные не элементы памяти, а сигналы. В Verilog, например, они бывают только цепными и регистровыми (последние могут запоминаться где-то).

Другие элементы VHDL и Verilog

В VHDL синтаксис позволяет описывать модель в разных стилях (структурное, потоковое, поведенческое описания), а также встраивать в описание фрагменты языков программирования высокого уровня (Си, Паскаль). Этим и достигается его большая универсальность и применяемость не только для описания архитектур вычислительных систем. Например, моделирование разных физических систем у него имеет поддержку в виде типов с физическими размерностями.

Verilog также поддерживает разные описания модели системы, но интерфейса с обычными языками программирования у него нет.

Заключение

Несмотря на похожие названия, Verilog HDL и VHDL - различные языки. Verilog - достаточно простой язык, сходный с языком программирования Си - как по синтаксису, так и по "идеологии". Малое количество служебных слов и простота основных конструкций упрощают изучение и позволяют использовать Verilog в целях обучения. Но в то же время это эффективный и специализированный язык. VHDL обладает большей универсальностью и может быть использован не только для описания моделей цифровых электронных схем, но и для других моделей. Однако из-за своих расширенных возможностей VHDL проигрывает в эффективности и простоте, то есть на описание одной и той же конструкции в Verilog потребуется в 3√4 раза меньше символов (ASCII), чем в VHDL.

Оба языка поддерживаются в качестве стандартов большим количеством программных продуктов, в том числе и open source, в области САПР. Имеются и компиляторы, и симуляторы для обоих языков, в том числе, например, и с первого языка на второй. Именно эти языки используются при проектировании (с помощью современных средств САПР ведущими производителями FPGA) не только самих СБИС, но и готовых модулей (ядер), мегафункций (megafunctions), предназначенных для решения достаточно сложных задач обработки сигналов.

Введение в язык описания аппаратуры: начало работы с VHDL для проектирования цифровых схем

В данной статье рассматривается VHDL, язык описания аппаратного обеспечения, и то, как он структурируется при описании цифровых схем. Мы также рассмотрим некоторые вводные примеры описаний схем и коснемся различий между типами данных " std_logic " и " bit ".

VHDL является одним из широко используемых языков описания аппаратуры (HDL, Hardware Description Language). VHDL означает язык описания аппаратуры VHSIC. В свою очередь, VHSIC означает высокоскоростная интегральная микросхема (Very-High-Speed Integrated Circuit).

Создание VHDL было инициировано Министерством обороны США в 1981 году. Сотрудничество таких компаний, как IBM и Texas Instruments, привело к выпуску первой версии VHDL в 1985 году. Xilinx, которая выпустила первую микросхему FPGA в 1984 году, вскоре поддержала VHDL в своих продуктах. С тех пор VHDL превратился в развитый язык в области проектирования, моделирования и синтеза цифровых схем.

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

Общая структура кода VHDL

Рассмотрим простую цифровую схему, показанную на рисунке 1.

Рисунок 1 – Простая цифровая схема

На этом рисунке показано, что есть два входных порта (a и b) и один выходной порт (out1). На рисунке показано, что входные и выходные порты имеют ширину один бит. Функциональность схемы – это выполнение логической операции И двух входов и вывод результата на выходной порт.

VHDL использует похожее описание; однако он имеет свой собственный синтаксис. Например, для описания входных и выходных портов этой схемы используются следующие строки кода:

entity circuit_1 is Port ( a : in STD_LOGIC; b : in STD_LOGIC; out1 : out STD_LOGIC); end circuit_1;

Давайте по очереди разберемся, что это значит.

Строка 1. Первая строка указывает произвольное название для описываемой схемы. Слово « circuit_1 », которое находится между ключевыми словами « entity » и « is », определяет имя этого модуля.

Строки с 2 по 4. Эти строки определяют входные и выходные порты схемы. Сравнивая эти строки со схемой, изображенной на рисунке 1, мы видим, что порты схемы вместе со своими функциями перечислены после ключевого слова « port ». Например, в строке 3 говорится, что у нас есть порт под названием « b ». Этот порт является входом, как указано ключевым словом « in » после двоеточия.

Что указывает ключевое слово « std_logic »? Как мы обсудим далее в этой статье, std_logic является в VHDL широко используемым типом данных. Его можно использовать для описания однобитового цифрового сигнала. Поскольку все порты входов/выходов на рисунке 1 передают единицу или ноль, мы можем использовать для этих портов тип данных std_logic .

Строка 5. Эта строка определяет конец оператора « entity ».

Следовательно, фрагмент кода entity определяет 1) название описываемой схемы и 2) порты схемы вместе со своими характеристиками, а именно: вход/выход и тип данных, которые должны передаваться этими портами. Фактически фрагмент кода entity описывает интерфейс модуля с окружающей его средой. Функции схемы, указанные в обсуждаемом операторе « entity », показаны на рисунке 1 зеленым цветом.

В дополнение к интерфейсу схемы с окружающей средой нам необходимо описать работу схемы. На рисунке 1 функциональность схемы – это выполнение логической операции И двух входов и вывод результата на выходной порт. Чтобы описать работу схемы, VHDL добавляет секцию « architecture » и связывает её с circuit_1 , определенной оператором entity . Код VHDL, описывающий архитектуру этой схемы, будет следующим.

architecture Behavioral of circuit_1 is begin out1 

Строка 6. Эта строка дает название, « Behavioral », для архитектуры, которая будет описана в следующих строках. Это имя встречается между ключевыми словами « architecture » и « of ». Данная строка также связывает эту архитектуру с « circuit_1 ». Другими словами, эта архитектура будет описывать работу « circuit_1 ».

Строка 8. Указывает начало описания архитектуры.

Строка 9. Строка 9 использует синтаксис VHDL для описания работы схемы. Логическая операция И (AND) двух входов находится в круглых скобках, а результат присваивается выходному порту с помощью оператора присваивания «

Строка 10. Указывает конец описания архитектуры. Как упоминалось выше, эти строки кода описывают внутреннюю работу схемы, которая здесь представляет простой логический элемент И (на рисунке 1 показан синим цветом).

Объединив то, что мы обсудили ранее, мы закончим описание « Circuit_1 » в VHDL. Мы получим следующий код:

entity circuit_1 is Port ( a : in STD_LOGIC; b : in STD_LOGIC; out1 : out STD_LOGIC); end circuit_1; ----------------------------------------------------- architecture Behavioral of circuit_1 is begin out1 

Однако нам еще нужно добавить еще несколько строк кода. Эти строки добавят библиотеку, содержащую некоторые важные определения, включая определения типов данных и операторов. Библиотека может состоять из нескольких пакетов (смотрите рисунок 2 ниже). Нам нужно сделать необходимый пакет(ы) данной библиотеки видимыми для проекта.

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

library ieee; use ieee.std_logic_1164.all entity circuit_1 is Port ( a : in STD_LOGIC; b : in STD_LOGIC; out1 : out STD_LOGIC); end circuit_1; architecture Behavioral of circuit_1 is begin out1 

Здесь мы добавляем две новые строки перед тем, что уже написали. Первая строка добавляет библиотеку « ieee », а вторая строка указывает, что требуется пакет « std_logic_1164 » из данной библиотеки. Поскольку « std_logic » является широко используемым типом данных, нам почти всегда нужно добавлять в VHDL код библиотеку « ieee » и пакет « std_logic_1164 ».

Библиотека может состоять из нескольких пакетов

Для проверки приведенного выше VHDL кода мы можем использовать симулятор Xilinx ISE.

Теперь, когда мы познакомились с основными блоками кода VHDL, давайте рассмотрим один из самых важных типов данных VHDL, т.е. тип данных « std_logic ».

Тип данных « std_logic » (против « bit »)

Как упоминалось выше, тип данных « std_logic » может использоваться для представления однобитового сигнала. Интересно, что существует еще один тип данных VHDL, « bit », который может принимать логическую единицу или логический ноль.

Итак, зачем нам нужен тип данных std_logic , если тип данных « bit » уже охватывает высокое и низкое логические состояния цифрового сигнала? Ну, цифровой сигнал на самом деле не ограничивается высоким логическим уровнем и низким логическим уровнем. Рассмотрим инвертор с тремя состояниями, показанный на рисунке 3.

Рисунок 3 – Схема инвертора с тремя состояниями

Когда на «включен» высокий логический уровень, «выход_данных» подключается либо к Vdd, либо к земле; однако, когда на «включен» низкий логический уровень, «выход данных» висит в воздухе, то есть он не имеет низкоимпедансного соединения с Vdd или землей, а вместо этого представляет для внешней схемы «высокий импеданс». Тип данных « std_logic » позволяет нам описывать цифровой сигнал в режиме с высоким импедансом, присвоив ему значение ' Z '.

Существует еще одно состояние, то есть к дополнение в высокому логическому уровню, низкому логическому уровню и высокому импедансу, которое может быть использовано при проектировании цифровых схем. Иногда нам неважно значение на конкретном входе. В этом случае представление значения сигнала с «неважным значением» может привести к более эффективному проектированию. Тип данных « std_logic » поддерживает состояние «неважное значение». Это позволяет улучшить аппаратную оптимизацию для таблиц поиска.

Тип данных « std_logic » также позволяет нам представить неинициализированный сигнал, присвоив ему значение ' U '. Это может быть полезно при моделировании фрагмента VHDL кода. Оказывается, что тип данных « std_logic » фактически может принимать девять значений:

  • ' U ': не инициализировано;
  • ' 1 ': обычный индикатор высокого логического уровня, также известный как «принудительный высокий логический уровень»;
  • ' 0 ': обычный индикатор низкого логического уровня, также известный как «принудительный низкий логический уровень»;
  • ' Z ': высокий импеданс;
  • ' - ': неважное значение;
  • ' W ': слабое неизвестное;
  • ' X ': неизвестное;
  • ' H ': слабая ' 1 ' (имеется в виду слабый ток);
  • ' L ': слабый ' 0 ' (имеется в виду слабый ток).

Среди этих значений мы обычно используем ‘ 0 ’, ‘ 1 ’, ‘ Z ’ и ‘ - ’.

Давайте посмотрим пример.

Пример 1

Напишем VHDL код для схемы на рисунке 4.

Рисунок 4 – Схема для примера 1

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

library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity circuit_2 is Port ( a : in STD_LOGIC; b : in STD_LOGIC; c : in STD_LOGIC; d : in STD_LOGIC; out1 : out STD_LOGIC; out2 : out STD_LOGIC); end circuit_2; architecture Behavioral of circuit_2 is signal sig1: std_logic; begin sig1 

Строки 1 и 2. Эти строки добавляют в код необходимые библиотеку и пакет. Поскольку используется тип данных « std_logic », мы должны добавить пакет « std_logic_1164 ».

Строки 3-10. Эти строки определяют имя модуля вместе с его входными/выходными портами. Этот фрагмент кода соответствует частям зеленого цвета на рисунке 4.

Строки 11-17. Эта часть кода описывает работу схемы (т.е. части синего цвета на рисунке 4). Как вы могли заметить, на рисунке 4 есть один внутренний узел; он обозначен как « sig1 ». Мы используем оператор « port » из « entity » для определения входных/выходных портов, но как мы можем определить внутренние узлы схемы? Для этого мы используем ключевое слово « signal ».

В строке 12 вышеприведенного кода ключевое слово « signal » сообщает программному обеспечению, что в схеме есть узел, помеченный как « sig1 ». Подобно определению портов, мы используем ключевое слово « std_logic » после двоеточия, чтобы указать требуемый тип данных. Теперь мы можем присвоить значение этому узлу (строка 14) или использовать его значение (строка 15).

Пример 2

Напишем VHDL код для схемы на рисунке 5.

Рисунок 5 – Схема для примера 2

Данная схема представляет собой мультиплексор два-к-одному. Когда на « sel » высокий логический уровень, выход нижнего логического элемента И будет в низком логическом состоянии независимо от значения на « b ». Мы можем сказать, что логический элемент И предотвращает распространение « b » на « sig2 ». С другой стороны, поскольку на « sel » высокий логический уровень, выход верхнего логического элемента И будет соответствовать « a ». Или, что то же самое, « a » достигнет « sig3 ». Поскольку в этом случае « sig2 » равен логическому нулю, то на выходе логического элемента ИЛИ будет такое же состояние, как у « sig3 ». Следовательно, когда на « sel » высокий логический уровень, « out1 » будет таким же, как « a ».

Аналогичное обсуждение покажет, что, когда на « sel » низкий логический уровень, « out1 » будет принимать значение « b ». Следовательно, исходя из значения « sel », мы можем позволить значению на одном или другом входе достичь выхода. Это называется мультиплексированием, а схема называется мультиплексором.

Мы можем описать схему на рисунке 5, используя следующий код:

library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity circuit_3 is Port ( a : in STD_LOGIC; b : in STD_LOGIC; sel : in STD_LOGIC; out1 : out STD_LOGIC); end circuit_3; architecture Behavioral of circuit_3 is signal sig1, sig2, sig3: std_logic; begin sig1 

Резюме

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

  • Фрагмент кода « entity » определяет 1) название описываемой схемы и 2) порты схемы; он устанавливает интерфейс между модулем и его окружением.
  • Фрагмент кода « architecture » описывает внутреннюю работу схемы.
  • Библиотеки VHDL содержат важные определения, включая определения типов данных и операторов. Сама библиотека может состоять из нескольких пакетов.
  • Нам почти всегда нужно добавлять в свой VHDL код библиотеку « ieee » и пакет « std_logic_1164 ».
  • Среди возможных значений типа данных « std_logic » мы обычно используем ‘ 0 ’, ‘ 1 ’, ‘ Z ’ и ‘ - ’.

Теги

Сохранить или поделиться

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.

Что такое VHDL. Простейший пример создания логического элемента в ПЛИС

Вот вокруг говорят ПЛИС, ПЛИС… Понятно что это микросхема такая… А как в нее электрическую схему заталкивать? Для этого существует несколько способов, один из которых это описание схемы устройства на специальном языке описания аппаратуры. Таких языков существует несколько: Verilog, AHDL, VHDL и наверное еще есть, но мне не попадались. Эти три вроде самые распространенные. Вот о VHDL я и попробую рассказать.

Почему VHDL? Он как и Verilog поддерживается большинством производителей ПЛИС, в то время как AHDL разрабатывался сугубо для Альтеры. Ну и в свое время по VHDL мне удалось достать литературу на русском языке. ПЛИС достаточно сложные устройства и читать кроме даташита еще и описание языка на буржуйском — рвет мозг на мелкие кусочки.

Чем VHDL отличается от обычных языков программирования типа Си, Паскаля и т.д.? Самое главное отличие в том, что VHDL описывает параллельные процессы. Если код на Си или Паскале у нас выполняется по очереди команда за командой и надо изощряться с таймерами и прерываниями чтоб обработать разные куски кода «одновременно», то на VHDL разные блоки программы выполняются параллельно друг другу, но в тоже время в VHDL есть часть комманд, которые выполняются последоватеьно. Поэтому структура программы в корне отличается от привычной микроконтроллерной.

Структура программы
Процесс программирования на VHDL чем то напоминает создание принципиальной схемы устройства.
Шаг 1: Включение в код используемых библиотек.
Шаг 2: Описание точек входа и точек выхода устройства (аналогично входам и выходам принципиальной схемы всего устройства).
Шаг 3: Описание точек входа и выхода элементов входящих в устройство (аналогично назначению функций ногам контроллера и другой логике в схеме)
Шаг 4: Описание архитектуры элементов входящих в устройство (вроде подбора логики типа ИЛИ-НЕ, И-НЕ, вобщем описание того как выход элемента завязан с его входом)
Шаг 5: Описание архитектуры всего устройства (типа соединения проводниками всех элементов схемы)

Чтобы это все было не голословно, попробуем создать с помощью VHDL простой элемент, например 16-ти разрядный счетчик, который считает по переднему фронту импульса.

Что еще нам надо знать, чтобы переходить к созданию первой программы на VHDL?
Основные структуры данных, с которыми мы можем работать. В литературе они называются классами объектов. Их три:
— константы
— переменные
— сигналы
Константы имеют тот же смысл, что и в других языках и определяются ключевам словом "constant":

constant MyConst: integer:=32; -- Константа с именем MyConst типа integer равная 32

Символы "--" означают комментарий

ВАЖНО: в VHDL нет разницы между строчными и прописными буквами в идентификаторах. То есть константы MyConst и mYcONST это на самом деле одна и таже константа.
Есть еще одно важное правило: идентификатор не должен оканчиваться подчеркиванием.

Переменные имеют практически тот же смысл, что и в других языках. Определяются ключевым словом "variable":

variable i: integer range 0 to 31; 

Переменная i типа integer принимающая значения в диапазоне от 0 до 31. Описание диапазона позволяет точно определить разрядность переменной, что существенно экономит ресурсы кристалла. Присваивание переменной выполняется с помощью знака ":=".

Сигналы это очень важный класс в VHDL. Они похожи на переменные, НО физически имеют смысл проводников на печатной плате. Это значит, что сигнал всегда имеет некоторое значение.
Описываются сигналы с помощью ключевого слова "signal":

 signal CLK, RESET : bit; signal data_out : bit_vector (15 downto 0); 

Первая строчка описывает два сигнала типа bit, вторая строчка описывает сигнал представленный в виде 16ти-разрядной шины. Причем нумерация бит (15 downto 0) или (0 to 15) имеет значение при операциях с сигналами. Назначение сигналов выполняется с помощью знака "

ВАЖНО. Все операции с сигналами выполняются параллельно. Тоесть код вида

совершенно не означает, что d у вас всегда будет равно b, как это было бы в Си или Паскале. Данные команды будут выполняться параллельно и существует достаточная вероятность что d будет некоторое время неравно b. «Некоторое время» зависит от задержек прохождения сигнала через различные элементы структуры ПЛИС и от прочих факторов.
Итак приступим:

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

Внешний квадрат (one_element) — это наша ПЛИС.
К ней слева подходят 3 входных сигнала:
Main_clk — тактовый сигнал, по которому собственно и работает наш счетчик,
Res — сигнал сброса счетчика в 0,
Enab — Разрешающий сигнал, наличие которого разрешает работу счетчика.
Справа выходной сигнал размерностью 16 бит (integer range 0 to 65535 дает нам 16 разрядов)
Это те сигналы, которые физически будут приходить непосредственно на ноги нашей ПЛИС.

Внутренний квадрат (counter) это тело нашего счетчика. К нему подходят идентичные сигналы, но названия у них свои. Почему? Потому что в одной ПЛИС мы можем создать множество одинаковых счетчиков, и чтобы система знала куда какой внешний сигнал заводить внутренний элемент имеет собственные названия сигналов.

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

Шаг 1 Включение в код используемых библиотек:
Как и во всех языках программирования, в VHDL есть стандартные библиотеки. Крайне рекомендуется вставлять следующий код:

 library IEEE; use IEEE.std_logic_1164.all; 

Данная библиотека дает возможность присваивать сигналам типа bit не только значения 0 и 1 но и еще 7 дополнительных значений включая Z-состояние. Более детально в следующих статьях или в литературе, чтобы не перегружать материал.

Шаг 2 Описание точек входа и точек выхода устройства:
В литературе это называется описанием интерфейса.
Интерфейс любого элемента описывается с помощью ключевого слова entity и представляет собой следующую конструкцию:

 entity имя_элемента is port ( список входных сигналов : in тип сигналов; список выходных сигналов : out тип сигналов ); end имя_элемента; 

Так как, в соответствии со структурой программы, в данном шаге мы описываем интерфейс всего устройства, значит входными и выходными сигналами у нас будут все линии подходящие к блоку «one_element» изображенные на схеме. Запишем их на VHDL:

 entity one_element is -- Счетчик port ( main_clk, res, enab : in bit; -- вход временной синхронизации, ас. сброс, разрешение. counter_out : out integer range 0 to 65535 --выход значения частоты ); end one_element; 

Шаг 3 Описание точек входа и выхода элементов входящих в устройство:
Интерфейс элементов входящих в состав схемы описывается аналогично интерфейсу всего устройства.
ВАЖНО: Если в схему входит несколько однотипных элементов (например 2 одинаковых счетчика и 3 одинаковых регистра), то в данной части программы мы описываем по одному элементу каждого типа (в продолжение примера — один счетчик и один регистр).
У нас в схеме счетчик только один, поэтому мы его и опишем:

 entity counter is -- описание 16-разрядного счетчика по переднему фпронту port ( c_in,enab_c,res_c:in bit; c_out:out integer range 0 to 65535 ); end counter; 

Шаг 4 Описание архитектуры элементов входящих в устройство:
Архитектура элементов описывается с помощью ключевого слова «architecture» и представляет собой конструкцию типа:

 architecture имя_архитектуры of имя_элемента is begin process(список запуска) variable список переменных; begin описание зависимости выходных сигналов от входных end process; end имя_архитектуры; 

Ключевое слово «process» означает описание параллельного процесса (обработки наших входных сигналов для получения выходных), который активируется «списком запуска».
ВАЖНО. Все логические блоки описанные как процесс выполняются параллельно друг другу. "
Список сигналов" это перечень сигналов, изменение которых активирует выполнение процесса.
Звучит очень сухо и заумно, но надеюсь что при разборе примера будет понятней.
У нас счетчик работает по переднему фронту сигнала c_in. Значит нам надо контролировать изменение этого сигнала и при обнаружении переднего фронта активировать счетчик.
Запишем это на VHDL:

 architecture count of counter is -- архитектура данного счетчика begin process (c_in, res_c) variable cnt:integer range 0 to 65535; begin if (res_c='0') then --если низкий уровень Reset - сбрасываем счетчик cnt:=0; elsif (c_in'event and c_in='1') then -- если передний фронт - считаем if (enab_c='1') then cnt:=cnt+1; end if; end if; c_out 

Запись if (c_in'event and c_in='1') then выполняет функцию детектора переднего фронта сигнала. Ключевое слово 'event это стандартный атрибут сигнала c_in, который определяет изменение сигнала. Каждый сигнал в проекте имеет стандартный набор атрибутов. Дословно нашу запись можно прочитать так: Если c_in изменился и при этом равен 1. Остальной код процесса работает как и в других языках программирования. Разве что стоит обратить внимание на запись c_out которая назначает выходному сигналу c_out значение переменной cnt
Все предыдущие шаги — это была подготовительная работа. Остался последний шаг:

Шаг 5 Описание архитектуры всего устройства:
Описание архитектуры всего устройства существенно отличается по структуре от описания архитектуры элемента, хотя производится так же с помощью ключевого слова «architecture»
В нашем примере эта часть выглядит так:

 architecture a of one_element is -- архитектура всего устройства component counter -- счетчик как компонент port ( c_in,enab_c,res_c:in bit; c_out:out integer range 0 to 65535 ); end component; begin mc: counter -- описание работы главного счетчика port map (c_in=>main_clk,enab_c=>enab,res_c=>res,c_out=>counter_out); end a; 

Как вы могли заметить у нас появилась новая конструкция component. Данная конструкция говорит о том, что элемент counter является компонентом, входящим в состав one_element, и содержит описание входных и выходных портов этого компонента.

Далее в операторных скобках begin — end идет самое важное: назначение входных и выходных сигналов устройства экземплярам компонентов входящих в данное устройство.
Эта часть записи равносильна соединению логическихэлементов проводниками. Т.к. у нас в проекте только один счетчик — то именно ему мы и назначаем все наши входные и выходные сигналы.
mc — это имя экземпляра счетчика.
port map — это команда назначения входных и выходных сигналов данному счетчику

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

 library IEEE; use IEEE.std_logic_1164.all; entity one_element is -- Счетчик port ( main_clk, res, enab : in bit; -- вход временной синхронизации, ас. сброс, разрешение. counter_out : out integer range 0 to 65535 --выход значения частоты ); end one_element; entity counter is -- описание 16-разрядного счетчика по переднему фпронту port ( c_in,enab_c,res_c:in bit; c_out:out integer range 0 to 65535 ); end counter; architecture count of counter is -- архитектура данного счетчика begin process (c_in, res_c) variable cnt:integer range 0 to 65535; begin if (res_c='0') then cnt:=0; elsif (c_in'event and c_in='1') then if (enab_c='1') then cnt:=cnt+1; end if; end if; c_outmain_clk,enab_c=>enab,res_c=>res,c_out=>counter_out); end a; 

P.S. Исправил код, внес уточнения замеченные в комментариях. Теперь сигнал сброса асинхронный.

P.P.S. Добавил видео работы элемента в железе:

Некоторые пояснения к видео:
Большая макетка с кучей проводов — это плата типа PINBOARD от Ди Хальта для микроконтроллеров AVR и ARM. На ней собран другой проект, не имеющий отношения к ПЛИС. Потому на ней куча проводов.
Я с этой платы использую питание 5 В, и секцию с ДИП-переключателями, чтобы притянуть входы макетной платы ПЛИС к +5 вольтам через резисторы 10k.
Так что не обращайте внимание на нее.

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

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

Назначение одного сигнала можно выполнить только один раз.

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

Сигналы нужны для связи между процессами одной архитектуры, т. к. переменные (variable) видны только внутри процесса.

По чему вы учились? у меня это была книга «Основы языка VHDL» Бибило П.Н.

На сколько я помню, (щас после переустановки винды софт весь снесен) если в одном процессе попытаться дважды выполнить назначение одного и того же сигнала MAXPLUS давал ошибку. Вот цитата из П.Н.Библо «Основы языка VHDL»: «Каждый источник сигнала есть драйвер. В каждом процессе сигнал должен иметь только один источник, иначе требуется специальная функция, которая будет определять значение сигнала, определяемого из нескольких драйверов.»

busy 

это часть кода из одного проекта (немного позже опишу его здесь) сигналу busy может быть назначено значение в 2 местах. Если условие if выполнилось тогда busy будет 1, в другом случае 0. Все компилируется в Квартусе.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *