Учебник языка описания аппаратуры VHDL
Язык VHDL (Very-High-Speed Hardware Description Language) используется во многих системах для моделирования цифровых систем, например в САПР OrCAD или Active HDL фирмы ALDEC.
ОБЪЕКТЫ ПРОЕКТА. ИНТЕРФЕЙС И АРХИТЕКТУРА
Начнём знакомство с создания очень простой модели логического элемента AND2, выполняющего функцию 2И (рис. 1).

Рис. 1. VHDL-модель логического элемента AND2, выполняющего функцию 2И
Мы видим, что модель состоит из двух частей. Верхняя часть (первые четыре строчки) – это интерфейс модели, нижняя – её архитектурное тело.
Описание интерфейса всегда начинается с ключевого слова entity, за которым следует имя моделируемого объекта (в данном случае элемента AND2). Слово entity переводится с английского языка как «сущность» или «объект».
В терминологии языка VHDL схему цифрового устройства принято рассматривать как проект, а каждый её компонент (триггер, регистр, память или процессор) – как объект этого проекта. Для любого компонента его окружением будут другие компоненты схемы, с которыми он обменивается данными. Этот обмен осуществляется через специальные «точки» внешнего интерфейса, называемые портами. Через них моделируемый объект взаимодействует с внешним миром, «окружением».
Полный список портов следует за ключевым словом port. Это могут быть входные (in), выходные (out) или двунаправленные (inout) порты.
С понятием «порт» ассоциируется, прежде всего, имя контакта элемента. Однако удобнее отождествлять его с именем сигнала, подаваемого на этот порт. В нашем примере входные порты IN1 и IN2, а также выходной порт OUT1 предназначены для подачи и съёма сигналов типа bit. Тип задаёт множество допустимых значений сигнала. Тип bit, используемый в первой редакции языка VHDL’87 (и в нашей первой модели), определён как:
type bit is ('0', '1');
Видно, что в нём декларированы всего два значения – логические '0' и '1'.
По аналогии с языками программирования высокого уровня интерфейс можно называть разделом объявлений или декларативным разделом программы. Тогда исполняемая её часть ассоциируется с понятием архитектурное тело модели. В нашем примере оно содержит всего один оператор:
OUT1 <= (IN1 and IN2);
Он то и реализует функцию, выполняемую логическим элементом AND2.
В качестве примера мы рассмотрели весьма простой цифровой объект – логический элемент AND2. Для сложных устройств, таких как микропроцессор или память, структура описания остаётся прежней – интерфейс и архитектурное тело. Возрастает лишь объём VHDL-кода.
Интерфейс во всех случаях удобно рассматривать как внешнее описание объекта, то есть описание в виде «чёрного ящика». Оно определяет «границы» модели, выделяя объект из окружения. В противовес ему - архитектурное тело есть внутреннее описание объекта, и как увидим позже оно может быть представлено не только на функциональном, но и на структурном уровне.
МНОГОЗНАЧНАЯ ЛОГИКА. СИГНАЛЫ И ЗАДЕРЖКИ. КОММЕНТАРИЙ
На рис.2 показана более совершенная модель того же самого объекта – элемента 2И. Какие новшества бросаются в глаза?

