Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lektsii.doc
Скачиваний:
58
Добавлен:
15.12.2018
Размер:
631.81 Кб
Скачать

Variable X:out real);

Она может быть вызвана со связыванием параметров:

variable sl,s2:natural:=12345; variable Random:real;

Uniform(X=> Random, SEEDl=>sl,SEED2=>s2);

или без связывания параметров:

uniform(sl,s2,Random);

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

Возврат из процедуры или функции

Оператор возврата из подпрограммы - последовательный опера­тор return - немедленно прерывает выполнение процедуры или вы­зова функции и возвращает управление в программу, вызвавшую процедуру или функцию.

Операторы assert и report

Эти операторы были введены в язык VHDL для выявления оши­бок моделирования и сообщения о них на консоль. У оператора ло­вушки assert следующий синтаксис:

\оператор assert\: := assert \6улевское выражение\ [report \строка сообщениях][severity \выражение\];

Здесь \6улевское выражение\ - проверка какого-либо условия пра­вильности моделирования, которое равно false, если найдена ошиб­ка и true, если моделирование верно; \строка сообщениях - выраже­ние типа string, представляющее строку сообщения о причине ошибки. Например, оператор

assert a(i)='0'

report "бит " & integer'image(i) &" не равен О";

вызовет выдачу на экран дисплея сообщения:

  • : error: бит 1 не равен О

  • : Time: 2000 ns, Iteration: 0, TOP instance.

если а(1) * 0 на 2-й микросекунде моделирования. Здесь атрибут integer'image возвращает строку, в которой представлено значение целого i в строковом виде. Выражение \выражение\ имеет предопре­деленный тип severity_level, состоящий из элементов note, warn-

ing, error и failure. Значение выражения соответствует уровню критичности найденной ошибки и при самом высоком уровне failure моделирование останавливается. Например, если требуется остановить моделирование, можно записать оператор:

assert 1/=1 report "конец моделирования " severity failure;

Если не нужно следить за ошибкой, а только вывести сообщение о ходе моделирования, то применяют оператор сообщения с синтак­сисом:

report \строка сообщениях [severity \выражение\];

Предыдущий пример можно переписать как:

report "конец моделирования " severity failure;

Оператор процесса

Оператор процесса - это параллельный оператор, представляю­щий основу языка VHDL. Его упрощенный синтаксис:

\оператор процесса\::=[postponed]

process [С\имя сигналах {Димя сигнала\})] [is]

{\объявление в процессе\}

begi n

{"^последовательный оператор\} end process;

Объявленными в процессе могут быть: объявление и тело подпро­граммы, объявление типа и подтипа, объявление константы, пере­менной, файла, псевдонима, объявление и спецификация атрибута, объявление группы, описание use. To, что объявлено в процессе, имеет область действия (видимость), ограниченную данным процес­сом.

Все процессы в программе выполняются параллельно. Процессы обмениваются сигналами, которые выполняют синхронизацию про­цессов и переносят значения между ними. Нескольким присваива­ниям одного сигнала в процессе соответствует один источник сигна­ла. Если над сигналами определена функция разрешения, то выходы источников сигнала могут объединяться. Сигналы нельзя объявлять в процессах. Процесс невозможно поместить в процесс, так как там есть место только для последовательных операторов.

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

wait on, стоящим последним в цепочке последовательных операторов тела процесса. В операторе процесса со списком чувствительности ставить операторы want не допускается.

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

Процесс представляет собой маленькую программу, которая вы­полняется на виртуальном процессорном элементе. Рассмотрим про­цесс, вычисляющий функцию синуса у от аргумента х по аппрокси­мирующей формуле:

sin(x) = С\Х + c2xs + с$х5 + со­процесс выглядит следующим образом:

process

type tabl is аггау(0 to 3) of real;

constant с:tabl:-CO.99999, -0.16666, 0.00831, -0.00019); variable xtmp, p: real:=0.0; begin

xtmp:=X;

p:=c(0)*xtmp;

for i in 1 to 3 loop

p:=p+c(i)*xtmp*X*X; end loop;

у<=ф;

wait on X; end process;

Программа этого процесса содержит исходные данные (констан­ты) и цепочку операторов, выполняемых последовательно. После пуска симулятора процесс начинает выполнение при определенном начальном значении сигнала X и останавливается на операторе wait. После прихода нового значения сигнала х процесс возобновляет свое исполнение до следующей остановки. При этом за один запуск про­цесса выполняются 10 операций умножения, 3 операции сложения, 5 операторов присваивания переменной и один оператор присваива­ния сигналу Y - результату программы процесса.

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

process(A) begin

с<= A or В; end process;

при моделировании дает графики,

как на рис. 20. Из графиков видно, что изменение результирующего сигнала С происходит только в моменты изменения сигнала А, т.е. процесс моделирует некоторую триггерную схему, а не схему ИЛИ.

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

При синтезе списки чувствительности игнорируются компилято­ром,

Процедуры и функции

Процедуры и функции относятся к числу подпрограмм. Выше уже упоминались вызовы процедуры и функции. В этом разделе рассмотрены правила составления и объявления процедур и функ­ций.

Спецификация процедуры

Спецификация процедуры описывает действия при вызове проце­дуры и имеет следующий синтаксис:

\спецификация процедуры\::=procedure

\имя процедуры\[(\список параметров\)] is

{\объявление в подпрограмме\}

begin

^последовательный оператор\}

end [procedure][\имя процедуры\];

\список параметров\::=(\элемент списка\ {; \элемент списка\})

Хэлемент списка\:: = [constant|vari able|signal]

\идентификатор\{,\идентификатор\}: [i nI out|i nout] \тип параметрах [ := \статическое выражение\]

Здесь \имя процедуры\ - идентификатор процедуры. В списке па­раметров указывается информация о формальных параметрах про­цедуры. Вместо формальных параметров подставляются фактиче­ские параметры во время вызова процедуры. В каждом из элементов списка параметров может объявляться, какой это параметр (кон­станта, переменная или сигнал), направление передачи параметра (i n, out или i nout), его тип или подтип и его значение по умолча­нию, равное статическому выражению.

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

Объявленными в процедуре могут быть: объявление и тело другой процедуры или функции, объявление типа и подтипа, объявление константы, переменной, файла, псевдонима, объявление и специфи­кация атрибута, объявление группы, описание use.

Рассмотрим пример программирования и использования процеду­ры в процессе для сортировки трех целых чисел.

process(al,a2,a3)

variable bl,b2,b3:integer;

procedure sort2(variable xl,x2:inout integer) is

variable t:integer; begin

if xl>x2 then

return; else

t:=xl; xl:=x2; x2:=t; end if; end procedure; begin

bl:*al; Ь2:=а2; ЬЗ:=аЗ; Sort2(b2,b3); Sort2(bl,b2); Sort2(b2,b3); cl<=bl; с2<=Ь2; c3<=b3; end process;

Здесь процедура Sort2 выполняет сортировку переменных xl,x2, выступающих формальными параметрами процедуры. Оператор return досрочно прерывает исполнение процедуры. Вызов процедуры выполняется с позиционным связыванием.

Следующий пример представляет использование процедуры со связыванием не всех формальных параметров.

procedure And4(variable xl,x2,x3,x4:in bit : = 'Г; signal y:out bit) is begin y<=xl and x2 and x3 and x4; end procedure;

При вызове этой процедуры:

And4(al,a2,a3,open,b);

четвертый входной параметр отсутствует, на что указывает ключевое слово open и вместо него подставляется начальное значение '1*.

При вызове процедуры входные параметры переменных копиру­ются внутрь процедуры. После исполнения процедуры выходные параметры записываются на место соответствующих переменных.

Входные параметры сигналов представляются как ссылки на сиг­налы. Параметру сигнала нельзя присваивать начальное значение, так как источник этого сигнала недоступен. Поэтому нельзя также использовать такие атрибуты сигнала, как 'delayed,'stable,'quiet или 'transaction, которые опрашивают источник сигнала. Выходной параметр сигнала передается в процедуру вместе с источником сиг­нала, в котором происходит присваивание сигналу. Это эквивалент­но тому, что цепочка последовательных операторов тела процедуры копируется в процесс на место вызова процедуры с соответствующей подстановкой параметров.

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

Спецификация функции

Спецификация функции имеет следующий синтаксис:

Хспецификация функции\::=[pure|impure] function \имя функции\ |\знак функции\ [(\список параметров\)] return \тип параметрах is {\объявление в подпрограме\} begin

^последовательный оператор\} return \выражение\; end [function][\имя функции\];

В ней знак функции - символ в кавычках, например, "+", список параметров - такой же, как в процедуре за исключением того, что

режимы параметров out и inout не допускаются. После ключевого слова return в объявлении функции указывается тип возвращаемого параметра. Объявления в функции могут быть такими же, как в процедуре. Выполнение функции должно оканчиваться оператором return, вычисляющим выражение возвращаемого параметра.

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

function Bit_To_int(x:bit_vector) return natural is variable t, j:integer:=0; begin

for I in x'reverse_range loop if ( x(i)='l') then

t:=t + 2**j; end if;

end loop; return t; end function Bit_To_int;

преобразует вектор битов в целое. При этом атрибут х'reverse_range возвращает диапазон, обратный диапазону представления входного параметра. Например, если входной параметр - bit_vector(7 downto 0), то в оператор цикла подставится диапазон 0 to 7. Таким образом, получается функция, универсальная для множества различных па­раметров-операндов.

Ключевые слова pure и impure обозначают идеальную и неиде­альную функции. В отличие от идеальной функции, неидеальная функция может возвращать различные результаты для одинаковых наборов входных параметров. Например, если входной параметр -глобальная переменная, которая изменится в момент вызова функ­ции, то результат будет отличаться от ожидаемого. Поэтому такие глобальные переменные не могут быть операндами в идеальных функциях.

Примером неидеальной функции является функция now из пакета standard, которая при вызове возвращает переменную предопреде­ленного типа delay_length, равную текущему моменту времени мо­делирования. Естественно, что при различных вызовах этой функ­ции она возвращает различные значения.

Объявление процедур и функций

Объявление подпрограмм вставляется в части объявлений процес­сов, блоков, в объявлениях объектов и пакетов, если эти подпро­граммы используются в этих программных единицах. Оно представ­ляет собой часть спецификации процедуры и функции до слова i s :

