Упрощение VHDL кода: тип данных std_logic_vector

Добавлено 24 октября 2018 в 15:06

В данной статье будет рассмотрен тип данных «std_logic_vector», который является одним из наиболее распространенных типов данных в VHDL.

В предыдущей статье о языке описания аппаратуры VHDL мы обсудили базовую структуру кода VHDL с помощью нескольких вводных примеров. В данной статье мы рассмотрим один из наиболее распространенных в VHDL типов данных, т.е. тип данных «std_logic_vector».

Сначала мы обсудим тот факт, что векторы позволяют нам иметь более компактное и читаемое описание VHDL, особенно при работе с большими схемами. Затем, рассмотрев некоторые важные особенности типа данных «std_logic_vector», мы рассмотрим некоторые стили кодирования, которые помогут нам избежать ошибок при использовании векторов.

Зачем нам нужны типы данных векторы?

Рассмотрим простую схему на рисунке 1, которая обсуждалась в предыдущей статье.

Рисунок 1 – Простая цифровая схема
Рисунок 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.

Рисунок 2
Рисунок 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
Рисунок 3

Рисунок 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.

Рисунок 4 – Трехэлементный вектор a_vec
Рисунок 4 – Трехэлементный вектор a_vec

Ключевое слово 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. С новым названием портов мы получим следующий рисунок.

Рисунок 5
Рисунок 5

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.

Рисунок 6 – ISE моделирование схемы, показанной на рисунке 5
Рисунок 6 – ISE моделирование схемы, показанной на рисунке 5

Интерпретация данных 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.

Рисунок 7
Рисунок 7

Стиль индексации данного вектора, который использует ключевое слово «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.

Рисунок 8
Рисунок 8

Выбор между восходящим и нисходящим порядком часто связан с предпочтениями разработчика, хотя он может быть рассмотрен в руководстве по кодированию, принятом конкретной организацией. Самое главное – выбрать один стиль, а затем последовательно следовать ему; смешивание двух разных стилей в одном проекте может легко привести к проблемам.

Например, рассмотрим таблицу истинности для приоритетного шифратора 4-к-2, показанную ниже. Для приоритетного шифратора мы обычно рассматриваем самый левый бит входного вектора как с самым высоким приоритетом. Например, в следующей таблице истинности, когда самый левый входной бит x(3) равен логической единице, нам не важны состояния остальных трех входных битов, и мы устанавливаем выходы y и v, т.е. y="11" и v="1".

Таблица истинности приоритетного шифратора
ВходыВыходы
x(3)x(2)x(1)x(0)y(1)y(0)v
1не важенне важенне важен111
01не важенне важен101
001не важен011
0001001
0000000

Мы видим, что эта таблица истинности предполагает, что входной вектор 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».
  • Выбор между восходящим и нисходящим порядком часто является вопросом стиля, но важно последовательно применять этот выбор во всем конкретном проекте.

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.


Сообщить об ошибке