Операторы параллельного присваивания сигналов в VHDL: с помощью выбора варианта и по условию
В данной статье будут рассмотрены операторы параллельного присваивания сигналов в VHDL.
В данной статье будет рассмотрена концепция параллелизма в языках описания аппаратного обеспечения. Затем мы обсудим два оператора параллельного присваивания сигнала в VHDL: присваивание сигнала с помощью выбора варианта (selected signal assignment) и присваивание сигнала по условию (conditional signal assignment). После нескольких примеров мы кратко рассмотрим эти два типа операторов присваивания сигнала.
Если вы не знакомы с концепцией VHDL, то смотрите первую статью о введении в VHDL.
Параллельный и последовательный операторы
Чтобы понять разницу между параллельными и последовательными операциями, рассмотрим простую комбинационную схему, показанную на рисунке 1.
Если мы рассмотрим работу этих трех логических элементов на рисунке, то заметим, что каждый логический элемент обрабатывает свой текущий входной сигнал(ы) независимо от других логических элементов. Эти физические компоненты работают одновременно. В тот момент, когда на них подано питание, они будут «одновременно» выполнять свои функции. Обратите внимание, что, хотя на практике логический элемент И имеет задержку выдачи корректного выходного сигнала, это не означает, что логический элемент ИЛИ остановит свою работу и дождется выходного сигнала элемента И. Элемент ИЛИ будет работать всё время; однако его выходной сигнал будет некорректным, пока не установятся его входные сигналы.
Теперь давайте рассмотрим описание VHDL для рисунка 1. Оно показано ниже:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity comb1 is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
c : in STD_LOGIC;
d : in STD_LOGIC;
out1 : out STD_LOGIC;
out2 : out STD_LOGIC);
end comb1;
architecture Behavioral of comb1 is
signal sig1: std_logic;
begin
sig1 <= ( a and b );
out1 <= ( sig1 or c );
out2 <= (not d);
end Behavioral;
Основное, что нам здесь интересно, – это определение этих трех логических элементов:
sig1 <= ( a and b );
out1 <= ( sig1 or c );
out2 <= (not d);
Каждая из этих строк описывает физический компонент на рисунке 1. Например, вторая строка, которая описывает логический элемент ИЛИ, принимает значения sig1
и c
как входы и выдает результат операции ИЛИ этих двух значений. Мы видели, что физические компоненты на рисунке 1 работают одновременно. Следовательно, разумно ожидать, что описание VHDL этих элементов должно определяться параллельным способом. Другими словами, три вышеуказанные строки кода выполняются одновременно, и нет никакого значения для порядка этих операторов. В результате мы можем переписать секцию architecture
приведенного выше кода следующим образом:
architecture Behavioral of comb1 is
signal sig1: std_logic;
begin
out1 <= ( sig1 or c );
out2 <= (not d);
sig1 <= ( a and b );
end Behavioral;
Поскольку эти операторы оцениваются одновременно, мы называем их параллельными операторами. Этот тип кода сильно отличается от того, что мы узнали в базовом компьютерном программировании, где строки кода выполняются одна за другой. Например, рассмотрим следующий код MATLAB:
clear all;
a=1;
b=0;
c=1;
d=0;
sig1=and(a, b);
out1=or(sig1, c)
out2=not(d)
Данный код выдает out1=1 и out2=1. Однако, если мы изменим порядок операторов на следующий, программа перестанет работать, потому что мы пытаемся использовать sig1
до его создания
out1=or(sig1, c)
out2=not(d)
sig1=and(a, b);
Хотя код VHDL, описывающий рисунок 1, выполнялся одновременно, показанный выше код MATLAB рассматривается последовательно (то есть одна строка за другой). VHDL поддерживает как параллельные операторы, так и последовательные. Понятно, что параллельные операторы VHDL позволят нам легко описать схему, такую как схема, показанная на рисунке 1 выше. В следующей статье мы увидим, что последовательные инструкции VHDL позволяют нам иметь более безопасное описание последовательных схем. Кроме того, используя последовательный VHDL, мы можем легко описать цифровую схему поведенческим образом. Эта возможность может значительно облегчить проектирование цифрового аппаратного обеспечения.
На следующем рисунке показана разница между параллельными и последовательными операторами.
Теперь давайте рассмотрим два параллельных оператора присваивания сигнала в VHDL: «оператор присваивания сигнала с помощью выбора варианта» (selected signal assignment statement) и «оператор присваивания сигнала по условию» (conditional signal assignment statement).
Присваивание сигнала с помощью выбора варианта или оператор “with
/select
”
Рассмотрим мультиплексор «n к 1», как показано на рисунке 3. Этот блок должен выбрать один из своих n входов и передать значение этого входа на выходной вывод, т.е. output_signal
.
Присваивание сигнала с помощью выбора варианта позволяет нам реализовать функциональность мультиплексора. Например, VHDL код, описывающий мультиплексор, будет следующим:
with control_expression select
output_signal <= value_1 when option_1,
value_2 when option_2,
...
value_n when option_n;
Здесь значение control_expression
будет сравниваться с n возможных вариантов, т.е. option_1
, option_2
, …, option_n
. Когда совпадение будет найдено, значение, соответствующее этому конкретному варианту, будет присвоено выходному сигналу, т.е. output_signal
. Например, если control_expression
совпадает с вариантом option_2
, то значение value_2
будет присвоено output_signal
.
Обратите внимание, что варианты оператора “with
/select
” должны быть взаимоисключающими, т.е. один вариант нельзя использовать более одного раза. Более того, все возможные значения control_expression
должны быть включены в набор вариантов. Следующий пример разъясняет эти моменты.
Пример 1. Используйте оператор "with
/select
" для описания одноразрядного мультиплексора «4-к-1». Предположим, что необходимо выбирать входы a
, b
, c
и d
. А двухбитовый сигнал sel
используется для выбора нужного входа и его присваивания выходу out1
.
Код для этого мультиплексора приведен ниже:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Mux1 is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
c : in STD_LOGIC;
d : in STD_LOGIC;
sel : in STD_LOGIC_VECTOR (1 downto 0);
out1 : out STD_LOGIC);
end Mux1;
architecture Behavioral of Mux1 is
begin
with sel select
out1 <= a when "00",
b when "01",
c when "10",
d when others;
end Behavioral;
Обратите внимание: поскольку тип данных std_logic
может принимать значения, отличные от "0" и "1", в последней строке оператора “with
/select
” должно использоваться ключевое слово “others
”, чтобы учитывать все возможные значения sel
.
На следующем рисунке показано моделирование данного кода с помощью симулятора Xilinx ISE. Как показано на этом рисунке, от 0 наносекунд до 300 наносекунд вход выбора, sel
, равен 00, и, следовательно, out1
принимает такое же значение как на входе a
. Аналогично вы можете проверить предсказуемую работу на оставшемся времени моделирования.
Пример 2. Используйте оператор “with/select” для описания приоритетного шифратора 4-к-2 с таблицей истинности, показанной ниже:
Входы | Выходы | |||||
---|---|---|---|---|---|---|
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 |
Для описания приведенной выше таблицы истинности может быть использован следующий VHDL код:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity prio_encoder1 is
Port ( x : in STD_LOGIC_Vector(3 downto 0);
y : out STD_LOGIC_Vector(1 downto 0);
v : out STD_LOGIC);
end prio_encoder1;
architecture Behavioral of prio_encoder1 is
begin
with x select
y <= "11" when "1000" | "1001" | "1010" | "1011" | "1100" | "1101" | "1110" | "1111",
"10" when "0100" | "0101" | "0110" | "0111",
"01" when "0010" | "0011",
"00" when others;
v <= ( x(3) or x(2) or x(1) or x(0) );
end Behavioral;
Результаты ISE моделирования показаны на рисунке 5.
Присваивание сигнала по условию или оператор "when
/else
"
Оператор “when
/else
” – это еще один способ описать параллельные присваивания сигналов, подобные тем, что были приведены в примерах 1 и 2. Поскольку синтаксис этого типа присваивания сигнала довольно нагляден, давайте сначала рассмотрим VHDL код для одноразрядного мультиплексора 4-к-1, используя оператор “when
/else
”, а затем обсудим некоторые детали.
Пример 3. Используйте оператор “when
/else
” для описания одноразрядного мультиплексора 4-к-1. Предположим, что необходимо выбирать входы a
, b
, c
и d
. А двухбитовый сигнал sel
используется для выбора нужного входа и его присваивания выходу out1
.
Код будет следующим:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Mux2 is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
c : in STD_LOGIC;
d : in STD_LOGIC;
sel : in STD_LOGIC_VECTOR (1 downto 0);
out1 : out STD_LOGIC);
end Mux2;
architecture Behavioral of Mux2 is
begin
out1 <= a when sel="00" else
b when sel="01" else
c when sel="10" else
d;
end Behavioral;
В данном случае выражения после "when
" последовательно оцениваются до тех пор, пока не будет найдено истинное выражение. Затем будет выполнено присваивание, соответствующее этому истинному выражению. Если ни одно из этих выражений не является истинным, будет выполнено последнее присваивание. В общем случае синтаксис оператора “when
/else
” будет следующим:
output_signal <= value_1 when expression_1 else
value_2 when expression_2 else
...
value_n;
Следует подчеркнуть, что выражения после “when
” оцениваются последовательно. В результате выражения, приведенные ранее, имеют более высокий приоритет по сравнению с последующими. Учитывая это, мы можем получить наглядную диаграмму этого присваивания, показанную на рисунке 6. Этот рисунок иллюстрирует присваивание сигнала по условию с тремя условиями “when
”.
Давайте рассмотрим основные особенности присваивания сигнала с помощью выбора варианта и присваивания сигнала по условию.
Присваивания: “with
/select
” против “when
/else
”
Как упоминалось выше, варианты присваивания “with
/select
” должны быть взаимоисключающими, т.е. один вариант не может использоваться более одного раза. Более того, все возможные значения управляющего выражения control_expression
должны быть включены в набор вариантов. В то время как присваивание “with
/select
” имеет общее управляющее выражение, присваивание “when
/else
” может работать с выражениями с разными аргументами. Например, рассмотрим следующие строки кода
out1 <= ‘0’ when reset1=’0’ else
d when clk=’1’;
В этом случае выражения оценивают два разных сигнала, то есть reset1
и clk
.
Для присваивания “when
/else
” мы можем включать (а можем и не включать) все возможные значения выражений, которые должны быть оценены. Например, мультиплексор из примера 3 охватывает все возможные значения sel
; однако приведенный выше код этого не делает. Приведенный выше код предполагает, что out1
должен сохранять свое предыдущее значение, если ни одно из выражений не является истинным. Это приводит к предположению о запирании в синтезированной схеме.
Другое важное различие между присваиваниями “with
/select
” и “when
/else
” можно увидеть, сравнив наглядную реализацию этих двух операторов. Приоритетная сеть на рисунке 6 включает в себя каскад из нескольких логических элементов. Однако присваивание “with
/select
” позволяет избежать этой цепочечной структуры и имеет сбалансированную структуру. В результате теоретически оператор “with
/select
” может иметь лучшую производительность с точки зрения задержки и пространства.
На практике мы обычно не видим этого различия потому, что многие пакеты программного обеспечения для синтеза, такие как Xilinx XST, стараются не выводить логику с приоритетным шифрованием. Хотя мы можем использовать в XST ограничение PRIORITY_EXTRACT для принудительного вывода приоритетного шифрования, Xilinx настоятельно предлагает использовать это ограничение по принципу «сигнал за сигналом»; в противном случае это ограничение может привести нас к неоптимальным результатам.
Резюме
- Параллельные операторы выполняются одновременно, и нет никакого значения для порядка следования этих операторов. Этот тип кода сильно отличается от того, что мы узнали в базовом компьютерном программировании, где строки выполняются одна за другой.
- Присваивание сигнала с помощью выбора варианта или присваивание “
with
/select
” позволяет нам реализовать функционал мультиплексора. - Варианты присваивания “
with
/select
” должны быть взаимоисключающими, т.е. один вариант не может использоваться более одного раза. Более того, все возможные значения управляющего выраженияcontrol_expression
должны быть включены в набор вариантов. - Для оператора “
when
/else
”, выражения после “when
” оцениваются последовательно. В результате выражения, приведенные ранее, имеют более высокий приоритет по сравнению с последующими. - Одно важное различие между присваиваниями “
with
/select
” и “when
/else
” можно увидеть, сравнив наглядную реализацию этих двух операторов. Оператор “when
/else
” имеет приоритетную сеть; однако присваивание “with
/select
” позволяет избежать этой цепочечной структуры и имеет сбалансированную структуру.