Управляющие структуры в VHDL схожи с управляющими структурами в языках программирования. Книмотносятся:
if/then
case
with … select
for… loop
while…loop
2.9.1 Оператор if
Условный оператор имеет в VHDL одну из следующих форм:
process
begin
if<условие>then
<операторы>
endif;
endprocess;
«Условие» в данной форме записи представляет собой любое выражение, возвращающее логическое значение. Например, D-триггер может быть описан с помощью оператора if, проверяющего условие прихода фронта тактового сигнала:
process
begin
ifclk’event and clk = ‘1’ then
q<= d;
endif;
endprocess;
Аналогично условному оператору во многих языках программирования, оператор if в VHDL имеет форму с веткой «иначе».
process
begin
if<условие>then
<операторы при выполнении условия>
else
<операторы при невыполнении условия>
endif;
endprocess;
В ряде случаев при описании конструкций управления требуется проверка вложенных условий. При этом образуется цепочка условных операторов, наподобие показанной ниже:
process
begin
if a = 1 then
count<= count + 1;
else
if a = 2 then
count<= count - 1;
if a = 3 then
count<= 0;
endif;
endif;
end if;
end process;
Каждый из использованных операторов требует индивидуальной завершающей строки endif. Для сокращения количества строк можно использовать альтернативную форму
process
begin
if<условие>then
<операторы>
elsif<условие>then
<операторы>
elsif<условие>then
<операторы>
else
<операторы>
endif;
endprocess;
Можно еще раз обратить внимание, что оператор if может использоваться только внутри процедурных блоков.
2.9.2 Оператор case
Оператор case имеет вид:
process
begin
case<выражение-селектор>is
when<вариант1> =><операторы1>
when<вариант2> =><операторы2>
whenothers =><операторы-иначе>
endcase;
endprocess;
В качестве примера рассмотрим арифметико-логическое устройство, выполняющее одну из операций над сигналами a. b в зависимости от значения сигнала cmd. Если cmd принимает значение, которому не сопоставлена операция, никаких действий не производится.
process
begin
casecmdis
when 0 => res <= a + b;
when 1 => res <= a – b;
when 2 => res <= a and b;
when 3 => res <= a or b;
when 4 => res <= a xor b;
whenothers =>null;
endcase;
endprocess;
В приведенном примере используется ключевое слово null, которое обозначает отсутствие каких-либо действий. Для синтезатора XST, встроенного в САПР ISE, упоминание строки whenothersтребуется. Более того, при перечислении вариантов переменной-селектора эта строка должна быть последней.
Рассмотрим мультиплексор 4-в-1, реализованный с помощью оператора case, в котором в качестве селектора выступает сигнал вида std_logic_vector(1 downto 0).
signalsel : std_logic_vector(1 downto 0);
begin
process(a, b, c, d, sel)
begin
caseselis
when “00” => q <= a;
when “01” => q <= b;
when “10” => q <= c;
when “11” => q <= d;
endcase;
endprocess;
Несмотря на то, что, на первый взгляд, перечислены все возможные варианты значений селектора, при синтезе такого кода будет обнаружена ошибка. Она связана с тем, что, с точки зрения VHDL, каждый разряд сигнала std_logic_vector может принимать значения не только ‘0’ и ‘1’, но и ‘X’, ‘U’, ‘Z’, ‘L’, ‘H’ и т.д., в соответствии со спецификацией языка. Поэтому синтезатор полагает данное описание неполным.
Разумеется, в синтезированной конструкции не будет возникать ситуаций, когда значение разряда будет непосредственно равно ‘X’ или ‘U’, т.к. значения «неизвестно» и «не определено» актуальны только для моделирования. Тем не менее, данную проблему неполноты описания необходимо разрешить. Например, разработчик может использовать то наблюдение, что кроме комбинаций “00”, “01”, “10” в работающей конструкции может появиться только “11”, которую и можно отнести к «остальным» (others). В этом случае приведенное выше описание можно переписать следующим образом:
process(a, b, c, d, sel)
begin
caseselis
when “00” => q <= a;
when “01” => q <= b;
when “10” => q <= c;
whenothers => q <= d;
endcase;
endprocess;
Подобное описание является корректным с точки зрения получения работоспособной конструкции, однако может служить источником несоответствия результатов моделирования реальному поведению такой системы. Дело в том, что при неопределенном в модели значении селектора (т.е. “XX”) в операторе case будет выполняться ветка whenothers (т.к. “XX”, так же, как и “11”, подпадает в этом описании под определение «остальные значения»). Поэтому, несмотря на то, что в реальной конструкции значение селектора при старте может быть равно, например, “00” (что соответствует операции q<= a), программа моделирования посчитает, что следует выполнить q<= d. Проблема усугубляется тем, что если a и d имеют при этом одинаковые значения, то разработчик не увидит разницы в поведении реального устройства и его модели, и посчитает, что модель составлена корректно. Однако при различных значениях на входах будет наблюдаться несоответствие результатов моделирования реально наблюдаемому поведению. Ошибки подобного рода трудны для диагностирования и требуют дополнительных усилий и времени для исправления.
Для повышения удобства моделирования кода можно использовать следующий прием:
process(a, b, c, d, sel)
begin
caseselis
when “00” => q <= a;
when “01” => q <= b;
when “10” => q <= c;
when “11” => q <= d;
whenothers => q <= ‘X’;
endcase;
endprocess;
В этом примере перечислены все возможные корректные значения сигнала-селектора, а в ветке whenothers записано, что выходной сигнал должен принять значение «неизвестно». Результаты синтеза такого описания будут эквивалентны предыдущему варианту, но при неинициализированных сигналах в модели на выходе такого устройства будет показано значение «неизвестно», что сразу обратит на себя внимание. Таким образом, идентификация ошибки может быть существенно облегчена.
2.9.3 Оператор with..select
Оператор with..select является еще одной разновидностью оператора выбора по селектору, как и case. Он используется вне процедурного блока, однако имеет ограничения по сравнению с case. Формат оператора with..select имеет следующий вид:
with<селектор>select
< приемник> <= <источник 1>when<значение 1>,
<источник 2>when<значение 2>,
<источник 3>when<значение 3>,
<источник 4>whenothers;
Обратите внимание, что отдельные варианты завершаются запятой, а точка с запятой ставится по завершении всей конструкции.
Пример реализации мультиплексора с помощью данной конструкции:
withselselect
q <= a when “00”,
bwhen “01”,
cwhen “10”,
dwhen others;
По сравнению с оператором case, с помощью данной конструкции невозможно описание «несимметричных» действий. Тогда как with..select может только выбрать одно из значений, записываемых в приемник, с помощью case можно описать цифровой узел, выполняющий действия с различными приемниками при разных значениях селектора. В показанном ниже примере при различных значениях селектора производится запись значений в различные приемники – q1 и q2, что потребует двух конструкций with..select.
process(a, b, c, d, sel)
begin
caseselis
when “00” => q1 <= a;
when “01” => q2 <= b;
when “10” => q1 <= c; q2 <= a;
when “11” => q1 <= d;
whenothers => q1 <= ‘X’; q2 <= ‘X’;
endcase;
endprocess;
К описанию отдельных значений селектора применимы те же соображения, что и для оператора case. С целью достижения большей адекватности моделирования следует учитывать возможность получения значений селектора ‘X’, ‘U’. Следовательно, все возможные значения, предусматриваемые при нормальной работе, должны быть явно определены, а строка whenothers должна соответствовать ситуации, когда значение селектора оказалось неопределенным.
2.9.4 Оператор for..loop
Оператор for..loop предназначен для организации циклов со счетчиком, управляющих процессом синтеза. Он не предназначен для описания цифровых счетчиков, а позволяет повторить синтез или моделирование некоей конструкции заданное число раз. Ниже приведен пример использования оператора for..loop. Можно обратить внимание, что переменная i не нуждается в отдельном объявлении.
process(A, B_BUS)
begin
for i in 7 downto 0 loop
c(i) <= a and b(i);
end loop;
end process;
Полученная в результате синтеза схема представлена на рис. (показаны только старшие разряды). Важно понять, что термин «цикл» относится не к цикличной работе описываемой конструкции, а к организации цикличной установки на кристалл определенного фрагмента схемы. Вообще, язык описания аппаратуры можно соотнести с программой управления неким автоматическим устройством, устанавливающим на кристалл компоненты. При этом сложность описания схемы не обязательно означает, что эта схема имеет сложную комплексную структуру и работает медленно.
Рис. 2.6 Результаты синтеза схемы с использованием оператора for..loop
2.9.5 Оператор while..loop
Оператор while..loop служит для организации цикла с условием продолжения исполнения. Он имеет следующий вид:
[<метка цикла>:] while<условие>loop
<операторы>
endloop [<метка цикла>];
Цикл такого вида используется для организации моделирования. Условием выхода из цикла может служить появление запрещающего сигнала или сигнала сброса, достижение некоторого момента времени или конца файла с тестовыми данными. В приведенном ниже примере тактовый сигнал для модели генерируется до тех пор, пока сигнал reset не примет значение ‘1’ (тогда выражение notreset примет значение ЛОЖЬ, что соответствует прекращению выполнения цикла).
process
begin
while not reset loop
clk<= '0'; wait for 5 ns;
clk<= '1'; wait for 5 ns;
endloop;
endprocess;
4Показанный пример не приводит к синтезу генератора тактового сигнала. Данный фрагмент используется только для моделирования, т.е. разработчик схемы указывает, что он может обеспечить появление сигнала с описываемым поведением на выводе clk.
2.9 Управляющие структуры
Управляющие структуры в VHDL схожи с управляющими структурами в языках программирования. Книмотносятся:
if/then
case
with … select
for… loop
while…loop