Как использовать нечистую функцию в 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
- Как мы используем молибден?
- Как создать список строк в VHDL
- Как остановить симуляцию в тестовом стенде VHDL
- Как создать ШИМ-контроллер на VHDL
- Как генерировать случайные числа в VHDL
- Как использовать процедуру в процессе в VHDL
- Как использовать функцию в VHDL
- Функция realloc() в библиотеке C:как использовать? Синтаксис и пример
- Функция free() в библиотеке C:как использовать? Учитесь на примере
- Как использовать шлифовальный станок