Рис.2. VHDL-модель логического элемента AND2, имитирующая задержку 20 ns
Во-первых, в ней заменён тип сигналов bit на новый тип std_logic. Это означает, что «шкала» цифрового сигнала расширена с двух (‘0’, ’1’) до девяти значений ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'). Теперь без всяких ухищрений можно имитировать высокоомный выход ('Z'), открытый коллектор ('H'), шинные структуры и прочие особенности реальной цифровой аппаратуры.
Эта замена узаконена в новой редакции языка VHDL’93, а потому использование старого типа bit в современных разработках считается «дурным тоном». Заметим, однако, что тип bit был «встроен» в язык VHDL, то есть он декларирован в пакете STANDART, который подключается к системе по умолчанию. В отличие от него тип std_logic определён в другом пакете std_logic_1164 и требует явного подключения. Это делается с помощью ключевых слов Library и Use (первые две строки на рис.2) .
Во-вторых, модель имитирует не только функцию объекта, но и его инерционные свойства, а именно задержку, с которой выход реагирует на переключения входных сигналов. На рис.2 эта задержка реализуется фразой «after 20 ns», приписанной в конце оператора назначения для выходного сигнала OUT1:
OUT1 <= IN1 and IN2 after 20ns;
Она означает, что данный сигнал должен получить новое значение, определяемое выражением (IN1 and IN2), не в текущий момент модельного времени (когда выполняется этот оператор), а чуть позже, когда модельное время продвинется ещё на 20 ns. Другими словами, выход OUT1 будет сохранять старое значение ещё некоторое время после того, как переключились входы.
Это очень важная и полезная особенность языка VHDL – ведь мы имеем дело с реальными компонентами, в которых ничего не происходит мгновенно. Именно по этой причине в языке VHDL кроме констант (constant) и переменных (variable) определён ещё один класс, называемый сигналами (signal).
Чем же сигналы отличаются от переменных? Прежде всего, своей идеологией. Их основное назначение – имитировать инерционность компонентов и параллельные процессы в схемах. По этой причине для сигналов введён специальный оператор назначения (<=), который «работает» аналогично оператору присваивания для переменной (:=), но с одним принципиальным отличием.
Переменная получает новое значение немедленно, как только выполнится оператор присваивания (:=). Последующие операторы будут работать уже с новым значением этой переменной.
Для сигналов всё сделано иначе. Даже в том случае, если вы не указали явно задержку распространения, то есть не написали фразу «after 20 ns», вновь вычисленное значение сигнала остаётся недоступным в текущем цикле работы моделирующей программы. Выполняя последующие операторы, она будет использовать старое значение этого сигнала.
Более того, если вы укажите нулевую задержку, написав «after 0 ns», то и в этом случае все сказанное останется в силе. Новое значение сигнала станет доступным только в следующем цикле, который моделирующая программа выполнит без продвижения модельного времени.
В третьих, в текст VHDL-кода введён комментарий. Он обозначается двумя дефисами, идущими подряд, то есть вот так:
-- variable IN1 : in std_logic; ERROR!!!
Здесь комментарием закрыта вся строка.
Наконец, в модели показано, что декларированные в интерфейсе порты по умолчанию являются сигналами, а не переменными. Попытка объявить порт IN1 как переменную ( variable IN1 : in std_logic; ERROR!!!) воспринимается компилятором как ошибка.
В то же время описание порта IN2 как сигнала ( signal IN2 : in std_logic; ) хотя и является избыточным, но вполне допустимо.
Рассмотренная на рис.2 модель страдает одним недостатком, – она не позволяет задать различную задержку при переключении выхода в 1 и в 0. Впрочем, этот недостаток обнаруживается и в фирменных моделях пакета OrCAD. Между тем язык VHDL позволяет решить эту проблему несколькими способами. Самый простой из них заключается в использовании оператора условного назначения (рис.3).

Рис.3. VHDL-модель логического элемента AND2, имитирующая
различные задержки по фронту (20 ns) и спаду (22 ns)
Здесь требуются небольшие пояснения. Дело в том, что оператор назначения выполняется только в том случае, если хотя бы один сигнал IN1 или IN2 изменился в текущем цикле моделирования. А дальше всё как в операторе IF или CASE. Проверяется условие (в примере IN1='1' and IN2 = '1') и если оно оказывается истинным, то выполняется соответствующее ему назначение (OUT1<= '1' after 22 ns). В противном случае выполняется альтернативное назначение '0' after 22 ns;. В дальнейшем мы рассмотрим и другие варианты решения той же самой задачи.
ПАРАМЕТРЫ НАСТРОЙКИ
Задание величин задержек в тексте модели (рис.3) нельзя назвать хорошим стилем программирования. Нередко возникает необходимость изменить их значения. Например, вы можете захотеть протестировать свою схему при минимальных, номинальных или максимальных задержках, или посмотреть, как поведёт себя схема при наихудших их сочетаниях. Другими словами, предпочтительнее определять такие параметры за «границами» модели и передавать их в модель как фактические параметры.
В языке VHDL есть изящный способ задавать такие параметры извне и настраивать их для конкретного экземпляра объекта. Делается это с помощью ключевого слова generic (общий, настраиваемый), за которым следует список передаваемых в модель параметров (рис.4).
В данном примере нет возможности продемонстрировать настройку параметров (мы это сделаем позднее). Здесь используются только те значения констант TPLH и TPHL, что определены умолчанием при объявлении названных параметров (TPLH : time := 20 ns ; TPHL : time := 22 ns). Потрясающие воображение аббревиатуры широко применяются в зарубежных САПР и требуют пояснения:
§ TPLH (Time Propagation Low High) – время (задержка) распространения переключения из 0 в 1;
§ TPHL (Time Propagation High Low) – задержка переключения из 1 в 0.
Заметим, что параметры в списке generic декларированы неявно как константы, аналогично тому, как это делалось при описании портов (они тоже неявным образом объявлялись сигналами).

