# Very High Speed Integrated Circuit Hardware Description Language/Interfaces VGA et PS2

Nous allons dans ce chapitre nous intéresser aux interfaces VGA (Video Graphics Array) et PS/2 (Personnal System/2). Les cartes d'évaluations modernes ont pratiquement toutes ces deux interfaces directement montées et exploitables.
La partie concernant l'interface VGA sera émaillée de nombreux exemples destinés à appréhender quelques techniques modernes de réalisations de jeux vidéo. On partira de simples dessins de rectangles pour arriver aux jeux vidéo nécessitant un décor de fond sur lequel des sprites mobiles viennent se déplacer.

## Interface VGA

L'interface VGA est intéressante car elle montre comment réaliser des signaux calibrés en temps à l'aide des compteurs. Un connecteur VGA montre que l'interface est complexe puisqu'elle gère 15 signaux différents. En ce qui nous concerne, nous allons nous intéresser à 5 signaux seulement définis dans la section suivante.

### Les signaux à construire

La gestion d'un écran VGA se fait à l'aide de cinq signaux : trois pour les couleurs et deux pour la synchronisation. Dans un premier temps nous n'allons pas nous occuper des signaux de couleurs, autrement dit on ne s'intéressera qu'aux deux signaux de synchronisation.
Les signaux de synchronisation à générer sont décrits dans la figure ci-après pour une résolution de 640 x 480. Ils sont appelés "hsynch" pour la synchronisation horizontale et "vsynch" pour la synchronisation verticale. Ils sont simples et nécessitent seulement deux compteurs pour être réalisés.
Dans cette figure, nous présentons en haut les trois signaux rouge, vert et bleu colorés. Ils sont identiques par commodité, mais ce n'est absolument pas le cas en général. Les signaux auxquels on s'intéresse en premier lieu, sont en-dessous et très simple. Repérez-les : "hsynch" et "vsynch".
Nous pouvons donc réaliser les deux signaux hsynch et vsynch à l'aide de l'architecture ci-dessous :
Dans cette figure, les éléments séquentiels sont représentés en bleu turquoise et les éléments combinatoires en vert. Les comparateurs sont des éléments combinatoires en principe. Mais comme on peut le voir ici, on les synchronise sur des fronts d'horloge descendants. C'est du combinatoire mais synchronisé. Le programme VHDL capable de générer les signaux de synchronisation est donné un peu plus loin (solution de l'exercice 1). Le code VHDL correspondant permet simplement de synchroniser l'écran VGA mais ne dessine rien à l'intérieur.

#### Exercice 1

Écrire un programme VHDL qui implante l'architecture ci-dessus.
Les valeurs que l’on donne dans le programme sont légèrement différentes de celles du dessin, mais cela n'a pas beaucoup d'importance. Si vous voulez comprendre d'où viennent ces valeurs, je vous conseille de lire cet exercice et sa solution.
On a donné ici que les quatre "process" qui réalisent nos quatre blocs.
Une correction importante de ce fichier a été réalisée (Mars 2012) : 755 a été remplacé par 751 et 659 par 656.
Voyons maintenant comment dessiner un rectangle.

### Dessiner un rectangle

On cherche à dessiner un rectangle dont la position est donnée par des entrées externes. Sa taille est fixée une fois pour toute, seule sa position peut changer. Regardez la figure ci-dessous et vous verrez une partie combinatoire ajoutée, dans laquelle on entre les positions du rectangle sur 10 bits (ici x_rect et y_rect). Tout dessin sur l'écran se trouvera dans ce composant. Les coordonnées x_rect et y_rect seront à terme, fournies par un composant externe qui pour nous sera un processeur softcore.
Dans ce schéma on a représenté en gris le composant de l'exercice 1. Le dessin explique de lui-même pourquoi on a ajouté dans la correction de l'exercice 1 des sorties appelées "pixel_row" et "pixel_column". Elles sont directement connectées au bloc combinatoire que l’on a ajouté à droite. Ce bloc permet de dessiner n’importe quoi à partir de la valeur des compteurs (justement "pixel_row" et "pixel_column") et ici des coordonnées d'une position d'un rectangle. Si les entrées "x_rect" et "y_rect" sont remplacées par d'autres données on peut imaginer dessiner n’importe quoi sur l'écran.
Tout dessin sur écran VGA consistera à ajouter une partie logique combinatoire (ou y ressemblant, nous pensons à de la mémoire en écrivant cela). Cette partie combinatoire aura forcément comme entrées les valeurs des deux compteurs notées "pixel_row" et "pixel_column" et d'autres entrées remplaçant ou complétant "x_rect" et "y_rect". Ces entrées devront, d'une manière ou une autre, donner des informations sur le type de dessin envisagé. Ses sorties seront naturellement des données sur les couleurs : signal du vert, du rouge et du bleu.
Prenez toujours le temps de réfléchir à l'aspect fonctionnel des choses : quelles sont les entrées, quelles sont les sorties et que doit faire tel ou tel composant ? Cette réflexion peut prendre du temps, mais elle est absolument nécessaire pour une compréhension approfondie.

#### Exercice 2

On dispose d'un composant combinatoire rect capable de dessiner un rectangle décrit en VHDL par son entité :
Sa couleur est fixée par son entrée coloRGB, sa position par x_rect et y_rect. Sa taille est fixée par delta_x et delta_y. Le code correspondant à son architecture est :
1°) Écrire le programme VHDL qui permet de dessiner un rectangle de taille 100x100 aux coordonnées 100 suivant x et 100 suivant y. On dispose pour cela d'une horloge à 50 MHz appelée clk_50. On réalise donc l'assemblage de la figure ci-dessus pour donner l'entité :
2°) Modifier le programme précédant pour qu’il dessine une balle et deux raquettes. La position de la balle sera donnée par des entrées externes mais les positions X des raquettes seront fixées.
Remarquez par la même occasion que l’on a choisi les positions verticales des raquettes sur seulement 8 bits. Économie économie !
Pour éviter des solutions trop longues, on commence par donner la partie VHDL commune aux deux questions :
Remarquez qu’il faut retirer des commentaires pour la question 2°)
Chercher ensuite la partie à compléter. C'est cette partie que nous donnons comme correction.
1°) Puisque 100 est notre chiffre pour la question, il nous faut d’abord le convertir en binaire soit : 0001100100 Mettez la partie VHDL ci-dessous dans la partie à compléter et vous aurez votre rectangle.
2°) C'est un peu plus complexe pour la balle et les deux raquettes. Il faut en effet utiliser des signaux intermédiaire pour chaque composant rectangle et faire un OU final.
N'oubliez pas de retirer les commentaires de l'entité VGATop avant d'y insérer ce code :
Pour ne pas rester cantonné au fondeur Xilinx, nous allons maintenant nous intéresser au même problème avec Altera.

