# TD5 VHDL et CAO

Nous allons dans ce chapitre explorer les liens entre VHDL et la Conception assistée par ordinateur (C.A.O. ou plus simplement CAO) puis passer en revue des exemples utilisant des séquenceurs.

# Génération automatique de Netlist

La majorité des logiciels de schéma modernes (frontal des logiciels de CAO) ont pour objectif de réaliser une liste des connexions électriques que l'on appellera netlist dans la suite de ce document. Ces netlists ont en général un format propriétaire mais certains outils sont capables générer ces netlists en VHDL. Il est ainsi possible à partir d'un schéma, de générer un circuit imprimé ou de choisir la logique programmable en utilisant des FPGAs ou ASICs.
La base du travail de l'électronicien reste donc toujours de rassembler des composants électroniques ayant une certaine fonctionnalité pour réaliser une nouvelle fonctionnalité. Nous allons présenter dans ce chapitre un certain nombre d'exemples.

# Décomposer une synthèse complexe

Lorsque l'on doit réaliser un circuit complexe, il convient en général de le décomposer le problème en deux parties : la partie de commande (ou séquenceur) et la partie opérative ou chemin de données. Si cette décomposition est classique en automatisme, elle demande une certaine expérience ici car à la différence de l'automatisme la partie opérative est elle-même composée d'un ou plusieurs circuits électroniques.
Disons pour simplifier que le niveau simple correspond à ce que l'on fait en schéma logique traditionnel : utiliser des composants simples existants et les assembler puis ajouter un séquenceur. C'est ce niveau qui va nous intéresser maintenant dans le cas très particulier où le chemin de données est réalisé par deux compteurs.

## Utilisations de compteurs

Le problème que l'on présente comme trame de travail dans cette section sera appelé compteur de passages dans toute la suite de ce chapitre. Il est décrit un tout petit peu plus loin.
Le chemin de données est constitué par un ou plusieurs compteurs. On lui ajoute une partie de commande spécifiée par un ou plusieurs diagrammes d'évolutions. Une exemple de réalisation pratique est détaillé ci-contre sous forme schématique.
Il s'agit d'un compteur de passages : deux capteurs séparés appelés Droite et Gauche font incrémenter les compteurs si des personnes (des voitures, des canettes de bière ou de tout autres objets fruit de votre imagination débordante) rentrent dans une pièce en passant d'abord par Droite puis par Gauche. Si elles sortent elles passent d'abord devant Gauche puis devant Droite et l'on décrémente les compteurs. C'est un cahier des charges pas très compliqué mais qui va nous occuper un certain temps.
Ici le chemin de données est réalisée en circuit TTL classiques et la partie commande est une GAL 20V8. Si vous disposez de FPGAs, il est possible de mettre à la fois la partie commande et le chemin de données dans ce circuit.
Remarque :
Si vous réalisez cet ensemble dans un FPGA, le compilateur doit vous donner au moins un avertissement : l'horloge est réalisée par une partie combinatoire ce qui est une pratique déconseillée.
Il est possible de contourner le problème mentionné en remarque, en s'arrangeant pour que la partie commande fonctionne sur front montant d'une horloge et la partie opérative sur front descendant de cette même horloge. Le compteur possédera alors une entrée de validation pour compter, que la partie commande mettra à un ou pas, pour autoriser un comptage/décomptage. Cette technique pourra être utilisée dès que la partie commande et la partie opératives sont toutes deux séquentielles. Cette façon de faire n'est pas considérée par tout le monde comme une bonne méthode ; un méthode consistant à utiliser des fronts d'horloge identiques pour les deux parties est explorée dans un cours de la WIKIVERSITE, et particulièrement au chapitre 4, tandis qu'une méthode utilisant la même horloge mais avec une sensibilité aux fronts différente est explorée dans la section suivante.
Le niveau de cette section sur la décomposition en chemin de données et séquenceur est relativement élevé. Plutôt que de la terminer ici, j'ai décidé de poursuivre ce travail dans un Cours WIKIVERSITE sur VHDL dans le chapitre VHDL et machines à états algorithmiques.
Nous allons maintenant plutôt nous concentrer sur les cas où la décomposition en deux parties est immédiate et donc va de soi.

