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

Теги

CPLDFPGAHDL (язык описания аппаратуры)VHDLXilinx ISEПрограммируемая логикаТип данных std_logic

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

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.