# Very High Speed Integrated Circuit Hardware Description Language/Améliorer l'ATMega8 avec l'ATMega16 et l'ATMega32

Nous avons l'intention dans ce chapitre de reprendre ce qui a été fait dans le chapitre précédent avec l'AVR ATMega8 de manière un peu plus systématique. Il doit être clair pour les lecteurs assidus de ce précédent chapitre, qu’il a été rédigé avec aucun recul. Ainsi plusieurs versions de l'ATMega8 ont été utilisées essentiellement pour la gestion des mémoires. Voici donc présentés les défauts du précédent chapitre que nous espérons corriger.
* Une seule version du processeur de base devra être utilisée contrairement aux deux versions du chapitre précédent (premier défaut à corriger)
* Nous n'avons pas, d’autre part, étudié la réalisation systématique des périphériques. C'est aussi ce deuxième défaut que nous espérons corriger dans ce chapitre
* La mémoire programme disponible dans la dernière version de l'ATMega8 était de 16 ko, donc exactement celle qui est présente dans l'ATMega16. Il y avait donc 8 ko inutilisables avec l'option de compilation ATMega8. Nous avons l'intention de les récupérer maintenant (troisième défaut à corriger)
* Enfin, la mémoire RAM disponible dans la dernière version de l'ATMega8 était de 4 ko. Celle qui est disponible dans un ATMega16 est de 1 ko et celle de l'ATMega32 est de 2 ko. Les options de compilations pour ATMega32 nous permettrons donc de récupérer 1 ko facilement. Mais les 2 ko restants seront à gérer à la main avec des pointeurs si l’on désire les utiliser.
* L'hébergement de mon site perso se termine le 5 septembre 2023 ! À ce jour je n'ai pas encore décidé comment je vais gérer ce problème dont je ne suis pas à l'origine. Il en résulte que l'ensemble des corrections qui utilisent mon site perso seront indisponibles à partir de cette date pour tout ce chapitre. SergeMoutou (discuter)

## Passage de l'ATMega8 vers l'ATMega16

Comme nous l'avons déjà exprimé en introduction, tout ce qui concerne la gestion des mémoires programme et mémoires RAM est déjà réalisé. La seule grande évolution dans l'ATMega16 est l'apparition d'une instruction de saut nouvelle dans la table de vecteur d'interruptions. Nous allons donc examiner maintenant ce problème. Côté périphérique il y a des différences mais nous n'en avons pas (ou peu) dans notre processeur (des périphériques). C'est à nous de les imaginer.

### Changer la gestion des vecteurs d'interruptions

Nous avons déjà évoqué ce problème dans la section Ajoutons une interruption du précédent chapitre.
Pour faire simple il vous faut donc regarder dans le fichier opc_deco.vhd autour de la ligne 99, et avoir :
au lieu de
pour notre ancien ATMega8.
Faites cela et vous avez un ATMega16 disponible. Cette simplicité de transformation est liée au fait que la seule nouvelle instruction dans l'ATMega16 (à part BREAK comme visible dans cette annexe d'un autre livre) est "JMP". Notre ATMega8 disposait déjà de l'instruction "jmp" et des 16 ko de mémoire programme.

### Changer les options de compilation

La compilation a toujours été réalisée avec l'option "-mmcu=atmega8" dans le chapitre précédent. Il suffit de compiler maintenant avec l'option "-mmcu=atmega16".
Voici un moyen automatique de compiler sous Linux :
Quelques précisions sur ce script :
* vous devez adapter le PATH à votre installation de l'ISE et de votre compilateur C pour l'AVR.
* memoryS6.bmm est lié au fait que l’on utilise maintenant une carte avec un spartan6 (lire documentation data2mem dans ce livre)
* la dernière ligne sert à programmer la carte Nexys3 en ligne de commande avec l'outil ADEPT de Digilent

### Ressource disponible

Nous mettons à disposition un fichier contenant l’ensemble de l'ATMega16. Il a été testé sur une carte digilent Nexys 3 possédant un FPGA spartan 6 avec une horloge à 100 MHz. Nous avons gardé la division par deux des versions précédentes : il fonctionne donc à 50 MHz. Vous pouvez le faire fonctionner juste en changeant le fichier ".ucf" sur les cartes spartan3, spartan3E et Basys2 mais à 25 MHz seulement. Gardez alors les contraintes de localisation des mémoires et le fichier memory.bmm que l’on trouve dans le chapitre Programmer in situ ...
* Téléchargez la ressource ATMega16. Les modifications futures concerneront le fichier "io.vhd". Nous mettrons dans ce fichier zip diverses versions de ce fichier "io.vhd". Si vous voulez réaliser un ATMega16 fonctionnel il faut en choisir un seul parmi ceux que vous trouverez.
** Pour le moment il y a un fichier "io_timer.vhd" qui implante un pseudo-timer (voir plus bas dans ce chapitre) avec une interruption de débordement.

## Comment donner un nom à un registre de périphérique

Pour comprendre ce qui va suivre, rappelez-vous que notre processeur n'a que très peu de périphérique à comparer à l'ATMega16 du commerce. Cela veut dire qu'une bonne partie des registres utilisés pour configurer et utiliser ces périphériques est libre. Ainsi deux solutions s'offrent à vous lorsque vous réalisez des périphériques :
* vous êtes conservateur (ce que nous avons été jusqu'ici) et vous gardez les noms des registres originaux de l'ATMega16.
* vous êtes révolutionnaire et vous ne voyez aucune raison pour garder les noms des registres originaux.
Examinons donc les tâches à accomplir dans les deux cas.

### Garder les noms de registres de l'ATMega16

Dans ce cas il vous faudra lire les documentations officielles de l'ATMega16 pour connaître le nom et les adresses des registres. Alors vous pourrez automatiquement utiliser ce nom en langage C (ou autre). C'est un avantage mais vous vous trouvez alors dans la situation où vous serez amené à écrire du code C peu lisible. Voici un exemple provenant du projet pacman (et ATMega8) :
Et la question subsidiaire : quel est le rapport entre DDRA et DDRC et le pacman ? Notez qu'en plus DDRA ne fait pas partie des PORTs de l'ATMega8 du commerce.
En résumé, il nous a fallu lire la documentation officielle du processeur pour connaître nom et adresses des registres. Aucun changement dans les programmes C mais un code peu lisible pour un lecteur non averti.

### Changer les noms de registre de l'ATMega16

L'autre technique consiste à ne pas garder le nom des registres. Le problème est que maintenant le compilateur C ne connaîtra pas vos noms. L'avantage est que cela vous évite de lire la documentation du processeur et l'inconvénient que cela vous oblige à gérer à la main vos noms de registres. Ceci est réalisé de manière simple :
qui montre comment ont été ajoutés des registres inexistants dans ce cas, à savoir, DDRA et PORTA.

### Des constantes pour simplifier l'écriture des adresses des registres

Nous n'avons pas utilisé la technique des constantes jusqu'à présent. Il est possible dans les "case" des process "io_rd" et "io_wr" d’utiliser directement les adresses des registres définies par leur nom si elles sont déclarées comme constantes comme ci-dessous.
Voila alors à quoi pourrait ressembler un process "io_wr" :
où l’on voit apparaître les constantes, ce qui rend le programme un peu plus lisible. Notez que cette façon de faire impose de ne pas appeler les sorties avec ces noms. On pourra garder la notation allemande de l'auteur : préfixer par "I_" pour une entrée et par "Q_" pour une sortie.

### Nos conventions sur les registres de périphérique dans ce chapitre

Notez avant de continuer que le nom des périphériques est choisi lors de la programmation en C et non lors de l'implantation matérielle. Ce qui est choisi lors de l'implantation matérielle est une adresse libre. Ce qui est choisi en C c’est comment vous appelez cette adresse.
Nous avons décidé de garder partiellement la première méthode et d'évoluer vers la deuxième.
* Les registres appelés PORTs seront gardés à une condition, c’est qu’ils écrivent/lisent directement leur données sur les broches du FPGA sans aucun traitement matériel. Contrairement aux processeurs du commerce, les PORTs ne seront pas implantés de manière bidirectionnelles. Nous distinguerons donc une écriture dans PORTB d'une lecture de PINB.
* Les nouveaux registres seront appelés en C avec des noms évocateurs.

### Des bonnes résolutions pas tenues

Nous avons eu l’occasion de développer un tas de périphériques depuis l'écriture du principe de la section précédente... et n'avons pas tenu ces promesses de clarification. Nous allons donc chercher à donner des conseils aux utilisateurs mieux organisés que nous...
Bon, reprenons dans le désordre. Ce que nous faisons dans ce livre s’appelle du co-design en anglais. Il s'agit donc de développer du matériel et de le programmer par du logiciel.
En général les concepteurs hardware et software ne sont pas les mêmes. Ce qui est fait ici est donc exceptionnel (dans le sens pas très général). Regardez par exemple le chapitre sur le microBlaze si vous voulez comprendre. Dans ce processeur il n'y a pas de registres de périphériques, mais seulement un espace d'adressage pour les mettre. C'est ainsi que cela se passe toujours si vous prenez un processeur conçu par les fondeurs (microBlaze pour Xilinx, NIOS pour Altera). Mais pour faire cela, Xilinx, Altera et d'autres ont inventé un mécanisme automatique (ou semi-automatique) pour lier le matériel et le logiciel.
Il est cependant possible, pour vous aussi, de considérer l'espace des registres de périphériques comme un espace vierge à remplir par vos propres périphériques. Même pour l'ATMega16 que l'on utilise. Comme le concepteur du matériel et du logiciel est le même vous avez le choix entre plusieurs options :
* adapter les noms de registres à vos applications et donner le même nom aux deux
** en C cela se fait en changeant le fichier iom16.h, ce que l'on déconseille ou en redéfinissant vos propres noms avec des "_SFR_IO8" dans le programme principal ou dans un fichier d'entête spécifique ce que l'on conseille.
** en VHDL cela se fait avec des définitions des constantes comme dans la précédente section
* garder les noms des registres que le compilateur C connaît et les utiliser aussi en VHDL
* garder les noms pour le C et prendre les adresses (et non des noms) pour les registres
Le dernier point est ce que l'on a utilisé au chapitre précédent, en tant que débutant, et ne nous a pas gêné. Il n'empêche que nous le déconseillons pour l'enseignement.
Le deuxième est plutôt ce que l'on utilise de manière classique maintenant.
Le premier point est pour nous l'idéal pédagogique mais demande un peu plus d'organisation (que nous en avons nous-même).

## Réalisation des périphériques

Un certain nombre de périphériques a déjà été implémenté dans le chapitre précédent. Nous allons maintenant les examiner de façon plus systématique et résoudre ainsi tous les problèmes que vous pourrez rencontrer dans votre vie d'architecte en périphérique.

### Comment interfacer un registre en écriture ?

Définir des PORTs en sortie se fait dans le process iowr du fichier io.vhd dont voici la version originale :
Vous voyez apparaître une correspondance entre des PORTs (en commentaire) et des numéros. Le nom des PORTs et les numéros correspondants proviennent de la documentation officielle du processeur. Comme déjà expliqué, on n’est pas obligé de le respecter mais le faire évite de changer le fichier avr/io.h connu du compilateur.
Vous constatez donc que le cœur de départ gère un certain nombre de PORTs : PORTB, PORTC, PORTD et d'autres registres qui ne nous intéressent pas pour le moment. Nous présentons l’ensemble de ce qui vient d’être dit sur un schéma ci-dessous. Tous les registres et PORTs ne sont pas dessinés et seul le processus "iowr" est montré . La terminologie utilisée dans les micro-contrôleurs sépare la notion de PORT et la notion de registre. Un port est un registre dont les valeurs sortent vers l'extérieur, sur des broches. Ce n’est pas le cas des registres. Nous ne suivrons pas cette terminologie ici de manière rigoureuse.
* Pour information I_CLK est relié à I_CLK_100 divisée par deux. Nous avons en effet rebaptisé l'horloge car nous utilisons maintenant une carte spartan6 disposant de 100 MHz. Pour les cartes avec horloge à 50 MHz vous pouvez rebaptiser l'horloge ou pas.
* Les dessins resteront avec l'horloge à 50 MHz, comme celui qui est ci-dessous. Une autre façon de dire les choses est que chaque fois que vous voyez "I_CLK_50" dans un dessin cela désigne l'horloge du processeur (qui peut donc être à 100 MHz)
Nous présentons maintenant un schéma où seul le processus "iowr: process(I_CLK)" permettant une sortie dans un PORT ou registre est dessiné : son objectif est de dispatcher "I_DIN[7:0]" dans les différents registres en fonction de la valeur de "I_ADR_IO".
Pour revenir un peu sur la façon utilisée pour nommer les registres, notez ici la présence des noms de PORTs. En effet ceux-ci sont directement reliés aux sorties "Q_PORTB", "Q_PORTC" et "Q_PORTD" du processeur sans aucun traitement préalable. Il est donc normal de les appeler PORT.

### Comment interfacer un registre en lecture ?

Les PORTs et registres en entrée se gèrent dans le process iord :
Vous pouvez voir que certains registres sont implantés bit par bit et d'autres en entier.
Nous avons présenté sous forme de programme VHDL le process "iord" mais nous allons en faire un schéma de principe. Comme d'habitude, prenez du temps devant ce schéma pour bien comprendre ce que l’on cherche à présenter.
Son principe est de former le signal "Q_DOUT[7:0]" à partir d'une adresse "I_ADR_IO[7:0]" et de données provenant de l'extérieur ou pas. Il est possible que vous soyez surpris par le sens de "Q_DOUT[7:0]" comme sortie du processus. Mais cette sortie devient une entrée du processeur, non ?

### Comment interfacer un registre à la fois en lecture et écriture ?

Nous cherchons maintenant à implémenter des périphériques classiques, nous voulons dire du genre de ceux que l’on trouve habituellement dans un vrai processeur. Prenons le cas du timer0 dans un ATMega16. Il nous donne accès à un registre appelé TCNT0 qui peut fonctionner tout seul quand le timer0 est en route, mais dans lequel on peut aussi écrire, ou lire. Comment faire cohabiter donc toutes ces possibilités ?
Nous ferons probablement plus tard un exemple avec une entité et une architecture, mais pour le moment vous pouvez lire la section Réaliser un périphérique avec interruption pour vous en faire une idée.
Une bonne idée, par exemple, est d'interfacer les PORTs en sortie et en entrée. Nous ne l'avons jamais fait mais cela permettrait par exemple d'écrire :
On rappelle en effet que PORTB++ est équivalent à PORTB = PORTB+1
Le fait que PORTB se trouve à droite de l'affectation veut dire qu’il faut le considérer comme une entrée. Le fait qu’il apparaisse à gauche veut dire qu’il est considéré comme une sortie.
Il vous faut boucler la sortie PORTB sur l'entrée PORTB
La remarque précédente impose donc le principe suivant :

### Comment interfacer un FIFO en écriture ?

Nous donnerons un exemple simple plus tard. Pour le moment, notez que ceci a déjà été fait dans un autre projet en deux étapes :
* connecter l'entrée du FIFO à I_DIN
* réaliser le signal d'écriture du FIFO : L_WE_UART2 <= I_WE_IO when (I_ADR_IO = X"24") else '0'; -- write UART2 ADCL
Le résultat présenté ici peut être généralisé à tout registre. Nous voulons parler d'un périphérique qui a déjà son registre d'entrée.
* votre périphérique est combinatoire et a besoin d'un registre pour être commandé. Il (le registre) sera réalisé dans le process "iowr" car le case est dans un "if(rising_edge(clk))". Cette façon de procéder crée donc automatiquement un registre.
* vous récupérez votre périphérique sur internet ou ailleurs et il possède déjà son propre registre (comme le FIFO) et une entrée de validation d'écriture. Dans ce cas vous connectez directement votre entrée de validation en dehors du case, avec un when, comme cela est fait avec le signal d'écriture du FIFO. Vous connectez l'entrée du registre directement à I_DIN comme pour le FIFO.
* votre périphérique possède bien un registre en entrée mais sans entrée de validation d'écriture. Cela nous semble anormal mais nous l'avons déjà fait. Procéder alors comme si votre périphérique était combinatoire. Votre écriture dans votre périphérique aura lieu avec une période d'horloge de retard, et c'est le seul défaut.
* votre périphérique peut être un mélange des deux premiers points. Vous avez à la fois à gérer une valeur qui va être mise dans un registre (donc dans le case) et une opération validation ponctuelle. C'est ce qui se passe dans la section "Comment gérer un compteur 32 bits" un peu plus loin.

### Comment interfacer un FIFO en lecture ?

Puisque nous nous intéressons à un FIFO en lecture, cela laisse entendre que c’est un périphérique (un bout de VHDL en tout cas) qui viendra écrire dedans. Mais notre processeur qui viendra le lire. Ceci n’est pas sans conséquence :
Dans cette section nous allons nous intéresser seulement à l'interface entre le FIFO et le processeur. Nous laissons tomber les explications sur le "done" et aurons certainement l’occasion de revenir dessus lors d'une implantation pratique.
Quelques informations supplémentaires sur le VHDL correspondant :
Les signaux ci-dessus sont utilisés dans le process de lecture :
Nous n'avons pas mis l’ensemble du process ici mais seulement ce qu’il y a de nouveau pour cet exemple.
Il y a un autre point sur lequel il nous faut revenir : la construction du signal read qui va au FIFO. Il se fait avec le simple code :
Avec ce signal, toute lecture de PIND en C provoquera simultanément une évacuation de la donnée du FIFO : c’est un décalage dans le FIFO grâce à ce signal qui permet de récupérer la donnée suivante la prochaine fois qu'on viendra le lire.
La figure montre qu’il faudra aussi câbler le FIFO, ce qui n’est pas montré dans nos extraits de code VHDL. Mais il faut bien laisser un peu de travail à nos lecteurs.
Pour information, nous avons eu l’occasion d’utiliser cette technique avec un module le lecture de clavier PS/2 qui remplissait le FIFO au fur et à mesure. Cette technique est utilisée aussi dans la gestion de l'accéléromètre de la manette Nunchuk du projet pacman plus bas.

### Comment interfacer un compteur 32 bits ?

Le titre de cette section n'est probablement pas assez explicite. Soyons donc plus concret. Imaginons que nous devons compter des événements qui peuvent être nombreux et nécessiter donc 32 bits. Puisque nous avons un processeur 8 bits, il nous faudrait en principe 4 ports en sortie et 4 ports en entrées pour gérer ces 32 bits en entrée et sortie. Supposons que nous n'ayons absolument pas besoin de lire les 32 bits car une partie d'affichage (ou autre) s'en occupe. La question est alors : est-il possible par une simple écriture dans un PORT (8 bits) d'incrémenter un compteur 32 bits ? Voila la question à laquelle nous désirons nous atteler maintenant.
La technique consiste à utiliser un compteur avec un signal de validation "en" et de construire ce "en" comme on l'a fait pour le signal "read" de la FIFO.

#### Un exemple pour comprendre

Pour être concret, voici un exemple qui a été donné comme examen final à des étudiants. Il est sur 16 bits mais montre bien les problèmes à résoudre pour y parvenir. Voici le compteur proprement dit :
Seule l'entité a un intérêt et le signal s_angle qui est sur 16 bits. Le compteur est un peu spécial car destiné à un cœur CORDIC.
Le câblage de ce composant se fait dans io.vhd par :
Ce code laisse entendre qu’il se passera quelque chose par une écriture dans PORTA ! Une confirmation ?
De tout cet ensemble d'extraits de code, il est possible de déduire qu'une écriture d'une valeur X dans PORTA incrémentera le compteur 16 bits de X.

### Comment interfacer une mémoire RAM en la projetant dans l'espace mémoire ?

Ce problème a déjà été traité dans ce livre lors de l'apparition du projet pacman. Mais nous l'avons alors traité d'une manière qui mérite d’être expliquée de nouveau.

### Comment interfacer une mémoire RAM avec des PORTs ?

Au lieu de projeter la RAM directement dans la RAM de l'AVR, il est possible de passer par des Registres/PORTs pour ce travail. Pourquoi ne pas reprendre, par exemple, les registres de l'EEPROM ? Cette technique est cependant plus lente mais probablement plus facile à mettre en œuvre. Il nous a suffit de rajouter un PORT à un BRAM et de la connecter à nos PORTs.
Voici un exemple de programme associé :
Les fins connaisseurs de l'écriture en EEPROM auront remarqué que l’on n'a pas respecté la façon dont cela se passe avec un ATMega. Nous avons utilisé EEARH comme registre de commande alors qu'en principe c’est le registre d'adresse haute ! Cette façon de faire présente un inconvénient : il ne nous est pas possible de déclarer directement des variables dans cette mémoire alors que cela est possible avec un vrai AVR. Voici par exemple un code trouvé dans la documentation officielle de l'ATMega16 où l’on voit que le registre de commande est EECR et que 2 bits sont concernés EEWE et EEMWE.
Nous nous intéresserons à ce problème un peu plus tard. Les changements sont mineurs :
* changer de registre de commande (une adresse à changer)
* trouver un mécanisme pour que le bit EEWE revienne à 0 tout seul en une période d'horloge
* tester : c’est ce qui prend le plus de temps
Voir aussi : Examen final LO11 2015 qui utilise aussi une mémoire RAM accessible par des PORTs sur une architecture ATTiny861 décrite dans la partie TP de ce livre.

### Pourquoi ne pas vouloir refaire les périphériques de l'ATMega16 original ?

Tout le monde s'accorde pour dire que l’intérêt d'enfouir un processeur dans un FPGA est tout simplement que ses périphériques peuvent être taillés sur mesure dans la partie restante du FPGA.
Quelques exemples peut être ?
* L'ATMega16 que nous proposons dispose d'une liaison série. Elle n’est pas complète puisqu'elle ne gère qu'une vitesse fixe. Toute instruction visant à changer cette vitesse échouera donc. Nous n'avons pas encore rencontré de situation où cela présentait un problème sérieux !
* La liaison i2c est gérée par l'ATMega16 avec un ensemble de registres. Nous avions comme objectif, de copier cette gestion pour gérer la manette Nunchuk (année scolaire 2012/2013) mais avons fini par abandonner cette idée par souci de simplification. Bien sûr cela aura comme conséquence qu'une gestion d'un accéléromètre i2c, par exemple, nécessitera de tout refaire... mais la simplification en valait la peine. Pour information un périphérique i2c plus générique a été réalisé en janvier 2017 un peu plus loin dans ce chapitre.
* Le problème des timer mérite probablement plus d'attention. En effet ces derniers peuvent être utilisés dans les systèmes multitâches. Ne pas les implémenter comme dans le processeur original nécessite donc de lire le code source du noyau multitâche que vous désirez utiliser. Dans ce cas précis, ce qui est gagné sur le matériel (simplification par la description d'un timer maison) est perdu sur le logiciel (nécessité de vérifier que le timer maison est compatible avec le logiciel). Les compétences techniques de l'équipe chargée du projet seront donc capitales pour choisir entre les deux options.