### Dessiner un rectangle sur DE2-115 (Altera)

Voici un exemple de tracé de rectangle destiné à la carte DE2-115 d'Altera. La grande différence avec ce qui a été fait jusqu'à présent est l'apparition des signaux VGA_BLANK, VGA_SYNC et VGA_CLK en sortie. Ils sont liés d'une manière ou une autre au fait que cette carte utilise des convertisseurs numériques analogiques pour les signaux de couleur.
Les contraintes pour la carte DE2-115 sont présentées sous forme de fichier csv. Ce genre de fichier peut facilement s'importer dans Quartus.

### Dessiner un ensemble de rectangles

On désire toujours rester dans le domaine du dessin de rectangles, mais en cherchant à dessiner des objets plus complexes, comme des afficheurs sept segments. Si l’on désire en plus pouvoir modifier la taille de ces objets, il faut concevoir l’ensemble avec intelligence. En effet modifier la taille implique un ensemble de calculs. Ces calculs ne doivent en aucun cas être réalisés par le VHDL mais par le compilateur avant implantation. Pour se faire on dispose d'une possibilité avec le generic, et voici comment cela fonctionne.
Soit le programme VHDL suivant :
Ses seules grandes différences sont l'apparition dans l'entité de quatre valeurs generic : x, et y pour la position et dx, dy pour la taille ainsi que l'entrée "e_rect" destinée à choisir un affichage ou non du rectangle. L'utilisation d'un tel rectangle se fera toujours par un "port map", mais il faudra lui ajouter un "generic map".

#### Exercice 3
##### Question 1

Écrire un programme VHDL réalisant un afficheur sept segments à partir du rectangle générique ci-dessus. L'ensemble devra être caractérisé par une position en x et en y ainsi qu'une taille. L'entrée se fera sur 4 bits.
La solution présentée a été réalisée par des étudiants. Ainsi l’ordre choisi pour les segments est un peu étrange, mais cette solution a été testée avec succès.
Tous les calculs réalisés dans les "generic map" (multiplications et divisions) ne sont pas réalisés par les ressources du FPGA mais par les ressources de votre PC : c’est le compilateur VHDL qui fait le travail.

##### Question 2

Réaliser un ensemble capable d'afficher deux raquettes, une balle, un rectangle de séparation en bas de l'écran et un affichage de deux scores sur deux digits. Vous remarquerez sur la figure ci-dessous le nombre impressionnant de fils d'entrées regroupés en bus... d'où la question : que va-t-on bien pouvoir faire de tous ces fils ? Ils seront reliés à un processeur par l'intermédiaire de PORTs tout simplement :
* à un PIC 16F84 embarqué
* à un AVR ATMEGA8® embarqué
Voici la figure correspondante :
On montre sur la figure ci-dessus ce que l’on cherche à réaliser. Encore une fois il est important de se poser devant celle-ci et de bien regarder quelles sont les entrées et quelles sont les sorties ? Les sorties sont les 5 signaux VGA non représentés qui en final vont faire le dessin ci-dessus, c'est-à-dire deux raquettes, une balle et quatre afficheurs de scores. Pour cadencer tout cela il faut une horloge, encore une fois non présentée. Toutes les entrées sont pour changer les positions, de la balle et des raquettes, et pour fournir des nombres à afficher sur les scores.

##### Question 3

Modifier l'exemple de la question 2 pour réaliser la figure ci-dessous.
Vu le nombre d'entrées de ce composants il ne peut qu'être utilisé avec un processeur embarqué :
* un ATMega8 embarqué l'utilise dans un projet étudiant décrit plus loin dans ce livre .
Le code ci-dessous n’est pas complet : il vous faudra chercher le reste dans les corrections précédentes de ce chapitre. Remarquez que l'afficheur sept segments a été renommé Aff7seg (au lieu de Aff en question 2). Nous pensons que cela sera plus compréhensible comme cela.
Le code donné dessine les murs de briques légèrement sur la droite (c'est volontaire) et les briques sont bleues.
Nous allons maintenant nous intéresser à un autre problème : comment afficher des caractères sur un écran vidéo ?

### Dessiner des caractères sur un écran

Nous allons nous intéresser dans cette section à l’affichage de caractères sur un écran V.G.A.. Depuis les débuts de l'informatique on utilise une ROM où l’on stocke les formes des caractères pour faire ce travail. Nous allons donc commencer à présenter la ROM de caractères que nous allons utiliser avec un passage obligé à un peu de terminologie.
Nous allons utiliser dans la suite des composants spécifiques à Xilinx. C'est embêtant pour la portabilité mais nous laissons des références au lecteur lui permettant de retrouver les projets libres contenant des ROM génériques.
On appellera dans la suite :
* RAMB16 une RAM/ROM de 16 kbits
* RAMB16_S9 une RAMB16 avec 9 bits de données (2 ko). Le neuvième bit est un bit de parité non utilisé pour nos besoins.
* RAMB16_S1 une RAMB16 avec 1 bit de données (16 kbits).

#### ROM/RAM de caractères

Nos investigations sur le sujet nous ont donné l’occasion d’utiliser deux ROMs différentes :
* celle de P. P. Chu associée à son livre "FPGA prototyping by VHDL Examples"
* celle d'un projet libre chez Opencores appelé Yet Another VGA.
Après divers essais nous avons décidé d’utiliser la deuxième car elle donne de plus beaux résultats pour un grossissement de 2x2... mais comme on dit les goûts et les couleurs...
La mise en œuvre de mémoires programmes pour nos processeurs nous a appris à aborder les mémoires avec précautions. Tous les F.P.G.A. modernes comportent des zones réservées à ces mémoires : on les appelle des BRAM. Si une option de compilation correcte manque, une synthèse utilisant ce que l’on appelle des mémoires distribuées sera réalisée. Ce qui est en général très inefficace. Pour éviter tout problème, nous avons décidé d’utiliser les modèles Xilinx qui forcent la synthèse avec les BRAM. Mais cela revient à changer le code source car ces modèles BRAMs ont leurs propres méthodes d'initialisation et malheureusement ce nouveau code est beaucoup moins portable ! Nous allons quand même publier le code correspondant ici, mais il vous faudra l'adapter pour les autres fondeurs comme Altera (voir remarque ci-dessous) :
Ce code nécessite la remarque suivante :
Pour tout autre fondeur que Xilinx, partez plutôt de la ROM originale publiée dans le projet Yet Another VGA chez Opencores. Elle est beaucoup plus portable que le code ci-dessus.
Pour la petite histoire la ROM ci-dessus a été obtenue à partir du fichier d'extension xdl généré à partir la ROM originale par une compilation dans l'ISE. Ce fichier a été ouvert par hasard, et nous a évité un travail long et fastidieux, comme quoi, la curiosité n’est pas toujours un vilain défaut !
Un travail correctement réalisé devrait utiliser une RAMB16_S1 plutôt qu'une RAMB16_S9 : cela éviterait le multiplexeur de sortie ! (Ne vous inquiétez pas si vous ne voyez pas de quel multiplexeur nous parlons puisqu'on va le retirer). Voici la BRAM correspondante, que nous utiliserons dans la section suivante.
Cette RAM sera modifiée lorsque l’on s'intéressera au jeu du pacman un peu plus loin.

#### Utilisation de la BRAM

S'il est important de posséder une BRAM de caractères il est encore plus important d’en voir le contenu sur un écran VGA. C'est à ce travail que nous allons nous consacrer maintenant.

##### Cahier des charges simplifié pour commencer

Afin de visualiser le contenu de notre ROM de caractères, nous désirons dessiner 4 lignes de 32 caractères dans le coin gauche de l'écran. Voici en image ce que cela donne. On a représenté les caractères non pas par leur contenu mais par des pavés numérotés.
Regardez attentivement cette figure : elle vous explique comment retrouver les numéros des pavés dessinés en haut à gauche de l'écran VGA. Ces numéros sont importants car ils représentent les codes ASCII des caractères que l’on dessinera. Puisque le dessin présente des pavés numérotés de 0 à 127, cela confirme que notre ROM de caractères contient 128 caractères.
* ce qui est au-dessus de l'écran représente quelques valeurs binaires spécifiques de pixel_x (ou pixel_column). Avec un peu d'imagination vous devinez comment ce compteur varie quand on se déplace vers la droite...
* ce qui est à gauche de l'écran représente quelques valeurs binaires spécifiques de pixel_y (ou pixel_row). Avec un peu d'imagination vous devinez comment ce compteur varie quand on se déplace vers le bas...
* le registre de 7 bits en bas est composée d'une partie bleue tirée de pixel_y (2 bits) et d'une partie jaune tirée de pixel_x (4 bits)
* si vous avez compris vous verrez assez facilement qu’à droite du pavé 31 il y a un pavé 0 non représentée car on désire ne pas le voir. Ce que nous essayons d'expliquer ici, c’est que la mise bout à bout de pixel_y(5:4) et pixel_x(7:3) donne un numéro au pavé à droite pavé 31. Un autre mécanisme sera nécessaire pour effacer ce pavé !

##### Principe de réalisation

Nous allons maintenant chercher une architecture générale capable de dessiner ces caractères. La voici en image :
L'inverseur est uniquement présent pour éviter de dessiner les caractères à l’envers (réflexion selon un axe vertical au milieu du caractère). On pourrait le supprimer en initialisant autrement la ROM, ce que nous n'avons pas le courage de faire.
Tout circuit de dessin de caractères à l'écran se conçoit en deux parties :
* Le circuit de génération de caractères est réalisé comme expliqué en section précédente : concaténation d'une partie de pixel_y avec une partie de pixel_x. Tout travail sur l’affichage de caractères commence par là. On rappelle que la simple concaténation utilisée génère automatiquement des pavés en dehors des zones : pavé 0 à droite du pavé 31.
* Le circuit combinatoire qui évite le dessin en dehors de la plage des 128 caractères n’est pas représenté dans la figure ci-dessus. Il a donc pour objectif d'empêcher l’affichage du pavé 0 qui est à droite du pavé 31.
Nous pouvons maintenant passer au code source.

##### Solution

Voici le programme VHDL complet qui réalise ceci :
Vous avez trois lignes (utilisez les commentaires pour cela) à changer pour retirer le zoom :
* char_addr <= pixel_y(6 downto 5) & pixel_x(8 downto 4); à remplacer par char_addr <= pixel_y(5 downto 4) & pixel_x(7 downto 3);
* rom_addr <= char_addr & pixel_y(4 downto 1) & not pixel_x(3 downto 1); à remplacer par rom_addr <= char_addr & pixel_y(3 downto 0) & not pixel_x(2 downto 0);
* '1' when (pixel_x(9)='0') and (pixel_y(9 downto 7)="000") else à remplacer par '1' when (pixel_x(9 downto 8)="00") and (pixel_y(9 downto 6)="0000") else
Avant de passer à la suite nous vous proposons un exercice d'application qui représente une petite modification par rapport à ce que l’on vient de présenter. Le résoudre par vous-même vous montrera que vous avez bien compris la génération de caractères.

##### Exercice 4

Puisque notre but, même s'il n’est pas encore avoué, est de remplacer la partie basse de la question 3 de l'exercice 3 par un ensemble de caractères, nous vous proposons le cahier des charges suivant :
* dessiner 32 caractères en bas de l'écran représentés par leur numéros dans le schéma ci-dessous
* utiliser encore le zoom 2x2
On rappelle que ce qui sera affiché à l'écran n’est pas le numéro des pavés mais les caractères dont le code ASCII est le numéro de pavé, soit ici la ligne : @ABCDEFGHIJKLMNOPQRST[\]^_
Les questions qu’il nous faut résoudre sont donc :
* Comment générer les numéros des pavés à partir de pixel_x et de pixel_y ?
* La méthode simple utilisée dans l'exemple précédent (concaténation d'une partie de pixel_x avec une partie de pixel_y) fonctionne-t-elle encore ?
Nous nous sommes aperçu après coup que si l’on continue à paver l'écran les numéros des pavés tombent justement sur 64, 65, 66, ... pour la ligne du bas. Ainsi la génération des numéros de pavés à partir de pixel_x et pixel_y reste identique à ce que l’on a déjà fait. Par contre pour ne garder que l’affichage de cette ligne du bas, il faut changer la génération de "text_bit_on" qui, rappelons-le, est responsable de l'extinction des caractères non voulus.
en

##### Afficher un texte quelconque

Jusqu'à présent les numéros des pavés à afficher étaient successifs. Mais qu'en est-il si l’on désire afficher une chaîne de caractères fixe quelconque ?
Ce problème est généralement résolu en utilisant une deuxième mémoire contenant la liste des codes ASCII à afficher. Puisque vous avez maintenant un peu l'habitude de la terminologie Xilinx vous devinez qu'une RAMB16_S9 sera nécessaire.
Il est facile de décomposer ce problème en deux parties :
* générer des pavés de la figure de l'exercice 4 portant les numéros 0, 1, ..., 31, 32, 33, .. 39 pour le bas de l'écran (au lieu des 64 à 95 dans la figure).
* utiliser les numéros précédemment générés comme adresse de la mémoire qui contient les codes ASCII.
Nous allons donc commencer par essayer de générer des pavés numérotés à partir de 0. Ils formeront les adresses de la RAM des caractères à afficher. Si l'adresse se nomme addr_pave (pour adresse pavé ou numéro pavé) on réalise simplement :
Quand pixel_x et pixel_y s'incrémentent addr_pave donne le numéro de chaque pavé. Voyez-vous correctement que la dernière ligne (de caractères sur l'écran VGA) commence par le numéro 64 et s'arrête à 103 ?
Voila, nous sommes prêts à ajouter la RAMB16_S9. Avant cela il nous faut pointer une petite subtilité qu’il nous faudra aussi résoudre.
Le fait d’utiliser deux mémoires synchrones retarde l'arrivée du pixel à afficher par rapport aux compteurs pixel_x et pixel_y. Chaque caractère est donc affiché avec un pixel de retard si l’on ne corrige pas cela.

##### Exercice 5

Réaliser l’ensemble capable d'afficher la chaine de caractères "GEII-TROYES SCORE : 0000 - LEVEL : 00" tout en bas de l'écran. En zoom 2x2, il y a juste la place. Il vous faudra correctement initialiser la RAMB16 et surtout au bon endroit.
Voici un schéma de principe qui explique ce que l’on cherche à réaliser :
Notez la présence de deux buffers (en jaune pâle) destinés à synchroniser le dessin à l'écran avec le balayage, sachant que l’on utilise deux mémoires synchrones. Pourquoi deux ? Parce qu'on utilise le 50 MHz alors que les compteurs de balayages sont en 25 MHz. Cela mériterait certainement bien d'autres explications, mais nous remettons celles-ci à plus tard.
Voici maintenant la solution complète en VHDL.
Nous donnons sans plus de commentaire le programme complet. Essayez de trouver comment est géré le retard de la recherche évoqué en remarque ci-dessus.
Question subsidiaire : comment changer le code source précédent pour que le numéro du pavé de la dernière ligne commence par zéro (au lieu de 64) pour que le texte à afficher se trouve en adresse 0.
Le minimum de changement est :
Remarquez qu'avec cette modification toute la colonne gauche contient un ensemble de pavés numéro 0... et qu'en fait toutes les colonnes contiennent le même numéro de pavé ! C'est
qui gère la zone d'affichage : ici la dernière ligne.

##### Conclusion

Ces essais nous ont montré un léger problème dans notre module de synchronisation VGA car les caractères les plus à gauches ne sont dessinés que partiellement. Jusqu'à présent nous avons toujours mis cela sur le compte des écrans mais le problème est qu'avec les fichiers sources de P. P. Chu associés à son livre, on n'a pas ce problème !
Nous avons donc corrigé notre fichier de synchronisation dans l'exercice 1 pour éviter ce problème.

### Mélanger dessins et caractères

Arrivé ici vous êtes capable d’une part de dessiner des rectangles et d’autre part d'afficher des caractères. Nous allons nous intéresser au mélange des deux maintenant.

#### Exercice 6

Reprendre la question 3 de l'exercice 3 en retirant toute la partie basse composée de deux afficheurs sept segments et en la remplaçant par le texte de la section précédente. À ce point, vous n'avez aucune idée sur la façon qu’il vous faudra mettre en œuvre pour afficher le bon niveau (level en anglais). Il vous faudra donc retirer l'entrée correspondante. Elle est d'ailleurs improprement appelée score alors qu'en réalité elle est utilisée pour afficher le niveau (voir le projet étudiant correspondant et le programme C de la correction).
Nous donnons en vrac une solution qui fonctionne mais qui mériterait d’être commentée et épurée.
Nous allons mettre toutes ces connaissances acquises à la lecture de ces sections à profit pour réaliser un pacman. Cela nous permettra de changer un peu de domaine de jeu pour laisser tomber les raquettes.

### Pacman