Рис.4. VHDL-модель логического элемента AND2, демонстрирующая
механизм задания внешних параметров модели TPLH и TPHL
Мы знаем, что язык VHDL ориентирован на описание цифровой аппаратуры, а потому в нём должен быть механизм определения физических типов данных, таких как время, напряжение, сопротивление, мощность и т. п. Для цифровых схем наиболее важным типом является тип time – время. Это предопределённый физический тип, широко используемый для описания временных параметров и в частности задержек. Он декларирован в пакете STANDART и выглядит так, как показано на рис.5.
Основной единицей измерения выбрана фемтосекунда, равная 10-15 с. Наиболее часто используемые единицы времени при моделировании цифровой аппаратуры – это наносекунды (1 ns = 10-9 c) и микросекунды (1us = 10-6 c). Чтобы записать значение временного интервала, надо указать число и вслед за ним единицу измерения, например 22 ns. Разделитель между ними не является обязательным, так что с точки зрения синтаксиса допустима и такая запись: 22ns.
Вернемся, однако, к модели логического элемента AND2, показанной на рис.4. Здесь есть ещё одна деталь, которую мы обошли молчанием. Речь идёт о возможности при объявлении сигнала задать ему начальное значение.
В нашем примере такое значение приписано сигналу OUT1:
OUT1 : out std_logic := 'H'
После указания типа сигнала стоит оператор присваивания (:=) и вслед за ним требуемое значение ('H' – слабая 1). В принципе на этом месте может быть любое разрешённое значение из набора ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-').

Рис.5. Декларация физического типа time для описания временных
параметров аппаратуры
VHDL - ШАБЛОНЫ
Знакомясь с языком DSL пакета DesignLab 8, мы широко использовали так называемые DSL-шаблоны. Это своеобразные «заготовки», которые требуют лишь небольшой редакции. Они ускоряют процесс создания модели и уменьшают число ошибок в ней. Особенно полезны шаблоны на начальных этапах изучения языка. А мы как раз и находимся в этой стадии. Поэтому поговорим о них более подробно.
В САПР OrCAD VHDL-шаблоны (Samples) можно вызвать как из графического редактора Capture, так и из моделирующей программы Simulate. Но это становится возможным лишь в том случае, когда открыт какой-нибудь VHDL-файл.
Шаблоны вызываются командой Edit/Samples. Далее из списка выбираются подходящие для наших целей образцы и переносятся в VHDL-файл. Для построения модели, показанной на рис.4, нам потребуются следующие «заготовки»: Design Unit (модуль проекта), Generic (настраиваемые параметры) и Conditional Assignment (оператор условного назначения). Они показаны на рис. 6.

Рис.6. VHDL-шаблоны (образцы, заготовки), ускоряющие процесс проектирования
бездефектных моделей
С помощью первого шаблона создаётся каркас VHDL-модели, второй шаблон вставляется в интерфейс, а третий – в архитектурное тело. После этого остаётся лишь заменить формальные имена идентификаторов, параметров и выражений реальными. Вот и вся работа. Результатом её будет модель, показанная на рис. 4.
ВАРИАНТЫ ОПИСАНИЯ АРХИТЕКТУРНЫХ ТЕЛ
Любой объект проекта имеет только один интерфейс, но у него может быть несколько архитектур. Это объясняется тем, что одна и та же функция при реализации даёт множество структурных решений. Сказанное подтвердим на простом примере, выбрав в качестве объекта проекта мультиплексор MUX2 (рис.7).

Рис.7. Условное графическое обозначение, таблица истинности и
логическая структура мультиплексора MUX2
Первое, что приходит в голову, это построить VHDL-модель мультиплексора (рис.8, а) по образу и подобию ранее созданной модели элемента AND2 (рис.1). И там и здесь используется логическая функция объекта, а потому мы назовём архитектурное тело именем FUNC (от слова functional).
При построении этой модели объект проекта рассматривался как примитив (чёрный ящик), его структурная организация не представляла для нас никакого интереса. Всё что нам требовалось - это зависимость выхода от входов, представленная логическим уравнением. Такое описание работы моделируемого объекта называется аналитическим.
К сожалению, для сложных объектов аналитическое описание получить очень трудно или невозможно вовсе. Поэтому вынужденно его приходится заменять алгоритмическим. Конечно, алгоритм всегда можно реализовать в виде функции или процедуры, но в языке VHDL есть для этих целей более эффективный инструмент. Он называется механизмом процессов. Оказывается кроме описания алгоритмов на абстрактном уровне – это ещё и великолепный способ моделировать параллельные операции.
Детально мы поговорим о нём позднее, а сейчас прокомментируем только самое необходимое. На рис.8 процесс используется в архитектуре 2. Она называется BEHAV (от слова behavior - поведение).

Рис.8. Возможные варианты описания архитектуры мультиплексора MUX2
Описание процесса начинается с ключевого слова process, за которым в круглых скобках следует так называемый список сигналов запуска. Процесс запускается только в том случае, когда хотя бы один из сигналов названного списка меняет своё значение. Операторы активного процесса выполняются последовательно один за другим, как в обычных процедурах. В нашем примере процесс включает единственный условный оператор.
Возникает, правда, законный вопрос, а могли ли мы подобным образом, то есть с помощью процессов, описать работу ранее рассмотренного логического элемента AND2? Думаю, что вы сами знаете ответ. Вот только модель получится более громоздкой.
Подведём некоторые итоги. Для структурных примитивов, рассматриваемых как чёрный ящик, возможно только поведенческое (функциональное или алгоритмическое) описание объекта. Рассмотренными примерами (архитектуры 1 и 2) мы исчерпали эти возможности.
Для более сложных объектов с известной структурой (или несколькими структурными решениями) появляются дополнительные ресурсы.
Во-первых, используя структурные данные, можно построить потоковую модель. Она реализована в архитектуре 3, имеющей имя FLOW (поток).
Во-вторых, на основании структурной информации можно построить чисто структурную модель. Она представлена архитектурой 4 и называется STRUCT (от слова structural - структурный). Фактически такая модель представляет собой список составных частей объекта (компонентов) и связей между ними, записанных в формате VHDL. Современные САПР умеют автоматически генерировать такой список, конвертируя электрическую схему цифровой аппаратуры в VHDL-код.
Структурная модель (архитектура 4) выглядит довольно тяжеловесно, и этому есть объяснение. Значительную часть архитектурного тела составляет раздел деклараций, где перечисляются все типы компонентов, используемых в моделируемом объекте. В нашем примере их три – это INVR, AND2 и OR2. Кроме того, здесь же объявляются и все внутренние сигналы схемы F1, F2 и F3.
Описание компонента (некоего шаблона) фактически повторяет интерфейс соответствующего объекта проекта и имеет то же самое имя. Да иначе и быть не может – ведь речь идёт об одном физическом объекте. Чтобы убедиться в сказанном, достаточно взглянуть на модель AND2 (рис.4) и сравнить её описание с компонентом AND2 на рис.8.
Компоненты INVR и OR2 тоже ссылаются на соответствующие VHDL-модели, которых к моменту написания этого текста ещё не существует. Надеюсь, что вы их построите самостоятельно, используя в качестве «прототипа» модель элемента AND2, показанную на рис.1.
В исполняемой части архитектурного тела (после слова begin) по объявленным ранее шаблонам компонентов INVR, AND2 и OR2 создаются их конкретные экземпляры. Причём каждый экземпляр получает свою уникальную метку (обычно это позиционное обозначение): DD1..DD4. При необходимости в карте настройки элемента (generic map) указываются желаемые параметры, а в карте порта (port map) отражаются структурные связи между компонентами схемы.
Разберём подробнее первую запись:
DD1 : AND2 generic map (10ns,12ns) --карта настройки задержек
port map (IN1=>D0, IN2=>F1,OUT1=>F2); -- карта порта
Она описывает элемент DD1, выполняющий функцию 2И (рис.7, в). Модель этого элемента имеет имя AND2. В ней объявлен список настраиваемых параметров generic (см. рис.4), которым теперь (при конкретизации объекта), мы можем передать желаемые параметры. Делается это через карту настройки generic map. В данном случае соответствие между формальными (TPLH, TPHL) и фактическими (10ns, 12ns) параметрами устанавливается позиционным сопоставлением, то есть первый формальный параметр TPLH получит первое значение в списке фактических параметров. И путать местами их нельзя.
Существует другой, более надёжный способ связывания, называемый поимённым или ключевым сопоставлением. Он демонстрируется при конкретизации элемента DD3 того же самого назначения:
DD3 : AND2 generic map (TPHL=>15ns, TPLH=>11ns) -- карта настройки
port map (D1, A, F3); -- карта порта
Обратите внимание на изменения в его карте настройки. Теперь мы поимённо назвали параметры задержки TPHL и TPLH и «настроили» их на конкретные значения 15ns и 11ns. Не знаю, заметили ли вы, что следуют они в карте настройки в обратном порядке.
Элементы DD1 и DD3 выполняют одну функцию, но имеют разные задержки, и этот эффект достигнут благодаря индивидуальной настройке их параметров.
Те же самые приёмы используются при связывании формальных имен портов (IN1, IN2, OUT1) с фактическими именами сигналов, действующих в моделируемой схеме (D0, D1, A, F1, F2, F3, Y).
Эта работа выполняется с помощью карт портов каждого экземпляра компонента. Для разнообразия в карте порта компонента DD1 используется поимённое связывание (port map (IN1=>D0, IN2=>F1, OUT1=>F2); ), а в компоненте DD3 связь между портами и сигналами реализуется позиционным сопоставлением (port map (D1, A, F3); ).
Как видно, язык VHDL ориентирован на явное описание элементов и неявное описание связей между ними. Однако и соединения тоже легко проследить по одинаковым именам сигналов (цепей) в картах портов. На рис.8 в архитектуре 4 одно из таких соединений выделено цветом. Выходной порт OUT1 элемента DD1 формирует сигнал F2, который по не описанному явно проводнику поступает на входной порт IN1 элемента DD2. Аналогичным образом обнаруживаются и все остальные соединения в схеме.
Рассмотренные способы поведенческого, потокового и структурного описания, казалось бы, составляют полный арсенал инструментальных средств разработчика цифровой аппаратуры. Но, оказывается, язык VHDL позволяет комбинировать их в произвольных сочетаниях. Это очень полезная особенность языка. Чаще всего она используется на начальных этапах проектирования, когда полного структурного описания проекта ещё не существует.
Смешанное описание позволяет «плавно» перейти от алгоритма к структуре, постепенно заменяя отдельные его абстрактные представления аппаратными решениями. На рис.9 показан один из многочисленных вариантов смешанного описания. Архитектура называется MIXED (смешанный). Она содержит фрагмент аналитического описания (Y<=F2 or F3;), описание инвертора процессом (process (A) ) и структурное описание остальной части мультиплексора (элементы DD2 и DD3).

