Пакет NUMERIC_STD языка VHDL


 

УДК 681.5:004.414.2

ПАКЕТ NUMERIC_STD ЯЗЫКА VHDL

Н.А. Авдеев, П.Н. Бибило

Объединенный институт проблем информатики Национальной академии наук Беларуси, г. Минск

 

Изучается пакет NUMERIC_STD, предназначенный для согласования арифметических типов данных языка VHDL с логическими типами, построенными на базе девятизначного алфавита моделирования логических схем. Проблема согласования таких типов данных возникает при совместном использовании алгоритмических описаний и структурных описаний логических схем.

Введение

Язык VHDL (Very high speed integrated circuits Hardware Description Language) широко используется во всем мире для разработки цифровых систем. Первый стандарт VHDL’87 языка появился в 1987 году [1], второй стандарт VHDL’93 – в 1993 году [2]. В 1999 году был принят стандарт VHDL-AMS аналогового надмножества языка VHDL [3], что позволило проводить описание смешанных (цифро-аналоговых) схем. Желательно, чтобы читатель был знаком с основными элементами языка VHDL. С языком VHDL можно познакомиться по книгам [4 – 10].

Для VHDL разработаны и стандартизированы ряд пакетов [6, 7], в которых описаны различные арифметические и логические функции для работы с различными типами данных. Одним из самых важных является пакет STD_LOGIC_1164, в котором декларируется девятизначный тип std_ulogic для описания различных состояний сигналов в проводниках (межсоединениях) между элементами и устройствами и важный подтип std _logic этого типа. Для типа std_logic разрешается назначать значение сигнала из различных источников, что является удобным для описания монтажных соединений в логических схемах и описания шин. Соответствующая “разрешающая” функция имеется в пакете STD_LOGIC_1164. Заметим, что в общем случае ее необходимо написать самому разработчику VHDL-кода. Кроме того, имеющиеся в практике проектирования синтезаторы логических схем (LeonardoSpestrum, XST, Synplify и др.), позволяющие по исходным алгоритмическим VHDL-описаниям синтезировать логические схемы, используют для описания синтезированных схем типы std_logic, std_logic_vector. Последний тип является обобщением типа std_logic на случай векторов – одноразмерных массивов значений типа std_logic. Таким образом, если часть VHDL-проекта синтезирована и описывается в терминах девятизначного логического алфавита, а часть проекта остается на алгоритмическом уровне описания и использует типы INTEGER и NATURAL, то возникает проблема совместимости типов std_logic, std_logic_vector с типами INTEGER и NATURAL. Логические функции, описанные в пакете STD_ LOGIC_1164, не позволяют напрямую использовать тип std_logic совместно с целыми типами INTEGER и NATURAL. Для того чтобы упростить согласование алгоритмических и схемных описаний, разработан VHDL-пакет NUMERIC_STD. Пакет NUMERIC_STD ориентирован на стандарт языка VHDL’93 [2]. Если необходима совместимость с VHDL’87, то требуется из пакета NUMERIC_STD удалить (например, закомментировать двумя знаками дефис) декларации и тела функций xnor (логическая функция “эквивалентость”), sll, srl, rol, ror (функции сдвига).

Данная работа посвящена описанию пакета NUMERIC_STD, его возможностям, особенностям и ограничениям, возникающим при моделировании VHDL-программ. Использование данного пакета при синтезе логических схем требует отдельного изучения и в данной работе не рассматривается. Приведенные в статье примеры работы функций пакета были проверены в системе моделирования ModelSim PE 5.7f, в которой содержится рассматриваемая в статье версия пакета NUMERIC_STD.

Основные типы данных

В пакете NUMERIC_STD определены два новых UNSIGNED, SIGNED числовых типа данных и различные арифметические функции над ними, которые поддерживаются системами синтеза. Тип UNSIGNED представляет собой числа без знака в векторном двоичном (булевом) представлении, а тип SIGNEDзнаковые числа (числа со знаком). Для векторов SIGNED старший (левый) бит определяет знак числа. Отрицательные числа представляются в дополнительном коде. Например, двоичный вектор 1001 представляет число –7. Вектор “1000” типа UNSIGNED понимается как 8, вектор “1000” типа SIGNED понимается как –8. Базовым элементом типов UNSIGNED и SIGNED является стандартный тип std_logic, описанный в пакете STD_LOGIC_1164. Тип std_logic является многозначным (перечислимым) подтипом типа std_ulogic (табл. 1). Типы UNSIGNED и SIGNED в пакете NUMERIC_STD (далее просто в пакете) определены следующим образом:

  type UNSIGNED is array (NATURAL range <>) of STD_LOGIC;
  type SIGNED is array (NATURAL range <>) of STD_LOGIC;

Размерности векторов UNSIGNED и SIGNED ограничены типом NATURAL, который определен в пакете STANDARD [7] как подтип типа INTEGER:

  type INTEGER is range -2147483648 to 2147483647;
  subtype NATURAL is integer range 0 to INTEGER‘high;

В VHDL через Shigh обозначается атрибут “верхняя граница” объекта S. Следовательно, запись INTEGERhigh означает верхнюю границу типа INTEGER. Далее в статье будут использоваться атрибуты left (левая граница), range (диапазон), length (длина диапазона). Заметим также, что в VHDL (и в текстах VHDL-кодов данной статьи) не различаются прописные и строчные буквы.

 

Таблица 1 – Элементы базового типа std_ulogic

Элемент типа
std_ulogic
Описание
U не инициализировано (Uninitialized)
X сильное неизвестное (Forcing Unknown)
‘0’ сильный нуль (Forcing 0)
‘1’ сильная единица (Forcing 1)
Z высокий импеданс (High Impedance)
W слабое неизвестное (Weak Unknown)
‘L’ слабый нуль (Weak 0)
H слабая единица (Weak 1)
‘–’ безразлично (Don’t care)

 