\объявление процедуры\::= procedure \имя процедуры\ [(\список параметров\)];

\объявление функции\::=[pure | impure] function \имя функции\ |\знак функции\[(\список параметров\)] return \тип параметрах;

Перегрузка процедур и функций

В языке VHDL допускается определять несколько процедур и функций с одинаковыми названиями. При вызове функции или процедуры из числа функций или процедур с одинаковым названием выбирается такая, которая подходит по типам и числу входных и выходных параметров. Такая функция загружается поверх осталь­ных функций с таким же названием. Перегрузка (overloading) про­цедур и функций удобна при вычислениях с различными типами. Например, над типами integer, real, подтипами signed, unsigned определены одни и те же арифметические функции, но имеющие различное сочетание типов аргументов и результатов.

Примером перегрузки служит следующая функция, которая не входит ни в один стандартный пакет:

function "and"(xl,x2:bit) return boolean is begi n

return (xl and x2) ='1'; end;

Если эта функция объявлена, то теперь транслятор не выдаст со­общения об ошибке при выполнении оператора if, вызывающего эту функцию:

variable a,b,c: bit; if (a and b and c) then

* * ■

end if; так как выражение в скобках будет иметь правильный тип boolean.

Реализация процедур и функций в аппаратуре

Компиляторы-синтезаторы, как правило, транслируют процедуру или функцию в комбинационную схему. Процедуры с операторами wait не допускаются, например, такие, которые симулируют схему с памятью.

Атрибуты

Атрибутом называют особенное, долговременное свойство предме­та. В языке VHDL сигналы, переменные и другие объекты, кроме своего значения, также имеют множество атрибутов. У каждого ти­па объектов есть несколько предопределенных атрибутов. Пользова­тель также может ввести ряд специальных атрибутов. Атрибуты бы­вают различного типа: атрибут - тип, значение, сигнал, функция, диапазон.

Атрибут объекта записывается как:

\имя объектах1 \имя атрибутах .

Ниже рассматриваются основные предопределенные атрибуты.

Атрибуты для скалярного типа

Для скалярного типа т предопределены следующие атрибуты:

т' left - самое левое значение множества элементов скалярного типа т. т' right - самое правое значение множества элементов типа т. T'high - наибольшее значение в множестве элементов типа т. Т' low - наименьшее значение в множества элементов скалярного типа т. T'image(x) - функция строкового представление выражения х типа т. T'value(x) - функция значения типа т от строкового представления х. T'pos(x) - функция номера позиции элемента х типа т. T'val (х) - функция значения элемента типа т стоящего в позиции X.

Примеры атрибутов:

type st is (one,two,three);

st'right = three, st'pos(three) = 2, st'val(l) = two. positive'low * 1, positive'high =2147483647. integer'valueC'LOOO") =1000, integer'image(330) ="330".

Атрибуты для регулярного типа

Для регулярного типа а предопределены следующие атрибуты:

A'left[(N)] - левое значение диапазона индексов по N-й размерности.

A' right[(n)] - правое значение диапазона индексов по N-й размерности.

Afhigh[(N)] - наибольшее значение диапазона индексов по N-й размерности.

А' 1 cw [ (n) ] - наименьшее значение диапазона индексов по N-й размерности.

A' range[(N)] - диапазон индексов по N-й размерности.

A' reverse_range[(N)] - обратный диапазон индексов по N-й размерности.

A' length[(N)] - протяженность диапазона индексов по N-й размерности.

A'ascending[(N)] - функция, равная true, если диапазон индексов He-N-й размерности - возрастающий.

Примеры применения атрибутов:

type s2 is array(2 downto 1, 0 to 3) of integer;

S2'left(l) - 2, s2'right(2) - 3,

S2'high(l) * 2, s2'low(2) = 0,

s2'range(2) = 0 to 3, s2'reverse_range(l)- 1 to 2,

S2'length(2) = 4.

Атрибуты сигналов

Атрибуты сигналов s:

S' stable [CO] - сигнал, равный true, если за промежуток време­ни т не было событий у сигнала S.

S'transaction - сигнал типа bit, меняет значение на противопо­ложное в циклах моделирования, в которых было присваивание нового значение сигналу S.

s'event - сигнал, равный true, если произошло событие в сигнале S в данном цикле моделирования.

sfactive - сигнал, равный true, если произошло присваивание нового значение сигналу s в данном цикле моделирования.

S'last_value - сигнал такого же типа, что и S, содержащий зна­чение s до последнего события в нем,

Примером применения атрибутов сигналов является следующий процесс, моделирующий синхронные триггеры.

process(CLK) begin

if CLK='l' and CLK'event then-- D- триггер

ql<=al; end if; if not CLK'stable then -- D- триггер

q2<=a2; end if; if CLK'last_value /= CLK then-- D- триггер

q3<=a3; end if; if CLK'active -- D- триггер

q4<=a4; end if; q5<=CLK'transaction; — т- триггер

end process;

Атрибуты пользователя

Эти атрибуты предназначены для присваивания объектам языка дополнительных свойств, не предусмотренных встроенными типами и атрибутами. При проектировании дискретных устройств такими свойствами могут быть: способ кодирования состояний автомата, указания компилятору-синтезатору по управлению оптимизацией, размещению блоков, их исполнению, назначение портов номерам выводов, начальное состояние схем памяти и т.п. Т.е. эти свойства не связаны напрямую с алгоритмом, реализуемым в программе. За­дание атрибута состоит из его объявления и спецификации.

Объявление атрибута имеет синтаксис, похожий на объявление переменной:

\объявление атрибутах::=attribute \идентификатор\: \ тип\

где \тип\ - любой ранее определенный тип, например, string, positive, time.

Спецификация атрибута имеет синтаксис:

Хспецификация атрибутах::=attribute \идентификатор\ of \имя объектах[{Димя объекта\}] | others I all : \класс объекта\ is \выражение\ \имя объекта\::= ((\идентификатор\ |\символьный литерал\

| \символ оператора\) [\сигнатура\]) \класс объекта\::- entity|architecture|configuration| package I procedure|function[type 1 subtype[ constant|si gnal jvari able | f i 1 e |component| label (literal |units I group

Здесь \идентификатор\ - имя атрибута, объявленного ранее, \имя объекта\ - имя объекта, которому присоединен атрибут,\сигнатура\ -

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

Атрибуты пользователя обычно не влияют на поведение модели и такие атрибуты игнорируются симулятором. Но они имеют большое значение для компилятора-синтезатора и для средств реализации схемы в кристалле. Поэтому, как правило, фирмы-поставщики ком­пиляторов и производители микросхем предлагают для использова­ния свои наборы атрибутов пользователя. В следующем примере ат­рибут указывает способ кодирования состояний автомата:

type \состояние\ is ( \сброс\ Дначало\,\работа\,\конеи.\); attribute enum_encoding : string;

attribute enunuencoding of \состояние\: type is "000 001 010 100" ;

Атрибут foreign

Специальный атрибут foreign присоединяется к архитектуре, процедуре или функции. Он указывает симулятору, что соответст­вующий объект должен моделироваться особенным способом. Это может быть специальная программа на ассемблере или другом язы­ке, или аппаратный ускоритель.

Структура программы на VHDL

Дискретная система может быть представлена в VHDL как объект проекта. Объект - это основная составная часть проекта. Объект может быть использован в другом проекте, который, в свою очередь, описан как объект или может являться объектом более высокого уровня в данном проекте. Симулятор может моделировать ВУ, толь­ко если оно оформлено в виде объекта проекта.

Объект проекта описывается набором составных частей проекта, таких как: объявление объекта, тело архитектуры объекта (или про­сто архитектура), объявление пакета, тело пакета и объявление конфигурации. Каждая из составных частей объекта может быть скомпилирована отдельно. Составные части проекта сохраняются в одном или нескольких текстовых файлах с расширением .VHD. В одном файле может сохраняться несколько объектов проекта.

Объект проекта обычно описывается согласно синтаксису:

\объект проекта\::= [\описание 1ibrary\]

[\описание use\]

\объявление объекта\

\тело архитектуры\

[\объявление конфигурации\] [\описание library\]::= library \идентификатор\ {, \идентификатор\};

где идентификаторы - названия библиотек, которые используются в объекте проекта. Они указывают транслятору местоположение этих библиотек. Описание use указывает, какие пакеты и какие элементы этих пакетов могут быть использованы в объекте. Части объекта проекта могут компилироваться раздельно. Но перед связыванием (elaboration) все части должны быть уже скомпилированы.

Объявление объекта

Объявление объекта указывает, как объект проекта выглядит снаружи и каким образом его можно включить в другом объекте проекта в качестве компонента, т.е. он описывает внешний интер­фейс объекта. Синтаксис объявления объекта:

\объявление объекта\::= entity \идентификатор\ is

[депеп"с(\объявление настроечной константы\

{; \объявление настроечной константы\});] [port (\объявление порта\ {Добъявление порта\});]

{\объявление в объекте\} [begin

{\оператор assert\ | \пассивный вызов процедуры\

| \пассивный процесс\ }] end [entity][\идентификатор\];

Здесь \идентификатор\ - имя объекта. Синтаксис объявления на­строечной константы и объявления порта рассмотрены в разделе описания объектов и типов языка. Порты представляют собой ин­терфейсные сигналы объекта проекта. Настроечные константы generic кодируют определенные свойства объекта проекта, напри­мер, разрядность линий связи, параметры задержки, кодирование структуры моделируемого устройства.

Объявленными в объекте могут быть: объявление и тело процеду­ры или функции, объявление типа и подтипа, объявление глобаль­ной переменной, файла, псевдонима, константы, объявление и спе­цификация атрибута, объявление группы, описание use.

В исполнительной части, которая открывается словом begin, вставляются параллельные операторы, которые не выполняют при­сваиваний сигналам, т.е. не влияют на поведение объекта. Поэтому

такие вызовы процедуры и процессы называются пассивными. Наи­более частое применение этих операторов - проверка соответствия входных сигналов, поступающих через порты, заданным требовани­ям или соответствие включения объекта в окружение, задаваемое ограничениями на настроечные константы generic. Например, про­веряется время предустановки сигнала относительно фронта синхро-серии, соответствие его уровней, разрядность входных данных и т.п. При несоответствии сигналов или настроечных констант, оператор assert выдает сообщение об ошибке.

Рассмотрим пример объявления объекта RS-триггера:

entity RS_FF is

generic(delay:time); port(R, S: in bit;

Q: out bit:='0'; nQ: out bit:='l'); begin

assert (R and S) /='1' report" in rs_ff r=s=1" severity error; end entity RS_ff;

В нем настроечная константа delay задает параметр задержки, например, от входа до выхода, который будет подставлен при ком­пиляции на этапе связывания компонентов. Порты R,S имеют режим ввода in, а порты Q,nQ - режим вывода out. При единичных сигна­лах на обоих входах, т.е. когда RS-тригтер функционирует непра­вильно, оператор assert выдает сообщение об ошибке.

Архитектура объекта

Архитектура объекта представляет собой отдельную часть, в ко­торой описано, каким образом реализован объект. Ее синтаксис:

\тело архитектуры\::= architecture \идентификатор\ of \имя объекта\ is

{\объявление в 6локе\} begin

{ \параллельный оператор\} end [architecture][\идентификатор\];

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

описание use, а также объявление компонента. Объявленные в теле архитектуры типы, сигналы, подпрограммы видимы только в преде­лах этой архитектуры.

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

Примером тела архитектуры служит архитектура для вышепри­веденного объявления объекта RS-триггера:

architecture behav of rs_ff is begin

processes,R)

variable qi: bit; begin

if S^'l' then

qi:='l' ; elsif R='l' then

qi:='0'; end if;

Q<=qi after delay; nQ<=not qi after delay; end process; end architecture behav;

Данную программу, включающую объявление объекта и тело ар­хитектуры можно транслировать и моделировать. Если из объявле­ния объекта удалить исполнительную часть и описание generic, то программа будет написана синтезируемым стилем и ее можно также компилировать в логическую схему.

Объявление компонента

Объявление компонента в теле архитектуры необходимо, если в архитектуре используется оператор вставки этого компонента, кото­рый будет рассмотрен ниже. Синтаксис объявления компонента сле­дующий :

\объявление компонентах::= component \идентификатор\ [is] [депеп'с(\объявление настроечной константы\

{; \объявление настроечной константы\});] [port (\объявление порта\ {Добъявление порта\});] end component [\идентификатор\];