## Réaliser un périphérique avec interruption

Le problème des interruptions a été évoqué ci-dessus et dans le chapitre sur l'ATMega8. Nous n'avons cependant jamais réalisé de périphérique qui réalise une telle interruption. C'est donc à cette tâche que nous allons nous atteler maintenant.

### Essai d'interruption

Dans le chapitre précédent nous avons déjà donné un programme qui utilise une interruption en réception rs232. Il vous faut commencer par essayer de le faire fonctionner. Le voici donc rappelé :
Lorsqu'un hyperterminal est connecté à 38400 bauds, 8 bits de données, sans parité et deux bits de stop, vous devez voir clignoter les leds au gré de vos envois par le clavier. Nous l'avons fait fonctionner à 100 MHz dans la carte Nexys 3 (architecturée autour d'un spartan6) sans problème.
Si ce programme fonctionne c’est parce que Juergen Sauermann, l'auteur du projet, a livré un exemple d'implantation matérielle d'interruption avec la rs232. Il nous faut donc lire ce code et l'adapter à nos besoins.

### Un pseudo-timer pour réaliser une interruption

Nous avons décidé, comme premier exemple, de réaliser une interruption par débordement d'un compteur/timer. Nous avons appelé cette section pseudo-timer car nous allons implanter un timer mais de manière très simplifiée (avec une pré-division fixe par exemple).

#### Le timer proprement dit

L'idée générale est de faire un compteur sur 20 bits mais avec lequel une écriture provoquera la seule mise à jour des 8 bits de poids fort.
Il suffit d’abord d'ajouter un process de gestion du timer
où pseudotimer est un signal 20 bits à déclarer dont seuls les bits de poids forts nous intéressent vraiment. Le reste est là pour réaliser une division de l'horloge qui fonctionne à 50 MHz. Comme on peut le voir la mise à 0 de l'overflow se fait par une écriture dans le timer. Pour votre information, ce n’est pas comme cela que fonctionne l' ATMega16. Et ce dernier offre la possibilité supplémentaire de mise à 0 de TOV0 par écriture directe dans le registre TIFR. Nous n'avons pas réalisé cette deuxième fonctionnalité par souci de simplification.

#### Autorisation de l'interruption timer

C'est un bit appelé TOIE0 du registre TIMSK (adresse 0x59) qui gère cette autorisation. Si l’on veut le gérer correctement il nous faut donc ajouter une ligne au process "iowr" comme ceci :
Voila, nous sommes prêt, il ne reste plus qu’à modifier le process "ioint".

#### Une lecture possible du timer

Il est possible de lire la valeur des 8 bits de poids forts de TCNT0 avec le process de lecture :

#### Process d'interruption

Avant toute chose, il faut connaître le numéro du vecteur d'interruption que nous tentons de réaliser. Cela peut se trouver dans la documentation officielle du micro-contrôleur ou dans le fichier "iom16.h" (/usr/lib/avr/include/avr/iom16.h avec un Linux Ubuntu) du compilateur avr-gcc. On touvre dans ce fichier (parmi bien d'autres choses) :
qui nous montre que le vecteur que l’on tente de réaliser porte le numéro 9.
Voici donc comment on a modifié le process "ioint" (intéressez-vous à ce qui concerne le vecteur 9) :
Ce code associé au programme c suivant fonctionne parfaitement :
Pouvez-vous calculer à quelle fréquence les LEDs s'allument et s'éteignent ?
Réponse : Si la fréquence est de 100 MHz et donc la période correspondante est de 10 ns. Le débordement du timer a lieu tous les 1024x1024x10 ns soit : 10485760 ns ce qui correspond à une fréquence de 95 Hz. Si vous regardez attentivement le code de l'interruption, vous verrez qu'elle compte de 0 à 100 et seulement à ce moment là, bascule le PORTC. Il faut donc compter 200 fois pour une période soit 2,097152000 s et donc une fréquence de 0,477 Hz.
Le fonctionnement du process ioint pour l'interruption ajoutée est dangereux et mérite d’être exploré plus en avant ! Il faut que d'une manière ou d'une autre le vecteur d'interruption disparaisse. Ceci ne peut être réalisé qu'avec TOV0. Mais ...

