Использование внутрипроцессных процедур для более чистого проектирования FSM на VHDL
В VHDL процедура может управлять внешними сигналами, пока сигнал остается в ее области видимости. Хотя процедура, объявленная в архитектуре, не имеет доступа к внешним сигналам — потому что эти сигналы не видны во время компиляции — процедура, определенная внутри процесса, может свободно читать и писать любой сигнал, видимый процессом.
Использование внутрипроцессных процедур — эффективный способ убрать повторяющуюся логику в автоматах состояний или сложных протоколах. Инкапсулируя общие действия — такие как временной переход состояния — внутри процедуры, вы уменьшаете шаблонный код, улучшаете читаемость и делаете намерение каждого шага кристально ясным.
Например, в контроллере светофора переименование операций в ChangeState(ToState, Minutes, Seconds) говорит читателю точно, что делает код, не вдаваясь в базовые присваивания переменных.
Практическое упражнение
В предыдущем уроке мы использовали нечистую функцию для управления сигналом Counter и определения изменений состояния. Однако мы не могли перенести присваивание State в эту функцию, потому что VHDL требует возвращаемое значение для каждого вызова функции. Вместо этого процедура — лишенная возвращаемого значения — идеально подходит для этого случая использования.
Ниже приведен полный тестбенч и модуль, демонстрирующий FSM светофора, управляемый внутрипроцессной процедурой.
Тестбенч
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T23_ProcedureInProcessTb is end entity; architecture sim of T23_ProcedureInProcessTb is 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 i_TrafficLights : entity work.T23_TrafficLights(rtl) generic map(ClockFrequencyHz => ClockFrequencyHz) port map ( Clk => Clk, nRst => nRst, NorthRed => NorthRed, NorthYellow => NorthYellow, NorthGreen => NorthGreen, WestRed => WestRed, WestYellow => WestYellow, WestGreen => WestGreen); Clk <= not Clk after ClockPeriod / 2; process is begin wait until rising_edge(Clk); wait until rising_edge(Clk); nRst <= '1'; wait; end process; end architecture;
Модуль светофора
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T23_TrafficLights is generic(ClockFrequencyHz : integer); port( Clk : in std_logic; nRst : in std_logic; 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 T23_TrafficLights is type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest); signal State : t_State; signal Counter : integer range 0 to ClockFrequencyHz * 60; begin process(Clk) is procedure ChangeState(ToState : t_State; Minutes : integer := 0; Seconds : integer := 0) is variable TotalSeconds : integer; variable ClockCycles : integer; begin TotalSeconds := Seconds + Minutes * 60; ClockCycles := TotalSeconds * ClockFrequencyHz - 1; if Counter = ClockCycles then Counter <= 0; State <= ToState; end if; end procedure; begin if rising_edge(Clk) then if nRst = '0' then State <= NorthNext; Counter <= 0; NorthRed <= '1'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '1'; WestYellow <= '0'; WestGreen <= '0'; else NorthRed <= '0'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '0'; WestYellow <= '0'; WestGreen <= '0'; Counter <= Counter + 1; case State is when NorthNext => NorthRed <= '1'; WestRed <= '1'; ChangeState(StartNorth, Seconds => 5); when StartNorth => NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1'; ChangeState(North, Seconds => 5); when North => NorthGreen <= '1'; WestRed <= '1'; ChangeState(StopNorth, Minutes => 1); when StopNorth => NorthYellow <= '1'; WestRed <= '1'; ChangeState(WestNext, Seconds => 5); when WestNext => NorthRed <= '1'; WestRed <= '1'; ChangeState(StartWest, Seconds => 5); when StartWest => NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1'; ChangeState(West, Seconds => 5); when West => NorthRed <= '1'; WestGreen <= '1'; ChangeState(StopWest, Minutes => 1); when StopWest => NorthRed <= '1'; WestYellow <= '1'; ChangeState(NorthNext, Seconds => 5); end case; end if; end if; end process; end architecture;
Волновая форма
Симуляция остается идентичной исходному FSM — только код стал более поддерживаемым и понятным.
Анализ
Перенос логики тайминга и смены состояния в единственную процедуру сохранил поведение модуля, но драматически улучшил читаемость. Процедура обеспечивает согласованную реализацию везде, где она вызывается.
Ключевые выводы
- Процедура внутри процесса может обращаться к каждому сигналу, видимому этому процессу.
- Инкапсуляция повторяющейся логики в процедурах улучшает ясность и поддерживаемость кода.
VHDL
- Введение в VHDL: создание вашего первого И-шлюза
- Пример процедуры VHDL: инкремент std_logic_vector
- Как молибден формирует современную промышленность: применение, преимущества и динамика поставок
- Создание списков строк в VHDL: лучшие практики и пример
- Использование нечистых функций в VHDL: повышение читаемости и поддерживаемости FSM
- Освоение функций VHDL: практическое руководство по эффективному дизайну
- Использование процедур в VHDL: упростите свой дизайн с повторно используемым кодом
- Построение надежного таймера в VHDL: подсчет тактовых циклов до часов
- Создание тактированного процесса в VHDL: практическое руководство
- Освоение техник шлифовального станка для режущих инструментов: практическое руководство