Функции, процедуры, пакеты и конфигурации.

Ланкевич Ю.Ю.

Подпрограммы

Определение.

   subprogram_specification ::=
                                procedure designator [ ( formal_parameter_list ) ]
            | [ pure | impure ] function designator  [(formal_parameter_list) ] return type_mark

Подпрограммы имеют две формы – функции и процедуры. В декларации функции определяется указатель на функцию, её формальные параметры (если они есть), возвращаемый тип, и явлиется ли функции impure. Функция является impure только в случае, если декларация функции содержит зарезервированное слово impure; иначе подразумевается pure функция.
Указатель на проедуру - это всегда идентификатор. Указатель на функцию - либо идентификатор, либо символ оператора. Если в качестве указателя используется оператор, то такая функция переопределяет оператор (and, or, nand, nor, xor, xnor, =, /=, <, <=, >, >=, sll, srl, sla, sra, rol, ror, +, –, &, +, –, *, /, mod, rem, **, abs, not).
Подпрограммы могут быть декларированы в тексте пакета, интерфейсе объекта проекта, архитектурном теле, процессе, процедуре или функции. Подпрограммы могут вызывать другие подпрограммы.

Функции

Общий вид оператора декларации функции

   [pure | impure] function имя функции (параметр {, параметр} )
                       return тип возвращаемого функцией значения is
                   раздел деклараций
                   begin
                       тело функции
                   end [имя функции];

Общий вид оператора вызова функции

   имя функции ( фактический параметр {, фактический параметр} );  

Функции

Функция имеет только входные параметры. Следующий иллюстративный пример показывает (декларирует) функцию BOOL_TO_SL, преобразующую тип BOOLEAN в тип STD_ULOGIC.
Пример.

function BOOL_TO_SL(X : boolean) return std_ulogic is
begin
    if X then
        return '1';
    else
        return '0';
    end if;
end BOOL_TO_SL;

Следующий пример показывает функцию преобразования типа bit в тип boolean. Напомним, что данные типы не эквивалентны.

function bit_bool (inp_bit : in bit) return boolean is
begin
    if (inp_bit = '1') then
        return true;
    else
        return false;
    end if;
end bit_bool;

Функция может содержать последовательные операторы, исключая операторы ожидания и назначения сигналов. В теле функции могут декларироваться локальные переменные.

Процедуры

Общий вид оператора декларации процедуры

   procedure имя процедуры ( параметр {, параметр} ) is
   раздел деклараций
   begin
       тело процедуры
   end [имя процедуры];

Процедура может иметь входные (in), выходные (out) и вход/выходные (inout) параметры. Это могут быть сигналы, переменные или константы. По умолчанию входные параметры – константы, выходные и вход/выходные параметры – переменные.
Общий вид оператора вызова процедуры

имя процедуры ( фактический параметр {, фактический параметр} );

Таким образом, вызов процедуры состоит из имени процедуры и списка фактических параметров. Процедуры могут вызываться последовательно или параллельно. При параллельном вызове происходит выполнение процедуры, когда какой-нибудь входной вход/выходной параметр изменился. VHDL-код с текстом подпрограммы, т.е. функции или процедуры может находится в разделе деклараций архитектурного тела, либо в пакете. Если подпрограмма определена в пакете, то текст подпрограммы должен быть в теле пакета. Если подпрограмма находится в пакете, то в этом случае перед текстом entity, где процедура используется (процедуры могут использоваться в архитектурных телах), требуется указание имени библиотеки и имени пакета, в котором содержится текст подпрограммы.

Процедуры

Следующий пример процедуры PARITY показывает отличия процедуры от функции: функция имеет только входные параметры, режим (mode) которых не специфицируется, в процедуре могут содержаться операторы назначения сигналов.

procedure PARITY ( signal X : in std_ulogic_vector;
                   signal Y : out std_ulogic) is
variable TMP : std_ulogic := '0';
begin
    for J in X'range loop
        TMP := TMP xor X(J);
    end loop;
    Y <= TMP; -- в процедуре могут быть операторы назначения сигналов
end PARITY;

Процедуры

Следующий пример показывает процедуру DISPLAY_MUX и ее вызов в архитектурном теле SUBPROG.

procedure DISPLAY_MUX ( ALARM_TIME, CURRENT_TIME : in digit;
                        SHOW_A                   : in std_ulogic;
                        signal DISPLAY_TIME      : out digit) is
begin
    if (SHOW_A = '1') then
        DISPLAY_TIME <= ALARM_TIME;
    else
        DISPLAY_TIME <= CURRENT_TIME;
    end if;
end DISPLAY_MUX;
architecture SUBPROG of DISP_MUX is
...
begin -- вызов процедуры
    DISPLAY_MUX (ALARM_TIME, CURRENT_TIME, SHOW_A, DISPLAY_TIME);
end SUBPROG;

Пакеты

Пакет (package) в VHDL – это VHDL-текст, который может включать множество деклараций:

  • типов;
  • подтипов;
  • констант;
  • процедур;
  • функций;
  • компонент;
  • и др.

В теле пакета (package body) находятся тела функций, тела процедур и другие VHDL-описания. Однако в пакете не декларируются, а в тело пакета не записываются entity, архитектурные тела (architecture), конфигурации (configuration). Функции и процедуры иногда называют подпрограммами. Пример функции языка VHDL: функция NOW возвращает текущее время системы моделирования. Приведем только пример декларации процедуры, более подробно подпрограммы будут рассмотрены далее. Пакет multiplexer показывает, что процедура MX декларируется в пакете и тело процедуры определяется в теле пакета.

 package multiplexer is
    procedure MX( signal SEL : in bit;
                  signal x0  : in bit;
                  signal x1  : in bit;
                  signal F   : out bit);
end multiplexer;
package body multiplexer is
    procedure MX( signal SEL : in bit;
                  signal x0  : in bit;
                  signal x1  : in bit;
                  signal F   : out bit) is
    begin
        case SEL is
            when '0' => F <= x0;
            when others => F <= x1;
        end case;
    end MX;
end multiplexer;

Конфигурации

Конфигурация – эффективный инструмент управления составом иерархически организованного проекта, она позволяет задать поддерево дерева иерархии проекта. Изучим применение конфигурации сначала на простом примере, когда при конкретизации компонента можно использовать различные архитектурные тела. Например, в схеме compare в архитектурном теле eq1 выходной сигнал EQ, равен 1 тогда и только тогда, когда значения входных сигналов совпадают. Это сравнение входных сигналов на равенство.

library ieee; -- прочитать библиотеку
use ieee.std_logic_1164.all; -- сделать библиотеку видимой
entity compare is
    port( A, B: in integer range 0 to 3;
          EQ: out std_ulogic);
    end compare;
architecture eq1 of compare is
begin
    EQ <= '1' when (A = B) else '0';
end eq1;
architecture lt1 of compare is
begin
    EQ <= '1' when (A < B) else '0';
end lt1;
architecture gt1 of compare is
begin
    EQ <= '1' when (A > B) else '0';
end gt1;

Легко видеть, что архитектурные тела lt1, gt1 определяют совсем другое поведение схемы compare. По синтаксису языка это допускается, по семантике (смыслу) это не корректно – все архитектурные тела, привязанные к одному и тому же entity, должны быть функционально эквивалентны и при конкретизации компонента по умолчанию предполагается, что будет использовано одно и то же архитектурное тело.

Конфигурации

Например, пусть имеется следующий VHDL-код, описывающий схему comp_3.

library ieee;
use ieee.std_logic_1164.all;
library work;
entity comp_3 is
    port ( signal a,b: in integer range 0 to 3;
           signal EQ,LT,GT: out std_ulogic);
end comp_3;
architecture str of comp_3 is
    component compare
        port ( a,b: in integer range 0 to 3;
               EQ: out std_ulogic);
    end component;
begin
    COMP1: compare port map (A,B,EQ);
    COMP2: compare port map (A,B,LT);
    COMP3: compare port map (A,B,GT);
end str;

В VHDL-коде метки COMP1, COMP2, COMP3 – это имена подсхем. Предполагается, что при моделировании (синтезе) для каждой из подсхем с именами COMP1, COMP2, COMP3 будет использоваться одно и то же архитектурное тело. Однако какое же из трех архитектурных тел eq1, lt1, gt1 будет использоваться? Если все три архитектурных тела содержатся в одном файле, то ответ достаточно прост: будет использоваться последнее “прочитанное” в проект архитектурное тело. В данном случае при конкретизации компонета compare будет использовано последнее архитектурное тело gt1. Чтобы использовать для подсхемы COMP1 архитектурное тело eq1, для COMP2 – архитектурное тело lt1, для COMP3 – архитектурное тело gt1, можно написать простую конфигурацию default_configuration.

Конфигурации

Пример конфигурации в VHDL
Library work;
 configuration default_configuration of comp_3 is
     for str
         for COMP1: compare use entity work.compare (eq1);
         end for;
         for COMP2: compare use entity work.compare (lt1);
         end for;
         for COMP3: compare use entity work.compare (gt1);
         end for;
     end for;
 end;

Поясним данный VHDL-код. Ссылка на рабочую библиотеку work говорит о том, что все архитектурные тела должны быть в рабочей библиотеке. В следующих двух строчках все “связывается” – указывается имя конфигурации (default_configuration), для какой схемы (comp_3) она составлена и для какого архитектурного тела (str). Далее записывается, что для подсхмы COMP1 требуется использовать архитектурное тело eq1, и т.д. Таким образом, из многих вариантов реализации одной и той же подсхемы можно выбрать нужный вариант.
! Конфигурация позволяет в случае одинакового интерфейса использовать различные архитектурные тела.