Рис.9. Смешанное описание архитектуры мультиплексора MUX2
Вероятно, вас уже давно мучает вопрос, как при моделировании задать желаемую архитектуру, если их несколько. Самое простое решение – использовать механизм умолчаний. Например, САПР OrCAD_9 по умолчанию выбирает первую архитектуру, расположенную сразу за описанием интерфейса. Если вам потребуется другая архитектура, то её надо просто переместить наверх. На первых порах можно смириться с таким неудобством.
Понятно, что все архитектуры должны давать одно и то же поведение объекта. А это значит, что по временным диаграммам мы не в состоянии выяснить, с какой архитектурой имеем дело в настоящий момент. Чтобы идентифицировать каждую архитектуру, придётся задать в ней хотя бы один отличительный признак. Для мультиплексора мы поступили просто: каждая архитектура формирует результат со своей задержкой. Для архитектуры 1 (FUNC) – это 5ns, для архитектуры 2 (BEHAV) – 10ns и т. д. (см. рис.8).
В дальнейшем мы познакомимся с понятием конфигурация (configuration), которая играет ключевую роль в организации VHDL-описания всего проекта, и, в частности, в ней задаётся желаемая архитектура.
Для пытливого ума предлагаю компромиссный вариант. Надо открыть VHDL-файл, автоматически создаваемый системой OrCAD при передаче вашей схемы на моделирование (команда Tools/Simulate) и добавить в него элемент конфигурации проекта, выделенный на рис.10 серым цветом.
Эта строка означает, что для элемента схемы HB1 (for HB1) надо использовать (use) объект проекта MUX2, находящийся в рабочей библиотеке work, с архитектурой, указанной в круглых скобках. В нашем примере – это архитектура behav. Добавлю, что рабочая библиотека – это библиотека проекта, в которой содержатся все откомпилированные VHDL-файлы, подключённые к проекту.