### Ressources

Téléchargez la ressource ATMega16. Le fichier "io_timer.vhd" implante le pseudo-timer de cette section "Réaliser un périphérique avec interruption" avec une interruption de débordement. Rappelons que ce fichier remplacera "io.vhd" dans le projet complet (ne pas mettre les deux fichiers dans un même projet même si les deux vous sont fournis !)
Mais le fonctionnement du flag TOV0 dans cette ressource ne correspond pas à ce qui se passe dans un vrai ATMega16. Nous allons donc ajouter quelques explications et la ressource correspondante.

#### Correspondance avec Juergen Sauermann et nouvelle ressource

Pour expliquer les changements réalisés, nous allons traduire la correspondance que nous avons eu avec Juergen Sauermann, l'auteur de ce cœur.
> Le vendredi 3 juillet 2015 Juergen Sauermann nous a répondu
Je vois. La phrase correspondante dans le manuel de l'AVR est probablement celle-ci :
THE OCF0A FLAG IS AUTOMATICALLY cleared when the interrupt is executed. Alternatively, the OCF0A flag can be cleared by softwar BY WRITING A LOGICAL ONE TO ITS I/O BIT LOCATION.
Ainsi la méthode dont je parlais était la seconde (WRITING A LOGICAL ONE TO ITS I/O BIT LOCATION.) tandis que toi tu te referais à la première (THE OCF0A FLAG IS AUTOMATICALLY CLEARED WHEN THE INTERRUPT IS EXECUTED).
Cela n'a pas été inventé par AVR mais déjà connu comme acquittement (ou INT_ACK) dans les Motorola 68020 et (peut-être dans des processeurs encore plus anciens). La bonne place pour générer INT_ACK est dans OPC_DECO.VHD, mais pas dans RETI. Plutôt dans le pseudo opcode d'interruption. Et cela parce que INT_ACK doit être généré au début de l'interruption et non pas à la fin.
Ajouter INT_ACK dans OPC_DECO.VHD est relativement simple:
1. Add an output INT_ACK to entity opc_deco
2. mettre INT_ACK à 0 par defaut (autour de la ligne 72):
3. Mettre INT_ACK à 1 quant l'opcode est exécuté (LIGNE 95):
Cela met Q_INT_ACK à 1 pour un cycle et peut être utilisé pour mettre la source d'interruption à 0. En théorie on pourrait créer un INT_ACK par source possible d'interruption au lieu d'un seul pour toutes les interruptions mais comme la majeure partie des interruptions ne sont pas automatiques, je ne ferai pas cela ici mais plutôt dans le process IOINT.
Tu peux maintenant connecter INT_ACK au process IOINT qui doit alors vérifier l'interruption courante pour générer le bon signal INT_ACK par source.
/// Jürgen
Notez que nous ne sommes pas sûr que Juergen nous tutoie, avec l'Anglais on ne sait jamais... Nous avons fait les modifications demandées.
Pour bien comprendre ce mail, il faut avoir à l'esprit qu'une interruption est réalisée par une insertion d'un opcode spécifique qui va mettre le drapeau d'interruption général (appelé I) à 0. Et ceci pour éviter toute nouvelle interruption pendant que l’on en exécute une. Ce drapeau I sera automatiquement repositionné à 1 par l'instruction RETI. Tout ce mécanisme est dans le cœur depuis le début... la seule chose qui a changé c’est l'apparition d'une nouvelle possibilité de mise à 0 d'un drapeau de manière automatique.
Nous publions dans cette section seulement le nouveau pseudotimer pour montrer qu’il est un peu plus complexe maintenant.
Nous avions promis en début de chapitre d'arrêter avec les versions différentes mais voilà, on ne fait pas toujours comme on veut. Voici donc une version qui permet la mise à 0 du bit TOV0 exactement comme dans celui que vous achèteriez : ATMega16 avec reset automatique du TOV0. Cette nouvelle ressource a été ajoutée en Juillet 2015.

