Упрощение VHDL кода: тип данных std_logic_vector
В данной статье будет рассмотрен тип данных «std_logic_vector», который является одним из наиболее распространенных типов данных в VHDL.
В предыдущей статье о языке описания аппаратуры VHDL мы обсудили базовую структуру кода VHDL с помощью нескольких вводных примеров. В данной статье мы рассмотрим один из наиболее распространенных в VHDL типов данных, т.е. тип данных «std_logic_vector
».
Сначала мы обсудим тот факт, что векторы позволяют нам иметь более компактное и читаемое описание VHDL, особенно при работе с большими схемами. Затем, рассмотрев некоторые важные особенности типа данных «std_logic_vector
», мы рассмотрим некоторые стили кодирования, которые помогут нам избежать ошибок при использовании векторов.
Зачем нам нужны типы данных векторы?
Рассмотрим простую схему на рисунке 1, которая обсуждалась в предыдущей статье.
Ниже показан VHDL код для этой схемы.
entity circuit_1 is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
out1 : out STD_LOGIC);
end circuit_1;
architecture Behavioral of circuit_1 is
begin
out1 <= ( a and b );
end Behavioral;
Теперь предположим, что нам необходимо написать VHDL код для схемы на рисунке 2.
Чтобы получить VHDL описание рисунка 2, мы можем расширить предыдущий код подобным образом:
library ieee;
use ieee.std_logic_1164.all;
entity circuit_2 is
Port ( a0 : in STD_LOGIC;
a1 : in STD_LOGIC;
a2 : in STD_LOGIC;
b0 : in STD_LOGIC;
b1 : in STD_LOGIC;
b2 : in STD_LOGIC;
out0 : out STD_LOGIC;
out1 : out STD_LOGIC;
out2 : out STD_LOGIC);
end circuit_2;
architecture Behavioral of circuit_2 is
begin
out0 <= ( a0 and b0 );
out1 <= ( a1 and b1 );
out2 <= ( a2 and b2 );
end Behavioral;
Приведенный выше код верен; однако мы увидим, что для этой схемы возможно иметь более компактное и читаемое описание VHDL. Недостатком вышеуказанного кода является то, что он представляет каждый из портов ввода/вывода в виде отдельных сигналов и не устанавливает никакой связи между ними.
Рассмотрим альтернативный способ отображения схемы на рисунке 2.
Рисунок 3 предполагает, что мы можем рассматривать a0
, a1
и a2
как трехбитовый входной порт, под названием, например, a_vec
. Аналогично, входные порты b0
, b1
и b2
могут быть сгруппированы как еще один трехбитовый входной порт под названием b_vec
. То, что делает схема, – это выполнение операции И элемента a_vec
с соответствующим элементом b_vec
. Это может показаться простой идеей, но через минуту мы увидим, как этот способ мышления делает код более удобочитаемым.
Тип данных «Std_Logic_Vector
»
Чтобы представить группу сигналов, VHDL использует векторные типы данных. Чтобы получить доступ к элементу вектора, нам нужно определить индекс. Например, предположим, что, как показано на рисунке 4, мы используем вектор с длиной три, чтобы представить три значения: val_0
, val_1
и val_2
. Чтобы получить доступ к значению элемента из этого вектора, мы можем использовать номера индексов. Например, a_vec(2)
даст значение самого правого элемента вектора на рисунке 4, которое равно val_2
.
Ключевое слово VHDL «std_logic_vector
» определяет вектор элементов типа std_logic
. Например, std_logic_vector(0 to 2)
представляет собой трехэлементный вектор типа данных std_logic
, причем диапазон индексов распространяется от 0 до 2.
Давайте, используем тип данных «std_logic_vector
» для описания схемы на рисунке 3. Мы будем использовать три вектора a_vec
, b_vec
и out_vec
для представления соответственно синих, красных и черных портов на рисунке 3. С новым названием портов мы получим следующий рисунок.
VHDL код для рисунка 5 будет следующим.
library ieee;
use ieee.std_logic_1164.all;
entity circuit_2 is
Port ( a_vec : in STD_LOGIC_VECTOR(0 to 2);
b_vec : in STD_LOGIC_VECTOR(0 to 2);
out_vec : out STD_LOGIC_VECTOR(0 to 2));
end circuit_2;
architecture Behavioral of circuit_2 is
begin
out_vec <= ( a_vec and b_vec );
end Behavioral;
Строки 4-6 данного кода используют тип данных «std_logic_vector
» для портов ввода/вывода схемы. Обратите внимание, что операция И в строке 11 будет применена к соответствующим элементам двух векторов a_vec
и b_vec
, т.е. a_vec(0)
логически умножается на b_vec(0)
, и результат присваивается out_vec(0)
, и так далее. Сравнивая это с предыдущим кодом, мы наблюдаем, что использование типа данных «std_logic_vector
» позволяет нам иметь гораздо более компактный и читаемый код. Это преимущество становится особенно очевидным при работе с большими схемами; просто представьте, насколько громоздким будет код, если бы мы использовали отдельные инструкции присваивания сигналов для выполнения логической операции И с элементами двух 32-разрядных векторов.
Моделирование ISE приведенного выше кода показано на рисунке 6.
Интерпретация данных Std_Logic_Vector
Существует один важный момент, который требует дополнительного внимания. Как показано в приведенном выше примере, тип данных «std_logic_vector
» – это способ представления группы сигналов или шины данных. Это просто строка из единиц и нулей, и нет никакой другой интерпретации для этой строки из единиц и нулей. Другими словами, если мы присвоим "011" вектору a_vec
, это не означает, что a_vec
равен 3 (десятичный эквивалент "011").
Мы не можем считать вес для разных позиций битов сигнала «std_logic_vector
». Однако мы можем использовать функции преобразования типов и приведение типов, чтобы интерпретировать строку из единиц и нулей в данном сигнале «std_logic_vector
» как число. Преобразование типа будет обсуждаться в следующей статье.
Восходящий или нисходящий индексный диапазон?
До сих пор мы использовали тип данных std_logic_vector
при определении портов ввода/вывода. Аналогично, мы можем определить сигнал типа std_logic_vector
. В качестве примера рассмотрим следующие строки кода:
signal a: std_logic_vector(0 to 3);
...
a <= “0010”
Здесь первая строка определяет сигнал типа «std_logic_vector
». Индекс находится в диапазоне от 0 до 3. Затем a
присваивается "0010". С этим присваиванием, как показано на рисунке 7, мы будем иметь a(0)=0, a(1)=0, a(2)=1 и a(3)=0.
Стиль индексации данного вектора, который использует ключевое слово «to
», называется восходящим. Мы также можем использовать ключевое слово «downto
» (вместо «to
»), когда нам нужен нисходящий индексный диапазон.
signal a: std_logic_vector(3 downto 0);
...
a <= “0010”
В этом случае, как показано на рисунке 8, мы будем иметь a(3)=0, a(2)=0, a(1)=1 и a(0)=0.
Выбор между восходящим и нисходящим порядком часто связан с предпочтениями разработчика, хотя он может быть рассмотрен в руководстве по кодированию, принятом конкретной организацией. Самое главное – выбрать один стиль, а затем последовательно следовать ему; смешивание двух разных стилей в одном проекте может легко привести к проблемам.
Например, рассмотрим таблицу истинности для приоритетного шифратора 4-к-2, показанную ниже. Для приоритетного шифратора мы обычно рассматриваем самый левый бит входного вектора как с самым высоким приоритетом. Например, в следующей таблице истинности, когда самый левый входной бит x(3) равен логической единице, нам не важны состояния остальных трех входных битов, и мы устанавливаем выходы y и v, т.е. y="11" и v="1".
Входы | Выходы | |||||
---|---|---|---|---|---|---|
x(3) | x(2) | x(1) | x(0) | y(1) | y(0) | v |
1 | не важен | не важен | не важен | 1 | 1 | 1 |
0 | 1 | не важен | не важен | 1 | 0 | 1 |
0 | 0 | 1 | не важен | 0 | 1 | 1 |
0 | 0 | 0 | 1 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
Мы видим, что эта таблица истинности предполагает, что входной вектор x имеет нисходящий индексный диапазон, потому что элемент с самым высоким индексом помещается в крайнее левое положение. Предположим, что, несмотря на выбор нисходящего индекса в таблице истинности, мы используем восходящий индексный диапазон при объявлении входного вектора x и присваиваем x значение "0001". Другими словами, мы имеем:
signal x: std_logic_vector(0 to 3);
...
x <= “0001”
Так как самый правый бит x
равен логической единице, учитывая общее определение приоритетного шифратора, мы ожидаем, что выходы y и v будут соответственно раны “00” и ‘1’. Однако, так как в приведенном выше коде логической единице равен x(3), основываясь на приведенной выше таблице истинности, выход будет равен y=“11” и v=‘1’. Чтобы избежать таких проблем, мы должны последовательно использовать нисходящий индексный диапазон во всем коде.
Резюме
- Тип данных «
std_logic_vector
» позволяет нам иметь более компактный и читаемый код. Этот тип данных предоставляет нам способ представления группы сигналов или шины данных. - Мы не можем предполагать вес для разных позиций битов сигнала «
std_logic_vector
». Однако мы можем использовать функции преобразования типов и приведение типов, чтобы интерпретировать строку из единиц и нулей в заданном сигнале «std_logic_vector
» как число. - Диапазон индексов, используемый в объявлении «
std_logic_vector
», может быть как возрастающим, так и убывающим. Первый использует ключевое слово «to
», а последний – ключевое слово «downto
». - Выбор между восходящим и нисходящим порядком часто является вопросом стиля, но важно последовательно применять этот выбор во всем конкретном проекте.