Объявление пакета

В пакет обычно объединяются декларации различных объектов и типов языка, связанных общим признаком. Затем декларации из пакета можно повторно использовать в различных частях проектов, ссылаясь на этот пакет. Многие пакеты стандартизированы и их ис­пользование упрощает разработку новых проектов, а также служит для стандартизации включения и тестирования этих проектов. Не­сколько пакетов, подчиненных одной цели, собирают в библиотеку library. Библиотека, в которой собраны программы и пакеты поль­зователя, по умолчанию имеет название WORK.

Синтаксис объявления пакета:

\объявление пакета\::= package \идентификатор\ is

{объявление в пакете} end [package][\идентификатор\];

В объявлении пакета могут быть объявленными объявление про­цедуры или функции, объявление типа и подтипа, объявление фай­ла, псевдонима, константы, глобальной переменной, объявление и спецификация атрибута, объявление компонента, объявление груп­пы, описание use.

Обычно в объявлении пакета объявляются типы, используемые во всех объектах проекта или ряда проектов. Если объявлены про­цедуры и функции, то их спецификации описываются в теле пакета. Т.е. объявление пакета представляет собой интерфейс пакета, также как объявление объекта - это интерфейс объекта проекта.

Константам может быть не присвоено значение. Такие константы называются отложенными (deferred). Например, это могут быть за­ранее неопределенная кодировка состояний или разрядность шин. Тогда эти константы должны получить значение в теле пакета.

При обращении к объектам с одинаковым именем, принадлежа­щим разным библиотекам, необходимо использовать селективное имя объекта, указывающее, какой библиотеке оно принадлежит, например:

signal my_bit: IEEE.std_logic_1164.x0lZ;

Тело пакета

Тело пакета необходимо приводить в паре с объявлением пакета, если в последнем объявлены подпрограммы или отложенные кон­станты. Обычно объявление и тело пакета оформляют в одном фай­ле. Тело пакета имеет синтаксис:

\тело пакета\::= package body \идентификатор\ is

{объявление в теле пакета} end [package body][\идентификатор\];

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

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

package short_boolean is

constant bO:boolean:=false; —псевдоним для false constant bl:boolean:=true; --псевдоним для true function b(x:bit) return boolean;— преобразование из bit в boolean function "not"(x:bit) return boo7ean; —функции вместо not, and, or function "and"(xl,x2:bit) return boolean ; function Mor"(xl,x2:bit) return boolean ; end package; package body short_boolean is

function b(x:bit) return boolean is begin

return x -'1'; end;

function "not"(x:bit) return boo7ean is begin

return x ^'O1; end;

function ,,and"(xl,x2:bit) return boolean is begin

return (xl and x2) ='1'; end;

function Mor"(xl,x2:bit) return boolean is begi n

return (xl or x2) ='l'', end; end package body;

Теперь, если данный пакет присоединить к объекту проекта с по­мощью описаний library и use, то подстановка констант и функций этого пакета во многих местах, например, в операторах if, when со­кращает запись программы.

Описание use

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