## Projet pacman avec l'ATMega16 (2013/2014)

Le projet Pacman de l'année précédente avec l'ATMega8 était un programme C de plus de 700 lignes. On s'approchait alors des limites de l'ATMega8. Le fait d’utiliser un ATMega16 maintenant va nous donner une bouée d'oxygène pour la taille du code (16 ko de mémoire programme contre 8 ko).

### Lire aussi

* Projet pacman pour 2012/2013 dans ce livre
* Étude de la manette Nunchuk aussi dans ce livre
Présentons maintenant les objectifs du projets.

### Objectifs

Il s'agit de gérer un jeu de pacman à partir d'une partie matérielle donnée. Cependant, contrairement à l'année précédente, la partie matérielle donnée n’est pas complète comme le montre la section suivante. Quant à la partie logicielle, il sera assez difficile aux étudiants de reprendre le travail de l'année précédente. L'ajout d'un ennemi change fortement la donne.
Contrairement à l'année précédente nous allons utiliser la carte platine d'évaluation spartan3e starter kit.

### Matériel : gestion de l'accéléromètre de la manette Nunchuk

Rappelons ce qui a été fait jusqu'à présent. Nous avons commencé par utiliser un vieux Joystick avec 4 contacts pour haut, bas, gauche et droite. Nous avons remplacé ce périphérique par une manette Nunchuk mais avec comme souci de ne pas changer le programme C de gestion des directions de déplacement. Donc le périphérique développé l'année précédente lisait les valeurs analogiques retournées par Nunchuk et à partir d'un certain seuil positionnait un bit par direction. Il se comportait donc comme le joystick du départ... mais tout cela est peu adapté à la gestion des accéléromètres.
Vous allez partir de la version de l'année précédente avec une gestion du joystick analogique de la manette Nunchuk et lui ajouter une gestion des accéléromètres tout en retirant les comparaisons et les seuils associés. L'ensemble des données de la Nunchuk sera systématiquement stocké dans un FIFO lisible depuis le processeur. Fini donc la gestion matérielle pour remplacer le joystick analogique par un joystick numérique ! C'est notre processeur qui devra tout gérer !
Un module capable de gérer TOUTES les données de la Nunchuk est disponible dans un autre chapitre. C'est lui que vous allez utiliser et interfacer dans "io2.vhd". Vous utiliserez deux registres réservés à l’i2c dans l'ATMega16 :
* TWDR en adresse 0x23 pour les données
* TWCR en adresse 0x56 pour les commandes et états commande TWEN est en bit b2 (écriture) et l'état TWINT = data présent en bit b7 (lecture)
Voici un condensé sous forme de schéma de ce qu’il y a à faire :
Même si cette figure ne représente pas tous les registres et PORTs que nous utilisons dans le projet pacman complet elle nous montre l'essentiel. L'ensemble du travail consiste donc à câbler et ajouter des lignes dans les deux "case" des deux process de lecture et d'écriture. Ce qui est peut être un peu délicat à comprendre est la réalisation du signal "s_readFIFO" tout en haut de la figure. Ce signal est là pour vider le FIFO au fur et à mesure de ses lectures par le processeur. Il sera relié à l'entrée readFIFO du module NunchukTop.
Pour information, votre point de départ est le projet de l'année précédente qui peut se résumer comme présenté dans la figure ci-dessous :

