Conception et implémentation des machines à états finis (FSM) en VHDL : Moore et Mealy.
Machines à États Finis (FSM)
Une FSM (Finite State Machine) est un circuit séquentiel qui :
Se trouve dans un état à la fois
Passe d'un état à l'autre selon des transitions (dépendent des entrées)
Produit des sorties selon l'état (et éventuellement les entrées)
Moore vs Mealy
Moore
Mealy
Sorties dépendent de
État uniquement
État + Entrées
Sorties
Plus stables
Réagit plus vite
Latence
1 cycle supplémentaire
Réponse immédiate
Recommandé pour FPGA
Oui (plus simple à synthétiser)
Possible mais attention aux glitches
Modèle 2 process (recommandé)
Exemple : détecteur de séquence 101 (Moore).
État actuel
Entrée i_data
État suivant
Sortie o_found
IDLE
0
IDLE
0
IDLE
1
GOT_1
0
GOT_1
1
GOT_1
0
GOT_1
0
GOT_10
0
GOT_10
1
FOUND
0
GOT_10
0
IDLE
0
FOUND
x
IDLE
1
Le style le plus courant sur FPGA : un process pour l'état, un pour les sorties.
library
IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity fsm_detector is
port (
i_clk : in std_logic;
i_rst : in std_logic;
i_data : in std_logic;
o_found : out std_logic
);
end entity fsm_detector;
architecture rtl of fsm_detector is
-- Déclaration du type état
type t_state is (IDLE, GOT_1, GOT_10, FOUND);
signal r_state : t_state;
signal w_next : t_state;
begin
-- Process 1 : registre d'état (séquentiel)
p_state_reg : process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_state <= IDLE;
else
r_state <= w_next;
end if;
end if;
end process p_state_reg;
-- Process 2 : logique de transition (combinatoire)
p_next_state : process(r_state, i_data)
begin
w_next <= r_state; -- par défaut : rester dans l'état
case r_state is
when IDLE =>
if i_data = '1' then
w_next <= GOT_1;
end if;
when GOT_1 =>
if i_data = '0' then
w_next <= GOT_10;
else
w_next <= GOT_1;
end if;
when GOT_10 =>
if i_data = '1' then
w_next <= FOUND;
else
w_next <= IDLE;
end if;
when FOUND =>
w_next <= IDLE;
when others =>
w_next <= IDLE;
end case;
end process p_next_state;
-- Sorties Moore : dépendent uniquement de r_state
o_found <= '1' when r_state = FOUND else '0';
end architecture rtl;
Modèle 3 process (variante)
Un troisième process pour les sorties (utile si les sorties sont complexes).
-- Process 3 : logique de sortie (combinatoire - Moore)p_outputs : process(r_state)begin -- Valeurs par défaut (évite les latches) o_found <= '0'; o_active <= '0'; case r_state is when FOUND => o_found <= '1'; when GOT_1 | GOT_10 => o_active <= '1'; when others => null; end case;end process p_outputs;
Exemple complet : FSM feux de circulation
library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity traffic_light is generic ( g_CLK_FREQ : integer := 100_000_000 -- 100 MHz ); port ( i_clk : in std_logic; i_rst : in std_logic; o_rouge : out std_logic; o_orange: out std_logic; o_vert : out std_logic );end entity traffic_light;architecture rtl of traffic_light is type t_light is (ROUGE, ORANGE, VERT); constant c_ROUGE_DUR : integer := 30 * g_CLK_FREQ; -- 30s constant c_ORANGE_DUR : integer := 3 * g_CLK_FREQ; -- 3s constant c_VERT_DUR : integer := 25 * g_CLK_FREQ; -- 25s signal r_state : t_light; signal r_counter : integer range 0 to c_ROUGE_DUR;begin p_fsm : process(i_clk) begin if rising_edge(i_clk) then if i_rst = '1' then r_state <= ROUGE; r_counter <= 0; else r_counter <= r_counter + 1; case r_state is when ROUGE => if r_counter = c_ROUGE_DUR - 1 then r_state <= VERT; r_counter <= 0; end if; when VERT => if r_counter = c_VERT_DUR - 1 then r_state <= ORANGE; r_counter <= 0; end if; when ORANGE => if r_counter = c_ORANGE_DUR - 1 then r_state <= ROUGE; r_counter <= 0; end if; end case; end if; end if; end process p_fsm; -- Sorties Moore o_rouge <= '1' when r_state = ROUGE else '0'; o_orange <= '1' when r_state = ORANGE else '0'; o_vert <= '1' when r_state = VERT else '0';end architecture rtl;
État
Durée
État suivant
ROUGE
30 s
VERT
VERT
25 s
ORANGE
ORANGE
3 s
ROUGE
Bonnes pratiques FSM
1. Toujours initialiser au reset
if i_rst = '1' then r_state <= IDLE; -- état de départ définiend if;
2. Couvrir tous les états avec when others
when others => r_state <= IDLE; -- état sûr par défaut
3. Valeurs de sortie par défaut
-- En début de process de sortieo_valid <= '0';o_data <= (others => '0');-- Puis surcharger dans chaque case
4. Un type énuméré par FSM
type t_uart_state is (IDLE, START, DATA, PARITY, STOP);-- Chaque FSM a son propre type → nommage clair
Encodage des états
Le synthétiseur choisit automatiquement l'encodage. Il est possible de le forcer :
Encodage
Usage
Ressources
Binaire
Peu d'états
Minimum de bascules
One-hot
Beaucoup d'états
Plus de bascules, plus rapide
Gray
Compteurs séquentiels
Transitions d'un seul bit
Sur FPGA moderne, laisser le synthétiseur décider est généralement optimal.
Catégories de FSM (Pedroni)
Il est utile de classer les machines à états en trois catégories selon leur complexité :
Catégorie 1 - FSM régulière (simple)
La machine passe d'un état à l'autre uniquement selon des signaux d'entrée externes. Pas de temporisation, pas de récursivité.
C'est la FSM classique : détecteur de séquence, contrôleur de protocole, décodeur de commandes.
-- Exemple : détecteur de flanc montanttype t_edge is (IDLE, DETECT);signal r_state : t_edge;process(i_clk)begin if rising_edge(i_clk) then if i_rst = '1' then r_state <= IDLE; else case r_state is when IDLE => if i_sig = '1' then r_state <= DETECT; end if; when DETECT => r_state <= IDLE; when others => r_state <= IDLE; end case; end if; end if;end process;o_pulse <= '1' when r_state = DETECT else '0';
Catégorie 2 - FSM temporisée (avec compteur)
La machine attend un nombre de cycles défini avant de changer d'état. Un compteur interne mesure le temps écoulé.
C'est le cas des feux de circulation, des générateurs PWM, des délais de démarrage.
-- FSM avec compteur internetype t_timed is (WAIT_LOW, WAIT_HIGH);signal r_state : t_timed;signal r_timer : unsigned(19 downto 0) := (others => '0');constant c_T_LOW : unsigned(19 downto 0) := to_unsigned(499_999, 20); -- 5 ms @ 100 MHzconstant c_T_HIGH : unsigned(19 downto 0) := to_unsigned(999_999, 20); -- 10 ms @ 100 MHzprocess(i_clk)begin if rising_edge(i_clk) then if i_rst = '1' then r_state <= WAIT_LOW; r_timer <= (others => '0'); else r_timer <= r_timer + 1; case r_state is when WAIT_LOW => if r_timer = c_T_LOW then r_state <= WAIT_HIGH; r_timer <= (others => '0'); end if; when WAIT_HIGH => if r_timer = c_T_HIGH then r_state <= WAIT_LOW; r_timer <= (others => '0'); end if; when others => r_state <= WAIT_LOW; r_timer <= (others => '0'); end case; end if; end if;end process;o_out <= '1' when r_state = WAIT_HIGH else '0';
Bonne pratique : remettre le compteur à zéro (r_timer <= (others => '0')) dès qu'on change d'état pour garantir un timing précis.
La machine passe par des états un nombre variable de fois selon un paramètre d'entrée (boucle sur N cycles, traitement de N octets, etc.).
C'est le cas des sérialiseurs, des contrôleurs SPI/UART, des moteurs de compression.
-- Sérialiseur 8 bits : envoie 8 bits un par untype t_ser is (IDLE, SHIFT);signal r_state : t_ser;signal r_shreg : std_logic_vector(7 downto 0);signal r_count : unsigned(2 downto 0);process(i_clk)begin if rising_edge(i_clk) then if i_rst = '1' then r_state <= IDLE; r_count <= (others => '0'); o_bit <= '0'; o_done <= '0'; else o_done <= '0'; case r_state is when IDLE => if i_valid = '1' then r_shreg <= i_data; r_count <= (others => '0'); r_state <= SHIFT; end if; when SHIFT => o_bit <= r_shreg(7); r_shreg <= r_shreg(6 downto 0) & '0'; -- décalage gauche r_count <= r_count + 1; if r_count = 7 then o_done <= '1'; r_state <= IDLE; end if; when others => r_state <= IDLE; end case; end if; end if;end process;
Tableau de synthèse
Catégorie
Déclencheur de transition
Exemples
Régulière
Entrées externes
Détecteur de séquence, décodeur
Temporisée
Compteur interne (N cycles fixes)
Feux, PWM, délai de démarrage
Récursive
Compteur interne (N variable)
UART TX, SPI, sérialiseur
Machines à états sûres (safe state machines)
Un FPGA peut se retrouver dans un état non défini au démarrage ou suite à un glitch. Une FSM sûre gère explicitement tous les cas imprévus.
-- Toujours inclure when otherscase r_state is when IDLE => ... when RUN => ... when DONE => ... when others => -- État inconnu : retour à l'état sûr r_state <= IDLE;end case;
-- Forcer l'encodage one-hot pour réduire les états illégaux (Vivado)attribute fsm_encoding : string;attribute fsm_encoding of r_state : signal is "one_hot";
Sur FPGA, les bascules ne se réinitialisent pas automatiquement à la mise sous tension. Toujours initialiser au reset et toujours couvrir when others.