Chapitre 17 — Synchronisation et gestion des entrées asynchrones
Objectif — Finalité du chapitre Fiabiliser l’acquisition des boutons, capteurs et signaux externes, réduire le risque de métastabilité, supprimer les rebonds et transférer correctement les informations entre domaines d’horloge. |
Objectifs pédagogiques
À la fin de ce chapitre, l’étudiant devra être capable de :
- identifier une entrée asynchrone par rapport à une horloge locale ;
- expliquer les contraintes de setup et de hold à l’origine de la métastabilité ;
- mettre en œuvre un synchroniseur à deux bascules ;
- comprendre que la métastabilité est réduite mais jamais supprimée absolument ;
- expliquer le rebond mécanique d’un bouton ;
- concevoir un anti-rebond à base de compteur ;
- générer une impulsion d’un cycle sur un front montant ou descendant ;
- synchroniser un bit entre deux domaines d’horloge ;
- éviter la synchronisation bit à bit naïve d’un bus ;
- expliquer le principe d’un handshake et d’une FIFO asynchrone ;
- commander fiablement un compteur avec un bouton physique.
Prérequis
- horloges, bascules et registres ;
- compteurs numériques ;
- processus cadencés et rising_edge ;
- contraintes temporelles, setup et hold ;
- génériques et bancs de test.
Organisation du chapitre
Section | Contenu | Compétence principale |
|---|---|---|
| 17.1 | Entrées asynchrones | Identifier les risques |
| 17.2 | Métastabilité | Mettre en œuvre un synchroniseur |
| 17.3 | Rebond des boutons | Filtrer une entrée mécanique |
| 17.4 | Détection de front | Créer une impulsion d’un cycle |
| 17.5 | Clock Domain Crossing | Transférer bits et bus |
| TP 13 | Synchronisation d’un bouton | Commander un compteur fiable |
Principe central — Chaîne recommandée Pour un bouton : synchroniser d’abord le signal, filtrer ensuite les rebonds, puis détecter le front afin de produire une seule impulsion. |
17.1 Entrées asynchrones
Une entrée est asynchrone lorsqu’elle peut changer indépendamment de l’horloge du circuit qui l’échantillonne. Son changement peut survenir à n’importe quel instant, y compris très près d’un front actif.
17.1.1 Boutons-poussoirs
Un bouton est commandé par une action humaine. Sa transition n’est pas alignée sur l’horloge FPGA et ses contacts rebondissent mécaniquement.
- entrée asynchrone ;
- fronts lents ou bruités selon le câblage ;
- plusieurs transitions lors d’un seul appui ;
- polarité dépendant de la carte.
17.1.2 Capteurs
Un capteur peut fournir une sortie numérique sans relation de phase avec l’horloge locale.
Capteur ou source | Caractéristique possible |
|---|---|
| Capteur de proximité | Impulsion lors d’une détection |
| Encodeur mécanique | Deux voies asynchrones et rebondissantes |
| Comparateur analogique | Transition indépendante de clk |
| Interruption d’un périphérique | Impulsion ou niveau asynchrone |
| Récepteur série externe | Domaine d’horloge différent |
17.1.3 Signaux externes
- GPIO provenant d’un autre circuit ;
- signal de statut d’un microcontrôleur ;
- impulsion issue d’un oscillateur indépendant ;
- donnée associée à une horloge externe ;
- reset externe.
17.1.4 Risque près d’un front d’horloge
Une bascule exige que son entrée soit stable pendant un intervalle avant et après le front. Ces intervalles correspondent aux temps de setup et de hold.
Situation | Conséquence probable |
|---|---|
| Entrée stable loin du front | Échantillonnage déterministe |
| Changement pendant la fenêtre critique | Risque de métastabilité |
| Impulsion plus courte qu’une période | Risque de ne jamais être échantillonnée |
| Bus modifié sans protocole | Mots incohérents possibles |
17.1.5 Échantillonnage d’une impulsion
Une impulsion asynchrone doit être assez longue pour être observée par l’horloge de destination, ou être convertie par un protocole garantissant sa capture.
Attention — Synchroniser ne suffit pas toujours Un synchroniseur à deux bascules réduit le risque de métastabilité, mais ne garantit pas la capture d’une impulsion trop courte. |
17.1.6 Niveau ou événement
Information | Technique |
|---|---|
| Niveau lent persistant | Synchroniseur à deux bascules |
| Impulsion pouvant être élargie | Étirement puis synchronisation |
| Événement sporadique | Toggle synchronizer ou handshake |
| Bus de données | Handshake, FIFO asynchrone ou protocole |
17.1.7 Mauvaise utilisation directe
| Bouton utilisé directement | VHDL |
| process(clk) begin if rising_edge(clk) then if bouton = '1' then compteur <= compteur + 1; end if; end if; end process; |
Ce code peut incrémenter le compteur pendant plusieurs cycles tant que le bouton reste appuyé. Il ne traite ni la métastabilité ni le rebond.
17.1.8 Utiliser un bouton comme horloge
| Forme déconseillée | VHDL |
| process(bouton) begin if rising_edge(bouton) then compteur <= compteur + 1; end if; end process; |
Le bouton ne bénéficie pas du réseau d’horloge, présente des rebonds et crée un domaine temporel non maîtrisé.
17.1.9 Chaîne fonctionnelle complète
| Organisation conceptuelle | Architecture |
| bouton_physique -> synchroniseur -> filtre_anti_rebond -> detecteur_de_front -> impulsion_1_cycle -> compteur |
17.1.10 Spécification de l’entrée
- polarité active ;
- durée minimale de l’impulsion ;
- fréquence maximale des changements ;
- besoin de filtrage ;
- latence acceptable ;
- relation avec d’autres signaux ;
- comportement au reset.
17.2 Métastabilité
La métastabilité est un état analogique transitoire d’une bascule lorsque les contraintes temporelles de son entrée sont violées. La sortie peut mettre un temps imprévisible à atteindre un niveau logique valide.
17.2.1 Définition
- état intermédiaire entre 0 et 1 ;
- durée de résolution non déterministe ;
- phénomène physique absent d’une simulation RTL idéale ;
- probabilité réduite par le temps de résolution disponible.
17.2.2 Causes
Cause | Exemple |
|---|---|
| Entrée asynchrone | Bouton proche d’un front |
| Passage de domaine | Signal de clk_a vers clk_b |
| Reset asynchrone relâché au mauvais instant | Sortie de reset proche du front |
| Violation de setup/hold | Chemin synchrone trop lent ou mal contraint |
17.2.3 Conséquences
- niveau logique retardé ;
- valeurs différentes observées par plusieurs destinations ;
- transition incorrecte d’une machine à états ;
- comptage supplémentaire ou manquant ;
- défaillance rare et difficile à reproduire.
17.2.4 Le simulateur ne prédit pas la métastabilité
Dans une simulation RTL classique, une bascule capture généralement 0 ou 1. Le risque physique doit être traité par l’architecture, les contraintes et les outils d’analyse CDC.
17.2.5 Synchroniseur à deux bascules
La première bascule peut devenir métastable. La seconde échantillonne sa sortie un cycle plus tard, laissant davantage de temps pour la résolution.
| Synchroniseur simple | VHDL |
| process(clk) begin if rising_edge(clk) then sync_1 <= entree_async; sync_2 <= sync_1; end if; end process; entree_synchrone <= sync_2; |
17.2.6 Latence
Étage | Rôle |
|---|---|
| sync_1 | Capture l’entrée et supporte le risque principal |
| sync_2 | Fournit une valeur plus fiable à la logique |
| Logique utilisateur | Utilise uniquement sync_2 |
La valeur devient généralement exploitable après environ deux fronts de l’horloge de destination.
17.2.7 Version avec reset synchrone
| Synchroniseur réinitialisable | VHDL |
| process(clk) begin if rising_edge(clk) then if reset = '1' then sync_1 <= '0'; sync_2 <= '0'; else sync_1 <= entree_async; sync_2 <= sync_1; end if; end if; end process; |
17.2.8 Attribut de placement
| Attribut couramment utilisé avec Vivado | VHDL |
| attribute ASYNC_REG : string; attribute ASYNC_REG of sync_1 : signal is "TRUE"; attribute ASYNC_REG of sync_2 : signal is "TRUE"; |
Cet attribut est spécifique à un flux d’outils. Il indique que les registres appartiennent à une chaîne de synchronisation afin d’influencer l’optimisation et le placement.
17.2.9 Regrouper les bascules
Implantation — Temps de résolution Placer les deux bascules proches réduit le délai de routage entre elles et augmente le temps disponible avant le second échantillonnage. |
17.2.10 Trois bascules
Une troisième bascule peut être utilisée lorsqu’une fiabilité supérieure est nécessaire, au prix d’un cycle de latence supplémentaire.
| Chaîne à trois étages | VHDL |
| process(clk) begin if rising_edge(clk) then sync_1 <= entree_async; sync_2 <= sync_1; sync_3 <= sync_2; end if; end process; |
17.2.11 Probabilité et MTBF
La fiabilité est souvent exprimée par le temps moyen entre défaillances, MTBF. Elle dépend notamment de la fréquence de l’horloge de destination, de la fréquence des changements asynchrones et du temps de résolution.
Facteur | Effet général sur le risque |
|---|---|
| Horloge destination plus rapide | Fenêtres critiques plus fréquentes |
| Entrée changeant plus souvent | Plus d’occasions de violation |
| Temps entre les étages plus grand | Probabilité de propagation réduite |
| Technologie et placement | Paramètres physiques différents |
17.2.12 Fanout du premier étage
La sortie de sync_1 ne doit pas piloter directement plusieurs fonctions. Seule la sortie du dernier étage doit être distribuée.
| À éviter | VHDL |
| fonction_a <= sync_1; fonction_b <= sync_1; |
| À privilégier | VHDL |
| fonction_a <= sync_2; fonction_b <= sync_2; |
17.2.13 Synchronisation multiple du même signal
Créer deux chaînes indépendantes pour un même signal peut conduire à des latences différentes dans le domaine de destination. Une chaîne unique doit alimenter la logique concernée.
17.2.14 Reset asynchrone
Un reset asynchrone peut être activé immédiatement, mais sa désactivation doit être synchronisée dans chaque domaine d’horloge pour éviter une sortie de reset incohérente.
| Principe de désactivation synchronisée | VHDL |
| process(clk, reset_async) begin if reset_async = '1' then r1 <= '1'; r2 <= '1'; elsif rising_edge(clk) then r1 <= '0'; r2 <= r1; end if; end process; reset_local <= r2; |
17.3 Rebond des boutons
Lors d’un appui ou d’un relâchement, les contacts mécaniques peuvent s’ouvrir et se fermer plusieurs fois pendant quelques millisecondes.
17.3.1 Origine mécanique
- élasticité des contacts ;
- vibrations et chocs ;
- oxydation ou bruit électrique ;
- temps de stabilisation variable.
17.3.2 Effet sur un circuit
Action humaine | Signal brut possible | Effet sans filtre |
|---|---|---|
| Un appui | 0-1-0-1-1 | Plusieurs fronts |
| Un relâchement | 1-0-1-0-0 | Plusieurs événements |
| Maintien | Niveau stable | Incrément chaque cycle si testé comme niveau |
17.3.3 Filtrage temporel
Le principe consiste à accepter un nouvel état seulement s’il reste stable pendant un nombre déterminé de cycles.
Fréquence clk | Durée souhaitée | Cycles nécessaires |
|---|---|---|
| 100 MHz | 10 ms | 1 000 000 |
| 50 MHz | 10 ms | 500 000 |
| 50 MHz | 20 ms | 1 000 000 |
| 10 MHz | 5 ms | 50 000 |
17.3.4 Calcul du nombre de cycles
| Formule | Calcul |
| NB_CYCLES = FREQUENCE_HORLOGE_HZ * DUREE_FILTRAGE_S |
Pour 50 MHz et 20 ms : 50 000 000 × 0,020 = 1 000 000 cycles.
17.3.5 Anti-rebond à compteur
| Entité générique | VHDL |
| entity anti_rebond is generic ( NB_CYCLES_STABLES : positive := 1_000_000 ); port ( clk : in std_logic; reset : in std_logic; entree : in std_logic; sortie : out std_logic ); end entity anti_rebond; |
| Architecture anti-rebond — Partie 1 | VHDL |
| architecture rtl of anti_rebond is signal etat_stable : std_logic := '0'; signal compteur : natural range 0 to NB_CYCLES_STABLES-1 := 0; begin process(clk) begin if rising_edge(clk) then if reset = '1' then etat_stable <= '0'; compteur <= 0; elsif entree = etat_stable then compteur <= 0; |
| Architecture anti-rebond — Partie 2 | VHDL |
| elsif compteur = NB_CYCLES_STABLES-1 then etat_stable <= entree; compteur <= 0; else compteur <= compteur + 1; end if; end if; end process; sortie <= etat_stable; end architecture rtl; |
17.3.6 Interprétation
1. Si l’entrée égale l’état validé, le compteur est remis à zéro.
2. Si elle diffère, le compteur mesure la durée de cette différence.
3. Si la différence persiste assez longtemps, le nouvel état est accepté.
4. Si le signal rebondit vers l’ancien état, le comptage recommence.
17.3.7 Synchronisation avant filtrage
L’entrée physique doit être synchronisée avant d’être utilisée par le compteur anti-rebond.
| Ordre des blocs | Architecture |
| bouton_async -> sync_2 -> anti_rebond -> bouton_filtre |
17.3.8 Latence du filtre
Le filtre ajoute une latence approximativement égale à la durée de stabilité demandée, plus la latence du synchroniseur.
17.3.9 Valeur générique pour la simulation
Simulation — Accélération Dans un banc de test, utiliser par exemple NB_CYCLES_STABLES = 4 ou 5 afin d’éviter de simuler un million de cycles. |
17.3.10 Impulsion unique après filtrage
Le niveau filtré reste à 1 pendant toute la durée de l’appui. Un détecteur de front est nécessaire pour n’incrémenter qu’une fois.
17.3.11 Autres méthodes
Méthode | Principe |
|---|---|
| Compteur de stabilité | Valide après N cycles identiques |
| Échantillonnage lent | Observe le bouton à intervalles espacés |
| Registre à décalage | Valide lorsque plusieurs échantillons concordent |
| Filtre externe | Réseau RC + trigger de Schmitt |
17.3.12 Anti-rebond et polarité
Certaines cartes utilisent des boutons actifs à 0. Une inversion peut être effectuée avant la chaîne de traitement.
| Bouton actif bas | VHDL |
| bouton_actif_haut <= not bouton_n; |
17.4 Détection de front
La détection de front compare la valeur courante d’un signal synchronisé à sa valeur mémorisée au cycle précédent.
17.4.1 Mémorisation de la valeur précédente
| Registre de retard | VHDL |
| process(clk) begin if rising_edge(clk) then signal_precedent <= signal_courant; end if; end process; |
17.4.2 Front montant
| Impulsion combinatoire d’un cycle | VHDL |
| impulsion_montee <= signal_courant and not signal_precedent; |
Précédent | Courant | Impulsion montée |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 1 | 0 |
| 1 | 0 | 0 |
17.4.3 Front descendant
| Détection de descente | VHDL |
| impulsion_descente <= not signal_courant and signal_precedent; |
17.4.4 Détection des deux fronts
| Changement quelconque | VHDL |
| impulsion_changement <= signal_courant xor signal_precedent; |
17.4.5 Module complet
| Détecteur de fronts | VHDL |
| entity detecteur_front is port ( clk : in std_logic; reset : in std_logic; entree : in std_logic; montee : out std_logic; descente : out std_logic ); end entity detecteur_front; architecture rtl of detecteur_front is signal precedent : std_logic := '0'; begin process(clk) begin if rising_edge(clk) then if reset = '1' then precedent <= '0'; else precedent <= entree; end if; end if; end process; montee <= entree and not precedent; descente <= not entree and precedent; end architecture rtl; |
17.4.6 Impulsion enregistrée
| Sorties calculées dans le processus | VHDL |
| process(clk) begin if rising_edge(clk) then if reset = '1' then precedent <= '0'; montee <= '0'; descente <= '0'; else montee <= entree and not precedent; descente <= not entree and precedent; precedent <= entree; end if; end if; end process; |
Cette version produit des sorties enregistrées. Leur chronologie doit être prise en compte dans le reste du système.
17.4.7 Front d’horloge et front de donnée
Distinction — Ne pas confondre rising_edge(clk) détecte le front actif de l’horloge. L’expression entree and not precedent détecte une transition d’une donnée déjà échantillonnée dans le domaine de clk. |
17.4.8 Détection avant anti-rebond
Détecter le front du signal brut produit une impulsion pour chaque rebond. La détection doit être appliquée après le filtrage.
17.4.9 Événement trop court
Si une impulsion asynchrone ne couvre aucun front d’horloge, elle peut être perdue. Il faut l’étirer, utiliser un toggle ou un handshake.
17.4.10 Toggle synchronizer
Dans le domaine source, chaque événement inverse un bit toggle. Le bit est synchronisé, puis un XOR entre deux échantillons successifs recrée une impulsion dans le domaine destination.
| Domaine source | VHDL |
| if rising_edge(clk_source) then if evenement = '1' then toggle_src <= not toggle_src; end if; end if; |
| Domaine destination | VHDL |
| process(clk_dest) begin if rising_edge(clk_dest) then toggle_d1 <= toggle_src; toggle_d2 <= toggle_d1; toggle_d3 <= toggle_d2; end if; end process; impulsion_dest <= toggle_d2 xor toggle_d3; |
Deux événements source trop rapprochés peuvent néanmoins se compenser avant d’être observés. Le débit maximal doit être spécifié.
17.5 Passage entre domaines d’horloge
Un Clock Domain Crossing, ou CDC, apparaît lorsqu’un signal produit dans un domaine d’horloge est utilisé dans un autre domaine dont la fréquence ou la phase diffère.
17.5.1 Domaines synchrones et asynchrones
Relation | Description |
|---|---|
| Même horloge | Même réseau et même référence |
| Horloges liées | Relation de fréquence/phase connue |
| Horloges asynchrones | Aucune relation de phase garantie |
| Horloge externe | Relation à définir par l’interface |
17.5.2 Synchronisation d’un bit de niveau
| Bit de contrôle | VHDL |
| process(clk_dest) begin if rising_edge(clk_dest) then ctrl_d1 <= ctrl_source; ctrl_d2 <= ctrl_d1; end if; end process; ctrl_dest <= ctrl_d2; |
17.5.3 Limite de la synchronisation bit à bit d’un bus
| Approche dangereuse | VHDL |
| process(clk_dest) begin if rising_edge(clk_dest) then bus_d1 <= bus_source; bus_d2 <= bus_d1; end if; end process; |
Chaque bit peut être capturé à un cycle différent lorsque plusieurs bits changent simultanément. Le mot résultant peut ne jamais avoir existé dans le domaine source.
Règle CDC — Bus multi-bit Un synchroniseur par bit n’assure pas la cohérence d’un mot. Il faut garantir la stabilité des données ou utiliser un protocole adapté. |
17.5.4 Bus codé Gray
Le code Gray ne change qu’un bit entre deux valeurs successives. Il est utilisé notamment pour transférer des pointeurs de FIFO, mais il ne résout pas tous les transferts de bus.
| Conversion binaire vers Gray | VHDL |
| gray <= binaire xor ('0' & binaire(binaire'high downto 1)); |
Le bus Gray synchronisé doit respecter des contraintes de skew afin que les bits se comportent comme prévu à l’interface.
17.5.5 Handshake request/acknowledge
Le domaine source place les données, les maintient stables et active request. Le domaine destination synchronise request, capture les données, puis retourne acknowledge.
Étape | Source | Destination |
|---|---|---|
| 1 | Place data et active req | Attend |
| 2 | Maintient data | Synchronise req |
| 3 | Maintient data | Capture data et active ack |
| 4 | Synchronise ack et retire req | Maintient ack |
| 5 | Libère data | Synchronise req bas et retire ack |
17.5.6 Principe VHDL du handshake
| Domaine source — émission | VHDL |
| if rising_edge(clk_source) then if reset_source = '1' then req_source <= '0'; elsif envoyer = '1' and req_source = ack_sync then data_hold <= data_source; req_source <= not req_source; end if; end if; |
| Domaine destination — détection | VHDL |
| process(clk_dest) begin if rising_edge(clk_dest) then req_d1 <= req_source; req_d2 <= req_d1; req_d3 <= req_d2; if req_d2 /= req_d3 then data_dest <= data_hold; nouveau_dest <= '1'; else nouveau_dest <= '0'; end if; end if; end process; |
Le signal d’acquittement doit être renvoyé vers la source par une chaîne de synchronisation. Cet extrait illustre le principe, pas un protocole complet prêt pour toutes les fréquences.
17.5.7 Données stables
Dans un transfert cohérent, le bus data_hold reste stable suffisamment longtemps avant et après la détection de request dans le domaine destination.
- la source ne modifie pas les données avant l’acquittement ;
- le contrôle traverse les synchroniseurs ;
- la destination capture le mot après détection ;
- les contraintes CDC et de bus doivent être analysées.
17.5.8 FIFO asynchrone : introduction
Une FIFO asynchrone possède une horloge d’écriture et une horloge de lecture indépendantes. Elle stocke les données et transfère des pointeurs synchronisés entre les domaines.
Élément | Rôle |
|---|---|
| Mémoire double port | Stocke les données |
| Pointeur d’écriture | Avance dans le domaine write_clk |
| Pointeur de lecture | Avance dans le domaine read_clk |
| Pointeurs Gray | Traversent les domaines avec un bit changeant |
| full / empty | Empêchent dépassement et sous-dépassement |
17.5.9 Pourquoi utiliser une IP de FIFO
- gestion vérifiée des pointeurs et drapeaux ;
- contraintes CDC intégrées ou documentées ;
- utilisation optimisée des blocs mémoire ;
- paramètres de profondeur et largeur ;
- réduction du risque d’erreur de conception.
17.5.10 Contraintes temporelles CDC
Les chemins asynchrones ne sont pas analysés comme des chemins synchrones classiques entre deux horloges indépendantes. Les contraintes doivent être appliquées avec prudence et les structures CDC doivent être vérifiées par l’outil.
| Exemple conceptuel de groupes asynchrones | Contraintes |
| set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b] |
Contraintes — Ne pas masquer une erreur Déclarer deux horloges asynchrones ne rend pas le transfert sûr. Il faut d’abord construire un circuit CDC correct. |
17.5.11 Outils d’analyse CDC
- détection de synchroniseurs absents ;
- fanout incorrect du premier étage ;
- bus multi-bits non protégés ;
- reconvergence de signaux synchronisés séparément ;
- reset asynchrone mal synchronisé ;
- pointeurs et handshakes incomplets.
17.5.12 Choix de la technique
Type de transfert | Technique recommandée |
|---|---|
| Bit de niveau lent | Deux bascules |
| Impulsion | Étirement, toggle ou handshake |
| Bus stable occasionnel | Handshake avec maintien des données |
| Flux continu | FIFO asynchrone |
| Compteur monotone | Code Gray selon l’architecture |
| Reset | Activation asynchrone, désactivation synchronisée |
Méthode de traitement d’une entrée externe
1. Identifier l’horloge du domaine destinataire.
2. Déterminer si l’information est un niveau, une impulsion ou un bus.
3. Évaluer la durée minimale et la fréquence des changements.
4. Synchroniser le bit de contrôle.
5. Filtrer le rebond ou le bruit si nécessaire.
6. Détecter le front après stabilisation.
7. Générer une impulsion d’un cycle.
8. Vérifier la latence acceptable.
9. Ajouter les attributs et contraintes adaptés au flux d’outils.
10. Simuler les scénarios de rebond et analyser le CDC.
Checklist
- La logique utilise-t-elle seulement le dernier étage du synchroniseur ?
- Le premier étage possède-t-il un fanout limité ?
- Le bouton est-il filtré avant la détection de front ?
- L’impulsion produite dure-t-elle exactement un cycle ?
- Une impulsion courte peut-elle être perdue ?
- Un bus est-il synchronisé avec un protocole cohérent ?
- Les resets sont-ils relâchés de manière synchronisée ?
- L’outil CDC signale-t-il des violations ?
Erreurs fréquentes
Erreur | Conséquence | Correction |
|---|---|---|
| Bouton utilisé comme horloge | Rebonds et domaine non maîtrisé | Horloge principale + enable |
| Une seule bascule de synchronisation | Risque plus élevé de propagation | Deux étages ou plus |
| Utilisation de sync_1 | Métastabilité distribuée | Utiliser sync_2 |
| Détection avant anti-rebond | Plusieurs impulsions | Filtrer puis détecter |
| Synchronisation bit à bit d’un bus | Mot incohérent | Handshake ou FIFO |
| Impulsion source trop courte | Événement perdu | Étirement ou toggle |
| Deux synchroniseurs indépendants | Latences divergentes | Chaîne unique |
Travaux pratiques
TP 13 — Synchronisation des entrées
1. Objectifs du TP
- synchroniser un bouton physique ;
- filtrer les rebonds avec un compteur ;
- détecter un front montant ;
- produire une seule impulsion par appui ;
- incrémenter fiablement un compteur affiché sur des LED ;
- simuler une forme d’onde contenant des rebonds ;
- implanter le système sur une carte FPGA.
2. Architecture du TP
| Chaîne complète | Architecture |
| bouton_async -> synchroniseur_2_ff -> anti_rebond -> detecteur_front -> impulsion_appui -> compteur_8_bits -> leds |
3. Organisation du projet
| Arborescence | Arborescence |
| tp13_entrees_async/ ├── src/ │ ├── synchroniseur_bit.vhd │ ├── anti_rebond.vhd │ ├── detecteur_front.vhd │ ├── compteur_bouton.vhd │ └── top_fpga.vhd ├── sim/ │ └── tb_compteur_bouton.vhd └── constraints/ └── carte.xdc_ou_qsf |
4. Synchroniseur de bit
| synchroniseur_bit.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity synchroniseur_bit is port ( clk : in std_logic; reset : in std_logic; entree_async : in std_logic; sortie_sync : out std_logic ); end entity synchroniseur_bit; |
| synchroniseur_bit.vhd — Architecture | VHDL |
| architecture rtl of synchroniseur_bit is signal sync_1 : std_logic := '0'; signal sync_2 : std_logic := '0'; attribute ASYNC_REG : string; attribute ASYNC_REG of sync_1 : signal is "TRUE"; attribute ASYNC_REG of sync_2 : signal is "TRUE"; begin process(clk) begin if rising_edge(clk) then if reset = '1' then sync_1 <= '0'; sync_2 <= '0'; else sync_1 <= entree_async; sync_2 <= sync_1; end if; end if; end process; sortie_sync <= sync_2; end architecture rtl; |
5. Filtre anti-rebond
| anti_rebond.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity anti_rebond is generic ( NB_CYCLES_STABLES : positive := 1_000_000 ); port ( clk : in std_logic; reset : in std_logic; entree : in std_logic; sortie : out std_logic ); end entity anti_rebond; |
| anti_rebond.vhd — Architecture | VHDL |
| architecture rtl of anti_rebond is signal etat_stable : std_logic := '0'; signal compteur : natural range 0 to NB_CYCLES_STABLES-1 := 0; begin process(clk) begin if rising_edge(clk) then if reset = '1' then etat_stable <= '0'; compteur <= 0; elsif entree = etat_stable then compteur <= 0; elsif compteur = NB_CYCLES_STABLES-1 then etat_stable <= entree; compteur <= 0; else compteur <= compteur + 1; end if; end if; end process; sortie <= etat_stable; end architecture rtl; |
6. Détecteur de front
| detecteur_front.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity detecteur_front is port ( clk : in std_logic; reset : in std_logic; entree : in std_logic; montee : out std_logic ); end entity detecteur_front; |
| detecteur_front.vhd — Architecture | VHDL |
| architecture rtl of detecteur_front is signal precedent : std_logic := '0'; begin process(clk) begin if rising_edge(clk) then if reset = '1' then precedent <= '0'; else precedent <= entree; end if; end if; end process; montee <= entree and not precedent; end architecture rtl; |
7. Compteur commandé
| compteur_bouton.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity compteur_bouton is generic ( NB_CYCLES_STABLES : positive := 1_000_000 ); port ( clk : in std_logic; reset : in std_logic; bouton_async : in std_logic; compteur : out std_logic_vector(7 downto 0) ); end entity compteur_bouton; |
| compteur_bouton.vhd — Signaux et synchronisation | VHDL |
| architecture structurelle of compteur_bouton is signal bouton_sync : std_logic; signal bouton_filtre : std_logic; signal impulsion : std_logic; signal compte_i : unsigned(7 downto 0) := (others => '0'); begin U_SYNC : entity work.synchroniseur_bit(rtl) port map ( clk => clk, reset => reset, entree_async => bouton_async, sortie_sync => bouton_sync ); |
| compteur_bouton.vhd — Filtre et front | VHDL |
| U_FILTRE : entity work.anti_rebond(rtl) generic map ( NB_CYCLES_STABLES => NB_CYCLES_STABLES ) port map ( clk => clk, reset => reset, entree => bouton_sync, sortie => bouton_filtre ); U_FRONT : entity work.detecteur_front(rtl) port map ( clk => clk, reset => reset, entree => bouton_filtre, montee => impulsion ); |
| compteur_bouton.vhd — Comptage | VHDL |
| process(clk) begin if rising_edge(clk) then if reset = '1' then compte_i <= (others => '0'); elsif impulsion = '1' then compte_i <= compte_i + 1; end if; end if; end process; compteur <= std_logic_vector(compte_i); end architecture structurelle; |
8. Banc de test avec rebonds
| tb_compteur_bouton.vhd — Déclarations | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use STD.ENV.ALL; entity tb_compteur_bouton is end entity tb_compteur_bouton; architecture simulation of tb_compteur_bouton is constant PERIODE : time := 20 ns; signal clk : std_logic := '0'; signal reset : std_logic := '0'; signal bouton_async : std_logic := '0'; signal compteur : std_logic_vector(7 downto 0); begin clk <= not clk after PERIODE / 2; DUT : entity work.compteur_bouton(structurelle) generic map ( NB_CYCLES_STABLES => 4 ) port map ( clk => clk, reset => reset, bouton_async => bouton_async, compteur => compteur ); |
| tb_compteur_bouton.vhd — Procédure d’appui rebondissant | VHDL |
| stimuli : process procedure appui_avec_rebond is begin bouton_async <= '1'; wait for 7 ns; bouton_async <= '0'; wait for 9 ns; bouton_async <= '1'; wait for 6 ns; bouton_async <= '0'; wait for 8 ns; bouton_async <= '1'; -- État haut durable. wait for 8 * PERIODE; end procedure; |
| tb_compteur_bouton.vhd — Relâchement et vérifications | VHDL |
| procedure relachement_avec_rebond is begin bouton_async <= '0'; wait for 8 ns; bouton_async <= '1'; wait for 6 ns; bouton_async <= '0'; -- État bas durable. wait for 8 * PERIODE; end procedure; begin reset <= '1'; wait for 3 * PERIODE; reset <= '0'; wait for 2 * PERIODE; appui_avec_rebond; assert unsigned(compteur) = 1 report "Un appui doit produire un seul comptage" severity error; relachement_avec_rebond; appui_avec_rebond; assert unsigned(compteur) = 2 report "Deux appuis doivent produire deux comptages" severity error; |
| tb_compteur_bouton.vhd — Fin et timeout | VHDL |
| report "Synchronisation et anti-rebond valides" severity note; stop; wait; end process; timeout : process begin wait for 5 us; assert false report "Timeout du TP 13" severity failure; end process; end architecture simulation; |
9. Signaux à observer
Signal | Observation |
|---|---|
| bouton_async | Rebonds non alignés sur clk |
| bouton_sync | Signal retardé par le synchroniseur |
| bouton_filtre | Un seul changement après stabilité |
| impulsion | Niveau haut pendant un seul cycle |
| compteur | Une incrémentation par appui |
10. Niveau supérieur FPGA
| top_fpga.vhd | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity top_fpga is port ( clk_carte : in std_logic; reset : in std_logic; bouton : in std_logic; leds : out std_logic_vector(7 downto 0) ); end entity top_fpga; architecture structurelle of top_fpga is begin U_SYSTEME : entity work.compteur_bouton(structurelle) generic map ( NB_CYCLES_STABLES => 1_000_000 ) port map ( clk => clk_carte, reset => reset, bouton_async => bouton, compteur => leds ); end architecture structurelle; |
11. Adaptation à la fréquence
Horloge | Filtrage 20 ms |
|---|---|
| 100 MHz | 2 000 000 cycles |
| 50 MHz | 1 000 000 cycles |
| 25 MHz | 500 000 cycles |
| 10 MHz | 200 000 cycles |
12. Contraintes de broches
| Exemple XDC conceptuel | Contraintes |
| set_property PACKAGE_PIN <PIN_CLK> [get_ports clk_carte] set_property IOSTANDARD LVCMOS33 [get_ports clk_carte] set_property PACKAGE_PIN <PIN_BOUTON> [get_ports bouton] set_property IOSTANDARD LVCMOS33 [get_ports bouton] set_property PACKAGE_PIN <PIN_LED0> [get_ports {leds[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {leds[0]}] create_clock -period 20.000 -name clk_carte [get_ports clk_carte] |
13. Validation sur carte
1. Adapter NB_CYCLES_STABLES à la fréquence de la carte.
2. Associer l’horloge, le bouton, le reset et les LED.
3. Vérifier la polarité du bouton.
4. Synthétiser et lire les avertissements CDC.
5. Examiner le schéma RTL.
6. Vérifier la chaîne de deux bascules.
7. Générer le bitstream et programmer la carte.
8. Appuyer plusieurs fois lentement sur le bouton.
9. Vérifier une seule incrémentation par appui.
10. Tester des appuis longs et rapides.
14. Commandes GHDL
| Compilation | Terminal |
| ghdl -a --std=08 src/synchroniseur_bit.vhd ghdl -a --std=08 src/anti_rebond.vhd ghdl -a --std=08 src/detecteur_front.vhd ghdl -a --std=08 src/compteur_bouton.vhd ghdl -a --std=08 sim/tb_compteur_bouton.vhd |
| Simulation | Terminal |
| ghdl -e --std=08 tb_compteur_bouton ghdl -r --std=08 tb_compteur_bouton --vcd=tb_compteur_bouton.vcd gtkwave tb_compteur_bouton.vcd |
15. Tableau de résultats
Test | Résultat attendu | Résultat observé | Conforme |
|---|---|---|---|
| Reset | Compteur = 0 | ||
| Premier appui rebondissant | Compteur = 1 | ||
| Maintien du bouton | Pas de nouveau comptage | ||
| Relâchement rebondissant | Pas de décrément/incrément | ||
| Deuxième appui | Compteur = 2 | ||
| Carte FPGA | Une LED-évolution par appui |
16. Questions d’analyse
1. Pourquoi le bouton est-il asynchrone par rapport à clk ?
2. Quelle bascule peut devenir métastable dans le synchroniseur ?
3. Pourquoi la logique doit-elle utiliser sync_2 et non sync_1 ?
4. Pourquoi le filtre est-il placé après le synchroniseur ?
5. Comment le compteur détecte-t-il que l’entrée est devenue stable ?
6. Pourquoi la détection de front est-elle placée après l’anti-rebond ?
7. Combien de cycles dure impulsion ?
8. Pourquoi une synchronisation bit à bit ne suffit-elle pas pour un bus ?
9. Quand utiliser une FIFO asynchrone ?
10. Comment adapter le générique à une horloge de 100 MHz ?
17. Extensions proposées
- Ajouter la détection du relâchement.
- Compter les appuis et les relâchements séparément.
- Créer un anti-rebond actif bas.
- Ajouter un bouton de décrémentation.
- Synchroniser un capteur d’impulsions.
- Implémenter un toggle synchronizer entre deux horloges.
- Étudier un handshake de données sur huit bits.
- Instancier une FIFO asynchrone fournie par l’outil.
18. Barème indicatif
Critère | Points |
|---|---|
| Synchroniseur à deux bascules | 3 |
| Filtre anti-rebond | 4 |
| Détection de front | 3 |
| Compteur fiable | 2 |
| Banc de test avec rebonds | 3 |
| Analyse CDC et chronogrammes | 2 |
| Validation sur carte | 2 |
| Qualité du compte rendu | 1 |
| Total | 20 |
Synthèse du chapitre
Notion | Résumé |
|---|---|
| Entrée asynchrone | Signal sans relation temporelle garantie avec clk. |
| Métastabilité | État analogique transitoire d’une bascule. |
| Synchroniseur | Chaîne de bascules réduisant le risque de propagation. |
| Rebond | Transitions multiples d’un contact mécanique. |
| Anti-rebond | Validation après plusieurs cycles stables. |
| Front montant | Transition 0 vers 1 du signal échantillonné. |
| Impulsion d’un cycle | Événement synchrone utilisable comme enable. |
| CDC | Passage entre domaines d’horloge. |
| Handshake | Échange request/acknowledge avec données maintenues. |
| FIFO asynchrone | File avec horloges de lecture et écriture indépendantes. |
| Code Gray | Codage à un seul bit changeant entre états successifs. |
| ASYNC_REG | Attribut d’outil pour les registres de synchronisation. |
Autoévaluation
1. Pourquoi une entrée externe est-elle asynchrone ?
Réponse : Elle peut changer indépendamment de l’horloge locale.
2. Qu’est-ce que la métastabilité ?
Réponse : Un état transitoire non logique d’une bascule après violation temporelle.
3. Quel est le rôle de la seconde bascule ?
Réponse : Fournir davantage de temps de résolution.
4. Le synchroniseur supprime-t-il tout risque ?
Réponse : Non, il réduit fortement la probabilité.
5. Pourquoi un bouton produit-il plusieurs fronts ?
Réponse : À cause du rebond mécanique.
6. Comment fonctionne le filtre par compteur ?
Réponse : Il valide un nouvel état après N cycles stables.
7. Quelle expression détecte une montée ?
Réponse : courant and not precedent.
8. Pourquoi ne pas synchroniser séparément chaque bit d’un bus ?
Réponse : Les bits peuvent être capturés à des cycles différents.
9. Quand utiliser un handshake ?
Réponse : Pour transférer occasionnellement un mot cohérent.
10. Quand utiliser une FIFO asynchrone ?
Réponse : Pour un flux de données entre deux horloges.
Exercice de consolidation
Concevoir une chaîne qui transforme une entrée capteur asynchrone en une impulsion d’un cycle à chaque front descendant stable.
| Correction proposée | VHDL |
| -- 1. Synchronisation. process(clk) begin if rising_edge(clk) then sync_1 <= capteur_async; sync_2 <= sync_1; end if; end process; -- 2. Filtrage : utiliser le module anti_rebond. -- 3. Mémorisation de l'état précédent. process(clk) begin if rising_edge(clk) then filtre_precedent <= filtre; end if; end process; -- 4. Impulsion de descente. impulsion_descente <= not filtre and filtre_precedent; |
Suite du cours — Transition vers la suite Après la synchronisation des entrées, le cours peut aborder l’inférence et l’utilisation des mémoires internes : ROM, RAM simple port, double port et blocs BRAM. |