Leçon 17 sur 22

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.1Entrées asynchronesIdentifier les risques
17.2MétastabilitéMettre en œuvre un synchroniseur
17.3Rebond des boutonsFiltrer une entrée mécanique
17.4Détection de frontCréer une impulsion d’un cycle
17.5Clock Domain CrossingTransférer bits et bus
TP 13Synchronisation d’un boutonCommander 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écaniqueDeux voies asynchrones et rebondissantes
Comparateur analogiqueTransition indépendante de clk
Interruption d’un périphériqueImpulsion ou niveau asynchrone
Récepteur série externeDomaine 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 critiqueRisque de métastabilité
Impulsion plus courte qu’une périodeRisque de ne jamais être échantillonnée
Bus modifié sans protocoleMots 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 persistantSynchroniseur à deux bascules
Impulsion pouvant être élargieÉtirement puis synchronisation
Événement sporadiqueToggle synchronizer ou handshake
Bus de donnéesHandshake, 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 asynchroneBouton proche d’un front
Passage de domaineSignal de clk_a vers clk_b
Reset asynchrone relâché au mauvais instantSortie de reset proche du front
Violation de setup/holdChemin 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_1Capture l’entrée et supporte le risque principal
sync_2Fournit une valeur plus fiable à la logique
Logique utilisateurUtilise 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 rapideFenêtres critiques plus fréquentes
Entrée changeant plus souventPlus d’occasions de violation
Temps entre les étages plus grandProbabilité de propagation réduite
Technologie et placementParamè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 appui0-1-0-1-1Plusieurs fronts
Un relâchement1-0-1-0-0Plusieurs événements
MaintienNiveau stableIncré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 MHz10 ms1 000 000
50 MHz10 ms500 000
50 MHz20 ms1 000 000
10 MHz5 ms50 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 lentObserve le bouton à intervalles espacés
Registre à décalageValide lorsque plusieurs échantillons concordent
Filtre externeRé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

000
011
110
100

 

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 horlogeMême réseau et même référence
Horloges liéesRelation de fréquence/phase connue
Horloges asynchronesAucune relation de phase garantie
Horloge externeRelation à 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

1Place data et active reqAttend
2Maintient dataSynchronise req
3Maintient dataCapture data et active ack
4Synchronise ack et retire reqMaintient ack
5Libère dataSynchronise 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 portStocke les données
Pointeur d’écritureAvance dans le domaine write_clk
Pointeur de lectureAvance dans le domaine read_clk
Pointeurs GrayTraversent les domaines avec un bit changeant
full / emptyEmpê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 lentDeux bascules
ImpulsionÉtirement, toggle ou handshake
Bus stable occasionnelHandshake avec maintien des données
Flux continuFIFO asynchrone
Compteur monotoneCode Gray selon l’architecture
ResetActivation 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 horlogeRebonds et domaine non maîtriséHorloge principale + enable
Une seule bascule de synchronisationRisque plus élevé de propagationDeux étages ou plus
Utilisation de sync_1Métastabilité distribuéeUtiliser sync_2
Détection avant anti-rebondPlusieurs impulsionsFiltrer puis détecter
Synchronisation bit à bit d’un busMot incohérentHandshake ou FIFO
Impulsion source trop courteÉvénement perduÉtirement ou toggle
Deux synchroniseurs indépendantsLatences divergentesChaî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_asyncRebonds non alignés sur clk
bouton_syncSignal retardé par le synchroniseur
bouton_filtreUn seul changement après stabilité
impulsionNiveau haut pendant un seul cycle
compteurUne 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 MHz2 000 000 cycles
50 MHz1 000 000 cycles
25 MHz500 000 cycles
10 MHz200 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

ResetCompteur = 0  
Premier appui rebondissantCompteur = 1  
Maintien du boutonPas de nouveau comptage  
Relâchement rebondissantPas de décrément/incrément  
Deuxième appuiCompteur = 2  
Carte FPGAUne 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 bascules3
Filtre anti-rebond4
Détection de front3
Compteur fiable2
Banc de test avec rebonds3
Analyse CDC et chronogrammes2
Validation sur carte2
Qualité du compte rendu1
Total20

 

Synthèse du chapitre

Notion

Résumé

Entrée asynchroneSignal sans relation temporelle garantie avec clk.
MétastabilitéÉtat analogique transitoire d’une bascule.
SynchroniseurChaîne de bascules réduisant le risque de propagation.
RebondTransitions multiples d’un contact mécanique.
Anti-rebondValidation après plusieurs cycles stables.
Front montantTransition 0 vers 1 du signal échantillonné.
Impulsion d’un cycleÉvénement synchrone utilisable comme enable.
CDCPassage entre domaines d’horloge.
HandshakeÉchange request/acknowledge avec données maintenues.
FIFO asynchroneFile avec horloges de lecture et écriture indépendantes.
Code GrayCodage à un seul bit changeant entre états successifs.
ASYNC_REGAttribut 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.