### Logiciel : lecture du FIFO pour les données Nunchuk

Nous allons vous donner deux sous-programmes pour vous aider à démarrer votre projet.

#### Lecture du FIFO et demande de données

Voici le code source correspondant :
Il faudra le compléter par un code du genre :
pour gérer correctement les déplacements de pacman.

#### Attente d'un départ

Voici comment on peut bloquer tout en attendant un appui sur le bouton Z :
On rappelle pour comprendre le code, que les deux boutons sont peésents dans le 6° octet retourné par la Nunchuk (donc la case 5 de notre tableau) que les deux boutons sont les deux poids faibles et qu’ils sont à 1 lorsqu’ils ne sont pas appuyés.

### Logiciel : gestion du deuxième ennemi

Nous avons ajouté un deuxième ennemi pour cette année scolaire. Votre programmation en C devra obligatoirement le gérer.
Comme nous avons l'intention d'examiner, à terme, la réalisation du pacman avec un petit système multitâche, nous demandons aux étudiants de cette année scolaire de lire l’article sur un système multitâche non préemptif et d'essayer d’utiliser les techniques correspondantes pour ce jeu. Ce système écrit par Ron Kreymborg a été porté pour les AVR ICI (mais avec au autre compilateur C). Téléchargez alors "multi.zip" pour avoir le code source.
Voici un sous-programme permettant de gérer le positionnement de notre deuxième ennemi :
Les sous-programmes donnés comme point de départ de l'année précédente peuvent être repris aussi.