Тип NATURAL включает в себя все положительные целые числа от 0 до 2147483647, а тип INTEGER – положительные и отрицательные числа от -2147483648 до 2147483647. Если перейти к двоичной системе счисления, то тип INTEGER ограничен 32-разрядным вектором, старший (левый) разряд которого означает знак числа (0 – число положительное, 1 – число отрицательное). Тип NATURAL ограничен 31-разрядным двоичным вектором.

Функции пакета NUMERIC_STD

Пакет NUMERIC_STD содержит описания следующих функций над типами UNSIGNED и SIGNED:

  • функции, реализующие арифметические операции;
  • функции, реализующие операции сравнения;
  • функции сдвига (и вращения);
  • функции изменения размерности;
  • функции преобразования типов;
  • функции, реализующие логические операции.

В основе описания всех функций пакета лежат вспомогательные функции изменения типов (табл. 2), функция преобразования вектора (табл. 3), функции изменения размерности (табл. 4).

Функции изменения типов

Язык VHDL позволяет создавать несколько функций с одинаковым именем. Это называется перегрузкой функций. Функция TO_INTEGER (табл. 2) является перегруженной функцией, которая в пакете описана для двух типов аргументов: UNSIGNED и SIGNED. С помощью этой функции можно преобразовать любой вектор SIGNED размерности 1…32 бита или вектор UNSIGNED размерности 1…31 бит в целое число INTEGER или в целое положительное (натуральное) число NATURAL соответственно.

 

Таблица 2 – Функции изменения типов (Conversion Functions)

Номер Функция Аргумент Новый размер (SIZE) Результат
D.1 TO_INTEGER UNSIGNED NATURAL
D.2 TO_INTEGER SIGNED INTEGER
D.3 TO_UNSIGNED NATURAL NATURAL UNSIGNED(SIZE-1 downto 0)
D.4 TO_SIGNED INTEGER NATURAL SIGNED(SIZE-1 downto 0)

Примечание. В табл. 2 (и других таблицах) номер – это номер, который функция имеет (в виде комментария) в пакете NUMERIC_STD. Номер облегчает поиск текста функции в пакете.

 

Пример применения функции TO_INTEGER.

  signal a_s : signed(3 downto 0) := "1011";
  signal a_u : unsigned(3 downto 0) := "1011";
  signal y_i : integer range -8 to 7 ;
  signal y_n : natural range  0 to 15 ;
    ...
  y_i <= to_integer(a_s);  -- результат y_i = -5
  y_n <= to_integer(a_u);  -- результат y_n = 11

Не следует использовать векторы, размерность которых превышает размерность типов INTEGER (32 бита) и NATURAL (31 бит), так как в некоторых ситуациях это может привести к неправильному результату функции, который не всегда просто отследить.

Функция TO_UNSIGNED позволяет преобразовать положительное целое число NATURAL (из диапазона от 0 до 2147483647) в “беззнаковый” вектор UNSIGNED заданной размерности.

Пример применения функции TO_UNSIGNED.

  signal a_n : natural := 8 ;
  signal y_u : unsigned(3 downto 0);
    . . .
  y_u <= to_unsigned(a_n, 4);  -- результат y_u = “1000”

С помощью функции TO_SIGNED можно преобразовать целое число INTEGER (из диапазона от -2147483648 до 2147483647) в знаковый вектор SIGNED заданной размерности.

Пример применения функции TO_SIGNED.

 

signal a_i : integer := -8 ;
signal y_s : signed(3 downto 0);
. . . 
y_s <= to_signed(a_i, 4);  -- результат y_s = “1000”

Если в функции TO_SIGNED или TO_UNSIGNED задать размерность для результирующего вектора меньше, чем необходимо для представления числа в двоичной форме, то старшие биты вектора-результата отбрасываются (теряются). Например,

 

y_u <= to_unsigned(16,4);   -- y_u = “0000”, старший бит 
                            -- отсутствует, т.к. 16 = “10000”
y_s <= to_signed(-9, 4);    -- y_s = “0111”, старший бит 
                            -- отсутствует, т.к. -9 = “10111”

Если в результате выполнения операции TO_SIGNED или TO_UNSIGNED потеряны старшие биты, то выдается одно из предупреждений:

# ** Warning: NUMERIC_STD.TO_SIGNED: vector truncated
# ** Warning: NUMERIC_STD.TO_UNSIGNED: vector truncated

при этом работа VHDL-программы не прекращается.

Функция TO_01 (табл. 3) побитно преобразует элементы ‘H’, ‘L’ входного вектора (тип SIGNED или UNSIGNED) в ‘1’, ‘0’ соответственно. Если хотя бы один из элементов входного вектора не равен одному из элементов подмножества {
H, ‘1’, L, ‘0’}, то всем элементам результирующего вектора по умолчанию присваивается ‘0’, или одно из возможных значений типа std_logic, которое можно указать при обращении к этой функции.

 

Таблица 3 – Функция TO_01 преобразования вектора (Translation Function)

Номер Функция Аргумент (S) XMAP Результат
T.1, 2 TO_01 UNSIGNED
SIGNED
STD_LOGIC:=’0 UNSIGNED(S’RANGE)
SIGNED(S’RANGE)

 

Пример применения функции TO_01.

  y_s <= to_01(a_s, 'U');	-- аргумент “H1L0” => результат “1100”
                                -- аргумент “HU10” => результат “UUUU”
  y_s <= to_01(a_s);	        -- аргумент “HLHL” => результат “1010”
                                -- аргумент “-101” => результат “0000”
  y_u <= to_01(a_u, '1');	-- аргумент “W101” => результат “1111”

Функции изменения размерности

Функция RESIZE (табл. 4) изменяет размерность вектора типа SIGNED или UNSIGNED. При увеличении размерности вектора UNSIGNED дополнительные старшие биты результирующего вектора заполняются нулями. При усечении вектора UNSIGNED лишние старшие биты результирующего вектора отбрасываются (теряются). При увеличении вектора SIGNED старший знаковый разряд копируется в дополнительные старшие биты, а при усечении вектора SIGNED
старший знаковый разряд копируется в старший бит результирующего вектора.

 

