
- •Типы vhdl-данных и их интерпретация
- •1 Объявление типов
- •2 Классификация скалярных типов данных
- •3 Целочисленные типы
- •4 Вещественные типы
- •5 Физические типы
- •6 Перечислимые типы
- •7 Подтипы
- •8 Атрибуты
- •9 Массивы
- •10 Многомерные массивы
- •11 Неограниченные массивы
- •12 Атрибуты
- •13 Операции
- •14 Преобразование типов
- •15 Агрегаты
- •16 Строковые литералы
- •17 Записи
- •18 Преобразование типов данных в процессе vhdl-синтеза
- •Перечислимый тип представляется набором шин:
12 Атрибуты
Как было отмечено ранее, при описании портов задавать границы диапазона индексов для неограниченного типа массива необязательно. В этом случае, ограничение должно быть задано при создании экземпляра объекта моделирования.
Однако, для описания поведения необходимо иметь информацию о характеристиках массива, доступную хотя бы и в обобщенной форме. В языке VHDL механизмом получения такой информации являются атрибуты объектов массивов. Всего доступно восемь различных атрибутов, каждый из которых определяет одно из свойств диапазона индексов:
левая граница – 'left (n);
правая граница – 'right (n);
нижняя граница – 'low (n);
верхняя граница – 'high (n);
содержимое диапазона – 'range (n);
содержимое диапазона в обратном порядке – 'reverse_range (n);
длина диапазона – 'length (n);
упорядоченность по возрастанию – 'ascending (n).
Где n определяет порядковый номер диапазона. Нумерация диапазонов выполняется по возрастанию, начиная с единицы. Причем, для получения информации о характеристиках первого диапазона, например, при работе с одномерными массивами, указывать порядковый номер не обязательно.
Результат применения атрибутов 'left, 'right, 'low, 'high и ‘length – целое число, а результат применения атрибута 'ascending – булево значение.
Атрибуты 'range и 'reverse_range могут быть использованы в любом контексте, где требуется дискретный диапазон. Их использование позволяет избежать проблем при задании границ диапазона и порядка следования значений в нем для общего случая.
Следует отметить, что в отличие от рассмотренных ранее атрибутов скалярных типов данных, атрибуты массивов предназначены для применения к объектам, а не к типам. Данное отличие достаточно важно в частности потому, что атрибут скалярного типа в общем случае нельзя применить к объекту скалярного типа.
Для широкой иллюстрации использования атрибутов объектов массивов рассмотрим реализацию приоритетного шифратора, который выдает порядковый номер (считая с нуля) первого слева единичного бита во входном слове. При этом модуль совершенно не зависит от диапазона индексов, который будет выбран при создании его экземпляра:
entity priority_encoder is
port (
word : in bit_vector;
pos : out natural;
zero : out boolean
);
end entity priority_encoder;
architecture behavioral of priority_encoder is
begin
encoding: process (word) is
begin
pos <= 0;
zero <= true;
lookup: for i in word'range loop
if word (i) = '1' then
if word'ascending then
pos <= (i - word'low);
else
pos <= (word'length - 1) - (i - word'low);
end if;
zero <= false;
exit lookup;
end if;
end loop lookup;
end process encoding;
end architecture behavioral;
13 Операции
В языке VHDL существует ряд операций, которые рассматривают массивы как единое целое. Следует отметить, что большинство операций, предоставляемых по умолчанию, могут применяться только к одномерным битовым или булевым массивам, которые предназначены для моделирования данных на аппаратном уровне.
К одномерным битовым и булевым массивам применимы логические операции:
отрицание (not A);
конъюнкция (A and B);
штрих Шеффера (A nand B);
дизъюнкция (A or B);
стрелка Пирса (A nor B);
неэквивалентность (A xor B);
эквивалентность (A xnor B).
При этом, все эти операции выполняются над соответствующими парами элементов массивов, начиная слева.
Кроме того, к тем же типам массивов применимы следующие операции сдвига:
логический сдвиг влево (A sll B);
логический сдвиг вправо (A srl B);
арифметический сдвиг влево (A sal B);
арифметический сдвиг вправо (A sar B);
циклический сдвиг влево (A rol B);
циклический сдвиг вправо (A ror B).
Для любого оператора сдвига вторым операндом должно быть целое число, причем, если это число меньше нуля, то направление сдвига изменяется на противоположное.
Любой логический оператор сдвига присваивает освободившимся элементам массива значение '0' или false (в зависимости от типа массива). Арифметические операторы сдвига присваивают освободившимся элементам массива значение первого сдвигаемого бита.
Последней группой являются операции сравнения:
равенство (A = B);
неравенство (A /= B);
меньше (A < B);
меньше либо равно (A <= B);
больше (A > B);
больше либо равно (A >= B).
Эти операции могут быть применены к любым одномерным массивам, элементами которых являются объекты дискретного типа. При этом массивы могут иметь разную длину, в таком случае считается, что массив a меньше массива b, если a'length < b'length, а все элементы a и b, начиная слева, равны. Сравнение всегда выполняется слева направо.
Также к одномерным массивам может быть применен оператор конкатенации (A & B). Каждый операнд этого оператора может быть либо одномерным массивом, либо скалярным объектом. Типы операндов, в случае одномерного массива подразумевается тип элемента, должны совпадать. Результатом применения оператора конкатенации является одномерный массив, который содержит, слева направо, все элементы левого и правого операндов.
Рассмотрим использование оператора конкатенации на примере реализации счетчика Джонсона (Johnson counter), который выполняет счет по схеме: "0000", "0001", "0011", "0111", "1111", "1110", "1100" и так далее. То есть, новое значение формируется путем сдвига текущего на один разряд влево с помещением в освободившуюся позицию значения обратного исходному значению самого левого бита (до сдвига).
entity johnson_counter is
port (
reset : in bit;
clock : in bit;
data_out : buffer bit_vector (3 downto 0)
);
end entity johnson_counter;
architecture behavioral of johnson_counter is
begin
counting : process (reset, clock) is
begin
if reset = '1' then
data_out <= (others => '0');
elsif clock'event and clock = '1' then
data_out <= data_out (2 downto 0) & not data_out (3);
end if;
end process counting;
end architecture behavioral;
Следует обратить внимание на использование режима buffer для порта вывода. Этот режим позволяет трактовать порт вывода как порт ввода-вывода, но только внутри модуля. Следует отметить, что на порты, работающие в буферном режиме, накладывается несколько ограничений. Например, в случае структурного описания к такому порту нельзя подключить порт вывода, а только другой буферный порт. Поэтому использование портов в этом режиме ограничено.
К неограниченным типам арифметического пакета применим более широкий спектр операций. В частности, кроме перечисленных выше логических операций, операций сдвига и операций сравнения к ним применимы следующие арифметические операции:
сложение (A + B);
вычитание (A - B);
умножение (A * B);
деление (A / B);
деление по модулю (A mod B);
взятие остатка от деления (A rem B).
При этом, например, при реализации таких модулей как сумматоры, в общем случае, следует отдавать предпочтение использованию функций из этого пакета перед реализацией модуля на низком уровне. Данная рекомендация особенно важна, если предполагается, что в дальнейшем будет выполняться синтез модели. Причина заключается в том, что средства для синтеза хорошо распознают арифметические операции стандартного пакета и реализуют их с использованием наиболее подходящих примитивов.
Как пример, рассмотрим реализацию накапливающего сумматора (accumulator):
library ieee;
use ieee.numeric_bit.all;
entity accumulator is
port (
reset : in bit;
clock : in bit;
enable : in bit;
data_in : in unsigned (7 downto 0);
data_out : out unsigned (7 downto 0)
);
end entity accumulator;
architecture behavioral of accumulator is
signal acc : unsigned (7 downto 0);
begin
process (reset, clock) is
begin
if reset = '1' then
acc <= (others => '0');
elsif clock'event and clock = '1' then
if enable = '1' then
acc <= acc + data_in;
end if;
end if;
end process;
data_out <= acc;
end architecture behavioral;
В данном примере показана основная альтернатива портам вывода, которые работают в режиме buffer. Она заключается в создании внутри сигнала, над которым выполняются те операции чтения и записи, которые должны выполняться над выводимыми в порт данными. А для записи данных в соответствующий порт вывода используется параллельный оператор назначения сигнала, который будет рассмотрен позже.