Компоненты и технологии, № 9'2002 Софт
Десятка наиболее распространенных ошибок
при проектировании на УН01. в системе Мах+Р1и5 II
Обычно хорошие курсы по языкам программирования содержат главу «десятка наиболее распространенных ошибок при программировании на ...». В полезности такой главы автор убедился на своем опыте при изучении языка С++. Обычно именно к этой главе чаще всего обращаются начинающие разработчики, когда переходят от чтения к созданию своих первых проектов. Отчасти такая глава заменяет более опытного наставника. Поэтому было решено в продолжение разговора о УИРЬ для Мах+Р1ив II создать материал именно в таком ключе. Данная статья включает в себя обобщение опыта автора и знакомых ему разработчиков. Другими словами, ничего кроме практики!
Глеб Варфоломеев, к. т. н.
Как отмечалось автором в предыдущей публикации [4], доступные сейчас в продаже книги по языку VHDL, такие, как, например, издания 2000-2001 годов [1, 2, 5], являются по сути краткими справочниками по VHDL. Причем, либо вообще без привязки к среде моделирования или проектирования [1], либо в привязке только к среде моделирования [2]. Что касается вопросов проектирования на VHDL в системе Max+Plus II, то существует монография [5], содержащая раздел, посвященный этой теме. Увы, внимательное рассмотрение этой главы показало, что она написана в характерном для автора монографии стиле. Глава, посвященная VHDL для Max+Plus II, по сути есть неполный перевод соответствующего раздела справочной системы Max+Plus II, дополненный примерами из той же справочной системы без особых комментариев. Причем вызывает удивление включение в главу описания не поддерживаемых диалектом VHDL Max+Plus II и Quartus конструкций. Зачем было тратить место в книге и внимание читателей на описание конструкций, после которых автор честно писал: «в Max+Plus II не поддерживается»? Так что подготовка насыщенного полезной информацией и примерами практического курса по проектированию на VHDL в системе Max+Plus II подобного тому, что написано для AlteraHDL [3], остается делом открытым. Отчасти закрыть эту брешь призваны нынешняя и предыдущая [4] публикации автора.
1. Вы забыли поставить «;»
в нужном месте
Самые примитивные, но и самые распространенные ошибки даже среди опытных разработчиков — это синтаксические ошибки, в частности, неверная расстановка точек с запятой. Чаще всего неправильная расстановка точек с запятой происходит в следующих местах. В блоке ENTITY:
_def ault_value;
___default_value <—
ENTITY_____entity_name IS
GENERIC(___parameter_name : string ::
__parameter_name : integer:
); <— А здесь обязательно!
PORT(
input_name, input_name : IN STD_LOGIC;
input_vector_name : IN STD_LOGIC;
__output_name,_____output_name : OUT STD_LOGIC — здесь «;» нет!
); <— А здесь обязательно!
END_____entity_name; <— И здесь не забыть!
И в блоке архитектуры:
ARCHITECTURE a OF____entity_name IS — здесь «;» нет!
SIGNAL signall : STD_LOGIC;
BEGIN <— здесь нет!
PROCESS(signall) <— здесь тоже нет!
END PROCESS; <— А здесь обязательно!
END a; <— И здесь не забыть!
А вообще лучший способ не делать синтаксических ошибок в коде на языке VHDL — это воспользоваться подменю VHDL Template... меню Template при создании нового проекта. Кроме того, ничто не мешает вам пользоваться этим меню как справочником для проверки правильности уже написанного кода.
Не забывайте также, что в VHDL не различаются строчные и прописные буквы, поэтому переменные var и VAR — это одна и таже переменная. Индексация массивов происходит с помощью круглых скобок: D(i). Присваивание значений сигналам происходит с помощью знака «<=», а переменным — с помощью знака «:=».
2. Некорректное приведение типов
На разработку языка VHDL, по-видимому, достаточно большое влияние оказал язык Pascal. В частности, от Pascal VHDL унаследовал строгую типизацию данных. Поскольку тип Real в VHDL Max+Plus II отсутствует, то автоматическое преобразование ти--------www.finestreet.ru---------------------------
Компоненты и технологии, № 9'2002
пов происходит только между подтипами, например, INTEGER и POSITIVE. Чаще всего пытаются работать одновременно с типами STD_LOGIC(_VECTOR) и BIT(_VECTOR).
В VHDL Max+Plus II нельзя непосредственно связывать разнотипные данные. Для преобразования разнотипных данных существует большое число так называемых функций преобразования. Ниже перечислены основные из них.
Для взаимного преобразования типов SIGNED, UNSIGNED и INTEGER в пакете std_logic_arith библиотеки ieee имеется три типа функций для взаимного преобразования типов. Функция CONV_INTEGER(x) превращает аргумент x типа INTEGER, UNSIGNED, SIGNED или STD_ULOGIC в величину типа INTEGER. Максимальный размер аргумента x составляет 31 бит для типа UNSIGNED, 32 бит для типа SIGNED. Для типа INTEGER величина x лежит в пределах от -2147483647 до 2147483647, что соответствует 32 бит с учетом знака. Функции CONV_UNSIGNED(x) и CONV_SIGNED(x) превращают аргумент x типа INTEGER, UNSIGNED, SIGNED или STD_ULOGIC в величину типа UNSIGNED и SIGNED, соответственно. В отличие от функции CONV_INTEGER, указанные функции снабжены дополнительным параметром SIZE. Этот параметр указывает разрядность выходной величины. Если разрядность x больше SIZE, то выходная величина будет равняться величине x, округленной до SIZE битов сверху. Если разрядность x меньше SIZE, то старшие разряды выходной величины будут заполнены нулями. Еще одна полезная функция — это CONV_STD_LOGIC_VECTOR. Эта функция приведения типов является специфической чертой VHDL. Программист, работающий на каком-либо языке «обычного» программирования, просто не поймет, зачем нужна функция преобразования переменной в массив битов. Но разработчик, пишущий на языке VHDL, всегда должен помнить о разрядности данных. Поэтому функция CONV_STD_LOGIC_VECTOR, приводящая типы INTEGER, UNSIGNED, SIGNED и STD_LOGIC к вектору типа STD_LOGIC_VECTOR, может оказаться очень полезной. Функция CONV_STD_LOGIC_VEC-TOR снабжена параметром SIZE, смысл которого тот же, что и для CONV_SIGNED.
Для использования вышеуказанных функций не забудьте указать
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
Как следует из этого заголовка, прототипы функций преобразования данных хранятся в файле \ieee\arith.vhd. Фирма Altera также любезно предоставила исходник тела (body) пакета std_logic_arith, который хранится в файле \ieee\arithb.vhd. Так что в принципе можно осуществить персональную настройку этого пакета под свои нужды.
Следует также напомнить об использовании констант в операциях присваивания и сравнения. Тип INTEGER (и его подтипы) допускает использование любых числовых
констант по десятичному, восьмеричному, двоичному и шестнадцатеричному основаниям. Остальным типам нужно присваивать либо строковую константу (в духе языка C, например, X»FE»), если они являются массивами, либо одиночный символ (например, '1'), допустимый для данного типа. Чтобы не ошибиться, лучше всего просмотреть определения нужного типа в соответствующем заголовочном файле библиотеки или заглянуть в справочник [1, 2].
3. Вы забыли подключить необходимую библиотеку
Язык VHDL имеет минимальный набор типов, функций и операций, которые содержатся в основной библиотеке std в пакете standard. Эта библиотека считается подключенной по умолчанию. Если вы хотите использовать более широкий набор типов или операций, например тип STD_ULOGIC, то необходимо подключать внешние библиотеки. Кроме того, VHDL поддерживает перегрузку операторов и функций, то есть переопределение действия определенного оператора для нового типа (или типов) данных (об этом мы поговорим далее). Библиотеки подключаются к VHDL-описаниям в стиле языка Pascal — вы просто указываете, какие функции хотите использовать, и предполагается, что указанные функции существуют в уже откомпилированном виде. Текст и заголовок библиотеки не включается в ваше описание. Уведомить компилятор, что вы желаете подключить нужную библиотеку можно так:
LIBRARY ieee, work;
USE ieee.std_logic_1164.all;
USE work.myreg.all;
В первой строке указано, что мы хотим включить в наш проект библиотеки ieee и work. В Max+Plus II имеются стандартные библиотеки altera, ieee и lpm. Библиотека ieee стандартная [1, 2] и в особых комментариях не нуждается. Библиотека lpm и пакет lpm_pack содержат определения основных «мегафункций» Max+Plus II, таких, как LPM_BUSTRI, LPM_MULT, LPM_DIVIDE и т. д. Библиотека altera и пакет maxplus2 содержат описания для многих логических микросхем серии 74хх, а также наиболее часто используемых устройств типа триггеров, буферов, счетчиков в коде Грея и т. д. Библиотека altera и пакет megacore содержат описания доступных «мегафункций», таких, как быстрое преобразование Фурье, аналоги некоторых микросхем «обвески» процессоров intel 8080 и motorola 68хх, преобразователи сигналов цветности и т. д. Пополнить пакет megacore можно путем покупки дополнительных мегафункций, предлагаемых как самой компанией Altera, так и многочисленными сторонними производителями. Стандартные библиотеки расположены в подкаталоге \maxplus2\vhdlxx, где xx — 87 или 93 в зависимости от того, какой диалект VHDL выбран переключателем VHDL Version в диалоговом окне VHDL Netlist Reader Settings меню Interfaces.
Библиотека work на самом деле библиотекой не является, а просто указывает компилятору, что в проекте будут использоваться пакеты, находящиеся в рабочем каталоге проекта или в текущем файле. Скажем, в указанном выше примере компилятор будет искать пакет myreg в том же каталоге, где расположен компилируемый в данный момент пакет. Следует отметить, что перед использованием пакет myreg должен быть откомпилирован (если он только не находится в том же файле, что и проект), иначе при компиляции нашего примера будет выдана ошибка «primary unit «myreg» denoted by prefix «WORK» must exist in the library» («Основной модуль «myreg», обозначенный префиксом «WORK», должен находится в библиотеке»).
В проект можно включать библиотеки, находящиеся в других каталогах. Для этого достаточно добавить пути к ним в окне VHDL Netlist Reader Settings. По сути, в системе Max+Plus II библиотека является просто путем к каталогу, в котором хранятся откомпилированные пакеты. А имя библиотеки, которое вводится в пункте Library Name окна VHDL Netlist Reader Settings, является просто кличкой соответствующего каталога. Поскольку VHDL не включает сам пакет в ваш проект, то вы обязательно должны указывать используемые вами пакет и библиотеку, даже если они уже включены в другой используемый в проекте пакет [2]. Например, пусть вы объявили о включении в свой проект пакета altera.lpm, в заголовке которого содержится включение пакета ieee.std_logic_1164. Однако, если вы хотите использовать в своем проекте, например, тип STD_LOGIC, то вы обязательно должны явно объявить об использовании ieee.std_logic_1164:
LIBRARY altera;
USE altera.maxplus2.ALL;
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
Ключевое слово ALL в конце оператора USE означает, что используются все описания, которые содержит указанный пакет. В большинстве случаев этого достаточно, поскольку, как уже говорилось выше, компилятор все равно включит в проект из библиотеки только то, что в нем используется. Однако, если вы используете перегрузку функций и операторов или, проще говоря, в используемых пакетах содержатся определения одной и той же функции, то при компиляции могут возникнуть проблемы. Чтобы их избежать, необходимо либо указать, какие конкретно описания будут использоваться в вашем проекте:
USE altera.maxplus2.a161_mux;
USE altera.maxplus2.a_7400;
USE ieee.std_logic_arith.»+»;
либо вообще отказаться от использования слова USE и в архитектурном разделе описания явно указывать, какому пакету принадлежит используемый компонент:
comp1 : altera.maxplus2.a_7400 PORT MAP (a, b, c);
result <= ieee.std_logic_arith.CONV_UNSIGNED(var,8);
e
Компоненты и технологии, № 9'2002
Правда, такой вариант не подходит для операций, обозначаемых спецсимволом (-, +, /,
<= и т. д.). Например, такая строка вызовет ошибку:
var := ieee.std_logic_arith.»+»(op1, op2); -- op1 + op1 эквивалентно «+»(op1, op2)
«Error:... Subprogram error: can't interpret subprogram call» («Не могу понять такого вызова подпрограммы»). Используемую библиотеку и пакет необходимо указывать явно, даже если пакет размещен в том же файле, что и декларация проекта, например:
LIBRARY work;
USE work.myreg.reg12;
4. Вы перепутали сигнал и переменную
Еще одна очень распространенная ошибка. Поскольку компилятор Max+Plus II по понятным причинам игнорирует задание задержек, то разница между сигналом и переменной, на первый взгляд, кажется нечеткой. На самом деле разница между сигналом и переменной есть и она существенная. Во-первых, сигнал нельзя объявлять в процессе (блоке последовательных операторов), а переменную — за пределами этого блока. Во-вторых, нужно помнить, что сигнал — это физически существующее в схеме соединение, даже если данный сигнал в процессе компиляции был исключен. Поэтому у сигнала не может быть более одного источника, если только сигнал не объявлен как STD_ULOGIC или STD_ULOGIC_VECTOR. Правда, толку от указанных типов сигналов в Max+Plus II немного. Фактически смысл использования сигналов, порожденных от типа URESOLVED [1], есть только в случаях, когда вам необходимы выходы с Z-состоянием. Но если таких выводов будет больше, чем количество физически доступных в выбранной микросхеме Z-буферов (установленных, как правило, на выводах микросхемы), и компилятор не сможет их преобразовать в набор мультиплексоров, то будет выдана ошибка. В отличие от сигналов, у переменных может быть сколько угодно источников, поскольку, как и для переменных в языках программирования, значение переменной будет определяться последним присвоением. Так, в следующем примере значение переменной var по завершении процесса будет равно значению сигнала signal2:
PROCESS
BEGIN
var := signal1; var := signal2;
END PROCESS;
А первое присвоение компилятор Max+Plus II просто проигнорирует, не выдав никакого сообщения. Другими словами, введение в VHDL переменных позволяет писать, так же как и на «обычных» языках программирования. Со всеми присущими этому способу недостатками с точки зрения создания устройств. Дело в том, что мы никогда не должны забывать, что программы выполняются всегда последовательно на одном-единствен-
ном процессоре (очень редко на нескольких). В то же время проекты, созданные на VHDL, можно сравнить с программами, выполняющимися одновременно на нескольких процессорах, число которых оценивается количеством блоков процессов плюс количеством параллельных операторов в проекте. Причем число этих процессоров может быть очень велико — практически ограничено только емкостью выбранной микросхемы (или микросхем). Поэтому конструкцию PROCESS и работу с переменными внутри нее имеет смысл применять только для описания работы синхронных последовательных устройств.
В системе Max+Plus II сигналы имеют еще одно немаловажное отличие от переменных. Как правило, сигнал, если он только не был исключен в процессе компиляции, заносится в SNF-файл. Это значит, что при создании SCF-файла для моделирования устройства, вы сможете добавить эти сигналы с помощью команды меню Node/Enter Nodes From SNF... и посмотреть их состояние. Переменные никогда не сохраняются в SNF-файле, и, значит, анализ их состояния при моделировании не возможен.
5. Вы пытаетесь использовать битовый сигнал вместо логической переменной
Чаще всего такую ошибку допускают разработчики, переходящие с языка AHDL или активно использующие оба языка параллельно. В языке AHDL, если не считать машин состояния, как было отмечено автором ранее [4], существует только один тип данных — группы (или массивы) логических уровней. В VHDL битовый тип (см. файл std\stan-dart.vhd) определен как
type BIT is ('0', '1');
а логический тип — как
type BOOLEAN is (FALSE, TRUE);
Оба типа являются бинарными перечислимыми предопределенными в VHDL типами. Однако их использование различно, аналогично языку Pascal. Логический тип можно непосредственно применять в конструкциях с проверкой условия, таких, как if и when. Битовый тип такой возможности не предоставляет:
ENTITY condsig2 IS
PORT
( high : IN BOOLEAN;
mid : IN BIT;
q V : OUT INTEGER RANGE 0 TO 3
); END;
ARCHITECTURE maxpld OF condsig2 IS
BEGIN
q <= 3 WHEN high ELSE -- когда high =true
2 WHEN mid = 1' ELSE -- WHEN mid ELSE вызовет
сообщение
0; -- об ошибке:
--Error: Type error: type n conditional waveform must be «BOOLEAN»
--Error: +: type is
--Error: +: port: type BIT
END maxpld;
Оба типа могут быть задействованы в логических операциях [2]. Однако логические операции, определенные в std/standart.vhd, допускают только однотипные аргументы и результат. То есть в выражении a <= b AND c, a, b и c — либо одновременно битовые, либо одновременно логические. Впрочем, ничто не мешает, если на то есть потребность, перегрузить логические операции для необходимых комбинаций типов аргументов и возвращаемого значения (см. далее п. 10. «Перегруженные функции»). Если вы хотите создавать проект в стиле языка AHDL, то можно выбирать сигналы и переменные логического типа, если вам не нужны группы (массивы) сигналов. К сожалению, автору неизвестен какой-либо широко распространенный пакет для VHDL, в котором бы описывались тип массива логических переменных и перегруженные логические и арифметические функции для работы с этим типом. Так что тем, кто переходит с языка AHDL, придется либо создать такой пакет, либо привыкать к строгим правилам языка VHDL.
6. Рекурсивное использование процесса
Идея этого примера, тесно связанного с п. 4. «Вы перепутали сигнал и переменную», заимствована из руководства по VHDL [2]. Предположим, что вы создали следующий проект:
ENTITY raz6 IS PORT (a
END raz6;
: IN BIT;
: IN INTEGER RANGE 0 TO 31;
: OUT INTEGER RANGE 0 TO 31);
ARCHITECTURE maxpld OF raz6 IS SIGNAL d : INTEGER RANGE 0 TO 31;
BEGIN
PROCESS
BEGIN
d <= b+c;
e <= d+3;
WAIT UNTIL a = '1' AND a'EVENT;
END PROCESS;
END maxpld;
На первый взгляд, все очевидно: после прихода положительного перепада на вход a на выходе e должна появляться сумма сигналов на входных шинах b и c, увеличенная на три. Однако, если мы осуществим ее моделирование, то заметим, что появление нужного значения на выходной шине e запаздывает на один тактовый сигнал (рис. 1),
Это происходит по той причине, что все назначения сигналов в теле процесса происходят после его окончания. В данном случае — после конструкции WAIT, то есть после тактового перепада. Поэтому в строке e <= d + 3 происходит сложение тройки с тем значением d, которое было на входе процесса, а не с тем значением, которое получится после выполнения строки e <= d + 3. Таким образом, вместо последовательного выполнения операций, мы получаем рекурсивный процесс с глубиной рекурсии, равной двум, Для того чтобы все-таки заставить процесс работать так, как он и должен работать в теории, то есть как блок последовательно выполняющихся операций, лучше всего использо-
b, c
e
Компоненты и технологии, № 9'2002
вать переменные [2]. Как было показано выше в п. 4, переменная не существует физически, а потому она не может накапливать задержки. Теперь наш проект может быть переписан в виде:
ENTITY raz6 IS PORT (a
END raz6;
: IN BIT;
: IN INTEGER RANGE 0 TO 31;
: OUT INTEGER RANGE 0 TO 31);
ARCHITECTURE maxpld OF raz6 IS BEGIN PROCESS
VARIABLE d : INTEGER RANGE 0 TO 31;
BEGIN
d := b+c; e <= d+3;
WAIT UNTIL a = '1' AND a'EVENT;
END PROCESS;
END maxpld;
В правильности его работы нас убеждает рис. 2.
7. Проигнорирован сигнал, заданный в списке чувствительности процесса
Достаточно часто встречающаяся ошибка, виной которой является не разработчик, а сама система. Стандартом языка VHDL предусмотрено, что запись
PROCESS (a) IS BEGIN
END PROCESS;
означает, что последовательные операторы, расположенные между ключевыми словами BEGIN и END, будут выполняться, если и только если произойдет изменение сигнала a. Если сигнал a однобитный, то, проще говоря, весь процесс запустится по фронту или спаду сигнала a. Если аргумент у слова PROCESS опущен, то процесс выполняется всегда независимо от состояния входных сигналов. Соответственно компилятор, работающий по таким правилам, должен бы был сгенерировать некоторый разрешающий импульс в момент изменения сигнала a. Для однобитового сигнала такая схема, например, может быть выполнена на 1-3 элементах задержки и элементе «исключающее ИЛИ». Так в теории. Но разработчики системы Max+Plus II почему-то решили по-иному, Выделитель списка цепей (Netlist Extractor) компилятора попросту игнорирует аргумент. Мало того, если a был введен в схему только ради того, чтобы управлять процес---------------------www.finestreet.ru -
сом, то при компиляции сигнал a будет признан ненужным (Ignored unnecessary INPUT pin У)и выкинут из схемы. К сожалению, чтобы заставить работать конструкцию типа PROCESS(a) как должно, придется добавлять дополнительные конструкции. Как указано в литературе [1], конструкция, аналогичная PROCESS(a), есть
WAIT ON a; END PROCESS
Увы, такую конструкцию нам тоже не удастся использовать из-за какой-то внутренней ошибки Max+Plus II. В списке шаблонов VHDL, кстати, для операции WAIT предлагается только
форма «WAIT UNTIL _________clk_name = '1';».
Не удастся также применить эквивалентную конструкцию:
WAIT UNTIL (a = '0' AND a'EVENT) OR (a = '1' AND a'EVENT);
Так что единственный способ заставить работать процесс как надо — это скопировать первый блок и сделать второй:
WAIT UNTIL a = '0' AND a'EVENT;
END PROCESS;
PROCESS
BEGIN
(копия того, что стоит здесь в первом блоке WAIT UNTIL a = '1' AND a'EVENT;
END PROCESS;
К счастью, как правило, в большинстве проектов достаточно, чтобы изменения происходили либо только по фронту, либо только по спаду тактового сигнала. Хуже обстоят дела, если сигнал a — многоразрядный. Атрибут 'EVENT в Max+Plus II нельзя применять к многобитовому сигналу (см. следующий пункт). Для того чтобы создать конструкцию, эквивалентную PROCESS (многоразрядный сигнал), необходимы некоторые ухищрения. Об этих ухищрениях автор надеется рассказать в одной из следующих публикаций.
8. Некорректное использование атрибута 'EVENT
Единственный допустимый сигнальный атрибут 'EVENT в Max+Plus II можно использовать только при соблюдении следующих правил:
1. Внутри процесса.
2. Сигнал, к которому применяется атрибут, должен быть типа 8ТО_ЬОС1С или 8ТО_иШС1С.
3. В составе условия для условных операторов при обязательном наличии второго статического условия.
Для группы сигналов (массива) его использовать нельзя. Например, при попытке скомпилировать файл
) THEN
если сигнал a был описан как
a, b
: IN STD_LOGIC_VECTOR(0 TO 7);
вы тут же получите от компилятора ошибку: «Unsupported feature error: EVENT attribute not supported for bit of multi-bit signals» («Атрибут 'EVENT не поддерживается для одного бита из массива битовых сигналов»). Если же попытаться применить атрибут 'EVENT сразу к целому массиву (что допускается стандартом VHDL'93), например так:
PROCESS (a)
BEGIN
IF (a'event AND a = «01») THEN
компилятор выдаст малопонятное сообщение о какой-то внутренней ошибке (по крайней мере, в версиях 9.4, 9.6 и 10.0). Не позволит вам компилятор использовать атрибут 'EVENT и за пределами блока последовательных операторов, ограниченных словами PROCESS ... END PROCESS. Например, такая конструкция, поддерживаемая VHDL'93:
вызовет ошибку при компиляции: «non-lo-cally-static attribute names are not supported» («нелокально-статические атрибуты не поддерживаются»). Смысл этой фразы в том, что атрибут 'EVENT допустимо использовать только так:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
b, c
e
PROCESS
PROCESS
BEGIN
Компоненты и технологии, № 9'2002
ENTITY EVENT IS PORT
ENTITY EXAMP1 IS PORT
: IN STD_ULOGIC;
ARCHITECTURE a OF EVENT IS BEGIN
PROCESS (d) -- d должен быть указан в списке чувствительности BEGIN -- Только в последовательном блоке
IF (d'event AND d = '1') THEN -- Самостоятельное использование EVENT не допускается (нельзя, например, написать IF d'event THEN)
END IF;
END PROCESS;
END a;
Кроме архитектурного тела, атрибут 'EVENT формально можно использовать в функциях и процедурах. Но при этом, в соответствии со стандартом VHDL, декларировать сигналы внутри функций и процедур нельзя. А передача сигнальных параметров не поддерживается диалектом VHDL Max+Plus II. В результате возникает смешная ситуация: следующий код, позаимствованный автором из учебника [1]
FUNCTION rising_edge(SIGNAL s : bit) RETURN boolean IS BEGIN RETURN s='1' AND s'event;
END;
успешно скомпилируется. Но на любую попытку использовать функцию rising_edge в архитектурном теле компилятор отреагирует сообщением: «Error: Unsupported feature error: signal parameter in a subprogram is not supported» («Ошибка неподдерживаемой конструкции: сигнальный параметр не поддерживается для подпрограмм»).
9. Вы инициализировали сигнал в разделе объявлений
VHDL'93 позволяет задавать начальные значения для сигналов в разделе объявлений в стиле языка С. Этот прием выглядит достаточно красиво и позволяет создавать более компактные описания. Но, увы, компилятор Max+Plus II не оценит вашего стремления к эстетике. Например, такой код
SIGNAL var : INTEGER RANGE 0 TO 2**8-1 := 10;
не вызовет нареканий со стороны компилятора. Однако, как указано в справке по VHDL Max+Plus II, компилятор (не сложно понять, почему это сделано) проигнорирует начальное значение «:=10». В результате, несмотря на то, что сигнал var объявлен, физически в синтезируемой схеме он еще не присутствует. И если вы попытаетесь использовать этот сигнал как источник, то поставите логический синтезатор (Logic Synthesizer) компилятора в сложную ситуацию. Поэтому стоящий в цепочке компилирования перед логическим синтезатором генератор списка цепей (Database builder) отреагирует на подобный код
a
ret
END EXAMP1;
: IN INTEGER RANGE 0 TO 2**8-1;
: OUT INTEGER RANGE 0 TO 2**16-1
ARCHITECTURE a OF EXAMP1 IS SIGNAL var : INTEGER RANGE 0 TO 2**8-1 := 10;
BEGIN ret <= var;
END a;
ошибками типа, «Node ret7 missing source» («У вывода ret7 отсутствует источник»). Исправить эту ситуацию можно, например, так
ENTITY EXAMP1 IS PORT
a
ret
END EXAMP1;
: IN INTEGER RANGE 0 TO 2**8-1;
: OUT INTEGER RANGE 0 TO 2**16-1
ARCHITECTURE a OF EXAMP1 IS SIGNAL var : INTEGER RANGE 0 TO 2**8-1;
BEGIN var <= 10; ret <= var;
END a;
Таким образом, начальное присвоение значения сигналам имеет смысл разве что как комментарий. Что касается переменных, то операция присваивания начальных значений переменным в блоке последовательных операторов так же бессмысленна, как и для сигналов. При этом инициализация переменных в функциях и подпрограммах разрешается. Это подтверждает следующий пример
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
ENTITY adder2 IS
PORT (op1, op2 a
result
END adder2;
IN INTEGER RANGE 0 TO 255; IN BIT
OUT UNSIGNED(7 downto 0));
ARCHITECTURE maxpld OF adder2 IS function CNST return INTEGER is
VARIABLE s : INTEGER := 2; -- Делаем начальное присвоение begin return s; end;
BEGIN PROCESS IS
VARIABLE var : INTEGER;
BEGIN
var := CNST;
result <= CONV_UNSIGNED(op1 + op2 + var,8);
WAIT UNTIL (a = '1' AND a'EVENT);
END PROCESS;
END maxpld;
В данном примере в теле функции СЫБТ происходит начальное присвоение переменной б числа два. Моделирование показывает, что схема работает правильно (рис. 3).
10. Перегруженные функции
Диалект УИЭЬ Мах+РІш II поддерживает перегрузку функций, операторов и процедур подобно языку С++. Для читателей, которые не знакомы с этой техникой, дадим краткое пояснение. Перегрузка используется для того, чтобы можно было одну и ту же операцию использовать для разных типов данных. Например, в математике операция скалярного умножения определена для очень широкого класса объектов: для скаляров (обычное умножение), векторов (в том числе бесконечномерных), функций из пространства Ь^и т. д. Аналогичная потребность появилась и в языках программирования. В принципе, перегрузка не обязательна, можно просто для разных объектов объявить разные функции. Например, для сложения чисел использовать операцию а+Ь, а для сложения массивов (векторов) одинаковой размерности использовать функцию 5иМУБСТ(а,Ь). Но очевидно, что это не слишком удобно. Гораздо удобнее было бы пользоваться записью «а+Ь», не заботясь о типе операндов а и Ь. Для этого необходима перегрузка оператора «+», то есть под оператором «+» на самом деле скрываются две совершенно различные операции. Возникает вопрос, какую из них выбрать в конкретном случае? В языке С++ это решается путем анализа типов входных переменных. Но разработчики языка УИЭЬ пошли дальше: выбор конкретной операции определяется не только типами входных аргументов, но и их количеством и типом выходных данных. Например, так выглядит определение многократно перегруженной операции «+» в заголовке пакета 8І^^іс_агіїЬ, находящегося в файле \ieee\arith.vhd.
function «+»(L: UNSIGNED; R: UNSIGNED) return UNSIGNED; function «+»(L: SIGNED; R: SIGNED) return SIGNED; function «+»(L: UNSIGNED; R: SIGNED) return SIGNED; function «+»(L: SIGNED; R: UNSIGNED) return SIGNED; function «+»(L: UNSIGNED; R: INTEGER) return UNSIGNED; function «+»(L: INTEGER; R: UNSIGNED) return UNSIGNED; function «+»(L: SIGNED; R: INTEGER) return SIGNED; function «+»(L: INTEGER; R: SIGNED) return SIGNED; function «+»(L: UNSIGNED; R: STD_ULOGIC) return UNSIGNED; function «+»(L: STD_ULOGIC; R: UNSIGNED) return UNSIGNED; function «+»(L: SIGNED; R: STD_ULOGIC) return SIGNED; function «+»(L: STD_ULOGIC; R: SIGNED) return SIGNED;
i «+»(L: UNSIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; i «+»(L: SIGNED; R: SIGNED) return STD_LOGIC_VECTOR; i «+»(L: UNSIGNED; R: SIGNED) return STD_LOGIC_VECTOR; i «+»(L: SIGNED; R: UNSIGNED) return STD_LOGIC_VECTOR; i «+»(L: UNSIGNED; R: INTEGER) return STD_LOGIC_VECTOR; i «+»(L: INTEGER; R: UNSIGNED) return STD_LOGIC_VECTOR; i «+»(L: SIGNED; R: INTEGER) return STD_LOGIC_VECTOR; i «+»(L: INTEGER; R: SIGNED) return STD_LOGIC_VECTOR; i «+»(L: UNSIGNED; R: STD_ULOGIC) return STD_LOGIC_VECTOR; i «+»(L: STD_ULOGIC; R: UNSIGNED) return STD_LOGIC_VECTOR;
d
END EVENT
Компоненты и технологии, № 9'2002
function «+»(L: SIGNED; R: STD_ULOGIC) return STD_LOGIC_VECTOR; function «+»(L: STD_ULOGIC; R: SIGNED) return STD_LOGIC_VECTOR;
Всего, как видно, реализовано целых двадцать четыре варианта операции «+». Но с учетом допустимых в VHDL Max+Plus II типов данных, это не все возможные комбинации. В частности, нет, казалось бы, очевидной комбинации, имеющей тип INTEGER. Это часто приводит к ошибкам. Например, в следующем проекте:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
ENTITY adder2 IS
PORT (op1, op2 : IN UNSIGNED(7 downto 0); result : OUT INTEGER);
END adder2;
ARCHITECTURE maxpld OF adder2 IS BEGIN
result <= op1 + op2;
END maxpld;
от компилятора будет получено следующее:
Error: Line 20: Type error: type in waveform element must be «INTE-GER»
Error: +: type is
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, UNSIGNED, RETURN UNSIGNED]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, UNSIGNED, RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[SIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[SIGNED, RETURN SIGNED] Error: +: ieee.std_logic_arith.»+»[UNSIGNED,
RETURN UNSIGNED]
Error: +: ieee.std_logic_arith.»+»[std_ulogic, SIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[SIGNED, std_ulogic,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[std_ulogic, UNSIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, std_ulogic,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[INTEGER, SIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[SIGNED, INTEGER,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[INTEGER, UNSIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, INTEGER,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[SIGNED, UNSIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, SIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[SIGNED, SIGNED,
RETURN std_logic_vector]
Error: +: ieee.std_logic_arith.»+»[std_ulogic, SIGNED,
RETURN SIGNED]
Error: +: ieee.std_logic_arith.»+»[SIGNED, std_ulogic,
RETURN SIGNED]
Error: +: ieee.std_logic_arith.»+»[std_ulogic, UNSIGNED,
RETURN UNSIGNED]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, std_ulogic,
RETURN UNSIGNED]
Error: +: ieee.std_logic_arith.»+»[INTEGER, SIGNED,
RETURN SIGNED]
Error: +: ieee.std_logic_arith.»+»[SIGNED, INTEGER,
RETURN SIGNED]
Error: +: ieee.std_logic_arith.»+»[INTEGER, UNSIGNED,
RETURN UNSIGNED]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, INTEGER,
RETURN UNSIGNED]
Error: +: ieee.std_logic_arith.»+»[SIGNED, UNSIGNED,
RETURN SIGNED]
Error: +: ieee.std_logic_arith.»+»[UNSIGNED, SIGNED,
RETURN SIGNED]
Error: +: ieee.std_logic_arith.»+»[SIGNED, SIGNED,
RETURN SIGNED]
Error: +: STD.STANDARD.»+»[TIME, TIME, RETURN TIME] (implicit)
Error: +: STD.STANDARD.»+»[TIME, RETURN TIME](implicit) Error: +: STD.STANDARD.»+»[REAL, REAL,
RETURN REAL](implicit)
Error: +: STD.STANDARD.»+»[REAL, RETURN REAL](implicit) Error: +: STD.STANDARD.»+»[INTEGER, INTEGER,
RETURN INTEGER](implicit)
Error: +: STD.STANDARD.»+»[INTEGER,
RETURN INTEGER](implicit)
Error: +: STD.STANDARD.»+»[Universal$Real, Universal$Real, RETURN Universal$Real](implicit)
Error: +: STD.STANDARD.»+»[Universal$Real,
RETURN Universal$Real](implicit)
Error: +: STD.STANDARD.»+»[universal_integer, universal_integer, RETURN universal_integer](implicit)
Error: +: STD.STANDARD.»+»[universal_integer,
RETURN universal_integer](implicit)
Этой многострочной ошибкой компилятор сообщает о том, что для выполнения операции «+» необходимо либо заменить типы op1 и op2 на INTEGER, либо заменить типы всех переменных так, чтобы была пригодна одна из форм операции «+». Для удобства компилятор в последующих строках перечисляет все эти формы. Любопытно, что в последних четырех строчках он перечисляет даже формы, которые требуют неподдерживаемых в Max+Plus II типов данных (REAL и TIME). Для решения подобных проблем следует либо изменить тип операндов, либо использовать функции приведения типов (см. выше п. 2. «Некорректное приведение типов»), как показано здесь:
ARCHITECTURE maxpld OF adder2 IS BEGIN
result <= CONV_INTEGER(op1 + op2);
END maxpld;
Если предполагается частое использование такой операции, то можно также добавить
еще одну перегрузку операции «+», которая бы сразу давала искомое решение:
function «+»(L: UNSIGNED; R: UNSIGNED) return INTEGER is VARIABLE s : UNSIGNED; begin s := L+R;
return CONV_INTEGER(s); end;
Если предполагается использовать перегруженную таким образом операцию суммирования в нескольких проектах, то лучше вынести ее в отдельный пакет,
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
PACKAGE sumI_package IS
function «+»(L: UNSIGNED; R: UNSIGNED) return INTEGER;
END;
PACKAGE body sumI_package IS
function «+»(L: UNSIGNED; R: UNSIGNED) return INTEGER is
VARIABLE s : UNSIGNED;
begin
s := L+R;
return CONV_INTEGER(s);
end;
END;
не забыв указать в проекте, что вы желаете использовать новую функцию:
LIBRARY work;
USE work.sumI_package.all;
В заключение отметим, что несмотря на достаточно серьезные ограничения диалекта VHDL Max+Plus II, о которых было сказано выше, в опытных руках VHDL становится мощным инструментом создания проектов для ПЛИС в системе Max+Plus II.
Литература
1. Бибило П. Н. Основы языка VHDL. М.: Со-лон-Р. 2000.
2. Суворова Е. А., Шейнин Ю. Е. Язык VHDL для проектирования систем на СБИС. СПб.: СПбГУАП. 2001,
3. Антонов А. П. Язык описания цифровых устройств AlteraHDL. Практический курс. М.: РадиоСофт. 2001.
4. Варфоломеев Г. Проектирование в системе Max+Plus II: VHDL против AHDL // Компоненты и технологии. 2002. № 7,
5. Стешенко В. Б. Плис фирмы Altera: проектирование устройств обработки сигналов. М.: Додэка. 2000.
е