Comment créer une machine à états finis en VHDL
Une machine à états finis (FSM) est un mécanisme dont la sortie dépend non seulement de l'état actuel de l'entrée, mais également des valeurs d'entrée et de sortie passées.
Chaque fois que vous avez besoin de créer une sorte d'algorithme dépendant du temps en VHDL, ou si vous êtes confronté au problème de l'implémentation d'un programme informatique dans un FPGA, cela peut généralement être résolu en utilisant un FSM.
Les machines à états en VHDL sont des processus cadencés dont les sorties sont contrôlées par la valeur d'un signal d'état. Le signal d'état sert de mémoire interne de ce qui s'est passé lors de l'itération précédente.
Cet article de blog fait partie de la série de didacticiels VHDL de base.
Tenez compte de l'état des feux de circulation à cette intersection :
Les feux de circulation ont un nombre fini d'états, auxquels nous avons donné des noms identifiables. Notre exemple de machine d'état n'a pas d'entrées de contrôle, la sortie est l'état des lumières dans les directions nord/sud et ouest/est. C'est le temps écoulé et l'état précédent des sorties qui fait avancer cette machine d'état.
Nous pouvons représenter les états en VHDL en utilisant un type énuméré . Ce sont des types de données comme signed
ou unsigned
, mais au lieu de nombres entiers, nous pouvons fournir une liste personnalisée de valeurs possibles. En fait, si vous regardez dans le package std_logic_1164, vous constaterez que le std_ulogic
type n'est rien de plus qu'un type énuméré avec les valeurs 'U'
, 'X'
, '0'
, '1'
, 'Z'
, 'W'
, 'L'
, 'H'
, et '-'
répertoriés comme valeurs d'énumération.
Une fois que nous avons notre type énuméré, nous pouvons déclarer un signal du nouveau type qui peut être utilisé pour garder une trace de l'état actuel du FSM.
La syntaxe pour déclarer un signal avec un type énuméré en VHDL est :type <type_name> is (<state_name1>, <state_name2>, ...);
signal <signal_name> : <type_name>;
En utilisant le signal d'état, la machine à états finis peut ensuite être implémentée dans un processus avec une instruction Case. L'instruction Case contient une instruction When pour chacun des états possibles, obligeant le programme à emprunter des chemins différents pour chaque état. L'instruction When peut également contenir du code qui doit être exécuté dans cet état particulier. L'état changera alors généralement lorsqu'une condition prédéfinie est remplie.
Ceci est un modèle pour une machine d'état à processus unique :process(Clk) is
begin
if rising_edge(Clk) then
if nRst = '0' then
State <= <reset_state>;
else
case State is
when <state_name> =>
<set_outputs_for_this_state_here>
if <state_change_condition_is_true> then
State <= <next_state_name>;
end if;
...
end case;
end if;
end if;
end process;
Remarque :
Il existe plusieurs façons de créer un FSM en VHDL. Découvrez les différents styles ici :
Machine d'état à un processus vs à deux processus vs à trois processus
Exercice
Dans ce didacticiel vidéo, nous allons apprendre à créer une machine à états finis en VHDL :
Le code final pour la machine d'état testbench :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T20_FiniteStateMachineTb is end entity; architecture sim of T20_FiniteStateMachineTb 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.T20_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;
Le code final pour la machine d'état module :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T20_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 T20_TrafficLights is -- 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 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 Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; 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 Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= North; end if; -- Green in north/south direction when North => NorthGreen <= '1'; WestRed <= '1'; -- If 1 minute has passed if Counter = ClockFrequencyHz * 60 -1 then Counter <= 0; State <= StopNorth; end if; -- Yellow in north/south direction when StopNorth => NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= WestNext; end if; -- Red in all directions when WestNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; 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 Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= West; end if; -- Green in west/east direction when West => NorthRed <= '1'; WestGreen <= '1'; -- If 1 minute has passed if Counter = ClockFrequencyHz * 60 -1 then Counter <= 0; State <= StopWest; end if; -- Yellow in west/east direction when StopWest => NorthRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if Counter = ClockFrequencyHz * 5 -1 then Counter <= 0; State <= NorthNext; end if; end case; end if; end if; end process; end architecture;
La forme d'onde après avoir saisi le run 5 min
commande dans la console ModelSim :
Analyse
Nous avons déclaré un type énuméré avec les huit états différents de nos feux de circulation. Ensuite, nous avons déclaré un state
signal de ce nouveau type que nous avons créé. Cela signifie que le signal ne peut avoir qu'une seule des huit valeurs d'état nommées, et aucune autre valeur.
Le FSM a été implémenté à l'aide d'une déclaration de cas dans un processus cadencé. A chaque front montant de l'horloge, le processus se réveille, et le state
signal est évalué. Le code dans exactement l'un des when
choix (branches) est autorisé à s'exécuter, en fonction de l'état actuel.
Dans notre code, c'est la valeur du Counter
signal qui déclenche les changements d'état. Lorsque le Compteur atteint une valeur prédéfinie, représentant 5 secondes ou 1 minute, un nouvel encodage d'état est attribué au State
signal. Ensuite, lorsque le processus se réveille sur le prochain front montant de l'horloge après la mise à jour de la valeur d'état, le FSM est dans un état différent.
Notez que nous n'attribuons pas '0'
à n'importe quel signal dans l'un des when
les choix. C'est parce que nous avons donné à tous les signaux de sortie une valeur par défaut de '0'
au début du processus. Vous vous souvenez peut-être d'un tutoriel précédent que c'est la dernière valeur affectée à un signal qui devient effective. Les affectations de signal ne deviennent effectives qu'après la fin du processus. Si nous attribuons '0'
au signal au début du processus, puis '1'
dans l'un des when
choix, le signal prendra la valeur '1'
.
Nous pouvons voir sur la forme d'onde que le State
le signal passe par les huit états. Les états verts fixes durent une minute, l'image de la forme d'onde a donc été coupée dans le North
et West
états.
À emporter
- Les algorithmes sont généralement mis en œuvre sous forme de machines à états finis (FSM)
- Un FSM peut être implémenté en utilisant une instruction case dans un processus cadencé
- Les états FSM peuvent être implémentés dans un type énuméré
Aller au tutoriel suivant »
VHDL
- Machine à états finis
- Comment garantir les meilleures performances de la machine d'état Qt
- Comment créer une liste de chaînes en VHDL
- Comment créer un banc d'essai piloté par Tcl pour un module de verrouillage de code VHDL
- Comment arrêter la simulation dans un testbench VHDL
- Comment créer un contrôleur PWM en VHDL
- Comment créer un banc d'essai d'auto-vérification
- Comment créer une liste chaînée en VHDL
- Comment la machine laser CNC OMNI crée des cadeaux de Noël personnalisés ?