# Conceptions autour de séquenceurs

La série d'exemples donnée ci-dessous a comme objectif de préparer le lecteur à des assemblages de composants autour de divers séquenceurs. Les schémas sont donnés pour expliquer ce que l'on veut faire, mais il vous faudra faire un effort de concentration pour bien les comprendre. Parfois on donnera quelques indications VHDL mais il nous semble bien plus important à ce niveau de raisonner sur les schémas et de bien comprendre le rôle du séquenceur.

## Le compteur de passages revu et corrigé

Le compteur de passages déjà présenté, est un dispositif destiné à compter des personnes qui entrent dans un pièce, sachant qu'elles ont la possibilité de sortir. Il est naturellement possible de compter des voitures qui entrent et sortent d'un parking avec le même principe.
Un ensemble de deux capteurs infrarouge perçoit le passage d'une personne et en fonction de son sens de passage incrémente ou décrémente un compteur qui décodé affiche le nombre de personnes sur deux afficheurs 7 segments. C'est ce que l'on a déjà nommé compteur de passage et que l'on continuera à nommer ainsi.
Nous avons eu l'occasion d'exprimer des doutes sur la création d'une horloge par le séquenceur. Si l'on veut éviter cela il nous faut une architecture du type :
Remarque : on notera sur cette figure que l'horloge du séquenceur et l'horloge du chemin de données (compteur/décompteur) est la même, mais que ces deux parties sont sensibles à des fronts d'horloge différents (à cause de l'inverseur en bas). Cet inverseur peut être retiré, ce qui est d'ailleurs considéré comme une bonne pratique dans les FPGA, mais une mauvaise pratique si on le garde.
La partie notée compteur/décompteur est en fait un peu plus complexe qu'un simple compteur/décompteur puisqu'elle sort directement sur deux afficheurs sept segments. Cela signifie qu'elle comporte un décodeur, mais ces détails ne nous intéressent pas pour l'instant. Par contre il est important de savoir que :
* "en" est une entrée de validation (enable en anglais) : lorsque en=1 les fronts d'horloge font bouger le compteur
* "du" est un diminutif de "down/up" que l'on traduit par comptage (up) et décomptage (down). Pour information, nous prendrons la convention suivante du=1 on compte (un compteur BCD cascadable est présenté dans un autre chapitre).
Il nous faudra comme composants :
* deux compteurs-décompteurs décimaux (cascadés) ainsi que la logique d'affichage capable de transformer la valeur BCD des deux compteurs en affichage sept segments.
* une logique de contrôle pour détecter le sens de passage
Seule la logique de contrôle est spécifiée à l'aide d'un graphe d'évolution.

### Travail sur le compteur de passages