структурной единице был видимый объект, объявленный в другом месте, используется описание use. Его синтаксис:

\описание use\::= use \селективное имя\ {, \селективное имя\ };

\селективное имя\::= \имя1\. \имя2\

\имя2\::= \идентификатор\ | \символьный литерал\ |аП

Здесь \имя1\ представляет собой обозначение места, где находит­ся объект, который должен быть видимым. Чаще всего это иденти­фикатор библиотеки и идентификатор пакета в ней, разделенные точкой. Идентификатор - название объекта, который должен быть видимым, символьный литерал - символьное имя функции, напри­мер, "*". Ключевое слово all означает, что видимы все объекты, объявленные в указанном месте. Например, чтобы были видимы функции сложения и вычитания из пакета std_7ogic_arith библио­теки IEEE используют описание:

use IEEE.std_logic_arith.,,-n, iEEE.std_logic_arith."+" ;

а если все объявления из этого пакета должны быть видимыми то используют use IEEE.std_logic_arith.all;

Псевдонимы

Под псевдонимом в языках программирования понимают другое имя объекта. Псевдонимы в VHDL помогают представить программу в виде, более удобном для чтения и моделирования. Объявление псевдонима имеет синтаксис:

\объявление псевдонимах::= alias \идентификатор\ |

символьный литерал\ | \символ оператора\ [:\подтип\] is \имя\ [\сигнатура\];

Наиболее часто псевдоним дают константам, переменным и сиг­налам. Например, псевдоним

alias COP: bit_vector(7 downto 0) is INSTRUCTION(31 downto 24);

помогает обращаться с полем кода операции сор команды instruction как с отдельным сигналом, не объявляя этот сигнал. При этом бит сор(7) равен биту instruction(31) .

j. акуке псевдоним можно присваивать типу. Например, если дать псевдоним

alias vect is std_logic_vector;

то можно сократить текст программы, объявляя сигнал типа vect вместо std_logic_vector, правда, при этом ухудшится чтение её дру­гими программистами.

Можно давать псевдоним функциям и процедурам. Например:

alias to_v is conv_std_logic_vector

[integer, integer return std_logic_vector];

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

Часто псевдоним дают длинным селективным именам, которые обозначают объекты, принадлежащие различным библиотекам, как например:

alias PI is IEEE.math_real.math_pi ;

- это псевдоним константы числа пи.

Псевдонимы поддерживаются многими компиляторами - синте­заторами, но не в полном объеме. Не поддерживаются, например, псевдонимы процедур и функций.

Метки в программе

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

В операторе процесса, блока и др. встречаются объявления кон­стант, процедур и функций, которые видимы в пределах данного оператора. Чтоб они были видимы в другом месте, можно использо­вать описание use, в котором, как адрес объявления, указывается метка процесса.

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

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

При моделировании симулятор работает со скомпилированной программой, в которой всем параллельным операторам поставлены

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

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

Имя метки может отражать специфику и алгоритм функциониро­вания данного оператора. Например, процесс, моделирующий ре­гистр - аккумулятор, может обозначаться меткой RG_ACC. В этом случае другому программисту легче разобраться в программе. По­этому вставка меток операторов это обязательное требование для проектов, предназначенных для повторного использования.

Объявление конфигурации

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

\объявпение конфигурации::=

configuration \идентификатор\ of \имя объекта\ is for \имя архитектуры\

{for Хуказатели вставки компонентах: \имя компонентах Хуказатель связываниях; end for;} end for; end [configuration] [\идентификатор\];

\ указатели вставки компонента \::=\метка вставки компонентах

{Дметка вставки компонентах} | others | all Хуказатель связываниях::= use entity

\имя объектах [(^идентификатор архитектуры\)]

При разработке вычислительного устройства его обычно тестиру­ют на различных этапах проектирования с помощью одного и того же испытательного стенда (test bench), который также описан с по­мощью пары объект-архитектура, например, alu_tb(tb_arch). В этом случае вставленный в испытательный стенд компонент тести-

руемого объекта, например, ALU с меткой иит (unit under test), имеет различное исполнение, т.е. архитектуру, например, RTL. Для того, чтобы не изменять описание архитектуры испытательного стенда, изменяют только его конфигурацию, например:

configuration testbench_for_alu of alu_tb is

for TB_ARCH

for UUT: ALU

use entity work.ALU(RTL); end for; end for;

end TESTBENCH_FOR_ALU;

Расширенный синтаксис объявления конфигурации предполагает такие возможности, как подключение других конфигураций, встав­ку компонента непосредственно в конфигурации (в указателе связы­вания), связывание настроечных констант компонента с новыми значениями, отключение компонента от схемы (отложенное включе­ние, когда в указателе связывания - ключевые слова use open).

Если в конфигурации используются компоненты, описанные в другой библиотеке, то их делают видимыми с помощью описаний library и use, которые ставят перед конфигурацией.

Конфигурации обычно игнорируются компиляторами-синтезато­рами.

Параллельные операторы

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

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

Оператор параллельного присваивания

Этот оператор имеет такой же синтаксис, как и оператор при­сваивания сигналу в процессе. Такой оператор эквивалентен опера­тору процесса, в котором этот оператор повторен в его исполнитель­ной части, а последним оператором стоит оператор wait со списком чувствительности. Например, следующие два оператора эквивалент­ны:

ADDER_D:A<=B+C;

adder_p:process begin А<=в+С; wait on в,С; end process;

Оператор условного параллельного присваивания

Оператор условного параллельного присваивания имеет синтак­сис:

\условное параллельное присваивание\::= \имя\<= [\способ задержки\]

{\график\ when \6улевское выражение\ else } \график\|>пеп \6улевское выражение\];

где определение способа задержки и графика представлено выше при описании оператора присваивания сигналу.

Любой оператор условного параллельного присваивания имеет эквивалентное представление в виде процесса, как например, опера­тор:

cntrl<= one when st=l else

two when st=2 or st=3 else three;

эквивалентен оператору

process(st,one,two,three) begin

if st=l then

cntrl<= one; elsif st=2 or st=3 then

cntrl<= two; else

cntrl<=three; end if; end process;

Оператор селективного параллельного присваивания

Оператор селективного параллельного присваивания имеет син­таксис:

\селективное параллельное присваивание\::= with \выражение\ select {\имя\<= [\способ задержки\]{\график\ when \альтернативы\,}

\rpa0MK\[when others ];

где \альтернативы\ имеют то же значение, что и в операторе case. Этот оператор эквивалентен соответствующему процессу, как на­пример, оператор:

with st select

cntrl<= one when 1,

two when 2 to 3, three when others;

выполняет такие же действия, что и процесс:

process(st,one,two,three) begin

case st is

when 1 => cntrl<= one;

when 2 to 3 => cntrl<= two; when others => cntrl<= three,-end case; end process;

Требования к выражению селектора и альтернативам оператора такие же, как у оператора case. Так, каждая из альтернатив должна быть такого же типа, что и \выражение\ и представлена статическим выражением или диапазоном. Никакие два значения, получаемые из выражений альтернатив, не должны быть равны друг другу.

Параллельный оператор assert

Этот оператор имеет такой же синтаксис, как и соответствующий ему последовательный оператор. Он выполняется точно также, как и процесс, в исполнительной части которого стоит последовательный оператор assert с таким же содержанием.

Параллельный вызов процедуры

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

Если программа предназначена для синтеза, то процедура, вызы­ваемая параллельно, не должна иметь операторов wait. Такая про­цедура отображается в комбинационную схему или комбинацию шин, т.е. некоторый специализированный процессорный элемент.

Отложенные параллельные операторы

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

Оператор вставки компонента

Оператор вставки компонента играет важную роль для реализа­ции иерархического проектирования. Его синтаксис:

\оператор вставки компонентах::= \метка экземпляра элементах :

Хвставляемый элемент\ [generic тар(\связывание настроечной константы\ {, \связывание настроечной константы\});] [port map (Дсвязывание порта\ {Дсвязывание порта\})]; Хвставляемый элемент\ ::= [component] \имя компонентах

I entity \имя объектах[идентификатор архитектуры] (configuration \имя конфигурации\

Действие этого оператора заключается в подстановке вместо себя одного экземпляра компонента (вставляемый элемент - компонент) или объекта (вставляемый элемент - объект проекта), или компо­нента, указанного в конфигурации. Если вставляется компонент, то он должен быть объявлен в данной архитектуре.

Сколько раз встречается имя вставляемого компонента - столько копий объекта вставляется в странслированную программу. При этом каждая копия имеет уникальное имя, указанное в метке ком­понента. Таким образом, при многократной вставке компонента в странслированном проекте дублируются несколько раз все парал­лельные операторы указанного компонента, а следовательно, дубли­руются сответствующие им виртуальные процессорные элементы программистской модели.

Фраза \связывание порта\ указывает порядок подключения сиг­налов данного тела архитектуры к портам - сигналам компонента. Связывание может быть как позиционным, так и ассоциативным (поименованным), а также комбинированным. При позиционном

связывании параметры-выражения подставляются в порядке, опре­деленном порядком следования имен параметров в объявлении ком­понента. При поименованном связывании каждое имя порта связы­вается с соответствующим сигналом с помощью символов "=>", причем порядок следования портов может быть произвольным. Вто­рой способ связывания предпочтителен, так как поименованное связывание более понятно при чтении и меньше вероятность допустить ошибку при записи.

Если на момент связывания, объект, выступающий компонентом не скомпилирован или в указанных в фразе use библиотеках не на­шлось объекта с подходящим названием и интерфейсом, то такой компонент не будет вставлен. Об этом транслятор даст предупрежде­ние. При моделировании такого устройства, на выходы неподклю­ченного компонента симулятор подает сигналы 'и' - не инициали­зировано. А при синтезе - компилятор представит его "черным ящи­ком" (black box), который он попытается заменить схемой из EDIF-файла с соответствующим названием, если такой имеется в рабочем каталоге.

Неподключенные порты с режимом out или inout допускается не указывать в списке связывания портов. Но более понятным и пра­вильным считается связывание таких портов с условным сигналом, обозначенным ключевым словом open.

В поименованном связывании допускается связывать вырезку порта с сигналом. Например, вставка компонента регистра, выходы которого подключены к различным сигналам, имеет вид:

u_rg: RG16 port map (clk=>clk,

E => \разр_зп_рг\,

DI => D,

D0(15)=> open,

D0(14)=> \знак_й\,

D0(13 downto 0)=>\мантисса_й\ );

Более полный синтаксис допускает связывать порт и сигнал в ви­де статического выражения с использованием вызовов функций, на­пример:

Dl => conv_integer(D(n-l downto 0)); To_Bit(DO(14)) => \знак_0\,

т.е. здесь для совмещения типов порта DI в режиме in и сигнала D, разряда DO(14) порта в режиме out и сигнала \знак_й\ используются функции преобразования типа.

Следует отметить, что компиляторы-синтезаторы, как правило, не допускают связывание порта с функцией преобразования типа. Если выход компонента не подключен, как, например, DO(15)=>openf или если выхода нет в списке связываний, то компилятор-синтезатор может минимизировать в компоненте цепи и логику, от­носящиеся к этому выходу, в примере - удалить триггер, подклю­ченный к D0(15).

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

Все компиляторы-синтезаторы поддерживают настроечные кон­станты целого типа.

Пусть на нижнем уровне иерархии описана модель синхронного регистра разрядности п, который принимает данное DI по фронту синхросигнала CLK:

entity RGn is

generi c(n: i nteger);

port(CLK:bit; DI, DO: bit_vector(n-l downto 0)); end entity;

architecture behav of RGn is begin process(CLK) begi n

if clk='1' and CLK'event then

DO<=Dl ; end process; end architecture behav;

Тогда следующий оператор в теле архитектуры более высокого уровня иерархии выполняет вставку компонента этого регистра и настраивает его разрядность равной п = 8:

U_RG8: entity RGn(behav) generic map(8),

port map (CLK,DI=>DATA_IN,DO=>DATA_OUT);

Оператор generate

Если необходимо неоднократно повторить один или несколько параллельных операторов, то используют оператор generate. Его синтаксис:

\оператор generate\::= \метка\: for \идентификатор\ in \диапазон\ generate

[{\объявление в 6локе\} begin]

{ \параллельный оператор\} end generate [\метка\];

Метка оператора generate необходима для обозначения сгенери­рованной структуры, \идентификатор\ - это параметр оператора gen­erate, а фраза \диапазон\ - диапазон его изменения. Они имеют та­кие же синтаксис и семантику, как и в операторе loop. В операторе могут быть вставлены такие же объявления, как в декларативной части тела архитектуры.

В отличие от оператора loop, который повторяет в цикле один или несколько последовательных операторов, оператор generate де­лает несколько копий параллельных операторов, когда параметр оператора пробегает все значения из заданного диапазона.

В следующем примере с помощью оператора generate запрограм­мирована схема сдвигового регистра длиной п на триггерах FD из библиотеки компонентов ПЛИС Xilinx, описанного в пакете UNlslM.umsim_viTAL с входом DI и выходом do, тактируемого син-хросерией clk.

signal t: std_logic_vector(l to n+1);

t(i)<=DI;

FIFO: for i in 1 to n generate

u_ tt: fd(c=>clk, D=>t(i), Q=>t(i+1)); end generate; D0<=t(n+1);

Для того, чтобы управлять структурой проектируемого устройст­ва используется условный оператор generate. Его синтаксис:

Хусловный оператор generate\::=\MeTKa\:if \6улевское выражение\ generate

[ {\объявление в 6локе\} begin]

{ \параллельный оператор\} end generate [\метка\];

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

RESn: if \подключить_Р1Л_1_иР\=1 generate

RESl:for i in DATA^BUS1range generate

U_ RES: PULLUP(DATA_BUS(i));

end generate; end generate;

если целое значение \подключить_Р1Л_1_иР\ равно 1, то к шине DATA_BUS подключаются компоненты нагрузочных резисторов PULLUP из библиотеки unisim.

Оператор block

В языке VHDL блок представляет собой выделенное множество параллельных операторов. Его синтаксис:

\оператор block\::=[\метка\]: block [\выражение сдерживаниях] [is] [депепс(\объявление настроечной константы\

{; \объявление настроечной константы\});] [generic тар(\связывание настроечной константы\

{, \связывание настроечной константы\});] [port (\объявление порта\ {;\объявление порта\});] [port map (\связывание порта\

{Дсвязывание порта\})]; {\объявление в 6локе\} begin

{\параллельный оператор\ } end block [\метка\];

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

Отдельное необязательное булевское выражение сдерживания не­явно задает специальный необъявляемый сигнал guard, который разрешает или запрещает (сдерживает) выполнение операторов при­сваивания сигналу с условием guard. Этот же сигнал может участво­вать как операнд в выражениях в пределах блока.

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

блоки с другим исполнением. Эквивалентным блоком можно заме­нить любую пару объект-архитектура, используемую как компонент.

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

Для этого, во-первых, процесс с оператором присваивания сигна­лу или соответствующий параллельный оператор помещают в блок с выражением сдерживания. Во-вторых, оператор присваивания сиг­налу оформляют как оператор со сдерживанием, для чего перед вы­ражением оператора ставится ключевое слово guarded. Например, блоки В1 и В2:

signal А,В,С: out integer bus :=0; disconnect c:integer after 2 ns;

Bl: block (sel = 1) is begin

с <= guarded A;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]