### Ressources pour commencer

On vous propose pour commencer votre projet un ensemble de ressources pour travailler. Voici ce que contient le fichier ReadMe.txt de la ressource :
Ce projet comporte :
* un processeur ATMega16 embarqué avec peu des périphériques originaux : seule la rs232 fonctionne
* un écran VGA gérant le pacman
* un périphérique gérant la manette Nunchuk de Nitendo.
* deux fichers ucf : un pour la carte spartan3 et un pour la carte spartan3E toutes deux de chez Digilent
Dans le répertoire Soft vous avez :
* un programme c ne gérant qu'un seul fantome sur les deux : pacman.c
* un autre programme c ne gérant qu'un seul fantome mais une poursuite plus sofistiquée du pacman par le fantome. Il a été réalisé par deux étudiants de GEII en projet 2012/2013 (Guillaume Demarquez et Pauline Skorupka) : pacmanEtu.c
* un script shell pour mettre le programme en question dans le processeur (par l'intermédiaire du fichier .bit) puis télécharger dans le FPGA.
Notez que la façon de télécharger utilisée dans le script n'est utilisable que sur un port parallèle. Si vous utilisez adept de digilent, il vous faudra tout adapter.
L'ensemble est téléchargeable ici :
* pacman2013.zip

### Correction partielle

La correction matérielle complète est téléchargeable ici : nexys3PacmanAtmega16.zip pour la carte Nexys 3. Le programme C de cette correction est le mien mais les étudiantes (KONAK Sumeyye et SAVRY Maelle) vous présentent le leur ici dans le WIKI de l'IUT de Troyes. Pour une fois nous nous demandons sérieusement si la rédaction du rapport de ce projet ne dépasse pas en qualité celui du tuteur que vous avez sous les yeux ? Allez donc lire pour comparer.