* Que se passe-t-il si Droite et gauche arrivent en même temps ? Modifier le graphe d'évolution pour gérer seulement le cas Droite='1'&Gauche='0' d'une part et Gauche='1'&Droite='0' d'autre part.
* Chercher le graphe d'états correspondant au problème à partir du graphe d'évolution donné.
* Écrire les équations de récurrence faisant intervenir l'entrée d'initialisation "Init".
* On cherche maintenant à réaliser ce même séquenceur mais en donnant la possibilité aux personnes de faire demi-tour entre les deux capteurs (c'est à dire de changer d'avis).
* Montrer qu'un graphe d'état de 9 états est nécessaire pour gérer ce problème. Écrire les équations de récurrence ou implanter avec un "case when" puis tester.
* Si droite et gauche arrivent en même temps on part en principe dans deux états S1 et S3 ce qui n'est pas possible. Le fait que cela ne soit pas possible implique que la logique qui a été générée considère un des deux états comme prioritaire. Il s'agit en principe de celui qui est présenté le premier dans le programme VHDL.
* Le graphe d'états a exactement la même allure, le même nombre d'états : seules les transitions sont différentes parce que réceptives (barrée, ce qui évite les transitions de l'état sur lui-même)

### Éléments d'implantation en VHDL

Nous allons donner un certain nombre d'indications pour une implantation complète dans un FPGA dans cette section. Les compteurs ne seront donc plus externes et devront être décrit et câblés en VHDL.
Commençons donc par le compteur BCD cascadable
On a laissé un certain nombre de points d'interrogations à remplir par le lecteur.
Nous allons maintenant nous intéresser au séquenceur.
où encore une fois on a laissé un peu de travail au lecteur.
Le transcodeur peut être trouvé ailleurs pour un fonctionnement led allumée si entrée correspondante à 0.

## En aura-t-on fini un jour avec ce compteur de passages ?

Nous allons ajouter une liaison RS232 à ce compteur de passages. Il nous faudra donc un hyperterminal pour voir s'afficher les nombres à l'intérieur. L'intérêt de cet exemple est de présenter un autre séquenceur (que celui du compteur de passage). Nous allons commencer par présenter l'ensemble complet.

### Insérer un composant pour gérer la RS232

La RS232 est décrite ailleurs (Wikiversité) mais nous tenons à dire avant de continuer qu'il est inutile de comprendre la liaison en question dans tous ses détails pour comprendre la problématique évoquée ici.
Comme tout dessin nous avons suivi un certain nombre de conventions pour le réaliser.
* le rectangle gris représente ce que nous avons l'intention d'étudier
* tous les fils de connexions sont volontairement représentés en rouge et certaines connexions ont été omises pour simplifier le dessin. Ces fils seront des signaux dans le programme VHDL.
D'autres parties ont été ajoutée par rapport à l'exercice précédent : un diviseur de fréquence par exemple. Il a une double fonction :
* ralentir l'horloge du séquenceur pour éviter les rebonds mécaniques des interrupteurs
* ralentir le comptage BCD. Il n'y a aucune raison physique à cela, mais les compteurs et le séquenceur doivent fonctionner sur la même horloge (avec sensibilité différente)
* réaliser un signal qui sert à multiplexer les deux afficheurs. Il n'y a en effet qu'une entrée sept segments pour les deux afficheurs et l'afficheur où va finir cette donnée est choisi avec la sortie "selaff".

### Construire ce composant

Le composant présenté par le rectangle gris est maintenant bâti petit à petit.

#### Générateur de bauds

La liaison série est une liaison sans horloge. Elle est cependant caractérisée par une vitesse de transmission. Nous désirons transmettre à 19200 bauds. Pour cela il nous faut réaliser un signal 16 fois plus rapide que la fréquence correspondante soit 307200 Hz.
Si l'on dispose d'une horloge à 50 MHz jusqu'à combien devra compter le compteur pour réaliser cette fréquence de 307200 Hz ? Combien de bit aura ce compteur ?
Réponse : M=163 et N= 8 bits.

#### Construction de la liaison série

Lire Utiliser les logicores Xilinx pour une réalisation très orientée Xilinx. Un schéma nous rappelle comment tout cela fonctionne :
Lorsque l'entrée "write_buffer" passe à un, on écrit les données dans "data-in" et la partie matérielle se débrouille alors pour les envoyer sur la RS232.
A ce point nous allons réaliser le schéma ci-dessous :
Rappelons-nous que ce composant est inséré dans un autre non représenté... et donc que les entrées rouges sont des signaux. La donnée qui est envoyée sur la RS232 est un nombre fixe correspondant au caractère 'A'.
Comment régler votre hyperterminal ?
Réponse : gtkterm (sous LINUX) est configuré comme ci-dessous (à part peut-être le PORT) :
PORT :/dev/ttyS1, vitesse : 19200, Parité : odd, Bits 8, Bit de stop : 1, contrôle de flux : none
Expliquer pourquoi plusieurs 'A' sont reçus dans l'hyperterminal
Réponse : parce que le signal "en" est un signal lent et qu'ainsi il resta à un pendant plusieurs périodes de l'horloge du logicore de transmission (qui elle est à 50 MHz).

#### Connexion au compteur de passages... et hop encore un séquenceur

On va donc ajouter un séquenceur qui va agir sur une partie combinatoire que l'on commence à présenter.

##### Partie combinatoire

Le but de la partie combinatoire est de calculer la parité et de gérer ce que l'on envoie suivant deux entrées appelées mux1 et mux2.
Le caractère '0' a comme code ASCII "0110000" sur 7 bits soit "00110000" sur 8 bit. On peut ainsi construire simplement les codes ASCII des chiffres de '0' à '9' : "0011" suivi du chiffre sur 4 bits.
Puisque l'on a choisi une parité impaire (odd en anglais) elle est calculée en inversant un ou exclusif sur tous les 8 bits. Nous pouvons remarquer que le ou exclusif de "0011" (partie constante de l'octet) est 0 et qu'ainsi seuls les 4 derniers bits sont nécessaires pour le calcul de cette parité.
Voici schématisé le travail réalisé par ce composant combinatoire dans la figure ci-contre (partie droite).
Il vous montre comment sont construits les neuf bits à envoyer, comment est calculée la parité et comment le dernier multiplexeur permet de choisir entre une valeur fixe (ici un retour chariot qui nécessitera de choisir dans configuration, CR/LF auto pour le GTKTerm) et une autre valeur.

