Machines à états finis¶
Une machine à états mémorise un état discret et choisit le suivant à partir de l'état courant et des entrées.
Concevoir d'abord sur papier¶
Écrivez :
- Chaque état et sa signification.
- L'état de reset.
- Les conditions de chaque transition sortante.
- Les sorties actives dans chaque état ou transition.
- Le comportement en cas d'entrées invalides ou simultanées.
- Le timing : sorties cadencées ou combinatoires.
Moore et Mealy¶
| Style | Dépendance des sorties | Effet |
|---|---|---|
| Moore | État courant | Généralement stables pendant tout un cycle |
| Mealy | État courant et entrées | Réponse dans le cycle, risque de glitch |
Des sorties enregistrées simplifient souvent le timing des interfaces.
Patron à deux processus¶
type state_t is (IDLE, ACTIVE, DONE);
signal state_q : state_t := IDLE;
signal state_d : state_t;
state_register : process(clk_i)
begin
if rising_edge(clk_i) then
if reset_i = '1' then
state_q <= IDLE;
else
state_q <= state_d;
end if;
end if;
end process;
next_state_logic : process(all)
begin
state_d <= state_q;
case state_q is
when IDLE =>
if start_i = '1' then
state_d <= ACTIVE;
end if;
when ACTIVE =>
if complete_i = '1' then
state_d <= DONE;
end if;
when DONE =>
if acknowledge_i = '1' then
state_d <= IDLE;
end if;
end case;
end process;
busy_o <= '1' when state_q = ACTIVE else '0';
done_o <= '1' when state_q = DONE else '0';
Atouts : séparation nette du registre et des transitions, sorties de Moore lisibles, état présent et suivant visibles. Risque : oublier les valeurs par défaut et inférer un latch.
Patron à un processus¶
process(clk_i)
begin
if rising_edge(clk_i) then
if reset_i = '1' then
state_q <= IDLE;
busy_o <= '0';
done_o <= '0';
else
done_o <= '0';
case state_q is
when IDLE =>
busy_o <= '0';
if start_i = '1' then
state_q <= ACTIVE;
busy_o <= '1';
end if;
when ACTIVE =>
if complete_i = '1' then
state_q <= DONE;
busy_o <= '0';
done_o <= '1';
end if;
when DONE =>
if acknowledge_i = '1' then
state_q <= IDLE;
end if;
end case;
end if;
end if;
end process;
Atouts : tout état et toute sortie enregistrée changent au front, aucun latch combinatoire accidentel. Risque : les grandes machines deviennent longues et la latence des sorties doit être soigneusement comprise.
Les deux styles sont valables. Adoptez une convention et testez le contrat temporel.
Priorité des transitions¶
Si deux conditions peuvent être vraies ensemble, l'ordre if/elsif crée une priorité :
Documentez cette priorité.
Encodage¶
Vivado peut choisir :
- binaire : peu de bascules, davantage de décodage ;
- one-hot : une bascule par état, décodage souvent simple et rapide ;
- Gray : un bit change entre états voisins lorsque le graphe le permet.
N'optimisez pas trop tôt. Validez d'abord fonction et timing, puis appuyez-vous sur les rapports.
Plan de vérification¶
Testez :
- le reset depuis chaque situation importante ;
- chaque transition légale ;
- les conditions qui doivent maintenir l'état ;
- les priorités entre conditions simultanées ;
- les durées minimales et maximales ;
- les sorties autour des fronts ;
- la récupération d'un état illégal si elle est exigée.
assert not (busy_o = '1' and done_o = '1')
report "busy and done must never be active together"
severity failure;
Tenez une table de couverture des transitions.
Erreurs fréquentes¶
- Coder avant d'établir le diagramme.
- Laisser une condition de transition ambiguë.
- Créer des glitches sur une sortie de commande.
- Oublier les valeurs par défaut d'une machine à deux processus.
- Confondre initialisation et stratégie de reset.
- Compter les cycles sans définir si l'entrée dans l'état vaut cycle zéro ou un.