Machines à États (Mealy/Moore)
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éfini
end if;2. Couvrir tous les états avec when others
when others =>
r_state <= IDLE; -- état sûr par défaut3. Valeurs de sortie par défaut
-- En début de process de sortie
o_valid <= '0';
o_data <= (others => '0');
-- Puis surcharger dans chaque case4. Un type énuméré par FSM
type t_uart_state is (IDLE, START, DATA, PARITY, STOP);
-- Chaque FSM a son propre type → nommage clairEncodage 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 montant
type 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 interne
type 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 MHz
constant c_T_HIGH : unsigned(19 downto 0) := to_unsigned(999_999, 20); -- 10 ms @ 100 MHz
process(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.
Catégorie 3 — FSM récursive (avec paramètre variable)
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 un
type 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 others
case 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.