##### Séquenceur

Le séquenceur met en œuvre le composant combinatoire en positionnant correctement mux1 et mux2. Comme d'habitude, nous présentons le graphe d'évolution de ce séquenceur (figure ci-contre).
Comme nous l'avons vu, "en" est un signal très lent et le séquenceur attendra que ce signal repasse à zéro pour revenir dans son état initial S0.
* En S1 Mux1 passe à 1 car on a l'intention d'envoyer les dizaines en premier.
* En S2, puisque "write_buffer" passe à 1 cela veut dire que l'on écrit dans le buffer
* En S3 Mux0 passe à 1 car on a l'intention d'envoyer les unités maintenant.
* En S4, puisque "write_buffer" passe à 1 cela veut dire que l'on écrit dans le buffer
* En S5 Mux2 passe à 1 pour envoyer le retour chariot (RC dans la suite)
* En S6 c'est là que l'on envoie véritablement ce RC... puis on reste bloqué en S7 tant que en (signal lent) ne repasse pas à 0.
A ce point ce que nous avons réalisé peut se schématiser ainsi :
... et seul un décalage de '1' entre la valeur affichée sur les deux digits et la valeur reçue dans l'hyperterminal nous indique une petite erreur.

##### deuxième séquenceur

Le défaut constaté dans la section précédente est dû au fait que "en" arrive trop tôt. En effet, quand il est positionné à 1 le compteur sera incrémenté seulement sur le front suivant (de l'horloge très lente), c'est à dire quand "en" retombe à 0.
Le nouveau séquenceur partira encore avec "en", mais attendra qu'il repasse à 0 avant de commencer à envoyer dans la RS232.

##### Indication VHDL

Le programme VHDL correspondant au schéma ci-dessus aura bien évidemment la structure suivante :
La réalisation de cet ensemble se fait de manière systématique. Ce qui peut vous surprendre à ce stade, c'est le "compteur modulo" que nous n'avons pas détaillé outre mesure.

## Et un petit dernier séquenceur pour la route...

Nous allons utiliser un séquenceur dans un tout autre contexte pour terminer cet ensemble d'exemples. L'envoi de données par la RS232 va maintenant servir à remplir une mémoire RAM avec des valeurs prédéterminés. Ces valeurs seront ensuite envoyées sur des LEDs pour réaliser divers chenillards à l'aide d'un compteur.

### Travail préliminaire

On désire réaliser un ensemble comprenant un diviseur de fréquence (déjà rencontré), un compteur 4 bits binaire possédant une entrée "en" de validation" et une entrée "init" (devinez pourquoi faire ?) et une mémoire.

#### Préparation

Pour éviter le changement des options de compilation (décrit dans une autre section) pour qu'ISE infère correctement une mémoire BRAM (Block RAM) nous allons utiliser un composant faisant partie des librairies Xilinx. Ce composant s'appelle RAM16X8S. Le meilleur moyen d'en trouver la documentation est le suivant :
* ouvrir une page blanche de schématique (qui ne nous servira pas par la suite en fait)
* chercher ce composant dans la catégorie mémoire et le dessiner sur la page blanche
* click droit sur le composant dessiné, puis "Object properties", puis "Symbol Info"
Vous avez en bas de la page de la documentation un exemple d'instanciation de ce composant en VHDL (puis en verilog, ce qui ne nous intéresse pas). Ce que l'on vous demande de faire à ce stade est de comprendre comment cette RAM est initialisée.
* fermer la page schématique sans enregistrer mais garder la documentation ouverte.
Calculer les valeurs prédéfinies à mettre dans la RAM pour réaliser un chenillard aller et retour d'une LED.

#### Travail à réaliser

Malheureusement le travail décrit ci-dessous est très lié au fabricant Xilinx. Si quelqu'un peu faire un travail similaire chez Altera ce sera sans refus.
On vous demande d'importer le diviseur de fréquence de ce qui a été fait précédemment, de réaliser un compteur sur 4 bits avec entrées "init" et "en".
Ajouter la mémoire RAM16X8S (dépendant de Xilinx) correctement initialisée, sans oublier les lignes :
avant votre entité "Travail_A_Realiser".
Le schéma de principe est présenté ci-dessous :
Cet ensemble est donc destiné à réaliser un chenillard visuel. C'est pour cela qu'on utilise un diviseur de fréquence : notre œil ne perçoit rien au-delà de 20Hz). Le compteur est destiné à générer les adresses (de la mémoire RAM) qui sort une suite de valeurs destinées à l'effet visuel.

### Modification du contenu de la RAM

On désire maintenant s'intéresser à la possibilité de modifier le contenu de cette mémoire à l'aide d'une liaison RS232. Par rapport au TP sur le compteur de passage, la liaison devra être dans l'autre sens : c'est le PC qui maintenant va fournir des valeurs au FPGA qui finiront dans une RAM (le problème des RAM dans les FPGAs est évoqué dans une autre section du chapitre 2). La réalisation de cette liaison se fait pratiquement comme dans la section précédente ("_rx" au lieu de "_tx") :
Un séquenceur sera responsable dès qu'il y a une donnée reçue ("buffer_full") de lire "data_out" et de l'écrire dans la mémoire puis d'incrémenter un compteur (d'adresses). Nous aurons en final deux compteurs : un pour lire le contenu de la mémoire et un pour écrire. Ils devront donc se partager le bus d'adresse, cela pourra se faire avec un multiplexeur dont l'entrée sera gérée par le séquenceur. Quand le séquenceur sera en train d'écrire dans la mémoire, il devra bloquer le compteur de lecture.

#### Travail à réaliser

Comme habituellement le travail à réaliser vous est donné sous forme schématique. Vous devez l'implanter en VHDL avec des PORT MAP.
Il y a beaucoup de travail, mais si vous respectez les noms des signaux (en rouge) et les noms de chacune des entrées et sorties de chaque bloc, le travail se fait rapidement. Les deux compteurs du haut sont identiques et sont simplement les compteurs de l'exercice précédent de ce document.
Le multiplexeur est classique et déjà présenté, mais vous avez une chance sur deux de le faire fonctionner comme il faut : comme le séquenceur ci-dessous vous l'indique, quand sel = 1 vous lisez e0 pour l'amener sur la sortie du multiplexeur !
Le plus long à concevoir est indiscutablement le séquenceur. Nous avons un petit peu hésité à le donner, il aurait été intéressant de vous le faire chercher. Son principe est de lancer le remplissage de la mémoire dès que le buffer est plein. Cette technique fonctionne correctement parce que nous avons justement 16 valeurs à envoyer à la RAM. Si vous avez moins de 16 valeurs à envoyer, n'attendez pas que le buffer soit plein car il ne le sera jamais. Vous pouvez commencer à lire dès qu'il y a une valeur dans le buffer, technique qui doit aussi être utilisée dans le cas où il y a plus de 16 valeurs. Dans ces cas, un des problème est de savoir quand la transmission est finie.
Une fois que toute la partie matérielle est en place, il vous faut réaliser un fichier binaire de 16 valeurs pour remplir la RAM. On vous propose le fichier C++ suivant :
que l'on compile avec g++ tpmemo.cpp -o tpmemo
On le lance alors avec ./tpmemo :
Tester avec gtkterm configuré comme ci-dessous :
PORT :/dev/ttyS0, vitesse : 19200, Parité : odd, Bits 8, Bit de stop : 1, contrôle de flux : none
Fichier -> envoi de fichier brut en cherchant votre fichier binaire créé.
Si tout fonctionne, vous obtiendrez un nouveau chenillard.