Рис.10. Простой способ задания рабочей архитектуры
Теперь, заменяя только одно слово, вы можете ссылаться на любую подключённую к объекту MUX2 архитектуру (FUNC, BEHAV, FLOW, STRUCT, MIXED). Не забывайте, их отличия в работе проявляются только разными задержками.
Обратите внимание ещё на одну вещь. Автоматически сгенерированный VHDL-код тоже имеет структуру объекта проекта, то есть содержит интерфейс с именем SCHEMATICS1 и архитектурное тело STRUCTURE.
Единственная его особенность - в нём нет портов, а, значит, отсутствуют и связи с внешним миром. Кажется – это совершенно бессмысленная «вещь в себе». Но как говорится, первое впечатление обманчиво. Перед нами «оболочка», в которую помещается объект проектирования для проверки правильности его работы. Иногда её называют «испытательным стендом», и служит эта установка для имитации процесса тестирования цифровой аппаратуры.
Внутри оболочки можно разместить не только тестируемый объект, но и VHDL-описания входных сигналов, которые должны на него подаваться в качестве внешних воздействий (рис.11). Генератор тестовых наборов может быть оформлен как самостоятельный объект проекта, но проще его «встроить» в оболочку, в качестве отдельных процессов. На рис.10 именно так и сделано, при этом демонстрируются различные способы VHDL-описания внешних воздействий. Обратите внимание на очень важный момент, они (внешние воздействия) являются внешними только для тестируемого объекта, но для «испытательного стенда» они остаются внутри оболочки.

Рис.11. «Испытательный стенд»
Наиболее просто реализуется временная диаграмма (waveform) с помощью оператора назначения (см. рис.10, сигнал A_TEST). Оказывается, что любому сигналу можно назначить не одно, а несколько будущих значений (транзакций). И пусть вас не смущает, что вы делаете назначение входному сигналу.
Именно эта возможность используется для описания внешнего воздействия A_TEST (A_TEST<='0', '1' after 600ns, '0' after 1200ns, '1' after 1800ns ; -- GEN3).
Фактически, данный перечень представляет собой список событий, каждое из которых указывает новое значение сигнала и момент времени, когда сигнал должен получить это значение.
Понятно, что для периодических сигналов данный способ описания может вызвать чувство раздражения, особенно в тех случаях, когда требуется много периодов. Такие сигналы предпочтительнее задавать с помощью процессов. Именно так описаны сигналы D0_TEST и D1_TEST. В обоих случаях используется оператор ожидания wait, о котором будет отдельный разговор.
В примере применяется самая простая его конструкция (wait for 100ns), указывающая, на какое время надо приостановить процесс. Некоторые отличия в его использовании, наблюдаемые для сигналов D0_TEST и D1_TEST, принципиального значения не имеют.
ПРАВИЛА НАПИСАНИЯ ИДЕНТИФИКАТОРОВ
Идентификаторы, то есть имена констант, переменных, сигналов, интерфейсов и архитектур, процедур и функций, пакетов и конфигураций, должны удовлетворять следующим правилам:
§ Первый символ имени должен быть буквой (строчной или прописной);
§ Последующие символы могут быть буквами, цифрами или знаком подчёркивания;
§ Идентификатор не должен начинаться или заканчиваться знаком подчёркивания. Он не может содержать и два подчёркивания подряд;
§ Никакие пробелы внутри идентификатора не допускаются;
§ Зарезервированные слова не могут использоваться в качестве идентификаторов;
§ Литеры верхнего и нижнего регистра не различаются;
§ Имя должно вмещаться в одну строку.
Приведём несколько примеров правильных и не правильных идентификаторов:
Binary_Counter и BinaryCounter – два правильных, но разных имени.
Adder4 и adder4 – два правильных и одинаковых имени.
FULL_ADD и FULLADD – хороший и плохой стили написания правильных имён;
Неправильные идентификаторы: 555LA3 (начинается с цифры), _SHIFT (начинается с подчёркивания), REG_ (заканчивается подчёркиванием), adder__sub (включает два подчёркивания подряд), A&B (содержит специальный символ &), IN, OUT, PORT (в качестве имени используются зарезервированные слова).
При проектировании реальных схем названные выше ограничения в написании идентификаторов оказываются порой настолько жёсткими, что с ними не хотят мириться разработчики аппаратуры. Приведу несколько таких ситуаций.
Имена многих реальных компонентов начинаются с цифры, например 7404 или 74ls193 (TTL-логика). Номер секции для многосекционных элементов принято указывать через дефис (специальный символ), например, так: IN1_-1 или OUT1_-4, что опять порождает неправильный идентификатор.
Чтобы удовлетворить «капризам» электронщиков, в языке VHDL определено понятие «расширенный идентификатор» (extended_identifier). Он заключается в backslash-скобки, внутри которых можно использовать любой символ, включая и пробел.
Если «запрещённые» имена использовались вами на этапе проектирования схемы, то редактор OrCAD Capture обнаружит их и автоматически «упакует» в backslash-скобки, например, так: \74ls193\ или \OUT1_-4\.
Если вы самостоятельно пишите VHDL-код, то компилятор будет браковать такие «ошибочные» имена, и вам придётся вручную превращать их в расширенные идентификаторы.
Заканчивая разговор об идентификаторах, следует сделать ещё одно важное замечание. Хотя язык VHDL является нечувствительным к регистру, этот тезис не распространяется на допустимые значения многозначной логики типа std_logic. Там разрешены только заглавные буквы ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-').
Например, если вы напишите Y<= 'z'; то получите диагностическое сообщение, что литерал (не идентификатор!) перечисления 'z' - не является элементом типа std_logic.
ТИПЫ ДАННЫХ
Прежде чем говорить о возможных типах данных в языке VHDL, рассмотрим весьма поучительный пример – модель логического элемента с программируемой функцией (рис.12).
По мнению специалистов «программируемая аппаратура произведёт в ближайшие годы такой же переворот, как микрокомпьютеры в начале 70-х годов». Это серьёзное заявление, конечно же, адресовано большим интегральным схемам, но мы начнём с низкого уровня.