Таблица 4 – Функции изменения размерности (Resize Functions)

Номер Функция Аргумент Новый размер (NEW_SIZE) Результат
R.1 RESIZE SIGNED NATURAL SIGNED(NEW_SIZE-1 downto 0)
R.2 RESIZE UNSIGNED NATURAL UNSIGNED(NEW_SIZE-1 downto 0)

 

Функция RESIZE написана таким образом, что работает со всеми значениями ‘L’, ‘0’, ‘H’, ‘1’, ‘U’, ‘X’, ‘Z’, ‘W’, ‘-‘ старшего (знакового) бита. Примеры, поясняющие работу функции RESIZE, приведены в табл. 5.

 

Таблица 5 – Примеры работы функции RESIZE

Аргумент
ARG
Усечение вектора
resize(ARG,3);
Увеличение вектора
resize(ARG,7);
unsigned(signed) unsigned signed unsigned signed
10010” 010” 110” 0010010” 1110010”
WHL01” L01” W01” 00WHL01” WWWHL01”
W10L” 10L” 0L” 00-W10L” -W10L”
X0101” 101” X01” 00X0101” XXX0101”

 

Функцию RESIZE можно реализовать с помощью стандартной операции конкатенации (&). Если необходимо выполнить усечение вектора SIGNED, то выражение

min_s <= resize(a_s,NEW_SIZE_MIN);

и

