В начале главы уже говорилось, что на моделируемое устройство в первую очередь следует посмотреть со стороны (“вид снаружи”) и для этого “черного ящика” определить интерфейс с внешним миром. Такая задача решается в VHDL декларацией интерфейса entity. Она задает имя описания, имена интерфейсных портов, направление передачи, тип портов. Порт – это сигнальная линия (шина), с помощью которой устройство (модель) взаимодействуют с окружающей средой. Например, для полусумматора (рис. 1.2) декларация будет выглядеть следующим образом:
Рис. 1.2. Полусумматор
entity half_adder is
port(a, b: in BIT; sum, cur: out BIT);
endhalf_adder;
Здесь описание с именем half_adderимеет два входных порта, aи b(in– означает входной порт), и два выходных порта, sumи carry(out– означает выходной порт). Тип портов определен как BIT– означает, что сигналы на линиях портов могут принимать значения: ‘0’ или ‘1’.
Другим примером может послужить дешифратор, показанный на рис. 1.3.
entity dec2x4 is
port(a, b, enable: in BIT; z: out BIT_VECTOR (0 to 3));
enddec2x4;
Здесь описание с именем dec2x4 имеет три входных порта и четыре выходных. Выходные порты описаны как массив. BIT_VECTOR– это одномерный массив, диапазон задается параметром Z(0 to3).
Рис. 1.3. Декодер 2х4
Каждый интерфейсный порт может функционировать в следующих режимах:
in– значение порта только считывается для использования внутри модели,
out– значение порта может только обновляться моделью, но не считывается,
inout– двунаправленный порт, значение считывается и обновляется моделью,
buffer– буферный порт, значение считывается и обновляется моделью, но источником сигнала может быть либо буфер, либо одиночный источник.
Архитектура (architecturebody) моделирует взгляд на устройство “изнутри”. Устройство может рассматриваться под разными углами и описываться по-разному. Оно может предстать либо как композиция более простых модулей (структурный стиль моделирования), либо как набор параллельно исполняемых алгоритмов (стиль dataflow), либо описано как процесс последовательного исполнения операций (стиль “поведенческий”), либо может быть раскрыто сочетанием указанных стилей.
С одним интерфейсом типа entity может быть связано несколько архитектур. Рассмотрение архитектурных стилей начнем с поведенческого (behavioralstyle).
Функционирование устройства рассматривается как процесс последовательного вычисления выражений, входящих в process. Толчком к запуску процесса является изменение (event-событие) какого либо сигнала, из входящих в список “воспринимаемых” сигналов (sensitivitylist). Этот список внешне очень похож на список параметров в других языках высокого уровня. Рассмотрим поведенческую модель декодера dec2х4 (рис.1.3). Декларация интерфейса его уже приводилась, а теперь она будет пополнена архитектурой поведенческого типа.
entity dec2x4 is
port(a, b, enable: in BIT; z: out BIT_VECTOR (0 to 3));
end dec2x4;
architecture dec_seq of dec2x4 is
begin
process (a, b, enable)
variable abar, bbar: BIT;
begin
abar:= not a; --выражение 1
bbar:= not b; -- выражение 2
If enable = ‘1’ then -- выражение 3
z(3) <= not (a and b); -- выражение 4
z(2) <= not (a and bbar); -- выражение 5
z(1) <= not (abar and b); -- выражение 6
z(0) <= not (abar and bbar); -- выражение 7
else z <= “1111”;
end if;
end process;
enddec_seq;
Архитектура имеет свое имя dec_seq. О принадлежности архитектуры к описанию интерфейса dec2х4 говорит выражение: dec_seqofdec2x4. Список сигналов, которыми запускается процесс, дан в скобках: process(a, b, enable). До начала процесса, если необходимо, декларируются переменные variableabar, bbar: BIT. Зона действия их ограничена данным процессом (от beginдо endprocess). Предположим, что в момент Tпроизошло изменение сигнала а. Новое значение переменной abar(выражение 1) вычисляется и присваивается без задержки – мгновенно (в качестве знака присваивания в данном случае используется =). Без задержки будут вычислены выражения 2 и 3.
Далее в ходе процесса одно за другим вычисляются выражения от 4 до 7, а новые значения сигналам z(0), z(1), z(2), z(3) будут присвоены (в качестве символа оператора присвоения используется <= ) лишь через некоторую, пусть даже фиктивную (бесконечно малую), задержку D. Это существенное отличие процедур присвоения для переменных и для сигналов.
Рассмотрим пример мультиплексора 4х1 (рис.1.4). Четыре входных сигнала A, B, Cи Dвыводятся через один порт Z. Коммутация осуществляется сигналом CTRL.
Рис. 1.4. Мультиплексор4х1.
entity MUX is
port (A, B, C, D: in BIT; CTRL: in BIT_VECTOR(0 to 1);
Z: out BIT);
end MUX;
architecture MUX_BEHAVIOR of MUX is
begin
PMUX: process (A, B, C, D, CTRL)
variable TEMP: BIT;
begin
case CTRL is
when "00" => TEMP := A:
when "01" => TEMP := B;
when "10" => TEMP := C;
when "11" => TEMP := D;
end case;
Z <= TEMP;
end process PMUX;
end MUX_BEHAVIOR;
АрхитектураполучиланазваниеMUX_BEHAVIOR, процессимеетярлыкPMUX. Переменной TEMPв зависимости от значения сигнала CTRLприсваивается значение одного из входных портов: A, когда CTRLимеет значение “00”; B, когда CTRLимеет значение “01”; A, когда CTRLимеет значение “10”; D, когда CTRLимеет значение “11”.
Для описания синхросигналов обычно используется атрибу EVENT. Он истинен когда происходит “событие” - изменение сигнала. Например, поведение D-триггера, показанного на рис.1.5, можно описать так:
Рис.1.5. D-триггер
entity dff is
port (data, clk: in BIT
q, notq: out BIT);
end dff;
architecture behav of dff is
begin
process (clk)
begin
if (clk’event and clk = ‘1’) then
q <= data;
notq <= not data;
end if;
end process;
end behav;
Выражение (clk’eventandclk= ‘1’) задает условие: если сигнал clk меняется с ‘0’ на ‘1’
Dataflow- означает поток данных. Описание в стиле dataflowиспользует в архитектуре параллельно вычисляемые выражения. Количество параллельно вычисляемых выражений может быть любым. Так как вычисления происходят параллельно, то порядок записи выражений не имеет значения. Толчком к началу вычислений является изменение любого из сигналов, входящих в выражения. Рассмотрим модель одноразрядного сумматора, показанного на рис.1.6.
Рис.1.6. Одноразрядныйсумматор
entity full_adder is
port (a, b, cin: in BIT; sum, cout: out BIT);
end full_adder;
architecture full_ad_conc of full_adder is
begin
sum <= (a xor b) xor cin after 15 ns;
cout <= (a and b)or(b and cin)or(cin and a) after 10ns;
endfull_ad_conc;
Для описания потока данных здесь используются два выражения. Всегда, когда происходят изменения сигналов a, bили cin, оба выражения вычисляются и сигналам sumи cout присваиваются новые значения через 15nsи 10ns, соответственно. Знак <= оператор присваивания. Выражение after15nsозначает задержку, вносимую логикой, реализующей вычисление выражения для sum. Если задержка не введена явно, то предполагается наличие бесконечно малой задержки D. Такой прием позволяет упорядочить события модели dataflow. Рассмотрим схему рис. 1.7 и ее модель.
Рис. 1.7. Цепочкаинверторов
entity fast_inverter is
port (a: in BIT; z: out BIT);
end fast_inverter;
architecture delta_delay of fast_inverter is
signal b,c: BIT;
begin
z <= not c; -- выражение1
c<= notb; --выражение 2
b<= nota; --выражение 3
enddelta_delay;
В выражениях 1, 2 и 3, присваивающих значения переменным z, c, b, подразумевается задержка D. Например, в момент Tпроисходит изменение сигнала a. Запускается процесс вычисления выражения 3 и через задержку D переменной bбудет присвоено новое значение (в момент T+D). В свою очередь изменение сигнала bзапустит процесс вычисления выражения 2 и значение сигнала с будет обновлено в момент T+2D. Изменение сигнала с приведет к запуску процесса вычисления выражения 1 и значение сигнала zбудет изменено в момент T+3D. Временные соотношения в схеме иллюстрирует рис. 1.8.
Рис. 1.8. Временные соотношения в цепочке инверторов.
Структурное описание интерпретирует устройство как набор компонентов, связанных между собой сигналами. Грубо говоря - это таблица соединений (netlist). Рассмотрим простую схему управления (рис.1.8) и ее структурную модель.
Рис. 1.9. Устройство местного управления
entityctr_lckis
port (data, mr, clk, din: in BIT; rdy, ctrla:out Bit);
end ctr_lck;
architecture str_view of ctr_lck is
component AND2 -- декларируетсякомпонентAND2
port (x,y in BIT; z: out BIT);
end component;
component DFF -- декларируется компонент DFF
port (d, clock: in BIT; Q,NOTQ: out BIT);
end component;
component NOR2 -- декларируется компонент NOR2
port (a,b: in BIT; z: out BIT);
end component;
signal s1,s2: BIT;
begin
D1: DFF port map (data, clk, s1,s2); -- выражение 1
A1: AND2 port map (s2, din, ctrla); -- выражение 2
N1: NOR2 port map (s1, mr, rdy); -- выражение 3
endstr_view;
Здесь декларируются три компонента: AND2, DFFи NOR2. В тело архитектуры экземпляры компонентов вводятся с помощью выражений 1, 2 и 3. Компоненты связаны между собой сигналами s1 и s2. В декларации называется имя компонента и его интерфейс, что очень схоже с декларацией интерфейса устройства. Выражение для экземпляра компонента должно в первую очередь дать имя экземпляру или ярлык. Например, D1 – это ярлык для экземпляра триггера DFF. Далее следует список связей (associationlist). Он устанавливает связь между портами компонента и портами и сигналами устройства. Существует два варианта связи: позиционный и поименный. В позиционном списке первый порт декларации (слева направо) компонента соответствует первому порту экземпляра компонента (слева направо), второй – второму и так далее. Именно такой вариант списка был использован в приведенном выше примере. Вместо этого можно было использовать поименный вариант списка связей, например, выражение 3 можно было бы записать так: