# Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/TP 4

L'objectif de ce troisième TP est enfin de lire les scan-codes d'un clavier. Nous allons construire petit à petit un ensemble de composants capable de cet exploit en partant du travail déjà réalisé dans le TP précédent. Nos techniques de conceptions seront identiques, c'est-à-dire facile à comprendre mais pas très professionnelles. Pour rendre ce travail un peu plus conformes aux règles de construction des horloges, nous avons ajouté en fin de ce chapitre une section spécifique (Mise en conformité des horloges). Cette section aura toute son importance pour des élèves ingénieurs mais pourra être laissée de côté par les étudiants L1 et L2.

## Introduction

On a retenu du TP précédent que l'appui sur une touche était compliqué car on recevait plus de fronts d'horloge (qui étaient comptés dans le TP précédent en question ) que prévu. On en attendait 11 et finalement on en recevait 33.
Techniquement cela peut s'expliquer par le fonctionnement du clavier. Lorsqu'on appui sur une touche, on finit par la relâcher et c’est ce relâchement qui envoie lui aussi des codes. On résume ci-dessous le fonctionnement des claviers à travers l'histoire (tiré de Wikipédia anglais) adaptée au clavier AZERTY :
IBM PS/2 scancodes (Clavier AZERTY) :
| Touche | set 1 (IBM PC XT) | set 1 (IBM PC XT) | set 2 (IBM PC AT) | set 2 (IBM PC AT) | set 3 (IBM 3270 PC) | set 3 (IBM 3270 PC) |
| Touche | Appui | relâchement | Appui | relâchement | Appui | relâchement |
| Q (lettre normale) | 1E | 9E | 1C | F0 1C | 1C | F0 1C |
| Retour Chariot / Entrée (clavier principal) | 1C | 9C | 5A | F0 5A | 5A | F0 5A |
| Retour Chariot / Entrée (clavier numérique) | E0 1C | E0 9C | E0 5A | E0 F0 5A | 79 | F0 79 |
| Touche Windows gauche | E0 5B | E0 DB | E0 1F | E0 F0 1F | 8B | F0 8B |
| Touche Windows droite | E0 5C | E0 DC | E0 27 | E0 F0 27 | 8C | F0 8C |
* On s'intéressera par la suite au clavier "set2 (BM PC AT)" du tableau ci-dessus.
Un article en anglais détaille les scancodes du clavier QWERTY. Notez que les scancodes codent la position des touches et par conséquent passer du clavier QWERTY au clavier AZERTY ne présente aucune difficulté.

## Lecture partielle d'un clavier PS/2

L'objectif de ce TP est de réaliser la lecture des codes (scancodes) d'un clavier sur un port PS/2. À la différence du TP précédent, on doit retrouver maintenant le scancode de la touche appuyée et non plus le nombre de fronts d'horloge que cet appui a généré.
Nous allons compléter le TP précédent, c'est-à-dire utiliser l'horloge échantillonnée (notée "Sortie" dans le schéma ci-dessous) comme horloge du compteur et du registre à décalage. Cette façon de faire n’est pas bonne en tous points, mais elle est simple et conduit à un séquenceur à trois états (schématisé par un GRAFCET dans la figure ci-dessous).
On parle de lecture partielle car notre montage final sera incapable :
* de savoir si ce qu’il affiche est un scancode d'appui ou de relâchement
* de faire la différence entre une touche normale et une touche étendue (qui envoie 0xE0 en plus)

## Travail à réaliser et sa correction

Comme dans les travaux précédents le travail à réaliser est donné sous le forme d'un schéma.

### Schéma à réaliser

On donne le schéma global de ce que vous allez réaliser ci-dessous. Comme d'habitude le travail à réaliser est présenté dans un rectangle bleu clair. Ce rectangle permet de déterminer en un seul coup d'œil les entrées et les sorties. Le rectangle gris représente quant à lui le travail déjà réalisé dans le TP1.
Par rapport au TP précédent on garde le compteur sur 8 bits mais il est actif sur front descendant maintenant, et toujours commandé par « sortie » qui commande aussi un registre à décalage de 11 bits. Après un transfert correct on trouvera dans ce registre les 8 bits de données, le bit de start, le bit de stop et le bit de parité. Seuls les 8 bits intéressants (Q1 ... Q8) seront transférés dans le registre d'affichage. On a ajouté d’autre part un circuit combinatoire détectant l'état cmpt=11. Sa sortie est utilisée par un séquenceur décrit par un grafcet. Son objectif est qu'une fois les 11 fronts d'horloge détectés, le transfert est lancé pour les 8 bits intéressants dans le registre d'affichage, puis il remet le compteur à 0 afin d'attendre le début d'un autre transfert.
La transformation d'un GRAFCET en programme VHDL est examinée dans un autre projet qu’il vous est fortement conseillé de parcourir.
Il est clair que la synthèse demandée est complexe. Comme toute synthèse complexe elle peut être décomposée en deux parties distinctes (voir ici pour des compléments) :
* un séquenceur
* une partie opérative ou chemin de données
On remarquera que puisque ces deux parties utilisent une horloge commune, le séquenceur fonctionne sur front montant tandis que la partie opérative fonctionne sur front descendant.
Aucun test de parité n'est réalisé dans cette version. Les évolutions possibles sont donc ce test de parité et un séquencement un peu plus proche de l'état du bus.
Quelques scancodes :

#### scancode hexadécimal pour clavier AZERTY

| Touche | Scancode | Touche | Scancode | Touche | Scancode |
| b | 0x32 | d | 0x23 | f | 0x2B |
| g | 0x34 | h | 0x33 | j | 0x3B |
| q | 0x1C | s | 0x1B | t | 0x2C |

### Éléments de VHDL donnés pour commencer votre travail

L'entité globale est :
L'ensemble des composants à réaliser est maintenant :
On utilisera complètement le travail fait en TP 1 et partiellement celui de TP 2.

### Questions

Relever certains scancodes en plus de ceux fournis en exemple pour les tests, et en particulier celui des flèches directionnelles.
Les flèches directionnelles (pas celles du clavier numérique) ont comme scancode :
* gauche : Ox6B
* droite : 0x74
* haut : 0x75
* bas : 0x72
Elles envoie toutes le code 0xE0 avant le scancode. On donne sans plus de commentaires la solution du TP 3 ici :
auquel on ajoute l'entité et l'architecture du TP 1.
Pour le fichier ucf, peu de modifications :

## Travail à réaliser sans sa correction

La correction donnée ci-dessus ne correspond pas tout à fait au schéma.

### Ajout du registre d'affichage

La correction donnée fonctionne correctement mais on s'est passé du registre d'affichage.

#### Exercice

On vous demande de créer et d'ajouter ce registre d'affichage.
Ce registre d'affichage n’est pas nécessaire sauf si on a l'intention d'étendre les fonctionnalités de notre lecture des codes. Ceci sera demandé dans la prochaine section.

### Gestion des scancodes 16 bits

On vous demande de modifier le séquenceur (GRAFCET) pour gérer l'envoi des scancodes sur deux octets ainsi que leurs affichage. Le principe est le suivant :
* ajouter un deuxième registre d'affichage
* modifier le GRAFCET de séquencement
Le principe du GRAFCET est le suivant :
* retirer l'action de reset de la troisième étape
* à partir de la troisième étape, on attend environ 100 ms et on va dans une quatrième étape qui réalise le reset et boucle au début du GRAFCET
* l'autre branche de la troisième étape sera conditionnée par compteur=12 puis on attend compteur=22 pour faire un transfert dans le deuxième registre d'affichage, puis le reset.
Un schéma sera plus parlant :
Dans cette figure on a pris soin de montrer en rouge les changements matériels auxquels il faut ajouter naturellement le GRAFCET. Dans le GRAFCET il faut utiliser un timer. Le timer est laissé sous la forme d'une variable "t" dans le GRAFCET. On a déjà dit qu’il est bon de choisir une valeur proche de 100 ms. Cela sous-entend donc que si un deuxième octet est envoyé par le clavier ce sera fait en moins de 100 ms.
Nous montrons dans la solution partielle ci-dessous comment il est possible de réaliser cela.
Évidemment le composant de comparaison est à changer :
On vous montre comment éviter de réaliser trop de composants : GRAFCET + TIMER. On peut utiliser la technique à deux process différents dans un même composant :
Ce sera tout pour nos indications.
Estimez la valeur "t" du timer qui a été choisie dans la solution ci-dessus.

## Mise en conformité des horloges

Cette section est difficile à comprendre. Même si elle ne fait intervenir que des notions du niveau indiqué, il est conseillé d'avoir du recul sur les notions présentées pour bien assimiler ce qui suit. Cependant, ce contenu n'est pas fondamental et peut être sauté en première lecture.
Nous avons traité dans le chapitre VHDL et machines à états algorithmiques le problème des horloges. Dans ce chapitre (et le précédent) nous avons utilisé sans discernement des horloges sur front montants et des horloges sur fronts descendants parce qu’il est souvent plus facile d’en comprendre le fonctionnement. Disons que cette facilité de compréhension est effective si vous séparez correctement le séquenceur et la partie opérative (ou chemin de données) ce qui n'a pas toujours été fait. Qu'en est-il du filtrage des fronts de l'horloge PS2 par exemple ?
Si l’on veut faire ce travail dans les règles de l'art avec des exigences professionnelles il serait certainement plus judicieux de n'utiliser que des horloges sur front montant.
Un essai rapide m'a permis de constater que changer tous les fronts d'horloge descendants en fonts montants ne perturbait pas le fonctionnement. Autrement dit le travail précédent de ce chapitre fonctionne encore si toutes les sensibilités d'horloges sont changées pour des fronts montants. Pourtant, pour ne prendre aucun risque, nous allons construire un nouveau recepteur PS/2 de A à Z en essayant à toute étape de respecter les règles d'horloges.

### Graphe d'états

Rappelons que le meilleur moyen de spécifier un montage qui respecte les horloges sont les (en) FSMD (Finite State Machine with Data path) utilisés par les anglo-saxons. Nous, Français, sommes plus habitués au GRAFCET et à sa version simplifiée : le graphe d'états (lire ou relire le chapitre description par graphe d'état du cours de logique séquentielle). La particularité de ce graphe d'états est que les actions seront elles aussi séquentielles et c’est ce fait que leur programmation en VHDL sera parfois délicate.
Nous allons commencer par présenter notre problème sous la forme d'un graphe d'états.
Cette figure est facile à lire et nous l'implanterons telle quelle. Il faut noter cependant qu’il est possible de retirer tous les états qui précèdent les transitions "=1", c'est-à-dire qui se font systématiquement. Comme nous allons utiliser une horloge à 50 MHz nous n'avons pas besoin de faire cette optimisation : cela a l'avantage de simplifier l'écriture VHDL (mais l'inconvénient de consommer un peu plus de puissance car on commute plus de bascules !).
Ce qui peut choquer le lecteur qui a suivi ce que nous avons déjà fait est le n initialisé à 9 alors que nous avions un compteur qui comptait jusqu'à 11 ! Voici les explications :
* quand vous passez de "e0" vers "e1" vous avez eu un front d'horloge ps2 et vous initialisez seulement votre compteur à 9 (cela fait donc 10 fronts)
* la sortie de l’ensemble des états "e5" "e2" "e3" se fait quand n=0 mais en passant par la transition e2 → e3 qui détecte un front supplémentaire... et voila les 11 fronts descendants de l'horloge PS2.
La construction de ce genre de graphe d'états demande donc une certaine expérience : l'initialisation du compteur à 11 serait catastrophique !
Le graphe d'état passe sous silence la détection du front (variable "fall_edge") et nous allons commencer par sa construction.

### Réalisation VHDL

Nous allons présenter étape par étape la réalisation du graphe d'états de la section précédente. Nous allons commencer par la détection des fronts descendants de l'horloge PS2, puis ajouter le séquenceur pour enfin ajouter à tout cela le chemin de données. Notre entité est :

#### Réalisation de la détection de front

Si l’on veut que la détection de front ne dure qu'une période d'horloge, on peut faire comme ci-dessous :
Cette partie est séquentielle et ne fonctionne que sur des fronts d'horloge montants.

#### Réalisation du séquenceur

Le séquenceur va être réalisé avec deux process. Cette façon de faire a été peu décrite dans ce document mais elle est tellement commune que nous laissons le soin au lecteur de trouver des documents expliquant ce style de programmation.
Puisque nous ne nous intéressons qu'au séquenceur, nous retirons tout ce qui concerne les actions. Ceci sera ajouté en section suivante.

#### Réalisation de l’ensemble

Dans cette section, nous allons assembler le code des deux sections précédentes à la description du graphe d'états pour en faire un ensemble complet avec six états. Nous présenterons ensuite la version en trois états qui a fortement inspiré ce code VHDL.
Bien sûr ce code nécessite au minimum l'ajout d'un registre d'affichage et l’affichage sur au moins deux afficheurs sept segments comme cela a été fait au TP1.

#### Version à trois États de Pong P. Chu

Pong P. Chu dans son livre "FPGA prototyping by VHDL examples" propose une version utilisant seulement trois états que nous donnons maintenant pour comparer avec ce que nous avons fait (d'ailleurs très inspiré par son livre). L’idée est en fait assez simple si l’on était capable de noter des actions qui se déroulent seulement quand on quitte les états. Ceux qui nous ont suivi jusque là se disent mais pourquoi donc, n'était-ce pas le cas comme expliqué dans la section glissement sémantique du chapitre VHDL et machines à états algorithmiques. Et bien non ! Pas si l’on reste coincé dans l'étape ! C'est vrai que l'action se réalise au sortir de l'étape mais seulement si elle est suivie par une transition toujours réceptive ("=1"). Autrement elle est aussi réalisée pendant qu'on reste dans l'étape mais avec un front d'horloge de retard. Tout cela est bien compliqué et probablement pas à la portée d'étudiants de niveau 14.
Et pour finir, un peu de travail.

### Travail à réaliser

On vous demande d’utiliser une des corrections que l’on vient de donner (à six états ou à trois états) et de l'insérer dans une application comme indiqué dans la figure ci-dessous pour en tester le bon fonctionnement.
L'appui sur la touche 'a' donnera 15 en hexadécimal comme résultat.
| TP 3 | TP 5 |