min_s <= a_s(a_s'left) & a_s(NEW_SIZE_MIN-2 downto 0);

эквивалентны.

Если необходимо увеличить размерность вектора SIGNED, то выражение

  max_s <= resize(a_s,NEW_SIZE_MAX);

и

  max_s <= ( NEW_SIZE_MAX-1 downto (a_s'left+1) => a_s(a_s'left) ) & a_s(a_s'left downto 0);

эквивалентны.

Если необходимо выполнить усечение вектора UNSIGNED, то выражение

min_u <= resize(a_u,NEW_SIZE_MIN);

и

min_u <= a_u(NEW_SIZE_MIN-1 downto 0);

эквивалентны.

Если необходимо увеличить размерность вектора UNSIGNED, то выражение

  max_u <= resize(a_u,NEW_SIZE_MAX);

и

  max_u <= (NEW_SIZE_MAX-1 downto (a_u'left+1) => '0') & a_u(a_u'left downto 0);

также эквивалентны.

Функции RESIZE и TO_01, в отличие от функций TO_INTEGER, TO_UNSIGNED, TO_SIGNED, работают корректно для векторов любой размерности не более 2147483647 бит (размерность типа NATURAL). Экспериментальная проверка была проведена для векторов размерностью 300 бит.

Следует обратить внимание на следующее выражение:

  d_i <= TO_INTEGER(a_u);

В этом выражении аргументом функции TO_INTEGER является переменная (сигнал) a_u типа UNSIGNED, следовательно, в соответствии с табл. 2, функция TO_INTEGER вернет тип NATURAL. Таким образом, переменной d_i типа INTEGER присваивается значение типа NATURAL. Выражение d_i <= TO_INTEGER(a_u); система моделирования ModelSim не считает ошибкой, так как тип NATURAL является подтипом типа INTEGER. Использование такого вызова функции при выполнении VHDL-программы не повлечет ошибки, потому что все значения типа NATURAL входят в множество значений типа INTEGER. Проблемы в работе VHDL-программы могут возникнуть при использовании следующего оператора:

  c_n <= TO_INTEGER(a_s);

В этом случае переменной c_n типа NATURAL присваивается значение переменной a_s, которая имеет тип SIGNED. Система моделирования также не считает это ошибкой при компиляции VHDL-кода, но при выполнении VHDL-программы не исключен случай, когда переменная a_s примет отрицательное значение, и тогда работа VHDL-программы прекратится, так как отрицательное число нельзя присвоить переменной c_n, которая имеет тип NATURAL. Принимая во внимание вышесказанное, не рекомендуется совместное использование знаковых и “беззнаковых” типов в функциях.

Логические операции

В табл. 6 приведен список логических операций, описанных в пакете NUMERIC_STD.

 

Таблица 6 – Логические операции (Logical Operators)

Номер Аргумент (L) Функция Аргумент (R) Результат
L.1, 8   not UNSIGNED UNSIGNED(L’LENGTH-1 downto 0)
SIGNED SIGNED(L’LENGTH-1 downto 0)
L.2-6 UNSIGNED and
or
nand
nor
xor
UNSIGNED UNSIGNED(L’LENGTH-1 downto 0)
L.9-13 SIGNED SIGNED SIGNED(L’LENGTH-1 downto 0)
L.7, 14 UNSIGNED xnor UNSIGNED UNSIGNED(L’LENGTH-1 downto 0)
SIGNED SIGNED SIGNED(L’LENGTH-1 downto 0)

 

Размерности левого (L) и правого (R) аргументов логических функций (табл. 6) обязательно должны совпадать. Размерность результата равна размерности аргументов. Все логические функции выполняются с помощью логических функций, оперирующих с векторами типа std_logic_vector. Данные функции описаны в пакете STD_LOGIC_1164.

Пример описания логической функции and в пакете NUMERIC_STD.

-- Id: L.2
function "and" (L, R: UNSIGNED) return UNSIGNED is
  variable RESULT: UNSIGNED(L'LENGTH-1 downto 0);
begin
  RESULT := UNSIGNED(STD_LOGIC_VECTOR(L) and STD_LOGIC_VECTOR(R));
  return RESULT;
end "and";

При выполнении логических операций значение H эквивалентно ‘1’, а L эквивалентно ‘0’:

Пример логической операции and (И).

  "HLHL" and "1111" = "1010"

Если значение компонента вектора равно ‘U’, то и результат любой логической операции над этим компонентом равен U. Значение X получается, если компонент вектора равен одному из следующих значений {X, Z, W, ‘–’}.

Пример логической операции or (ИЛИ).

  "UX01ZWLH–" or "–01HLUXWZ" = "UX11XUXXX"

 

Операции сдвига

В пакете описаны две группы операций (операторов) сдвига и вращения (табл. 7).

Первая группа состоит из операторов SHIFT_RIGHT (сдвиг вправо), SHIFT_LEFT (сдвиг влево), ROTATE_LEFT (вращение влево), ROTATE_RIGHT (вращение вправо), описанных для векторов типа UNSIGNED и SIGNED:

Пример применения операторов сдвига SHIFT_RIGHT, SHIFT_LEFT, ROTATE_RIGHT, ROTATE_LEFT.

signal sr_u, sl_u : UNSIGNED(7 downto 0);
signal sr_s, sl_s : SIGNED(7 downto 0);
signal rr_u, rl_u : UNSIGNED(7 downto 0);
signal rr_s, rl_s : SIGNED(7 downto 0);
..
sr_u <= SHIFT_RIGHT("11001001",2);    -- sr_u = "00110010"
sr_s <= SHIFT_RIGHT("11011010",3);    -- sr_s = "11111011"
 
sl_u <= SHIFT_LEFT("11001100",2);     -- sl_u = "00110000"
sl_s <= SHIFT_LEFT("00110011",4);     -- sl_s = "00110000"
 
rr_u <= ROTATE_RIGHT("00110011",2);   -- rr_u = "11001100"
rr_s <= ROTATE_RIGHT("11100011",2);   -- rr_s = "11111000"
 
rl_u <= ROTATE_LEFT("11000011",3);    -- rl_u = "00011110"
rl_s <= ROTATE_LEFT("10110111",5);    -- rl_s = "11110110"

Следует обратить внимание на работу оператора SHIFT_RIGHT. Если аргументом этой функции является вектор SIGNED, то в освободившиеся при сдвиге старшие разряды (левые) записывается значение старшего разряда вектора–аргумента.

 

Таблица 7 – Функции сдвига и вращения (Shift and Rotate Functions)

Номер Функция Аргумент Шаг сдвига
(COUNT)
S.1 SHIFT_LEFT UNSIGNED NATURAL
S.3 SIGNED
S.2 SHIFT_RIGHT UNSIGNED NATURAL
S.4 SIGNED
S.5 ROTATE_LEFT UNSIGNED NATURAL
S.7 SIGNED
S.6 ROTATE_RIGHT UNSIGNED NATURAL
S.8 SIGNED
S.9 sll UNSIGNED INTEGER
S.10 SIGNED
S.11 srl UNSIGNED INTEGER
S.12 SIGNED
S.13 rol UNSIGNED INTEGER
S.14 SIGNED
S.15 ror UNSIGNED INTEGER
S.16 SIGNED

 

Пример применения операторов сдвига SHIFT_RIGHT.

  signal sr_s, sr_s1 : SIGNED(7 downto 0);
    ..
  sr_s  <= SHIFT_RIGHT("11011010",3);   -- sr_s  = "11111011"
  sr_s1 <= SHIFT_RIGHT("01011010",3);   -- sr_s1 = "00001011"

Операторы sll (сдвиг влево), srl (сдвиг вправо), rol (вращение влево), ror (вращение вправо) второй группы выполняют те же функции, что и соответствующие операторы из первой группы, но имеют следующие отличия:

  • функции sll, srl, rol, ror не совместимы с VHDL’87;
  • обращение к этим функциям как к операторам, например, чтобы сдвинуть вектор a на два бита вправо, надо записать a srl 2;
  • позволяют задавать сдвиг не только положительным, но и отрицательным числом. Последнее означает изменение направления сдвига на противоположное.

Пример применения операторов сдвига sll, srl.

signal sll_u, srl_u : UNSIGNED(7 downto 0);
signal sll_s, srl_s : SIGNED(7 downto 0);
 
signal sll_u1, srl_u1 : UNSIGNED(7 downto 0);
signal sll_s1, srl_s1 : SIGNED(7 downto 0);
..
srl_u  <= "10100111" srl 3;           -- srl_u  = "00010100"
srl_s  <= "11100110" srl 1;           -- srl_s  = "01110011"
 
srl_u1 <= "10100111" srl -3;          -- srl_u1 = "00111000"
srl_s1 <= "01100110" srl -1;          -- srl_s1 = "11001100"
 
sll_u  <= "11010111" sll 2;           -- sll_u  = "01011100"
sll_s  <= "11100110" sll 1;           -- sll_s  = "11001100"
 
sll_u1 <= "11010111" sll -2;          -- sll_u1 = "00110101"
sll_s1 <= "11100110" sll -1;          -- sll_s1 = "01110011"

При сдвиге вправо с помощью операторов sll, srl вектора SIGNED не происходит “размножения” знака вектора в освободившиеся старшие разряды результирующего вектора, как в операторе SHIFT_RIGHT

Пример применения операторов sll, srl для сдвига вправо вектора SIGNED.

signal srl2_s, srl3_s : SIGNED(7 downto 0);
signal sll2_s, sll3_s : SIGNED(7 downto 0);
..
srl2_s <= "01101110" srl 3;           -- srl2_s = "00001101"
srl3_s <= "11101110" srl 3;           -- srl3_s = "00011101"
 
sll2_s <= "00111001" sll -2;          -- sll2_s = "00001110"
sll3_s <= "10111001" sll -2;          -- sll3_s = "00101110"

Это связано с особенностью описания тела функций sll, srl в пакете.

Пример описания функций сдвига sll, srl в пакете NUMERIC_STD.

-- Id: S.12
  function "srl" (ARG: SIGNED; COUNT: INTEGER) return SIGNED is
  begin
    if (COUNT >= 0) then
      return SIGNED(SHIFT_RIGHT(UNSIGNED(ARG), COUNT));
    else
      return SHIFT_LEFT(ARG, -COUNT);
    end if;
  end "srl";
 
-- Id: S.10
  function "sll" (ARG: SIGNED; COUNT: INTEGER) return SIGNED is
  begin
    if (COUNT >= 0) then
      return SHIFT_LEFT(ARG, COUNT);
    else
      return SIGNED(SHIFT_RIGHT(UNSIGNED(ARG), -COUNT));
    end if;
  end "sll";

Как видно из описания для сдвига вектора вправо, вектор SIGNED преобразуется в вектор UNSIGNED и передается функции SHIFT_RIGHT, которая не размножает старший (знаковый) разряд вектора.

Пример применения операторов вращения rol, ror.

signal rol_u, ror_u : UNSIGNED(7 downto 0);
signal rol_s, ror_s : SIGNED(7 downto 0);
signal rol_u1, ror_u1 : UNSIGNED(7 downto 0);
signal rol_s1, ror_s1 : SIGNED(7 downto 0);
..
rol_u  <= "01101110" rol 2;           -- rol_u  = "10111001"
rol_s  <= "10110111" rol 3;           -- rol_s  = "10111101"
 
rol_u1 <= "01101110" rol -2;          -- rol_u1 = "10011011"
rol_s1 <= "10110111" rol -3;          -- rol_s1 = "11110110"
 
ror_u  <= "10110111" ror 3;           -- ror_u  = "11110110"
ror_s  <= "01101111" ror 3;           -- ror_s  = "11101101"
 
ror_u1 <= "10110111" ror -3;          -- ror_u1 = "10111101"
ror_s1 <= "01101111" ror -3;          -- ror_s1 = "01111011"

Функции сдвига работают с векторами любой длины, которая ограничена размерностью типа NATURAL. Операции сдвига можно заменить стандартным оператором & конкатенации.

Пример замены оператора сдвига SHIFT_RIGHT оператором конкатенации.

signal c_sr_s, sr_s, byte : SIGNED(7 downto 0);
..
byte <= "11101110";
 
sr_s <= SHIFT_RIGHT(byte,3);   -- результат sr_s = “11111101”
 
c_sr_s <= (7 downto 5 => byte(byte'length-1))&byte(7 downto 3);
                                -- результат c_sr_s = “11111101”

Пример замены оператора сдвига rol оператором конкатенации

signal c_rol_u, rol_u, byte_u : UNSIGNED(7 downto 0);
..
byte_u <= "11011100";
rol_u <= byte_u rol 3;    -- результат rol_u = “11100110”
c_rol_u <= byte_u(4 downto 0)&byte_u(7 downto 5);
                           -- результат c_rol_u = “11100110”

 

Операции сравнения

Описанные в пакете функции сравнения приведены в табл. 8.

Если хотя бы один из аргументов L, R (табл. 8) содержит один или несколько символов, не входящих в подмножество {H,L,‘0’,‘1’}, то результат сравнения равен false и выдается предупреждение

  "NUMERIC_STD.""*"": metavalue detected, returning FALSE",

где * – выполняемая операция сравнения (>, <, <=, >=, =, /=).

Сравниваемые с помощью операций табл. 8 векторы могут быть разной длины. Все функции сравнения основаны на функциях сравнения векторов STD_LOGIC_VECTOR из пакета STD_LOGIC_1164.

 

Таблица 8 – Операции сравнения (Comparison Operators, Match Functions)

Номер Аргумент (L) Функция Аргумент (R) Результат
C.1, 7, 13, 19, 25, 31 UNSIGNED >, <,>=, <=, =, /= UNSIGNED BOOLEAN
C.2, 8, 14, 20, 26, 32 SIGNED SIGNED
C.3, 9, 15, 21, 27, 33 NATURAL UNSIGNED
C.4, 10, 16, 22, 28, 34 INTEGER SIGNED
C.5, 11, 17, 23, 29, 35 UNSIGNED NATURAL
C.6, 12, 18, 24, 30, 36 SIGNED INTEGER
M.1 STD_ULOGIC STD_MATCH STD_ULOGIC BOOLEAN
M.2 UNSIGNED UNSIGNED
M.3 SIGNED SIGNED
M.4 STD_LOGIC_VECTOR STD_LOGIC_VECTOR
M.5 STD_ULOGIC_VECTOR STD_ULOGIC_VECTOR

 

Операция STD_MATCH сравнивает два аргумента одинаковых типов на равенство. Аргументы могут быть следующих типов: std_ulogic, unsigned, signed, std_logic_vector, std_ulogic_vector. Типы аргументов обязательно должны совпадать. Операция STD_MATCH сравнивает аргументы по следующим правилам.

  1. При сравнении любого компонента вектора с значением ‘–’ результат равен true.
  2. Значение ‘H’ при сравнении эквивалентно ‘1’, а значение ‘L эквивалентно ‘0’.
  3. Если значение компонента равно ‘U’,‘X’,‘Z’,‘W’, то результат сравнения с любым (кроме ‘–’) значением соответствующего компонента второго аргумента, будет равен false.

Ниже приведена компактная условная запись правил сравнения:

 

 

Длины аргументов в функции STD_MATCH должны совпадать, в противном случае на экран при моделировании выдается сообщение

  "NUMERIC_STD.STD_MATCH: L'LENGTH /= R'LENGTH, returning FALSE"

и результату присваивается значение false.

Возникает ошибка при использовании следующих операторов:

c_boolean <=101=101; -- ошибка

Возможно, система моделирования не может определить, какую функцию использовать, так как не может однозначно идентифицировать тип вектора “101”. Вектор “101” может иметь тип BIT_VECTOR, STD_LOGIC_VECTOR, SIGNED и т.д. Если же тип аргументов задан строго, то ошибки не возникает. Например,

c_boolean <= 10 = 10;     -- нет ошибки

не считается ошибкой. Здесь 10 – число INTEGER. В результате выполнения таких операторов (назначить сигналу c_boolean значение, равное результату проверки отношения равенства двух целых чисел 10) сигнал c_boolean примет значение true.

 

Арифметические операции (Arithmetic Operators)

К арифметическим операциям относятся: abs (абсолютное значение),+ (сложение), (вычитание), * (умножение), / (деление, т.е. получение целой части частного), rem (получение остатка от деления), mod.

Преимуществом типов UNSIGNED и SIGNED над типом STD_LOGIC_VECTOR является то, что для этих типов описаны арифметические операции (1), (2)

  UNSIGNED {+, –, *, /, rem, mod} UNSIGNED,                       (1)
  SIGNED {+, –, *, /, rem, mod} SIGNED,                           (2)

не только над векторами типов UNSIGNED, SIGNED, но и операции (3), (4)

  UNSIGNED{+,–,*,/,rem,mod} NATURAL,                              (3)
  SIGNED {+,–,*,/,rem,mod} INTEGER.                               (4)

над вектором и целым числом.

Особенности работы, характерные для всех арифметических функций abs, , +, *, /, rem, mod, заключаются в следующем:

  • если один из аргументов, какой либо арифметической функции является вектором, в котором хотя бы один из компонентов вектора не равен ‘0’,‘1’,‘L’,‘H’, то результат функции будет вектор “XX…XX”;
  • если аргументами арифметической функции являются векторы, то их размерность ограничена размерностью типа NATURAL;
  • если одним из аргументов арифметической функции является целое число (NATURAL, INTEGER), то перед выполнением функции это число преобразуется в вектор с помощью соответствующей функции (TO_UNSIGNED, TO_SIGNED) изменения типа. Размерность этого вектора равна размерности
    второго аргумента, который является вектором. Таким образом, если целое число будет больше чем можно задать вектором такой размерности, то при преобразовании числа в вектор старшие (значащие) биты будут потеряны и результат операции будет неверный.

Функции “–” и “abs” описаны только для типа SIGNED (табл. 9). Данные функции возвращают вектор, размерность которого совпадает с размерностью аргумента. Функция “–” (изменение знака числа) возвращает число, взятое с противоположным знаком, а функция “abs” возвращает абсолютное значение числа (аргумента). Размерность аргументов этих функций ограничена типом NATURAL.

 

Таблица 9 – Арифметические функции abs, –

Номер Функция Аргумент
(ARG)
Результат
A.1 abs SIGNED SIGNED(ARG’LENGTH-1 downto 0)
A.2 SIGNED SIGNED(ARG’LENGTH-1 downto 0)

 

Пример применения функций abs, –.

  signal a_s, b_s, c_s : SIGNED(5 downto 0);
    ...
  a_s <= TO_SIGNED(-27,6);              -- a_s = "100101" (-27)
  b_s <= -(a_s);                        -- b_s = "011011" (27)
  c_s <= abs(a_s);                      -- c_s = "011011" (27)

При использовании данных функций необходимо учитывать следующую особенность их выполнения. Например, если взять четырехразрядный вектор SIGNED, присвоить ему значение –8 (“1000”) и применить к нему одну из функций abs, –, то результатом будет являться число –8 (“1000”), а не 8 (“1000”):

Пример применения функций abs, –.

  signal a_s : SIGNED(3 downto 0) :=1000; -- a_s = “1000” (-8)
  signal b_s, c_s : SIGNED(3 downto 0);
     ...
  b_s <= - (a_s);    -- результат b_s = -8 (“1000”)
  c_s <= abs(a_s);  -- результат c_s = -8 (“1000”)

Функции сложения, вычитания (табл. 10), умножения (табл. 11), деления (табл. 12) допускают выполнение операции (1), (2) не только над одноименными типами, но и операции (3), (4) для комбинаций типов. Следует придерживаться таких комбинаций и не использовать других, так как в пакете не описаны функции для типов аргументов, отличных от приведенных выше комбинаций. Следующие комбинации типов аргументов арифметических функций “официально” не поддерживаются:

UNSIGNED {арифметическая операция} SIGNED;
UNSIGNED {арифметическая операция} INTEGER;
SIGNED {арифметическая операция} NATURAL.

Функция “+” (“–”) осуществляет функцию сложения (разности) двух векторов (которые могут быть разной размерности) или вектора и целого числа. При сложении двух векторов (SIGNED, UNSIGNED) разной размерности результирующий вектор принимает размерность того из аргументов, который имеет большую разрядность.

 

Таблица 10 – Функции арифметических операций сложения и вычитания

Номер Аргумент (L) Функ-ция Аргумент (R) Результат
A.3, A.9 UNSIGNED +
UNSIGNED UNSIGNED(MAX(L’LENGTH, R’LENGTH)-1 downto 0)
A.4,10 SIGNED SIGNED SIGNED(MAX(L’LENGTH, R’LENGTH)-1 downto 0)
A.5, 11 UNSIGNED NATURAL UNSIGNED(L’LENGTH-1 downto 0)
A.6,12 NATURAL UNSIGNED UNSIGNED(R’LENGTH-1 downto 0)
A.7,13 INTEGER SIGNED SIGNED(R’LENGTH-1 downto 0)
A.8,14 SIGNED INTEGER SIGNED(L’LENGTH-1 downto 0)

 

Пример применения функции сложения для векторов различной размерности.

-- Для unsigned
signal l_u : unsigned(3 downto 0) :=1100;   -- “1100” = (12)
signal r_u : unsigned(5 downto 0) := “000100”; -- “000100” = (4)
signal c_u : unsigned(5 downto 0);
..
c_u <= l_u + r_u; 		-- результат c_u = “010000” (16)
 
-- Для signed
signal l_s : signed(3 downto 0) :=1010;     -- (-6)
signal r_s : signed(5 downto 0) := “010010”;   -- (18)
signal c_s : signed(5 downto 0);
..
c_s <= l_s + r_s; -- c_s = “001100” (12)

При сложении (вычитании) вектора (SIGNED, UNSIGNED) и целого числа (NATURAL, INTEGER) результирующий вектор (независимо от значения целого числа) имеет размерность вектора.

Пример применения функции сложения.

-- Для unsigned + natural
signal l_u  : unsigned(5 downto 0) := "000100"; -- "000100"=(4)
signal r_n  : natural := 5;
signal y_nu : unsigned(5 downto 0);
...
y_nu <= l_u + r_n;        -- результат y_nu = “001001” (9)
 
-- Для signed + integer
signal l_s  : signed(0 to 5) := "010010";  -- "010010" = (18)
signal r_i  : integer := -2; 
signal y_is : signed(5 downto 0);
...
y_is <= l_s + r_i;   -- результат y_is = “010000” (16)

 

Рассмотрим пример сложения трехразрядного вектора (UNSIGNED) и целого числа 9 (NATURAL):

signal l_u : unsigned(2 downto 0) := "110"; -- "110" = (6)
signal r_n : natural range 0 to 15 := 9;    -- (9) = “1001”

Очевидно, что для представления r_n в двоичной форме необходимо четыре разряда. Если выполнить операцию сложения:

  signal y_nu : unsigned(2 downto 0);
     ...
  y_nu <= l_u + r_n; -- “110”(6) + 9(“1001”) = “111” (7) !Ошибка

то результат получается неверным, т.к. целое число 9 (“1001”) преобразуется к вектору размерности 3 и, следовательно, складываются векторы “110” + “001” = “111”. Чтобы избежать такой ошибки, необходимо предварительно перевести целое число в вектор необходимой размерности. Для этого можно использовать функцию to_unsigned:

signal l_u : unsigned(2 downto 0) := "110"; -- "110" = (6)
signal r_n : natural range 0 to 15 := 9;    -- (9) = “1001”
signal y_nu : unsigned(3 downto 0);
...
y_nu <= l_u + to_unsigned(r_n,4); 
                       -- “110”(6) + 9(“1001”) = “1111” (15)

В данном случае получается правильный результат (15), но если сложить целое число 9 с вектором “111” (7), то заданной выше разрядности недостаточно. Для получения правильного результата необходимо добавить еще один разряд, чтобы не потерять перенос (значение старшего разряда):

signal r_n : natural range 0 to 15 := 9;    -- (9) = “1001”
signal l_u  : unsigned(2 downto 0) := "111"; -- "111" = (7)
signal y_nu : unsigned(4 downto 0);
. . .
y_nu <= l_u + to_unsigned(r_n,5); 
                      -- “111”(7) + 9(“01001”) = “10000” (16)

или

y_nu <= l_u +0& to_unsigned(r_n,4); 
                       -- “111”(7) + 9(“01001”) = “10000” (16)

При умножении двух векторов (табл. 11), размерность результирующего вектора равна сумме размеров векторов-аргументов.

Пример применения функции умножения.

signal l_u : unsigned(3 downto 0) := "1101";  -- "1101" = (13)
signal r_u : unsigned(2 downto 0) := "100";   -- "100" = (4)
signal y_u : unsigned(6 downto 0); 
...    
y_u <= l_u * r_u;	-- результат y_u = “0110100” (52)

 

Таблица 11 – Функции арифметической операции умножения

Номер Аргумент (L) Функция Аргумент (R) Результат
A.15 UNSIGNED * UNSIGNED UNSIGNED((L’LENGTH+R’LENGTH-1) downto 0)
A.16 SIGNED SIGNED SIGNED((L’LENGTH+R’LENGTH-1) downto 0)
A.17 UNSIGNED NATURAL UNSIGNED((L’LENGTH+L’LENGTH-1) downto 0)
A.18 NATURAL UNSIGNED UNSIGNED((R’LENGTH+R’LENGTH-1) downto 0)
A.19 SIGNED INTEGER SIGNED((L’LENGTH+L’LENGTH-1) downto 0)
A.20 INTEGER SIGNED SIGNED((R’LENGTH+R’LENGTH-1) downto 0)

 

При умножении вектора и целого числа, результирующий вектор имеет удвоенную размерность вектора-аргумента:

Пример применения функции умножения.

signal l_s : signed(4 downto 0) := "11101"; -- "11101" = (-3)
signal r_i : integer := 13;
signal y_s : signed(9 downto 0);
..
y_s <= l_s * r_i;       -- результат y_s = "1111011001" (-39)

При делении (табл. 12) одного вектора на другой, результатом является вектор, размерность которого равна размерности вектора-делимого.

 

Таблица 12 – Функции деления

Номер Аргумент (L) Функция Аргумент (R) Результат
A.21 UNSIGNED / UNSIGNED UNSIGNED(L’LENGTH-1 downto 0)
A.22 SIGNED SIGNED SIGNED(L’LENGTH-1 downto 0)
A.23 UNSIGNED NATURAL UNSIGNED(L’LENGTH-1 downto 0)
A.24 NATURAL UNSIGNED UNSIGNED(R’LENGTH-1 downto 0)
A.25 SIGNED INTEGER SIGNED(L’LENGTH-1 downto 0)
A.26 INTEGER SIGNED SIGNED(R’LENGTH-1 downto 0)

 

При делении, когда делитель является вектором (UNSIGNED, SIGNED), а делимое целым числом (NATURAL, INTEGER), следует вести контроль того, чтобы значение целого числа-результата не превышало максимально возможного значения вектора-аргумента (делителя) заданной размерности

signal l_n : NATURAL := 20;
signal r_u : UNSIGNED(2 downto 0) := "010";
signal y_u : UNSIGNED(2 downto 0);
...
y_u <= l_n / r_u;  -- y_u = 20 / “010” = “010” – Ошибка
                   -- Для результата “1010” (10) нужно 4 бита

При возникновении такой ситуации выдается следующее предупреждение об ошибке:

# ** Warning: NUMERIC_STD."/": Quotient Truncated

При делении на нуль выдается сообщение об ошибке

# ** Error: DIV, MOD, or REM by zero,

но работа VHDL-программы продолжается. При этом результат деления не является определенным. Если значение делителя больше значения делимого, то результат деления 0. Нуль (вектор “00…00” или целое число 0) можно делить на любое число. Результат такой операции всегда 0.

Если поделить на –1 наименьшее отрицательное число, представимое вектором заданной разрядности (это число представляется вектором, в котором имеется единица в левом разряде, а остальные разряды содержат нуль), то результатом будет являться само это число:

signal l_s : SIGNED(2 downto 0) := "100";     -- (-4)
signal y_s : SIGNED(2 downto 0);
...
y_s <= l_s /(-1); -- y_s = “100”(-4) / (-1) = “100”(-4) - Ошибка

Эта ошибка подобна ошибке в операции abc.

Операторы rem и mod вычисляют остаток от деления. Результирующий вектор для операций rem и mod имеет размерность делителя, если аргументы векторы, а если один из аргументов является целым числом, то размерность результата равна размерности второго аргумента-вектора (табл. 13). В языке VHDL [1] операции rem и mod определяются следующим образом.

Для операции L rem R должно выполняться соотношение

  L = (L/R)*R + (L rem R),

где L/R целая часть частного, (L rem R) – результат выполнения операции rem (остаток). Остаток имеет знак операнда L.

Для операции L mod R должно выполняться соотношение

  L = N*R + (L mod R),

где N – некоторое целое число, (L mod R) – результат выполнения операции mod. Результат имеет знак операнда R.

Пример выполнения операций rem, mod языка VHDL.

13 rem 5 = 3,                   13 mod 5 = 3;
13 rem (-5) = 3,                13 mod (-5) = -2;
(-13) rem 5 = -3,               (-13) mod 5 = 2;
(-13) rem (5) = -3,            (-13) mod (5) = -3.

 

Таблица 13 – Функции rem, mod

Номер Аргумент (L) Функция Аргумент (R) Результат
A.27, 33 UNSIGNED rem
mod
UNSIGNED UNSIGNED(R’LENGTH-1 downto 0)
A.28, 34 SIGNED SIGNED SIGNED(R’LENGTH-1 downto 0)
A.29, 35 UNSIGNED NATURAL UNSIGNED(L’LENGTH-1 downto 0)
A.30, 36 NATURAL UNSIGNED UNSIGNED(R’LENGTH-1 downto 0)
A.31, 37 SIGNED INTEGER SIGNED(L’LENGTH-1 downto 0)
A.32, 38 INTEGER SIGNED SIGNED(R’LENGTH-1 downto 0)

 

Пример применения функций rem, mod (пакета NUMERIC_STD) для типа SIGNED.

"0011" rem "0101" = "0011"   -- 3 rem 5 = 3        
"0011" mod "0101" = "0011"   -- 3 mod 5 = 3
 
"1101" rem "0101" = "1101"   -- (-3) rem 5 = -3
"1101" mod "0101" = "0010"   -- (-3) mod 5 = 2
 
"0011" rem "1011" = "0011"   -- 3 rem (-5) = 3
"0011" mod "1011" = "1110"   -- 3 mod (-5) = -2
 
"1101" rem "1011" = "1101"   -- (-3) rem (-5) = -3
"1101" mod "1011" = "1101"   -- (-3) mod (-5) = -3

Как показано в примерах, для типа SIGNED функции rem, mod пакета NUMERIC_STD корректно реализуют соответствующие операции языка VHDL. Заметим, что результаты операций rem, mod над типом UNSIGNED одинаковы.

Заключение

Пакет NUMERIC_STD является удобным и достаточно надежным средством для согласования типов данных при моделировании смешанных VHDL-описаний – алгоритмических описаний и описаний логических схем. Рекомендуем пользователям, которые начинают использовать данный пакет в своей работе, провести лично проверку найденных (и представленных в данной статье) неточностей в работе функций этого пакета и предусмотреть возможные ошибки. Особенно внимательно нужно следить за размерностями векторов, представляющих результирующие значения операций, так как размерность результата операции числа и вектора определяется именно размерностью вектора.

ЛИТЕРАТУРА

  1. IEEE Standard VHDL Language Reference Manual, IEEE Std 1076-1987.
  2. IEEE Standard VHDL Language Reference Manual, IEEE Std 1076-1993.
  3. IEEE Standard VHDL Analog and Mixed-Signal Extensions, IEEE Std 1076.1-1999.
  4. Бибило П.Н. Синтез логических схем с использованием языка VHDL. – М.: СОЛОН-Р, 2002. – 384 с.
  5. Бибило П.Н. Основы языка VHDL. – М.: СОЛОН-Р, 2002. – 224 с.
  6. Сергиенко А.М. VHDL для проектирования вычислительных устройств. К.: ЧП “Корнейчук”, ООО “ТИД “ДС”, 2003. – 208 с.
  7. Перельройзен Е.З. Проектируем на VHDL – М.: СОЛОН – Пресс, 2004. – 448 с.
  8. Поляков А.К. Языки VHDL и VERILOG в проектировании цифровой аппаратуры. – М.: СОЛОН-Пресс, 2003. – 320 с.
  9. Стешенко В.Б. EDA. Практика автоматизированного проектирования радиоэлектронных устройств. – М.: “Нолидж”, 2002. – 768 с.
  10. Суворова Е.А., Шейнин Ю.Е. Проектирование цифровых систем на VHDL. – СПб.: БХВ-Петербург, 2003. – 576 с.
Контактная информация:

Автор идеи и контента: Бибило П.Н.
Разработчики: Голанов В.А., Зарембо Д.В.
На основе Wordpress CMS

Статистика за сегодня:

Сайт размещен на сервере ОИПИ НАН Беларуси