Рис.12. VHDL-модель логического элемента с программируемой функцией
В описание интерфейса введён параметр настройки FUNC. Он имеет целый тип и может принимать значения в диапазоне от 0 до 7. По умолчанию параметру FUNC определено значение 0, то есть элемент инвертирует данные с входа IN1, игнорируя вход IN2 (верхняя строка в операторе case).
Задавая различные значения параметру FUNC, мы можем программировать элемент на любую из 7 логических функций, определённых в языке VHDL (not, and, or, nand, nor, xor, xnor). Например, если задать FUNC=1 (как в нашем примере), то элемент будет выполнять логическое умножение 2И.
На рис.13 показан уже известный нам мультиплексор MUX2 (см. рис.7, в), реализованный на элементах с программируемой функцией. Элементы DD1 и DD3 настроены на операцию 2И, элемент DD2 реализует функцию 2ИЛИ, а DD4 «превращён» в инвертор.

Рис.13. Мультиплексор MUX2, построенный на элементах с программируемой функцией
На рис.14 показан фрагмент структурной модели MUX2, где хорошо видна настройка отдельных экземпляров элемента PROG_LOGIC на желаемую функцию.

Рис.14. Фрагмент структурной модели, «запрограммированной» на работу
мультиплексора MUX2
Рассмотренный пример демонстрирует статическую настройку, напоминающую программирование ПЛМ пережиганием перемычек. Другими словами, оперативно изменять настройку мы не можем. Действительно, чтобы поменять функцию, надо внести изменения в исходный VHDL-код, а затем выполнить новую компиляцию модели.
Между тем в современной схемотехнике уже давно существует целый класс динамически программируемых устройств с триггерной памятью реконфигурации (FPGA). В них реализована быстрая (оперативная) смена настроек, что позволяет использовать одну и ту же аппаратуру в разных ролях.
Попробуем и мы усовершенствовать своё детище, обеспечив элементу PROG_LOG «динамическую реконфигурацию». Одно из возможных решений показано на рис.15.

Рис. 15. VHDL-модель логического элемента PROG_LOG с динамически
перепрограммируемой функцией
Теперь элемент PROG_LOG имеет управляющий вход, на который подаётся 3-разрядный двоичный код, задающий выполняемую функцию. Другими словами, управляющий вход представляет собой многоразрядный контакт, на который подаётся шинный сигнал. На языке VHDL такой сигнал описывается как одномерный массив типа std_logic_vector. В нашем примере размер массива (ширина шины) задаётся убывающим диапазоном (2 downto 0) и содержит три одиночных сигнала (проводника).
Такое решение позволяет оперативно (в ходе одного эксперимента) управлять логической функцией элемента PROG_LOG. Кстати, свободная позиция “111” позволяет реализовать ещё и режим высокоомного выхода (рис.16).

