Leçon 11 sur 22

Chapitre 11 — Compteurs numériques

Objectif — Finalité du chapitre

Concevoir des compteurs binaires, modulo N et BCD, ajouter des fonctions de contrôle, générer des impulsions périodiques et piloter un affichage décimal sur carte FPGA.

Objectifs pédagogiques

À la fin de ce chapitre, l’étudiant devra être capable de :

  • décrire un compteur binaire ascendant, descendant et bidirectionnel ;
  • dimensionner un compteur en fonction d’une valeur maximale ;
  • concevoir un compteur modulo N paramétrable ;
  • gérer enable, reset et chargement parallèle ;
  • distinguer enroulement, saturation et débordement ;
  • générer une impulsion périodique à partir d’une horloge rapide ;
  • éviter la création de mauvaises horloges secondaires ;
  • concevoir un compteur BCD de 0 à 9 ;
  • chaîner plusieurs chiffres BCD ;
  • piloter un ou plusieurs afficheurs sept segments ;
  • simuler puis valider les compteurs sur une carte FPGA.

Prérequis

  • processus cadencés et rising_edge ;
  • reset synchrone et asynchrone ;
  • registres et types unsigned ;
  • génériques et fonctions de conversion ;
  • décodeur BCD vers sept segments.

Organisation du chapitre

Section

Contenu

Compétence principale

11.1Compteurs binairesCompter dans différents sens
11.2Compteur modulo NLimiter le comptage à une plage
11.3Compteur avec contrôleAjouter reset, enable, load et saturation
11.4Diviseur de fréquenceCréer un rythme périodique à partir de cycles
11.5Compteur BCDCompter et afficher en décimal
TP 8Compteurs et affichageSimuler et valider sur FPGA

 

Concept clé — Compteur = registre + logique de prochaine valeur

Un compteur est un registre dont la prochaine valeur est calculée à partir de la valeur courante, généralement par addition ou soustraction.

 

11.1 Compteur binaire

Un compteur binaire mémorise un mot et le modifie à chaque front d’horloge autorisé. Un compteur sur N bits possède 2^N états binaires possibles.

11.1.1 Compteur ascendant

Le compteur ascendant ajoute 1 à chaque front actif.

Compteur ascendant — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity compteur_ascendant is
    port (
        clk   : in   std_logic;
        reset : in  std_logic;
        q     : out unsigned(7 downto 0)
    );
end entity compteur_ascendant;
Compteur ascendant — Architecture  |  VHDL
architecture rtl of compteur_ascendant is
    signal compteur : unsigned(7 downto 0);
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= (others => '0');
        elsif rising_edge(clk) then
            compteur <= compteur + 1;
        end if;
    end process;

    q <= compteur;
end architecture rtl;

 

11.1.2 Séquence de comptage

Cycle

Valeur décimale

Valeur sur 4 bits

Initial00000
110001
220010
330011
440100
15151111
1600000

 

11.1.3 Débordement naturel

Lorsqu’un unsigned atteint sa valeur maximale, l’addition de 1 produit un enroulement modulo 2^N si le résultat est conservé sur la même largeur.

Enroulement — Exemple

Sur 4 bits, 1111 + 1 donne 0000. La retenue sortante est perdue si elle n’est pas stockée dans un bit supplémentaire.

 

11.1.4 Compteur descendant

Compteur descendant  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= (others => '1');
    elsif rising_edge(clk) then
        compteur <= compteur - 1;
    end if;
end process;

 

Après 0000, le compteur revient à 1111 sur la même largeur.

11.1.5 Compteur ascendant-descendant

Commande de direction  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= (others => '0');
    elsif rising_edge(clk) then
        if direction = '1' then
            compteur <= compteur + 1;
        else
            compteur <= compteur - 1;
        end if;
    end if;
end process;

 

direction

Opération

1Comptage ascendant
0Comptage descendant

 

11.1.6 Compteur paramétrable

Entité générique  |  VHDL
entity compteur_binaire is
    generic (
        N : positive := 8
    );
    port (
        clk       : in   std_logic;
        reset     : in   std_logic;
        direction : in  std_logic;
        q         : out unsigned(N-1 downto 0)
    );
end entity compteur_binaire;

 

Architecture générique — Processus  |  VHDL
architecture rtl of compteur_binaire is
    signal compteur : unsigned(N-1 downto 0);
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= (others => '0');
        elsif rising_edge(clk) then
            if direction = '1' then
                compteur <= compteur + 1;
            else
                compteur <= compteur - 1;
            end if;
        end if;
    end process;
Architecture générique — Sortie  |  VHDL
    q <= compteur;
end architecture rtl;

 

11.1.7 Sortie terminale

Une sortie terminal_count peut indiquer que le compteur a atteint sa valeur maximale ou minimale.

