Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> VHDL

Как использовать нечистую функцию в VHDL

Нечистая функция может читать или записывать любой сигнал в пределах своей области действия, даже те, которых нет в списке параметров. Мы говорим, что функция имеет побочные эффекты .

Под побочными эффектами мы подразумеваем, что не гарантируется, что функция будет возвращать одно и то же значение каждый раз, когда она вызывается с одними и теми же параметрами. Если функция может считывать сигналы, которых нет в списке параметров, возвращаемое значение может зависеть и от этих теневых параметров. Кроме того, функция может изменять внешние сигналы, не присвоенные ее возвращаемому значению.

Эта запись в блоге является частью серии учебных пособий по основам VHDL.

Хотя мы можем объявить нечистые функции везде, где мы можем объявить обычную, чистую функцию, имеет смысл использовать их только внутри процессов. При объявлении в архитектуре, где мы обычно объявляем наши сигналы, ни один из сигналов не будет находиться в ее области действия во время компиляции. Таким образом, нечистая функция не может делать больше, чем чистая функция, объявленная в архитектуре или в пакете.

Мотивация использования нечистых функций в основном заключается в том, чтобы расхламить код. Мы могли бы манипулировать любым сигналом с помощью чистой функции, просто добавив его в список параметров, но если список параметров станет слишком длинным, это скорее запутает, чем упростит.

Синтаксис для объявления нечистой функции просто пишет impure function вместо function при его объявлении. Синтаксис универсальной функции см. в учебнике по функциям.

Упражнение

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

Если CounterVal функция вернула true , время истекло и пришло время перейти к следующему состоянию FSM. В том же процессе нам также пришлось сбросить Counter сигнал, иначе функция не будет работать в следующем состоянии. Срок действия таймера уже истек.

Counter signal всегда будет установлено значение 0 когда функция вернула true. Не лучше ли было бы, если бы это произошло в CounterVal функцию вместо нескольких мест в коде конечного автомата?

В этом видеоуроке мы улучшим код FSM из предыдущего урока, используя нечистую функцию:

Окончательный код нечистой функции testbench :

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity T22_ImpureFunctionTb is
end entity;

architecture sim of T22_ImpureFunctionTb is

    -- We are using a low clock frequency to speed up the simulation
    constant ClockFrequencyHz : integer := 100; -- 100 Hz
    constant ClockPeriod : time := 1000 ms / ClockFrequencyHz;

    signal Clk         : std_logic := '1';
    signal nRst        : std_logic := '0';
    signal NorthRed    : std_logic;
    signal NorthYellow : std_logic;
    signal NorthGreen  : std_logic;
    signal WestRed     : std_logic;
    signal WestYellow  : std_logic;
    signal WestGreen   : std_logic;

begin

    -- The Device Under Test (DUT)
    i_TrafficLights : entity work.T22_TrafficLights(rtl)
    generic map(ClockFrequencyHz => ClockFrequencyHz)
    port map (
        Clk         => Clk,
        nRst        => nRst,
        NorthRed    => NorthRed,
        NorthYellow => NorthYellow,
        NorthGreen  => NorthGreen,
        WestRed     => WestRed,
        WestYellow  => WestYellow,
        WestGreen   => WestGreen);

    -- Process for generating clock
    Clk <= not Clk after ClockPeriod / 2;

    -- Testbench sequence
    process is
    begin
        wait until rising_edge(Clk);
        wait until rising_edge(Clk);

        -- Take the DUT out of reset
        nRst <= '1';

        wait;
    end process;

end architecture;

Окончательный код для модуля светофора :

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity T22_TrafficLights is
generic(ClockFrequencyHz : integer);
port(
    Clk         : in std_logic;
    nRst        : in std_logic; -- Negative reset
    NorthRed    : out std_logic;
    NorthYellow : out std_logic;
    NorthGreen  : out std_logic;
    WestRed     : out std_logic;
    WestYellow  : out std_logic;
    WestGreen   : out std_logic);
end entity;

architecture rtl of T22_TrafficLights is

    -- Calculate the number of clock cycles in minutes/seconds
    function CounterVal(Minutes : integer := 0;
                        Seconds : integer := 0) return integer is
        variable TotalSeconds : integer;
    begin
        TotalSeconds := Seconds + Minutes * 60;
        return TotalSeconds * ClockFrequencyHz -1;
    end function;

    -- Enumerated type declaration and state signal declaration
    type t_State is (NorthNext, StartNorth, North, StopNorth,
                        WestNext, StartWest, West, StopWest);
    signal State : t_State;

    -- Counter for counting clock periods, 1 minute max
    signal Counter : integer range 0 to ClockFrequencyHz * 60;

begin

    process(Clk) is

        -- This impure function reads and drives the Counter signal
        -- which is not on the parameter list.
        impure function CounterExpired(Minutes : integer := 0;
                                       Seconds : integer := 0)
                                       return boolean is
        begin
            if Counter = CounterVal(Minutes, Seconds) then
                Counter <= 0;
                return true;
            else
                return false;
            end if;
        end function;

    begin
        if rising_edge(Clk) then
            if nRst = '0' then
                -- Reset values
                State   <= NorthNext;
                Counter <= 0;
                NorthRed    <= '1';
                NorthYellow <= '0';
                NorthGreen  <= '0';
                WestRed     <= '1';
                WestYellow  <= '0';
                WestGreen   <= '0';

            else
                -- Default values
                NorthRed    <= '0';
                NorthYellow <= '0';
                NorthGreen  <= '0';
                WestRed     <= '0';
                WestYellow  <= '0';
                WestGreen   <= '0';

                Counter <= Counter + 1;

                case State is

                    -- Red in all directions
                    when NorthNext =>
                        NorthRed <= '1';
                        WestRed  <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= StartNorth;
                        end if;

                    -- Red and yellow in north/south direction
                    when StartNorth =>
                        NorthRed    <= '1';
                        NorthYellow <= '1';
                        WestRed     <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= North;
                        end if;

                    -- Green in north/south direction
                    when North =>
                        NorthGreen <= '1';
                        WestRed    <= '1';
                        -- If 1 minute has passed
                        if CounterExpired(Minutes => 1) then
                            State <= StopNorth;
                        end if;

                    -- Yellow in north/south direction
                    when StopNorth =>
                        NorthYellow <= '1';
                        WestRed     <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= WestNext;
                        end if;

                    -- Red in all directions
                    when WestNext =>
                        NorthRed <= '1';
                        WestRed  <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= StartWest;
                        end if;

                    -- Red and yellow in west/east direction
                    when StartWest =>
                        NorthRed   <= '1';
                        WestRed    <= '1';
                        WestYellow <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= West;
                        end if;

                    -- Green in west/east direction
                    when West =>
                        NorthRed  <= '1';
                        WestGreen <= '1';
                        -- If 1 minute has passed
                        if CounterExpired(Minutes => 1) then
                            State <= StopWest;
                        end if;

                    -- Yellow in west/east direction
                    when StopWest =>
                        NorthRed   <= '1';
                        WestYellow <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= NorthNext;
                        end if;

                end case;

            end if;
        end if;
    end process;

end architecture;

Форма сигнала после того, как мы ввели run 5 min команда в консоли ModelSim:

Анализ

Как мы видим из сигнала, выходной сигнал модуля остается неизменным после того, как мы добавили нечистую функцию. Мы вообще не меняли логику, только код.

Оценка Counter сигнал был перемещен из кода FSM в новую нечистую функцию CounterExpired . Counter <= 0; строка для очистки Counter сигнал также был перемещен в нечистую функцию.

В результате получается более читаемый FSM-код, который легче поддерживать. Это субъективно, но для меня CounterExpired(Seconds => 5) приятнее для глаз, чем Counter = CounterVal(Seconds => 5) .

Как далеко вы должны зайти с использованием нечистых функций, зависит только от вас и от того, кто платит за ваши услуги. Некоторые люди считают, что их следует использовать с осторожностью, потому что может быть труднее увидеть все причины и следствия алгоритма, скрытого в подпрограмме. Другие, как и я, считают, что если вы четко формулируете свои намерения, код, который легче читать, на самом деле делает его менее подверженным ошибкам.

По этой причине вы с большей вероятностью найдете нечистые функции в коде тестового стенда, чем в производственных модулях. Тестовые стенды обычно более сложны, чем модуль, который они тестируют, а требования к правильности кода менее строгие, чем для кода RTL.

Вывод

Перейти к следующему руководству »


VHDL

  1. Как мы используем молибден?
  2. Как создать список строк в VHDL
  3. Как остановить симуляцию в тестовом стенде VHDL
  4. Как создать ШИМ-контроллер на VHDL
  5. Как генерировать случайные числа в VHDL
  6. Как использовать процедуру в процессе в VHDL
  7. Как использовать функцию в VHDL
  8. Функция realloc() в библиотеке C:как использовать? Синтаксис и пример
  9. Функция free() в библиотеке C:как использовать? Учитесь на примере
  10. Как использовать шлифовальный станок