Как использовать нечистую функцию в 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:как использовать? Учитесь на примере
- Как использовать шлифовальный станок