Les techniques graphiques utilisées dans le jeu de pacman sont universelles. Elles n'ont cependant pas été utilisées jusqu'à présent même si le jeu de Casse-briques aurait très bien pu les mettre en œuvre (ce qui sera d'ailleurs essayé dans des futurs projets).
Ces techniques peuvent être résumées comme ceci :
* vous disposez d'un fond d'écran qui joue le rôle de décor. Traditionnellement il est réalisé avec un pavage dans lequel sont dessinés diverses briques de taille constante. Ce décor est en principe fixe. Cette technique est complètement identique au dessin de caractères déjà évoquée dans ce chapitre.
* vous disposez d'un ou plusieurs objets mobiles que l’on appelle sprite (terme anglo-saxon qui ne semble pas avoir été traduit. Ces sprites ont une partie transparente et une partie opaque.
Si l’on reprend le jeu de casse-briques, l’affichage du score et des briques constitue le décor et la (ou les) raquette(s) ainsi que la balle constituent les sprites. Jusqu'à maintenant nous avons utilisé la technique de dessin de rectangles pour les sprites. Le décor quant à lui était un mix : rectangles pour les briques et pavés pour les scores (à partir de 2012).

#### Construction du décor

Nous allons utiliser la technique des dessins de caractères pour construire le décor. Puisque nous disposons d'une ROM de caractères 8x16 (horizontal x vertical), nous allons l’utiliser telle quelle. Une utilisation de briques 16x16 aurait certainement été bien mieux, esthétiquement parlant, mais nous remettons cela à plus tard, dans un futur projet.
Le décor est changeant : les Pac-gommes disposées tout au long du labyrinthe sont mangées par pacman et devront donc disparaitre. Conséquence, le décor doit avoir une image en RAM. Comme déjà indiqué, la première chose à faire dans ce cas précis, est de choisir le nombre et la taille des pavés numérotés que l’on va disposer à l'écran. Ces numéros (de pavé) constitueront l'adresse de la dite RAM.

##### Construction des numéros de pavés

Nous allons tout reprendre les explications depuis le début. Il nous faut donc construire un nouveau pavage. Nous allons choisir des pavés 16x32 (horizontal x vertical). On y placera à l'intérieur des pavés de taille 8x16 qui seront donc grossis d'un facteur 2.
Nous rappelons dans cette figure comment il est simple de former les numéros de pavés à l'aide d'une simple concaténation. Ce faisant, les numéros des pavés dépassent la taille de l'écran vers la droite puisqu’il vont jusqu'à 63 pour la première ligne. Ceci est visible dès la seconde ligne qui commence par le numéro 64.
Ces numéros de pavés vont nous servir à adresser une mémoire qui contiendra donc l’ensemble du décor. Le fait que certains des pavés ne soient pas visibles nous fait perdre de la place dans la mémoire, mais ceci est compensé par la facilité de réaliser les adresses (nous n'insisterons jamais assez, par simple concaténation).

##### Construction des pavés de base du décor

À part le texte qui sera affiché en bas de l'écran, nous n'avons pas besoin de beaucoup de pavés pour fabriquer un labyrinthe. Les voici présentés :
Il faut savoir que nous avons décidé de ne pas coder les couleurs dans la mémoire. Les couleurs de cette figures sont celles que nous avons choisies mais elles seront obtenues par un circuit combinatoire assez complexe. Les numéros entre parenthèses représentent l'adresse où se trouve le dessin du caractère.
Cette figure permet de comprendre le début de l'initialisation de la mémoire :
Cette mémoire est commandée par une RAMB16_S9 qui sera destinée à réaliser le labyrinthe complet.

##### Exercice 7

À partir du contenu de la RAMB16_S9, pouvez-vous dessiner ce qui sera reproduit sur votre écran VGA ?
Une chaîne de caractères est écrite en bas de l'écran. Pouvez-vous la donner en vous aidant des codes ASCII contenu dans cette mémoire.
Le contenu de la ROM se lit ici de la droite vers la gauche !!!

#### Construction et gestion des sprites

Les sprites sont des parties mobiles. Leur dessin sera stocké lui aussi dans la ROM de caractères. Étant donné qu'un sprite se superpose au décor qui lui aussi utilise la ROM de caractères, cela indique qu’à un certain moment cette ROM de caractères peut être accédée deux fois simultanément ! pour le décor du fond et pour le sprite ! Il en résulte immédiatement qu’il nous faudra ajouter un port à la ROM de caractères. D'autre part, il nous faudra gérer deux sprites : le pacman et l'ennemi et choisir une priorité puisque les mémoires contenant plus de deux PORTs n'existent pas.

##### Exercice 8

Nous donnons le contenu de la RAMB16_S1 qui gère les sprites :
Pouvez-vous dessiner sur une feuille ce que cela donnera sur un écran VGA ?

##### Affichage des sprites

Pour gérer les sprites il nous faut prévoir les entrées correspondantes dans l'entité. La voici présentée :
On y voit apparaître les coordonnées du pacman sur 10 bits et le numéro du sprite car le pacman est représenté bouche ouverte ou fermée et allant vers la droite, la gauche, le haut et le bas. Cela fait en tout 8 sprites différents d'où les trois bits "pacman_sprite_number". Le fait que les coordonnées soient sur 10 bits indique qu’il peut être affiché n’importe où sur l'écran. Si l’on désire interfacer ces coordonnées avec un processeur 8 bits, on peut se limiter à 8 bits et relier directement les deux bits de poids faibles à 0.

##### Le programme complet

Voici le programme complet avec sprite et décor.
Ce programme est un peu long, mais nous sommes obligé de le donner quasiment complètement puisque le contenu de la RAMB16_S1 a changé et en plus elle a été transformée en RAMB16_S1_S1.
Le contenu de la RAMB16_S9_S9 qui dessine le fond de l'écran, le décor, est en adresse haute. Certains lecteurs attentifs peuvent se demander pourquoi. La raison est liée à l’utilisation future de cette partie : l'interfacer avec un processeur. Mais nous voulons encore beaucoup plus : utiliser cette RAM comme RAM du processeur. Pour cela il nous faut laisser la partie basse intacte de la RAM car les variables en C iront s'y loger. Nous espérons accéder à cette zone de mémoire haute en C avec des pointeurs... mais ceci sera examiné dans un autre chapitre.
Pour les utilisateurs éventuels de la carte "Nexys3", nous venons de découvrir que le pacman donné ci-dessus ne se compile pas. Ce n’est pas lié à la version de l'ISE 14.5 qui compile correctement quand on cible des spartan3 ou des spartan3E mais se révèle beaucoup plus pointilleux pour une cible spartan6 !!! Ce problème a été réglé dans la ressource nexys3PacmanAtmega16.zip pour la carte Nexys 3.

### Ressources

Pour une gestion plus avancée des cartes VGA :
* GITHUB:vga_configurable.vhd
* GITHUB:lib.vhd
* Youtube : vidéo en anglais expliquant les synchronisations
Passons maintenant à un tout autre sujet, la lecture d'un clavier PS/2.

## Les claviers PS/2

Le protocole PS/2 est un protocole bidirectionnel du périphérique vers l'hôte. Nous limiterons notre discussion ici en considérant le périphérique comme un clavier. Il peut sembler à première vu que la communication entre un clavier et un hôte est mono directionnelle (clavier --> hôte), mais il n'en est rien.
C'est l'hôte qui demande à telle ou telle diode électroluminescente de s'allumer sur le clavier. Quand on appuie sur majuscule, cette information est envoyée à l'ordinateur qui en retour demande au clavier d'allumer sa diode Caps Lock.
Comprendre le protocole PS/2 nécessite ainsi de s'interroger sur les deux sens de communication :
* clavier vers hôte
* hôte vers clavier
Le brochage du port PS/2 définit les signaux conventionnels. Dans ce cours nous ne gardons pas les noms conventionnels pour les deux principaux signaux :
* la broche 5 appelée "Horloge" sera appelée "ps2_clk"
* la broche 1 appelée "Données" sera appelée "ps2_data"
La figure ci-après vous montre un exemple de protocole PS/2 avec ces appellations.

### Le protocole PS/2

C'est le système maître qui alimente le périphérique PS/2. Les signaux "ps2_data" et "ps2_clk" sont bidirectionnels et à sortie collecteur ouvert. Une résistance de 10K ohm environ doivent être placées entre ces deux signaux et l'alimentation ce qui garantie un niveau haut hors de toute transaction.
Les octets de commandes et messages sont transmis de façon synchrone et série sur 11 bits ( les 8 bits à transmettre, poids faible en premier, précédés d'un bit start (0) et suivi d'un bit de parité et d'un bit stop (1). Le bit de parité impaire vaut 1 si le nombre total de 1 dans l'octet et le bit de parité lui-même est impair. Durant la transmission, c’est le périphérique qui fournit l'horloge en la positionnant à un niveau bas (front descendant) tandis que l'émetteur (système ou périphérique) place data à un niveau bas pour un bit 0 ou le laisse inactif pour un bit 1.
La fréquence d'horloge est au maximum de 33 kHz mais le plus couramment de 15 kHz.
Entre les transmissions, le bus peut être :
* Idle : CLK et DATA sont tous les deux au niveau haut . Il n y a pas d'activité.
* Inhibit : Le système maintient CLK au niveau bas.
* Request to send : Le maître maintient DATA au niveau bas mais laisse CLK flotter. Il est prêt à émettre.

### Chronogrammes de l'échange clavier vers hôte

Puisque c’est le clavier qui est à l'initiative de l'échange des données, c’est lui qui aura la lourde tâche de la fabrication de l'horloge. De l'autre côté, l'hôte échantillonnera les données sur les fronts descendants de cette horloge comme indiqué dans le chronogramme ci-dessous :
On remarque que le clavier envoie d’abord un bit de "start" à '0', puis les données sur 8 bits en commençant par le poids faible, puis un bit de parité suivi d'un bit de stop qui est '1'. Ceci est bien conforme au protocole PS/2 décrit dans la précédente section.

#### Exercice 7

Imaginez une architecture électronique capable de lire le protocole PS/2.
On aura l’occasion d'implanter cette architecture en VHDL.
Sur cette figure, intéressez-vous à la partie droite. En haut un registre à décalage de 11 bits pour recevoir les 11 bits de données qui arrivent directement de "ps2_data". L'horloge de ce registre est un peu complexe car elle passe à travers un anti-rebond. Cette même horloge incrémente un compteur capable de compter jusqu'à 11 (4 bits). Il est suivi d'un comparateur qui compare justement à 11. Puis vient l’unité de contrôle (ou séquenceur) : une fois que le compteur est à 11 on transfert les 8 bits intéressant dans un registre d'affichage, puis on remet le compteur à 0.

### Echange hôte vers clavier

À continuer

### Implémentation

Un ensemble de TPs permet de comprendre le PS2 :
Cette série de TPs construit pas à pas un module capable de réaliser la lecture du PS2.
La fin du TP3 (particulièrement la mise en conformité des horloges) montre des implantations directement utilisables et beaucoup plus compactes que celles construites de toute pièce.
Pour dire les choses autrement, si vous désirez comprendre le PS2 faites les TPs proposés, si vous chercher un module tout prêt à être utilisé allez directement à la mise en conformité des horloges et prenez le code VHDL correspondant.
En ce qui nous concerne, nous avons expérimenté :
* les TPs (qui n'étaient bien sûr pas corrigés à l'époque) avec des étudiants de deuxième année de DUT GEII (L2 = niveau 15)
* un interfaçage entre le module de P.P Chu et le picoBlaze avec des étudiants de branche de l'UTT (L3) énoncé des TPs Printemps 2012
* un interfaçage entre le module de P.P Chu et l'ATMega8 avec des étudiants de branche de l'UTT (L3) énoncé des TPs Printemps 2013