Для создания проектов из нескольких модулей требуется их объединение. Причины разбиения проекта на модули могут быть различны, начиная от простого ограничения сложности каждого отдельного модуля, и заканчивая необходимостью использовать аппаратные узлы, имеющиеся в составе ПЛИС, которые включаются в проект с помощью библиотечного представления. Предположим, что необходимо создать схему, аналогичную показанной на рис. 2.5.
Рис. 2.5 Графическое изображение схемы из нескольких модулей
Требуется, чтобы в библиотеке компонентов имелись модули, соответствующие компонентам and2 и or2. Их описания могут быть следующими:
Длямодуляand2:
libraryIEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity my_and2 is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
c : out STD_LOGIC);
end my_and2;
architecture Behavioral of my_and2 is
begin
c <= a and b;
end Behavioral;
Длямодуля or2:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity my_or2 is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
c : out STD_LOGIC);
end my_or2;
architecture Behavioral of my_or2 is
begin
c <= a or b;
endBehavioral;
Модуль верхнего уровня на языке VHDL будет иметь следующий интерфейс:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entitytop_level is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
c : in STD_LOGIC;
d : in STD_LOGIC;
q : out STD_LOGIC);
endtop_level;
Внутри модуля верхнего уровня должны присутствовать два компонента and2 и один компонент or2. Они описываются с применением подхода componentinstantiation: ранее созданные компоненты указываются в тексте модуля верхнего уровня с перечислением сигналов, которые необходимо подключить к их портам. Однако при взгляде на схему можно заметить, что для соединения выходов компонентов and2 с входами or2 требуются два проводника, которые являются внутренними цепями и не имеют отдельного имени. Стандарт VHDL не допускает прямое соединения выхода одного компонента с входом другого (т.е., например, нельзя указать для порта a компонента or2, что он подключен непосредственно к порту q компонента and2). Для этого требуется объявление отдельных внутренних цепей после ключевого слова architecture:
architecture Structural of my_or2 is
signal net1, net2 : std_logic;
begin
-- Создадим далее схему соединений:
component1 : my_and2 port map (a => a, b => b, q => net1);
component2 : my_and2 port map (a => c, b => d, q => net2);
component3 : my_or2 port map (a => net1, b => net2, q => q);
endStructural;
Порты верхнего уровня выделены в данном примере жирным шрифтом, чтобы отличать их от совпадающих имен портов компонентов (такое совпадение не является ошибкой).
Рассмотримэлементызаписи
component2 : my_and2 port map (a=>c, b => d, q => net2);
Здесь and2 – имя компонента, как оно приведено в его описании после ключевого слова module.
сomponent2 - уникальный идентификатор, присвоенный разработчиком данному экземпляру компонента (в схеме присутствует также component1 такого же типа).
a – ссылка на порт a компонента;
с – указание цепи, к которой должен быть подключен этот порт.
Имена портов компонента можно не указывать, описывая только список сигналов, к которым необходимо подключать порты:
component1 : my_and2 port map (a, b,net1);
component2 : my_and2 port map (c, d, net2);
component3 : my_or2 port map (net1, net2, q);
В этом примере подключение к портам компонента осуществляется в порядке их описания в самом компоненте. Это так называемое позиционное назначение сигналов (orderedmapping) когда позиция сигнала в списке подключений совпадает с позицией порта в описании компонента. Предыдущий вариант, с явным упоминанием имен портов компонента, называется именованным назначением (namedmapping).
Нетрудно видеть, что позиционное назначение несколько компактнее, поскольку не включает в себя имена портов компонента. Тем не менее, именованное назначение позволяет избежать неявных ошибок, когда разработчик перепутал порядок следования имен. В качестве примера рассмотрим абстрактный компонент, который имеет сигналы сброса и разрешения работы:
entity example is
Port ( clk : in STD_LOGIC;
data : in STD_LOGIC;
reset : in STD_LOGIC;
en :inSTD_LOGIC;
…
<прочиесигналы>
Не вдаваясь в подробности реализации такого модуля, рассмотрим варианты его подключения с использованием позиционного назначения сигналов.
inst0 : example port map(net1, net2, net3, net4);
inst1 : example port map (clk_net, data_net, en_net, reset_net);
inst2 : example port map (clk_net, data_net, reset_net, en_net);
Первый вариант объявления не дает возможности быстро убедиться, что подключение выполнено правильно, поскольку имена цепей отличаются только номером. Второй вариант (inst1), использует «содержательные» имена, которые включают в себя фрагменты имен портов – например, clk_net дает понятие о том, что этот сигнал следует подключить к порту clk. Однако требуется отдельно сопоставить порядок объявления портов в описании модуля с порядком упоминания сигналов, чтобы убедиться, что для inst1 перепутан порядок следования сигналов en_net и reset_net. Позиционное назначение не позволит САПР выявить или исправить эту логическую ошибку, поскольку никаких формальных запретов на подключение сигнала en_net к входу reset не существует. Сравнимэтосименованнымназначением:
inst3 : example port map(clk =>clk_net, data =>data_net, en =>en_net, reset =>reset_net);
В этом примере порядок упоминания сигналов en и reset также не совпадает с порядком объявления сигналов в описании модуля. Однако явное упоминание имен сигналов заставит САПР подключить цепь en_net именно к входу en, несмотря на то, что на третьем месте в объявлении компонента находится reset. Таким образом, именованное назначение представляется более безопасным с точки зрения устранения логических ошибок при соединении компонентов.
Объявленные внутри архитектуры внутренние цепи называются сигналами (signal). Сигналами являются любые проводники, соединяющие узлы внутри цифровой схемы. Для удобства можно условно считать сигналы аналогами локальных переменных в программировании – точно так же, как программисты создают локальные переменные для хранения данных, не выходящих за пределы программного модуля, разработчик цифровой схемы определяет сигналы для передачи состояний, которые не должны выходить за пределы цифрового модуля.
VHDL имеет в своем составе также переменные (variable), константы (constant) и параметры, называемые generic. Переменные отличаются от сигналов тем, что сигналы представляют узлы разрабатываемой схемы, а переменные – данные в САПР, которые требуются в процессе ее разработки. Введение переменной может привести к формированию соответствующего ей сигнала, а может и не привести, что зависит от контекста ее использования.
Примеры объявления переменных и констант:
variable a : integer := 0;
constantinit_data :std_logic_vector(3 downto 0) := “1100”;
Начальное значение для переменных и констант задается после оператора :=. Этот оператор не является обязательным, т.е. можно просто объявить переменную, описав ее имя и тип.
Начальное значение для констант, сигналов и переменных действительно не только для моделирования, но и для синтезируемого кода. В процессе загрузки конфигурации FPGA устанавливает также начальные состояния триггеров и блоков памяти, которые и являются устройствами хранения значений сигналов, переменных и констант.