Détection du maximum  |  VHDL
terminal_max <= '1'
    when compteur = (compteur'range => '1')
    else '0';

 

Détection de zéro  |   VHDL
terminal_zero <= '1'
    when compteur = 0
    else '0';

 

11.1.8 Compteur avec impulsion de retenue

Impulsion lors de l’enroulement  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= (others => '0');
        carry_out <= '0';
    elsif rising_edge(clk) then
        carry_out <= '0';

        if compteur = (compteur'range => '1') then
            compteur <= (others => '0');
            carry_out <= '1';
        else
            compteur <= compteur + 1;
        end if;
    end if;
end process;

 

carry_out vaut 1 pendant un seul cycle lors du passage du maximum à zéro.

11.1.9 Compteur asynchrone et compteur synchrone

Critère

Compteur synchrone

Compteur asynchrone en cascade

HorlogeCommune à toutes les basculesSortie d’une bascule vers la suivante
VitesseMeilleureLimitée par la propagation
Analyse temporellePlus simplePlus délicate
Usage FPGARecommandéGénéralement déconseillé

 

Bonne pratique — FPGA

Les compteurs FPGA doivent être décrits de manière synchrone, avec une horloge distribuée par le réseau d’horloge dédié.

 

11.1.10 Ressources synthétisées

Fonction

Ressource probable

Compteur N bitsN bascules + additionneur/incrémenteur
DirectionMultiplexeur entre +1 et -1
EnableClock enable ou multiplexeur de maintien
ResetEntrée reset dédiée ou logique de données
Terminal countComparateur

 

11.2 Compteur modulo N

Un compteur modulo N possède N états utiles, numérotés de 0 à N-1. Après N-1, il revient à zéro.

11.2.1 Définition du modulo

Le modulo indique le nombre d’états distincts de la séquence. Un compteur modulo 10 suit 0, 1, ..., 9, puis revient à 0.

Modulo

États utiles

20 à 1
80 à 7
100 à 9
600 à 59
1000 à 99

 

11.2.2 Retour à zéro

Compteur modulo 10  |   VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= 0;
    elsif rising_edge(clk) then
        if compteur = 9 then
            compteur <= 0;
        else
            compteur <= compteur + 1;
        end if;
    end if;
end process;

 

11.2.3 Dimensionnement du registre

Il faut choisir le plus petit nombre de bits B tel que 2^B soit supérieur ou égal à N.

Modulo N

Bits minimaux

Plage binaire disponible

210 à 1
3 à 420 à 3
5 à 830 à 7
9 à 1640 à 15
17 à 3250 à 31
33 à 6460 à 63
10070 à 127

 

Codage — États inutilisés

Un compteur modulo 10 sur 4 bits possède six codes binaires non utilisés : 1010 à 1111. Le circuit doit normalement les éviter ou revenir vers un état valide.

 

11.2.4 Utilisation d’un générique

Entité modulo générique  |  VHDL
entity compteur_modulo is
    generic (
        MODULO : positive := 10
    );
    port (
        clk   : in   std_logic;
        reset : in  std_logic;
        tick  : out std_logic;
        q     : out natural range 0 to MODULO-1
    );
end entity compteur_modulo;

 

11.2.5 Architecture avec natural

Compteur modulo N — Processus  |  VHDL
architecture rtl of compteur_modulo is
    signal compteur : natural range 0 to MODULO-1 := 0;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= 0;
            tick <= '0';
        elsif rising_edge(clk) then
            tick <= '0';

            if compteur = MODULO-1 then
                compteur <= 0;
                tick <= '1';
            else
                compteur <= compteur + 1;
            end if;
        end if;
    end process;
Compteur modulo N — Sortie  |  VHDL
    q <= compteur;
end architecture rtl;

 

11.2.6 Architecture avec unsigned

Une largeur explicite peut être fournie par un second générique, ou calculée dans un paquetage avec une fonction clog2.

Génériques MODULO et LARGEUR  |  VHDL
entity compteur_modulo_u is
    generic (
        MODULO  : positive := 10;
        LARGEUR : positive := 4
    );
    port (
        clk   : in   std_logic;
        reset : in  std_logic;
        q     : out unsigned(LARGEUR-1 downto 0)
    );
end entity compteur_modulo_u;

 

Vérification du dimensionnement  |  VHDL
assert (2 ** LARGEUR) >= MODULO
    report "LARGEUR insuffisante pour MODULO"
    severity failure;

 

11.2.7 Fonction clog2

Fonction de calcul du nombre de bits  |  VHDL
function clog2(n : positive) return natural is
    variable valeur : natural := n - 1;
    variable bits   : natural := 0;
begin
    while valeur > 0 loop
        valeur := valeur / 2;
        bits := bits + 1;
    end loop;

    return bits;
end function;

 

Réutilisation — Paquetage utilitaire

Une fonction comme clog2 est généralement placée dans un paquetage partagé afin d’être réutilisée par plusieurs composants.

 

11.2.8 Compteur modulo avec tick

La sortie tick est une impulsion d’un cycle lorsque le compteur revient à zéro. Elle peut activer un étage suivant sans créer une nouvelle horloge.

Utilisation du tick  |   VHDL
if rising_edge(clk) then
    if tick_unites = '1' then
        dizaines <= dizaines + 1;
    end if;
end if;

 

11.2.9 États invalides

Récupération d’un état invalide  |  VHDL
if compteur >= MODULO-1 then
    compteur <= 0;
else
    compteur <= compteur + 1;
end if;

 

La comparaison >= ramène aussi un éventuel état supérieur à MODULO-1 vers zéro.

11.2.10 Applications

  • compteur décimal ;
  • base de temps ;
  • temporisateur ;
  • adresse circulaire ;
  • balayage d’afficheurs ;
  • division de fréquence ;
  • comptage d’événements limités.

11.3 Compteur avec contrôle

Un compteur pratique comporte souvent plusieurs commandes : activation, remise à zéro, chargement parallèle, direction, saturation ou indication de débordement.

11.3.1 Activation

Compteur avec enable  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= (others => '0');
    elsif rising_edge(clk) then
        if enable = '1' then
            compteur <= compteur + 1;
        end if;
    end if;
end process;

 

Lorsque enable vaut 0, le compteur conserve sa valeur.

11.3.2 Remise à zéro

Reset

Description

Action

AsynchroneTesté avant rising_edgeImmédiate
SynchroneTesté après rising_edgeAu front actif
Actif hautreset='1'Niveau 1 actif
Actif basreset_n='0'Niveau 0 actif

 

11.3.3 Chargement parallèle

Compteur avec load  |   VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= (others => '0');
    elsif rising_edge(clk) then
        if load = '1' then
            compteur <= valeur_chargee;
        elsif enable = '1' then
            compteur <= compteur + 1;
        end if;
    end if;
end process;

 

Le chargement est prioritaire sur l’incrémentation.

11.3.4 Ordre de priorité

Priorité

Commande

Effet

1resetValeur initiale
2loadChargement parallèle
3enable et directionComptage
4aucuneMaintien

 

11.3.5 Saturation

Un compteur saturant reste bloqué à sa valeur maximale au lieu de revenir à zéro.

Compteur saturant ascendant  |  VHDL
if rising_edge(clk) then
    if enable = '1' then
        if compteur < MAXIMUM then
            compteur <= compteur + 1;
        else
            compteur <= compteur;
        end if;
    end if;
end if;

 

L’affectation explicite compteur <= compteur peut être omise dans un processus cadencé.

11.3.6 Saturation descendante

Blocage à zéro  |   VHDL
if enable = '1' then
    if compteur > 0 then
        compteur <= compteur - 1;
    end if;
end if;

 

11.3.7 Débordement

Le débordement peut désigner l’enroulement d’un compteur non signé ou le dépassement de la plage signée.

Détection d’enroulement non signé  |  VHDL
overflow <= '1'
    when compteur = (compteur'range => '1')
         and enable = '1'
    else '0';

 

11.3.8 Impulsion de débordement enregistrée

Overflow d’un cycle  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur <= (others => '0');
        overflow <= '0';
    elsif rising_edge(clk) then
        overflow <= '0';

        if enable = '1' then
            if compteur = MAXIMUM then
                compteur <= (others => '0');
                overflow <= '1';
            else
                compteur <= compteur + 1;
            end if;
        end if;
    end if;
end process;

 

11.3.9 Underflow

Sous-dépassement descendant  |  VHDL
if compteur = 0 and direction = '0' and enable = '1' then
    underflow <= '1';
else
    underflow <= '0';
end if;

 

11.3.10 Compteur complet

Compteur contrôlé — Partie 1  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur  <= (others => '0');
        overflow  <= '0';
        underflow <= '0';
    elsif rising_edge(clk) then
        overflow  <= '0';
        underflow <= '0';

        if load = '1' then
            compteur <= valeur_chargee;

 

Compteur contrôlé — Partie 2  |  VHDL
        elsif enable = '1' then
            if direction = '1' then
                if compteur = MAXIMUM then
                    compteur <= (others => '0');
                    overflow <= '1';
                else
                    compteur <= compteur + 1;
                end if;
            else
                if compteur = 0 then
                    compteur <= MAXIMUM;
                    underflow <= '1';
                else
                    compteur <= compteur - 1;
                end if;
            end if;
        end if;
    end if;
end process;

 

11.3.11 Saturation ou enroulement

Politique

Au maximum + 1

Usage

EnroulementRetour à zéroComptage cyclique
SaturationReste au maximumMesure bornée
SignalementRetour à zéro + overflowChaînage ou alarme
Chargement spécialValeur définieSéquence personnalisée

 

11.3.12 Anti-rebond pour comptage manuel

Un bouton mécanique peut produire plusieurs impulsions pour un seul appui. Un compteur manuel doit utiliser un circuit anti-rebond et une détection d’impulsion.

Attention — Validation sur carte

Sans anti-rebond, le compteur peut avancer de plusieurs unités à chaque appui, même si la logique VHDL du compteur est correcte.

 

11.4 Diviseur de fréquence

Un diviseur de fréquence utilise un compteur de cycles pour produire un événement moins fréquent à partir de l’horloge système.

11.4.1 Principe

Pour produire une impulsion toutes les K périodes, le compteur compte de 0 à K-1 puis revient à zéro.

Horloge source

Période visée

Nombre de cycles

100 MHz1 ms100 000
100 MHz10 ms1 000 000
50 MHz1 s50 000 000
10 MHz100 µs1 000

 

11.4.2 Compteur de cycles

Base de temps générique  |  VHDL
entity generateur_tick is
    generic (
        NB_CYCLES : positive := 50_000_000
    );
    port (
        clk   : in   std_logic;
        reset : in  std_logic;
        tick  : out std_logic
    );
end entity generateur_tick;

 

Générateur de tick — Initialisation  |  VHDL
architecture rtl of generateur_tick is
    signal compteur : natural range 0 to NB_CYCLES-1 := 0;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= 0;
            tick <= '0';
        elsif rising_edge(clk) then
            tick <= '0';
Générateur de tick — Comptage  |  VHDL
            if compteur = NB_CYCLES-1 then
                compteur <= 0;
                tick <= '1';
            else
                compteur <= compteur + 1;
            end if;
        end if;
    end process;
end architecture rtl;

 

11.4.3 Génération d’une impulsion périodique

tick reste à 1 pendant un cycle d’horloge source. Il sert de clock enable pour les compteurs plus lents.

Utilisation du tick  |   VHDL
process(clk, reset)
begin
    if reset = '1' then
        compteur_lent <= 0;
    elsif rising_edge(clk) then
        if tick_1hz = '1' then
            compteur_lent <= compteur_lent + 1;
        end if;
    end if;
end process;

 

11.4.4 Sortie carrée divisée

Pour produire un signal carré, une sortie peut être inversée toutes les K périodes. Sa période complète vaut alors 2K périodes de l’horloge source.

Sortie carrée  |   VHDL
if compteur = NB_CYCLES-1 then
    compteur <= 0;
    sortie_divisee <= not sortie_divisee;
else
    compteur <= compteur + 1;
end if;

 

Objectif

Valeur NB_CYCLES

Impulsion à f_clk / KK
Signal carré à f_clk / (2K)K

 

11.4.5 Limites des horloges secondaires

Une sortie de compteur utilisée directement comme horloge crée un nouveau domaine d’horloge et peut ne pas utiliser correctement le réseau global.

Forme déconseillée  |  VHDL
process(horloge_divisee)
begin
    if rising_edge(horloge_divisee) then
        q <= d;
    end if;
end process;

 

Bonne pratique — Préférer un enable

Conserver l’horloge principale et utiliser une impulsion tick comme activation. Cette méthode simplifie l’analyse temporelle et évite un domaine d’horloge supplémentaire.

 

11.4.6 Quand une horloge secondaire est nécessaire

  • utiliser un PLL ou MMCM ;
  • utiliser un buffer d’horloge dédié ;
  • déclarer les contraintes temporelles correspondantes ;
  • traiter correctement les traversées de domaines d’horloge ;
  • suivre les recommandations du fabricant FPGA.

11.4.7 Précision de la base de temps

La précision dépend directement de l’oscillateur source. Un compteur ne corrige pas les tolérances ou dérives de fréquence.

Source d’erreur

Conséquence

Tolérance de l’oscillateurPériode légèrement différente
TempératureDérive possible
Valeur NB_CYCLES arrondieErreur systématique
JitterVariation instantanée des fronts

 

11.4.8 Calcul de NB_CYCLES

Formule  |   Calcul
NB_CYCLES = FREQUENCE_HORLOGE_HZ
            / FREQUENCE_TICK_HZ

 

Pour une horloge de 50 MHz et un tick de 1 Hz, NB_CYCLES = 50 000 000.

11.4.9 Tick de balayage d’afficheurs

Un tick de quelques centaines d’hertz à quelques kilohertz peut commander le changement rapide entre plusieurs afficheurs.

11.4.10 Compteur libre et sélection d’un bit

Chaque bit d’un compteur binaire libre divise approximativement la fréquence par une puissance de deux.

Bit q(k)

Fréquence approximative

q(0)f_clk / 2
q(1)f_clk / 4
q(2)f_clk / 8
q(k)f_clk / 2^(k+1)

 

Nuance — Usage limité

Sélectionner un bit de compteur peut convenir pour une LED ou un test simple, mais ne remplace pas une architecture d’horloge correctement contrainte.

 

11.5 Compteur BCD

Le code BCD représente chaque chiffre décimal par quatre bits. Un compteur BCD parcourt uniquement les valeurs binaires de 0000 à 1001.

11.5.1 Comptage de 0 à 9

Compteur BCD à un chiffre  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        bcd <= (others => '0');
        retenue <= '0';
    elsif rising_edge(clk) then
        retenue <= '0';

        if enable = '1' then
            if bcd = 9 then
                bcd <= (others => '0');
                retenue <= '1';
            else
                bcd <= bcd + 1;
            end if;
        end if;
    end if;
end process;

 

11.5.2 Codes valides et invalides

Décimal

BCD

00000
10001
50101
91001
10Deux chiffres : 0001 0000

 

Les codes 1010 à 1111 ne représentent pas un chiffre BCD valide.

11.5.3 Chaînage de plusieurs chiffres

La retenue des unités active le compteur des dizaines. La retenue des dizaines peut activer les centaines.

Chaînage BCD — Unités  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        unites   <= 0;
        dizaines <= 0;
    elsif rising_edge(clk) then
        if tick = '1' then
            if unites = 9 then
                unites <= 0;
Chaînage BCD — Dizaines  |  VHDL
                if dizaines = 9 then
                    dizaines <= 0;
                else
                    dizaines <= dizaines + 1;
                end if;
            else
                unites <= unites + 1;
            end if;
        end if;
    end if;
end process;

 

11.5.4 Compteur 00 à 99

État

Dizaines

Unités

0000
0909
1010
4242
9999
Après 9900

 

11.5.5 Version hiérarchique

Deux instances d’un compteur BCD à un chiffre peuvent être chaînées. Le tick de retenue des unités devient l’enable des dizaines.

Instances BCD  |   VHDL
U_UNITES : entity work.compteur_bcd(rtl)
    port map (
        clk     => clk,
        reset   => reset,
        enable  => tick_1hz,
        bcd     => unites,
        retenue => tick_dizaines
    );

U_DIZAINES : entity work.compteur_bcd(rtl)
    port map (
        clk     => clk,
        reset   => reset,
        enable  => tick_dizaines,
        bcd     => dizaines,
        retenue => open
    );

 

11.5.6 Affichage décimal

Chaque chiffre BCD est converti en sept signaux correspondant aux segments a à g.

Décodeur BCD — Chiffres 0 à 4  |  VHDL
with bcd select
    segments <= "1111110" when "0000", -- 0
                "0110000" when "0001", -- 1
                "1101101" when "0010", -- 2
                "1111001" when "0011", -- 3
                "0110011" when "0100", -- 4
Décodeur BCD — Chiffres 5 à 9  |  VHDL
                 "1011011" when "0101", -- 5
                "1011111" when "0110", -- 6
                "1110000" when "0111", -- 7
                "1111111" when "1000", -- 8
                "1111011" when "1001", -- 9
                "0000000" when others;

 

11.5.7 Afficheurs multiplexés

Sur de nombreuses cartes, les segments sont partagés et un signal d’activation sélectionne rapidement le chiffre visible.

Phase

Afficheur activé

Donnée BCD envoyée

0Unitésunites
1Dizainesdizaines

 

11.5.8 Balayage à deux chiffres

Sélection du chiffre  |  VHDL
process(all)
begin
    if selection = '0' then
        bcd_affiche <= std_logic_vector(unites);
        anodes <= "10";
    else
        bcd_affiche <= std_logic_vector(dizaines);
        anodes <= "01";
    end if;
end process;

 

La polarité des anodes ou cathodes dépend de la carte. Les valeurs doivent être adaptées.

11.5.9 Fréquence de multiplexage

Le balayage doit être suffisamment rapide pour éviter le scintillement visible, mais pas inutilement rapide.

Nombre de chiffres

Fréquence globale indicative

Fréquence par chiffre

21 kHz500 Hz
42 kHz500 Hz
44 kHz1 kHz

 

11.5.10 Affichage actif à 0

Inversion des segments  |  VHDL
segments_sortie <= not segments_actifs_haut;

 

Important — Carte cible

Toujours vérifier si les segments et les sélections d’afficheurs sont actifs à 0 ou actifs à 1.

 

11.5.11 Horloge, tick et affichage

Une architecture robuste conserve l’horloge principale pour tous les processus et utilise deux enables : un tick lent pour le comptage et un tick rapide pour le balayage.

Signal

Rôle

clkHorloge principale unique
tick_1hzActive le compteur décimal
tick_scanChange l’afficheur actif
selectionChoisit unités ou dizaines

 

Méthode de conception d’un compteur

1. Définir la séquence de comptage.

2. Déterminer le modulo ou la plage.

3. Calculer la largeur minimale.

4. Choisir la politique de limite : enroulement ou saturation.

5. Définir reset, enable, load et direction.

6. Définir les sorties terminales ou ticks.

7. Écrire un processus cadencé unique.

8. Initialiser les impulsions de sortie à zéro à chaque cycle.

9. Simuler les limites et les retours à zéro.

10. Vérifier les ressources et contraintes temporelles.

Checklist de validation

  • Le compteur possède-t-il assez de bits ?
  • Le retour à zéro se produit-il au bon état ?
  • Le reset possède-t-il la bonne priorité ?
  • Le chargement est-il prioritaire sur le comptage ?
  • Le tick dure-t-il exactement un cycle ?
  • Le compteur reste-t-il stable lorsque enable vaut 0 ?
  • Les états invalides sont-ils récupérés ?
  • Une horloge secondaire a-t-elle été créée inutilement ?
  • Les afficheurs utilisent-ils la bonne polarité ?

Erreurs fréquentes

Erreur

Conséquence

Correction

Comparer à MODULO au lieu de MODULO-1Un état supplémentaireRetour à zéro à N-1
Largeur insuffisanteValeur non représentableCalculer clog2
Tick non remis à zéroImpulsion trop longueValeur par défaut à chaque cycle
Bouton utilisé sans anti-rebondComptages multiplesFiltrage et impulsion
Bit de compteur utilisé comme horlogeNouveau domaine non contraintUtiliser clock enable
Affichage BCD de codes 10 à 15Motifs invalidesLimiter à 0..9
Polarité sept segments incorrecteAffichage inverséInverser les sorties

 

Travaux pratiques

TP 8 — Compteurs et affichage

1. Objectifs du TP

  • réaliser un compteur binaire sur LED ;
  • réaliser un compteur modulo 10 ;
  • réaliser un compteur de 00 à 99 ;
  • générer un tick lent ;
  • piloter deux afficheurs sept segments ;
  • simuler tous les retours à zéro ;
  • valider le système sur une carte FPGA.

2. Organisation du projet

Arborescence proposée  |  Arborescence
tp8_compteurs_affichage/
├── src/
│   ├── compteur_binaire8.vhd
│   ├── compteur_modulo10.vhd
│   ├── compteur_00_99.vhd
│   ├── generateur_tick.vhd
│   ├── decodeur_7segments.vhd
│   ├── afficheur_2digits.vhd
│   └── top_fpga.vhd
├── sim/
│   ├── tb_compteur_binaire8.vhd
│   ├── tb_compteur_modulo10.vhd
│   ├── tb_compteur_00_99.vhd
│   └── tb_afficheur_2digits.vhd
└── constraints/
    └── carte.xdc_ou_qsf

 

3. Partie A — Compteur binaire sur LED

compteur_binaire8.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity compteur_binaire8 is
    port (
        clk    : in   std_logic;
        reset  : in   std_logic;
        enable : in  std_logic;
        q      : out std_logic_vector(7 downto 0)
    );
end entity compteur_binaire8;
compteur_binaire8.vhd — Architecture  |  VHDL
architecture rtl of compteur_binaire8 is
    signal compteur : unsigned(7 downto 0);
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= (others => '0');
        elsif rising_edge(clk) then
            if enable = '1' then
                compteur <= compteur + 1;
            end if;
        end if;
    end process;

    q <= std_logic_vector(compteur);
end architecture rtl;

 

4. Banc de test du compteur binaire

tb_compteur_binaire8.vhd — Test  |  VHDL
reset <= '1';
enable <= '0';
wait for 2 * PERIODE;

reset <= '0';
enable <= '1';

for i in 1 to 20 loop
    wait until rising_edge(clk);
    wait for 1 ns;

    assert unsigned(q) = to_unsigned(i, 8)
        report "Comptage binaire incorrect"
        severity error;
end loop;

enable <= '0';
wait until rising_edge(clk);
wait for 1 ns;

assert unsigned(q) = to_unsigned(20, 8)
    report "Le compteur doit rester stable"
    severity error;

 

5. Partie B — Compteur modulo 10

compteur_modulo10.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity compteur_modulo10 is
    port (
        clk     : in   std_logic;
        reset   : in   std_logic;
        enable  : in   std_logic;
        chiffre : out unsigned(3 downto 0);
        retenue : out std_logic
    );
end entity compteur_modulo10;
compteur_modulo10.vhd — Architecture  |  VHDL
architecture rtl of compteur_modulo10 is
    signal compteur : unsigned(3 downto 0);
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= (others => '0');
            retenue <= '0';
        elsif rising_edge(clk) then
            retenue <= '0';

            if enable = '1' then
                if compteur = 9 then
                    compteur <= (others => '0');
                    retenue <= '1';
                else
                    compteur <= compteur + 1;
                end if;
            end if;
        end if;
    end process;

    chiffre <= compteur;
end architecture rtl;

 

6. Banc de test modulo 10

Vérification du retour à zéro  |  VHDL
for i in 1 to 10 loop
    wait until rising_edge(clk);
    wait for 1 ns;

    if i < 10 then
        assert chiffre = to_unsigned(i, 4)
            severity error;
        assert retenue = '0'
            severity error;
    else
        assert chiffre = to_unsigned(0, 4)
            severity error;
        assert retenue = '1'
            report "La retenue doit signaler 9 vers 0"
            severity error;
    end if;
end loop;

 

7. Partie C — Compteur 00 à 99

compteur_00_99.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity compteur_00_99 is
    port (
        clk       : in   std_logic;
        reset     : in   std_logic;
        enable    : in   std_logic;
        unites    : out unsigned(3 downto 0);
        dizaines  : out unsigned(3 downto 0)
    );
end entity compteur_00_99;

 

compteur_00_99.vhd — Compteur des unités  |  VHDL
architecture structurelle of compteur_00_99 is
    signal tick_dizaines : std_logic;
begin
    U_UNITES : entity work.compteur_modulo10(rtl)
        port map (
            clk     => clk,
            reset   => reset,
            enable  => enable,
            chiffre => unites,
            retenue => tick_dizaines
        );
compteur_00_99.vhd — Compteur des dizaines  |  VHDL
    U_DIZAINES : entity work.compteur_modulo10(rtl)
        port map (
            clk     => clk,
            reset   => reset,
            enable  => tick_dizaines,
            chiffre => dizaines,
            retenue => open
        );
end architecture structurelle;

 

8. Banc de test 00 à 99

Test de 105 impulsions  |  VHDL
for valeur in 1 to 105 loop
    wait until rising_edge(clk);
    wait for 1 ns;

    attendu := valeur mod 100;

    assert to_integer(dizaines) = attendu / 10
        report "Dizaines incorrectes"
        severity error;

    assert to_integer(unites) = attendu mod 10
        report "Unites incorrectes"
        severity error;
end loop;

 

9. Partie D — Diviseur de fréquence

generateur_tick.vhd  |   VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity generateur_tick is
    generic (
        NB_CYCLES : positive := 50_000_000
    );
    port (
        clk   : in   std_logic;
        reset : in  std_logic;
        tick  : out std_logic
    );
end entity generateur_tick;

architecture rtl of generateur_tick is
    signal compteur : natural range 0 to NB_CYCLES-1;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= 0;
            tick <= '0';
        elsif rising_edge(clk) then
            tick <= '0';

            if compteur = NB_CYCLES-1 then
                compteur <= 0;
                tick <= '1';
            else
                compteur <= compteur + 1;
            end if;
        end if;
    end process;
end architecture rtl;

 

10. Configuration simulation et carte

Contexte

NB_CYCLES conseillé

Simulation rapide5 ou 10
Carte 50 MHz, tick 1 Hz50 000 000
Carte 100 MHz, tick 1 Hz100 000 000
Carte 100 MHz, scan 1 kHz100 000

 

Méthode — Accélération de simulation

Ne pas simuler des dizaines de millions de cycles pour vérifier la logique. Utiliser une petite valeur du générique dans le banc de test.

 

11. Partie E — Décodeur sept segments

decodeur_7segments.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity decodeur_7segments is
    port (
        bcd      : in   std_logic_vector(3 downto 0);
        segments : out std_logic_vector(6 downto 0)
    );
end entity decodeur_7segments;
decodeur_7segments.vhd — Architecture  |  VHDL
architecture rtl of decodeur_7segments is
begin
    with bcd select
        segments <= "1111110" when "0000",
                    "0110000" when "0001",
                    "1101101" when "0010",
                    "1111001" when "0011",
                    "0110011" when "0100",
                    "1011011" when "0101",
                    "1011111" when "0110",
                    "1110000" when "0111",
                    "1111111" when "1000",
                    "1111011" when "1001",
                    "0000000" when others;
end architecture rtl;

 

12. Partie F — Affichage à deux chiffres

afficheur_2digits.vhd — Sélection  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        selection <= '0';
    elsif rising_edge(clk) then
        if tick_scan = '1' then
            selection <= not selection;
        end if;
    end if;
end process;

 

Multiplexage des chiffres  |  VHDL
process(all)
begin
    if selection = '0' then
        bcd_actif <= std_logic_vector(unites);
        anodes <= "10";
    else
        bcd_actif <= std_logic_vector(dizaines);
        anodes <= "01";
    end if;
end process;

U_DEC : entity work.decodeur_7segments(rtl)
    port map (
        bcd      => bcd_actif,
        segments => segments
    );

 

13. Niveau supérieur FPGA

top_fpga.vhd — Tick de comptage  |  VHDL
architecture structurelle of top_fpga is
    signal tick_1hz  : std_logic;
    signal tick_scan : std_logic;
    signal unites    : unsigned(3 downto 0);
    signal dizaines  : unsigned(3 downto 0);
begin
    U_TICK_1HZ : entity work.generateur_tick(rtl)
        generic map (
            NB_CYCLES => 50_000_000
        )
        port map (
            clk => clk_carte,
            reset => reset,
            tick => tick_1hz
        );
top_fpga.vhd — Compteur décimal  |  VHDL
    U_COMPTEUR : entity work.compteur_00_99(structurelle)
        port map (
            clk => clk_carte,
            reset => reset,
            enable => tick_1hz,
            unites => unites,
            dizaines => dizaines
        );

    -- Une seconde instance produit tick_scan.
    -- Le bloc d'affichage utilise les deux chiffres.
end architecture structurelle;

 

14. Contraintes de carte

Exemple conceptuel  |  Contraintes
create_clock -period 10.000    -name clk_carte   [get_ports clk_carte]

set_property PACKAGE_PIN <PIN_CLK> [get_ports clk_carte]
set_property PACKAGE_PIN <PIN_SEG_A> [get_ports {segments[6]}]
set_property PACKAGE_PIN <PIN_DIGIT0> [get_ports {anodes[0]}]
set_property PACKAGE_PIN <PIN_DIGIT1> [get_ports {anodes[1]}]

 

15. Procédure de validation sur carte

1. Définir la fréquence exacte de l’oscillateur.

2. Configurer NB_CYCLES pour le tick de comptage.

3. Configurer un tick de balayage rapide.

4. Associer les segments et anodes aux broches.

5. Vérifier les polarités.

6. Synthétiser et examiner les avertissements.

7. Vérifier la contrainte temporelle de l’horloge.

8. Programmer la carte.

9. Activer le reset.

10. Observer le comptage de 00 à 99.

16. Commandes GHDL

Compilation  |   Terminal
ghdl -a --std=08 src/compteur_binaire8.vhd
ghdl -a --std=08 src/compteur_modulo10.vhd
ghdl -a --std=08 src/compteur_00_99.vhd
ghdl -a --std=08 src/generateur_tick.vhd
ghdl -a --std=08 src/decodeur_7segments.vhd

 

Simulation du compteur 00 à 99  |  Terminal
ghdl -a --std=08 sim/tb_compteur_00_99.vhd
ghdl -e --std=08 tb_compteur_00_99
ghdl -r --std=08 tb_compteur_00_99    --vcd=tb_compteur_00_99.vcd

gtkwave tb_compteur_00_99.vcd

 

17. Observations demandées

  • Observer l’incrémentation binaire sur huit bits.
  • Vérifier le retour 9 vers 0.
  • Vérifier le passage 09 vers 10.
  • Vérifier le passage 99 vers 00.
  • Mesurer la durée du tick.
  • Observer le balayage des afficheurs.
  • Vérifier l’absence de scintillement visible.
  • Comparer simulation et carte FPGA.

18. Tableau de résultats

Bloc

Test

Attendu

Observé

Conforme

Binaire20 cyclesq=20  
Modulo 1010 cycles0 + retenue  
00 à 9910 cycles10  
00 à 99100 cycles00  
TickNB_CYCLESImpulsion d’un cycle  
AffichageValeur 4242 visible  

 

19. Questions d’analyse

1. Pourquoi un compteur sur N bits possède-t-il 2^N états ?

2. Quelle différence existe entre compteur modulo N et compteur binaire naturel ?

3. Pourquoi tester MODULO-1 avant l’incrémentation ?

4. Comment déterminer la largeur minimale d’un modulo 100 ?

5. Quelle différence existe entre saturation et enroulement ?

6. Pourquoi remettre tick à zéro au début de chaque cycle ?

7. Pourquoi préférer un clock enable à une horloge divisée par logique ?

8. Comment la retenue des unités commande-t-elle les dizaines ?

9. Pourquoi faut-il accélérer le diviseur dans le banc de test ?

10. Comment adapter le décodeur à des segments actifs à zéro ?

20. Extensions proposées

  • Ajouter un comptage ascendant-descendant de 00 à 99.
  • Ajouter un chargement parallèle.
  • Ajouter un mode de saturation.
  • Créer un compteur de 000 à 999.
  • Créer un chronomètre minutes-secondes.
  • Ajouter un bouton pause/reprise.
  • Ajouter un anti-rebond.
  • Afficher la valeur hexadécimale.
  • Créer un compteur d’événements externes synchronisés.

21. Barème indicatif

Critère

Points

Compteur binaire2
Compteur modulo 103
Compteur 00 à 994
Diviseur/générateur de tick3
Affichage sept segments3
Bancs de test et assertions2
Validation sur carte2
Qualité du compte rendu1
Total20

 

Synthèse du chapitre

Notion

Résumé

Compteur ascendantAjoute 1 à chaque front autorisé.
Compteur descendantSoustrait 1.
Modulo NSéquence de 0 à N-1.
EnableAutorise le comptage.
LoadCharge une valeur parallèle.
SaturationBloque le compteur à une limite.
OverflowSignale un dépassement ou enroulement.
TickImpulsion périodique d’un cycle.
DiviseurCompte les cycles de l’horloge source.
Compteur BCDCompte de 0 à 9 par chiffre.
Chaînage BCDLa retenue active le chiffre suivant.
MultiplexagePartage les segments entre plusieurs chiffres.

 

Autoévaluation

1. Que devient un compteur 4 bits après 15 ?

Réponse : Il revient à 0 en mode enroulement.

2. Quelles valeurs parcourt un modulo 10 ?

Réponse : 0 à 9.

3. Combien de bits faut-il pour modulo 100 ?

Réponse : 7 bits.

4. Que fait enable=0 ?

Réponse : Le compteur conserve sa valeur.

5. Quelle commande est généralement prioritaire ?

Réponse : Le reset.

6. Qu’est-ce qu’un compteur saturant ?

Réponse : Il reste à la limite.

7. Quelle durée doit avoir un tick ?

Réponse : Un cycle d’horloge source.

8. Pourquoi éviter une horloge secondaire logique ?

Réponse : Elle crée un domaine mal distribué et difficile à contraindre.

9. Quel code BCD représente 9 ?

Réponse : 1001.

10. Comment afficher 42 ?

Réponse : Dizaines=4 et unités=2.

Exercice de consolidation

Concevoir un compteur modulo 60 avec reset synchrone, enable, chargement parallèle et tick lors du passage 59 vers 0.

Correction proposée  |  VHDL
process(clk)
begin
    if rising_edge(clk) then
        tick <= '0';

        if reset = '1' then
            compteur <= 0;
        elsif load = '1' then
            compteur <= valeur_chargee;
        elsif enable = '1' then
            if compteur >= 59 then
                compteur <= 0;
                tick <= '1';
            else
                compteur <= compteur + 1;
            end if;
        end if;
    end if;
end process;

 

Suite du cours — Transition vers la suite

Après les compteurs, le cours peut aborder les machines à états finis, qui utilisent des registres d’état et une logique de transition pour piloter des systèmes séquentiels complexes.