Process(st,one,two,three) begin
Case st is
when1 => cntrl<= one;
when2 to3 => 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 необходима для обозначения сгенерированной структуры, \идентификатор\ - это параметр оператора generate, а фраза \диапазон\ - диапазон его изменения. Они имеют такие же синтаксис и семантику, как и в операторе loop. В операторе могут быть вставлены такие же объявления, как в декларативной части тела архитектуры.
В отличие от оператора loop, который повторяет в цикле один или несколько последовательных операторов, оператор generate делает несколько копий параллельных операторов, когда параметр оператора пробегает все значения из заданного диапазона.
В следующем примере с помощью оператора generate запрограммирована схема сдвигового регистра длиной п на триггерах FD из библиотеки компонентов ПЛИС Xilinx, описанного в пакете UNlslM.umsim_viTAL с входом DI и выходом do, тактируемого син-хросерией clk.
signalt: std_logic_vector(l ton+1);
t(i)<=DI;
FIFO: fori in1 ton 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 inDATA^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А,В,С: outinteger bus:=0; disconnectc:integer after 2 ns;
Bl: block(sel = 1) is begin
с <= guarded A;
end block Bl;
B2: block(sel = 2) is begin
с <= guarded D;
end block B2;
выдают в общую шину, представленную сигналом с, целое значение А при условии равенства 1 управляющего сигнала sel (т.е. когда сигнал guard<=(sel = 1) равен true) и значение сигнала D при другом условии в выражении сдерживания. Если guard = false, то источник сигнала отключается от шины, т.е. выполняется его сдерживание. Отключение источника может происходить с задержкой, устанавливаемой в объявлении disconnect, которое следует за объявлением сигнала. В приведенном примере отключение происходит с задержкой 2 не.
В случае, когда не выбран ни один источник, сигнал принимает свое предыдущее значение, если источник сигнала типа register или предварительно заданное значение, если источник сигнала типа bus. Ключевыми словами register и bus сигналы обозначаются при их объявлении. Благодаря механизму сдерживания, один сигнал может иметь несколько источников - выходов блоков. При этом правильное поведение этого сигнала состоит в выборке не более одного источника сигнала одновременно, т.е. сигнал guard защищает (guards) общую шину от неправильного функционирования. Таким образом,
еще одной основной функцией блоков в VHDL является организация нескольких источников для одного сигнала.
Обычно трансляция операторов блоков поддерживается компиляторами-синтезаторами. Но использование механизма подключения к общей шине, обеспечиваемого блоками, а также программирование поведения модели, связанное с ключевыми словами register, bus, disconnect, как правило, запрещаются. Также не поддерживаются связывания портов и настроечных констант в блоках.
Стили программирования
Операторы процесса задают поведение сигналов в зависимости от событий, происходящих во времени. Поэтому если архитектура включает только операторы процесса, то говорят, что такая архитектура описана поведенческим стилем.
Операторы параллельного присваивания, вызова процедуры, условного и селективного параллельного присваивания указывают потоки данных между линиями связи, обозначенными идентификаторами сигналов, а также обработку этих потоков. Поэтому если в теле архитектуры встречаются только такие операторы, то говорят, что эта архитектура написана стилем потоков данных.
Наконец, в архитектуре, описанной только операторами вставки компонента, закодирована структура проектируемого устройства. Сами операторы отвечают структурным блокам, а сигналы, связанные с портами — линиям связи между этими блоками. Поэтому говорят, что такая архитектура описана структурным стилем.
Естественно, архитектуры, описанные различными стилями, могут иметь произвольное количество операторов generate и block. Часто встречаются архитектуры, описанные смешанным стилем.
В программах, предназначенных для синтеза, регистры и другие схемы с памятью можно описать только с помощью оператора процесса или вставкой компонента с памятью. Так как в архитектуре, описанной стилем потоков данных, нет операторов процессов, то такая архитектура описывает комбинационную схему. Исключением является случай, поддерживаемый некоторыми компиляторами, когда регистр задается оператором block со сдерживаемыми операторами параллельного присваивания, выражение сдерживания которого является условием разрешения записи.
Хорошим стилем программирования считается, когда в иерархическом проекте объекты верхних уровней описываются структурным стилем.