Рис.16. Оперативное управление логической функцией элемента PROG_LOG
Рассмотренные примеры показывают, что язык VHDL, как и большинство языков программирования, поддерживает различные типы данных. В частности, они дают разработчику аппаратуры возможность непосредственно представлять шины в виде целого числа или массива цифровых сигналов.
Вспомним уже известные нам типы данных. Тип BOOLEAN состоит из двух значений TRUE (истина) и FALSE (ложь). Он декларируется строкой:
type boolean is (false, true);
Этот тип широко применяется в условных операторах, например, в операторе if A='0' then …(см. рис.9) выражение A='0' принимает значения булевского типа.
Тип BITтоже содержит только два значения '0' и '1':
type bit is ('0', '1');
Однако будет ошибкой отождествлять оба типа: значение TRUE не эквивалентно значению '1', а FALSE не есть '0'.
Мы использовали тип bit в самой первой модели (см. рис.1) и отметили его «убогость». Оба названных типа являются предопределёнными, они декларированы в пакете STANDARD, который хранится в файле с аналогичным названием standard. vhd.
В этом же пакете объявлен и физический тип TIME – время:
type time is range - to …(см. рис. 5)
И, наконец, здесь же декларируется уже известный нам целочисленный тип INTEGER:
type integer is range - to ; (см. рис.12)
Кроме названных типов, в старой редакции VHDL’87 широко использовался тип BIT_VECTOR, определяемый как массив битов:
type bit_vector is array (natural range <>) of bit;
По уже известным причинам этот тип заменён более совершенным типом std_logic_vector (версия VHDL’93):
type std_logic_vector is array ( natural range <>) of std_logic;
С ним мы встречались совсем недавно (см. рис.15).
Чтобы закончить разговор о стандартных типах данных, «встроенных» в язык VHDL, приведём оставшиеся типы для «полноты картины».
Символьный тип CHARACTER представляет собой набор символов ASCII-кода:
type character is (nul, ..'0',..'9',.. 'A', ..'Z', ..'a', ..'z', ..del, ..'А', ..'Я', ..'а', ..'я' );
Здесь приведены только некоторые из возможных значений данного типа, чтобы показать, что кириллические символы тоже допустимы в языке VHDL. Правда OrCAD по каким-то причинам их не поддерживает.
Строковый тип STRING декларирован как массив символов:
type string is array (positive range <>) of character;
Обратите внимание, символы заключаются в одиночные, а строки – в двойные кавычки.
В язык VHDL встроен ещё один тип SEVERITY_LEVEL, предупреждающий о степени серьёзности обнаруженной ошибки:
type severity_level is (note, warning, error, failure);
Он содержит всего четыре значения: note – уведомление, warning – предупреждение, error – ошибка и failure – авария (фатальная ошибка). Названный тип используется совместно с оператором assert для контроля временных соотношений в схеме. Позднее мы познакомимся с ним более подробно.
На рис.17 приведена классификация типов данных языка VHDL. Серой заливкой выделены типы, наиболее часто используемые при построении VHDL-моделей цифровой аппаратуры (std_logic, std_logic_vector, time, boolean, integer). Пунктиром показаны типы данных, которые не находят применения в процедурах функциональной верификации, выполняемых методом моделирования.

Рис.17. Классификация типов данных языка VHDL
Может показаться странным, что в язык описания аппаратуры VHDL введён вещественный тип REAL. Между тем, он широко используется для высокоуровневых поведенческих описаний и для определения физических типов. Например, физический тип VOLTAGE (напряжение) можно декларировать так:
type VOLTAGE is range 0.0 to 10.0;
Тип STRING тоже находит применение при моделировании цифровой аппаратуры. Он используется оператором report, чтобы уточнить обнаруженное нарушение временных соотношений в схеме.
Кроме типа в языке VHDL может быть декларирован подтип данных (subtype), задающий подмножество значений данного типа. Например, для типа INTEGER предопределены два подтипа NATURAL и POSITIVE:
subtype natural is integer range 0 to integer'high;
subtype positive is integer range 1 to integer'high;
Первый подтип исключает из диапазона целых чисел все отрицательные числа, второй – сужает диапазон ещё на одну позицию, исключая 0.
Аналогичным образом, подтип DELAY_LENGTH (длина задержки) исключает из физического типа TIME (время) все отрицательные значения:
subtype delay_length is time range 0 fs to time'high;


