Exploiter les procédures in-process pour un design FSM VHDL plus propre
En VHDL, une procédure peut piloter des signaux externes tant que le signal reste dans sa portée. Bien qu'une procédure déclarée dans une architecture n'ait pas accès aux signaux externes - car ces signaux ne sont pas visibles au moment de la compilation - une procédure définie à l'intérieur d'un processus peut librement lire et écrire n'importe quel signal que le processus peut voir.
L'utilisation de procédures in-process est un moyen efficace de désencombrer la logique répétitive dans les machines d'état ou les protocoles complexes. En encapsulant des actions communes - comme une transition d'état chronométrée - dans une procédure, vous réduisez le code boilerplate, améliorez la lisibilité, et rendez l'intention de chaque étape limpide.
Par exemple, dans un contrôleur de feu tricolore, renommer les opérations en ChangeState(ToState, Minutes, Seconds) dit au lecteur exactement ce que fait le code sans plonger dans les affectations de variables sous-jacentes.
Exercice pratique
Dans un tutoriel précédent, nous avons utilisé une fonction impure pour piloter un signal Counter et déterminer les changements d'état. Cependant, nous n'avons pas pu déplacer l'affectation State dans cette fonction car VHDL exige une valeur de retour pour chaque appel de fonction. Au lieu de cela, une procédure - dépourvue de valeur de retour - convient parfaitement à ce cas d'usage.
Ci-dessous se trouve le testbench et le module complets qui démontrent une FSM de feu tricolore pilotée par une procédure in-process.
Testbench
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;
Module Feu Tricolore
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;
Forme d'onde
La simulation reste identique à la FSM originale - seul le code est devenu plus maintenable et plus facile à comprendre.
Analyse
En déplaçant la logique de chronométrage et de changement d'état dans une seule procédure, nous avons préservé le comportement du module tout en améliorant considérablement la lisibilité. La procédure assure une implémentation cohérente partout où elle est appelée.
Points clés à retenir
- Une procédure à l'intérieur d'un processus peut accéder à chaque signal visible pour ce processus.
- L'encapsulation de la logique répétitive dans des procédures améliore la clarté et la maintenabilité du code.
VHDL
- Introduction à VHDL : construire votre première porte ET
- Exemple de procédure VHDL : incrémenter un std_logic_vector
- Comment le molybdène façonne l'industrie moderne : usages, avantages et dynamiques d'approvisionnement
- Créer des listes de chaînes en VHDL : Bonnes pratiques et exemple
- Utilisation des fonctions impures en VHDL : Améliorer la lisibilité et la maintenabilité des FSM
- Maîtriser les fonctions VHDL : Guide pratique pour un design efficace
- Utilisation des procédures en VHDL : Simplifiez votre design avec du code réutilisable
- Construire un minuteur fiable en VHDL : Compter les cycles d'horloge jusqu'aux heures
- Maîtriser les techniques de la rectifieuse d'outils : guide pratique