Le guide du rootard VHDL

Les questions récurrentes 

Avant propos

Le langage VHDL pose des problèmes car il est double. Il y a l'excellent descripteur de comportements logico-temporels dont la finalité est la construction de modèles validés par la simulation et il y a le descripteur de circuits qui est en fait un spécificateur pour le synthétiseur. Si le code synthétisable est toujours simulable, l'inverse n'est pas vrai et là réside la principale difficulté.
Cet ensemble de questions-réponses s'adresse à ceux qui veulent construire des circuits de qualité. Après plus de 10 ans de pratique auprès de débutants, c'est toujours les mêmes questions qui posent problème. Cela oblige à rappeler avec force quelques principes simples.
 S'imposer quelques règles rigides lorsqu'on débute donnera de bonnes habitudes et l'on pourra toujours avec l'expérience assouplir ces principes.
Il n'est pas question pour moi de récrire mon cours de VHDL. Chaque fois que cela sera nécessaire, j'y ferrais cependant référence.

Concevoir un circuit ou ce que VHDL ne sait pas faire

Comme dans tout projet, la qualité provient avant tout de l'analyse qu'on a pu faire du problème posé. Plus celle-ci est approfondie, plus la solution technique devient évidente. Cela s'appelle structurer le projet c'est à dire le décomposer jusqu'à voir apparaître des fonctions bien connues. Dans le cas du VHDL cette granularité est bien représentée par un compteur ou une mémoire.
Ce que ne peut pas faire VHDL à votre place, c'est structurer le circuit. Malheureusement les synthétiseurs essaient d'être de plus en plus "intelligents" et bien souvent vont trouver une solution à partir des fichiers sources qui leur est proposé. Pour un circuit pas ou peu structuré, une solution pourra être trouvée mais qu'en est-il de sa qualité (surface, vitesse,documentation, re-utilisation) résultante ?

     ==> "Il faut STRUCTURER"

Concevoir synchrone

Écriture standard

 L'horloge choisie doit être le signal le plus rapide du système c'est à dire celui qui donnera le temps minimal d'échantillonnage des autres signaux (qualifiés de lent).     ==> UNE SEULE HORLOGE
Chaque fois que l'on écrit h'event  AND..., le synthétiseur comprend que h est une horloge.

Si le circuit est synchrone, tous les processus avec mémorisation par registres devront s'écrire:

    synchrone: PROCESS
        BEGIN
            WAIT UNTIL rising_edge (clk); -- c'est donc synchrone de clk
            IF clear = '1' THEN
                 q <=  OTHERS => '0';    -- remise à zéro synchrone
            ELSIF  condition_logique THEN   -- Entrée Enable des bascules D
                q <= entree_particuliere;
            END IF;
        END PROCESS;

Remarques:

Traiter le front d'un signal lent

On a souvent besoin de de traiter l'événement que constitue un front (montant ou descendant) d'un signal quelconque (donc lent par rapport à l'horloge). Pour rester conforme à la contrainte du synchronisme, il faut donc créer pour le signal lent une détection de front. On échantillonne le signal, on mémorise sa valeur, une succession 01 détecte un front montant, une succession 10 détecte un front descendant.

 
Detection: PROCESS
        VARIABLE detect : std_logic_vector(1 DOWNTO 0);
        BEGIN
            WAIT UNTIL rising_edge (clk); -- c'est donc synchrone de clk
              front_montant <= '0';  front_descendant <= '0' ;
             detect(1) := detect(0);
             detect(0) := signal_lent;
             IF detect = "01" THEN
                         front_montant <= '1';
             END IF;

             IF detect = "10" THEN
                        front_descendant <= '1';
            END IF;

        END PROCESS; 

Observation:

Séparer au maximum le combinatoire et le séquentiel

Si dans un processus, on constate un grand nombre d'imbrications de IF (plus de 3) il faut se poser la question de la simplification éventuelle de cette écriture.

WAIT UNTIL rising_edge (clk); 
  IF condition1 THEN ...
        IF condition 2 THEN ...
            IF condition 3 THEN ...

On a ici associer à une fonction de mémorisation une fonction combinatoire de condition1, condition 2 etc...
Si cette fonction est combinatoire a t-on décrit toutes les combinaisons possibles ?  Cette fonction si elle est trop complexe va créer des retards importants et faire chuter la fréquence max imale du circuit. Enfin, pour la lisibilité de la description, n'aurait-on pas eu intérêt à séparer cette partie combinatoire de la partie purement séquentielle.

Un circuit combinatoire

On privilégie toujours l'écriture en instruction concurrente. C'est plus économique en nombre de lignes et c'est surtout plus lisible.  (voir cours VHDL  pages 75 et suivantes)
    S1 <=   e1 OR (e2 AND NOT e3);  -- une équation

Un contre - exemple

     faux_combi : Process ( e1) --ERREUR
            BEGIN
                IF (e1 OR (e2 AND NOT e3)) = '1' THEN
                        s1 <= '1';
                ELSE
                        s1 <= '0';
                END IF;
            END PROCESS;

observations:
vrai_combi : Process  
            BEGIN
                WAIT ON e1, e2, e3;  -- toutes les entrées puisque c'est combinatoire
                IF (e1 OR (e2 AND NOT e3)) = '1' THEN
                        s1 <= '1';
                ELSE
                        s1 <= '0';
                END IF;
            END PROCESS;

Le choix des bibliothèques et des types

Cette question me semble d'une grande importance pour le débutant. Dans ce domaine, la facilité d'utilisation peut entraîner de grandes déconvenues.
Après consultation du site IEEE, je conseille les bibliothèques suivantes (voir cours VHDL pages 40 et suivantes):

    LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    USE IEEE.NUMERIC_STD.ALL;
     SIGNAL   niveau_entity : std_logic_vector(31 DOWNTO 0);
     SIGNAL   niveau_architecture : signed(31 DOWNTO 0);

      BEGIN
             niveau_entity <= std_logic_vector( niveau_architecture - 212); --exemple de cast et d'opération mixte

EXEMPLE d'erreur souvent rencontrée:

             un: Process
                WAIT until rising_edge(clk);
                IF condition1 THEN
                        s <=  une_certaine_equation; -- premiere affectation de s
                END IF;
                END PROCESS;

        deux: Process
                WAIT until rising_edge(clk);
                IF condition2 THEN
                        s <=  une_autre_equation; -- deuxieme affectation de s
                END IF;
                END PROCESS;

observations:
  Si le type de s est std_ulogic, une erreur va apparaître lors de la synthèse (compilation) du circuit. Si le type est std_logic, il n'y a pas d'erreur car on a décrit un bus (avec un seul fil)
                  correct: Process
                WAIT until rising_edge(clk);
                IF condition1 THEN
                        s <=  une_certaine_equation;
               ELSIF condition2 THEN
                        s <=  une_autre_equation;
                END IF;
                END PROCESS; 

    ==> Réserver le type std_logic exclusivement au bus, autrement utiliser std_ulogic