# Very High Speed Integrated Circuit Hardware Description Language/Programmer in Situ et déboguer

Nous avons l'intention d'examiner dans ce chapitre toutes les techniques permettant de programmer et déboguer in Situ. Tous les projets réalisés dans les chapitres précédents nécessitaient une compilation complète de tout le projet chaque fois qu'un programme C était changé. Cela était parfois très long (10 min sur de vieux PCs) et donc assez contraignant. Il est ainsi intéressant de passer en revue diverses techniques pour simplement envoyer un programme dans la RAM du FPGA sans recompiler l’ensemble du projet. Dans l'univers des Microcontrôleurs existants, on appelle cela la programmation in-situ (In-System Programming : ISP dans la suite de ce document). Chez Atmel ou chez MicroChip par exemple l'ISP (In-System Programming) se décline en deux façons différentes : le SPI (Serial Peripheral Interface) et le JTAG. Cet ensemble est aussi parfois appelé (en) In Circuit Serial Programming (ICSP). Les fichiers envoyés seront au format HEX (Intel) ou binaire.
Après avoir examiné ces problèmes de programmation, nous nous tournerons vers les problèmes de débogage certainement encore plus complexes.

## Introduction (pour Xilinx)

Même si nous avons déjà eu l’occasion d'évoquer ce sujet dans une annexe du chapitre sur l'AVR ATMega8, nous pensons qu'une deuxième explication ne sera pas de trop. Regardez attentivement la figure ci-dessous :
Elle montre clairement que la chaîne de compilation matérielle (à gauche) n'a aucun lien avec la chaîne de compilation logicielle (à droite). Autrement dit, l'ISE de Xilinx est incapable de mettre un programme logiciel compilé en mémoire matérielle. Comment peut-on modifier ce fait ?
La méthode généralement utilisée consiste à proposer un utilitaire capable de transformer le résultat d'une compilation en un fichier VHDL. Les débutants en VHDL ont un peu de mal à se faire à cette idée : quel est le rapport entre VHDL et mémoire ? Pour eux, nous allons proposer plus loin dans ce chapitre une section qui examine la réalisation de mémoire en VHDL. Voici en image un exemple encore lié à l'ATMega8.
Qu'a-t-on ajouté ? On a ajouté un programme externe "make_mem" capable de transformer un fichier HEX en fichier VHDL qui sera inséré dans le projet (pour l'exemple avec le fichier mem_content.vhd). Cette façon de faire est à mon avis la plus simple et tout projet devrait la proposer au moins pour les débutants. Mais elle est cependant loin d’être un must. En effet tout changement dans le programme C nécessite de réaliser la compilation des programmes C ce qui prend quelques secondes puis de transformer le fichier HEX en fichier VHD ce qui ne prend aussi que quelques secondes et enfin de compiler le projet VHDL en entier ce qui peut prendre un certain temps (10 - 15 min sur un vieux PC). Lancer ISE pour compiler un projet important est toujours consommateur de temps. Il nous faut donc explorer d'autres solutions et c’est bien l'objectif de ce chapitre.

## Présentation générale des techniques de base

Pour ne pas tout réinventer et rester dans l'univers de ce qui se fait couramment avec les micro controleurs modernes, nous voyons diverses façons de procéder pour envoyer un programme compilé dans notre FPGA :
* utiliser le protocole ISP existant (sur certains processeurs),
* utiliser la RS232,
* utiliser l'interface JTAG.
La mise en œuvre d'un de ces protocoles consiste donc à ajouter un module capable de dialoguer avec un PC pour remplir les mémoires. Cela consiste à intervenir directement dans le FPGA : votre fichier BIT est téléchargé dans votre FPGA et vous voulez vous servir d'une liaison quelconque pour y mettre un programme. Mais un petit coup d’œil à la figure précédente montre qu’il y a un autre point d'entrée : le fichier d’extension ".bit". C'est ce que nous allons examiner maintenant.

### Avant de commencer, un petit détour par data2mem

"data2mem" est un utilitaire fourni par Xilinx pour résoudre les problèmes de compilation. Il est capable de prendre des valeurs hexadécimales et les mettre dans la BRAM du FPGA. En clair, il est capable de prendre un fichier matériel compilé (d'extension ".bit") et d'y insérer des valeurs qui finiront dans la BRAM lors du téléchargement du fichier ".bit". Cette solution réduit elle aussi le temps de compilation matérielle : vous compilez votre logiciel et data2mem vous insère le résultat dans le fichier ".bit" que vous n'êtes donc pas obligé de refaire en entier. Pourtant, même si nous avons l'intention d'essayer cette solution, nous préférons aller de l'avant et explorer d'autres solutions. La raison est simple :
* "data2mem" est un outil en ligne de commande, donc délicat à maîtriser
* "data2mem" est un outil Xilinx, donc propriétaire dont on ne connaît pas le code source. Le forum de Xilinx indique même que certaines versions de l'ISE ont un data2mem comportant des bogues (surtout pour les spartan6 !!!)
* "data2mem" nécessite une connaissance des BRAM approfondie ou en tout cas connaître la façon dont votre BRAM est utilisée par votre outil de synthèse.
Pour ceux qui ne sont pas convaincu par la première raison indiquée, voici le résultat affiché quand on le lance :
De quoi refroidir quelques ardeurs non ?
L'environnement Microblaze XPS (Xilinx Platform Studio) utilise data2mem, comme vous pouvez le constater dans le chapitre sur le MicroBlaze de ce livre.
On le trouve aussi dans un autre projet openMSP430, sous la forme :
qui montre que l’on peut charger des fichiers au format elf (ce qui était vrai aussi pour le MicroBlaze). Cette commande nous fait aussi deviner la présence d'un fichier spécial d'extension .bmm, qui est un fichier pour décrire où et comment la ROM est implanté dans les BRAM (Bloc RAM). Et c’est là que réside le problème. Nous avons utilisé cette technique pour deux processeurs et la réalisation du fichier .bmm n'a jamais été une partie de plaisir. Nous avons même échoué sur un troisième projet. Il nous manque certainement de l'expérience pour comprendre quelques subtilités mais préférons passer du temps à utiliser les SOCs plutôt que de devenir spécialiste de data2mem ! La réalisation automatique de ce fichier .bmm est décrite un peu plus loin dans ce chapitre.
Il existe aussi un outil sous DOS/windows pour faire cela comme expliqué dans la section suivante pour le cas particulier du picoBlaze mais nous ne l'avons jamais utilisé.

### Utiliser la RS232 pour programmer

Nous avons déjà eu l’occasion d'examiner la RS232 dans un chapitre précédent. Nous allons donc utiliser les résultats de ce chapitre pour réaliser une programmation à travers la RS232. L'avantage de cette technique est qu’il n’est pas difficile de trouver pour n’importe quel système d'exploitation (Windows, (GNU) Linux, Mac OS X ou autres) un logiciel capable d'envoyer et/ou recevoir des fichiers par une liaison série qui passe éventuellement par l’USB.

### Utiliser un cœur Série asynchrone pour programmer

Il existe deux projets chez OpenCores qui ont des fonctions élémentaires de programmation par la liaison série. Ces cœurs peuvent être utilisés pour remplacer un bootloader. Leur mise en œuvre est un peu plus générale que ce que l’on vient de faire mais cela nécessite un peu de réflexion. Il faudra en effet partager la mémoire entre le processeur et le cœur série. Pour les trouver chez Opencores :
* Dans les projets chercher "Communication contoller" puis "UART to Bus" de Litochevski Moti et Muller Steve
* Dans les projets chercher "(en) System controller" puis "RS232 System Controller" de John Clayton.
Le premier projet est disponible en Verilog et en VHDL et relativement compact. Le deuxième projet est en verilog et a servi pour développer un processeur.
Développons un peu l’utilisation pratique de ces deux cœurs à partir de leur documentation (nous ne les avons pas mis en œuvre).

##### Projet UART to Bus

Ce projet est destiné à réaliser une communication série permettant de remplir ou de lire une mémoire. Il comporte deux modes distincs :
* un mode texte où commandes et données sont envoyés en caractères ASCII donc facile à utiliser avec un hyperterminal
* un mode binaire où commandes et données sont envoyées et reçues en binaire.
Les deux seules commandes sont :
* 'W' ou 'w' pour écriture avec la syntaxe : "W 0D 16FFRC" où RC désigne le retour Chariot réalisé par LF ou CR. Cette commande écrit 0x0D en adresse 0x16FF.
* 'R' ou 'r' pour lecture avec la syntaxe "R 1A2FRC" où RC désigne le retour Chariot réalisé par LF ou CR. Cette commande retourne le contenu d'une mémoire en adresse 0x1A2F.

##### Projet RS232 System Controller

En plus des commandes du projet précédent (write et read) une nouvelle commande 'i' pour initialisation qui est un reset est présente. Ce projet nécessite un bus Whishbone qui peut être partagé entre un processeur et ce cœur ce qui en fait un outil de choix pour développer un processeur en VHDL.
Ce qui reste à faire dans ce chapitre est la réalisation d'un chargeur de mémoire universel par rs232 (sans bootloader) d'un fichier HEX et tout cela de manière indépendante du fondeur...

### Utiliser l'interface Série synchrone pour programmer directement

L'interface In System Programming (ISP) peut se traduire par "programmation à même le circuit". Dans ce chapitre nous garderons la terminologie anglo-saxonne.
Ce type de programmation des Microcontrôleurs est assez généralisée. On la trouve bien entendu dans les AVR de chez Atmel et dans les microcontôleurs PIC.
Ce système est tiré du protocole Serial Peripheral Interface : certains constructeurs respectent le nom des broches de ce protocole, d'autres non.
Utiliser le protocole In-System Programming (ISP) existant est probablement très attractif. Cela permettrait par exemple d’utiliser un environnement de développement comme AVRStudio de compiler avec cet environnement et de télécharger directement dans le FPGA, tout cela de manière transparente. Mais le protocole utilisé ne dépend-t-il pas des fabricants et voir des familles chez un même fabricant ? La réponse est évidemment affirmative. Cela lui enlève beaucoup d’intérêt du coup : vous allez passer beaucoup de temps à développer un module ISP pour un AVR (par exemple), mais ce même module sera incapable de dialoguer avec MPLAB et un PIC !
Nous présentons quand même un cœur qui permet de ne pas partir de rien !

#### Un cœur chez OpenCore

* spi master slave de Doin Jonny est probablement un cœur à examiner pour continuer ce chapitre. Sa description est traduite maintenant.
Doin Jonny, auteur de ce cœur écrit : Ce projet est parti du besoin d’avoir une simple interface SPI écrite en VHDL pour utiliser un interfaçage FPGA/circuit générique.
Le cœur ainsi réalisé génère un circuit petit et efficace capable d'opérer avec des horloges lentes jusqu'à des horloges de 50 MHz.
Ce projet comporte deux cœurs indépendants : SPI_MASTER et SPI_SLAVE le maître et l'esclave.
Les deux cœurs sont écrits en VHDL avec une architecture pipeline et des domaines d'horloge bien séparés pour l'horloge du bus SPI et celle de l'interface parallèle.
La conception ciblait originellement les Spartan-6 de chez Xilinx mais est écrit en code synthétisable indépandant de la téchnologie. Les cirsuits préservent les ressources d'horloge du FPGA en utilisant directement l'horloge rapide pour toutes les bascules avec une entrée "clock enables" (CE) pour les registres.
Le maître et l'esclave ont été vérifiés en hardware en utilisant la carte "Digilent Atlys board (Spartan-6 @100 MHz)" avec une horloge SPI de 500 kHz à 50 MHz, avec aucun déphasage et des opérations robustes.
Si vous trouvez ce cœur utile, faites le moi savoir: jdoin@opencores.org
Performances
* cœur VHDL, complètement synchrone, conçu avec une architecture RTL pipelinée classique, avec une horloge globale grande vitesse
* Interface SPI très petite et efficace
* paramétrisable avec des generics : (N, CPOL, CPHA, PREFETCH, SPI_2X_CLK_DIV)
** modes SPI (CPOL, CPHA): supporte les modes 0,1,2,3
** largeur des mots (N): de 8 bits à le limite de synthèse (accepte n’importe quelle largeur de mots)
** Lookahead input data request (PREFETCH): pipelined data request for back-to-back data transmission
** SPI diviseur par 2x de l'horloge rapide système
* Très économique : pas de FIFO, juste un buffer de sortie parallèle pour les données reçues
* Lecture et écriture parallèles similaires à de la RAM
* domaines d'horloges indépendants pour le bus série et la lecture et l'écriture du bus parallèle avec des pipelines de transferts
* Peut être utilisé pour contrôler des cirsuits généraux SPI (maîtres) ou comme interface à des unités centrales (esclaves)
* Indépendant du fondeur, entièrement conçu en LUT/FF, n'utilise aucune structure spécifique à Xilinx, IOBs ou registres à décalage
* Synthetise jusqu'à +210 MHz dans une Spartan-6 (lowest grade), en utilisant seulement de la logique CLB
* Vérifiée en silicium avec une horloge à 100 MHz, utilisant des fréquences SPI de 500 kHz jusqu'à 50 MHz dans une Spartan-6 XC6SLX45-2
* Très petite: 41 slices pour 2 ports (une interface maître + une interface esclave) avec 32 bits de largeur de mots
Signalons qu'un autre chapitre de ce livre utilise ce cœur SPI (partie esclave de ce cœur) dans un domaine complètement différent (Communication entre processeur et FPGA).
Avant de passer au vif du sujet, il nous faut examiner là où l’on a l'intention de mettre le programme, j’ai nommé les mémoires.

## Mémoire et FPGA

Vous pouvez lire la section RAM et FPGA du wikibook Conception et VHDL qui vous montre un exemple d'utilisation de RAM en VHDL.
Tous les FPGA modernes proposent deux sortes de RAM : une mémoire bloc (appelée BRAM dans la suite de ce document) et une mémoire distribuée (appelée DRAM dans la suite de ce document).
Si vous voulez écrire dans ces mémoires en VHDL, il vous faut respecter un certain nombre de règles pour que votre outil de synthèse infère la présence de mémoires. S'il ne le fait pas, il va remplacer la mémoire par de la logique traditionnelle, ce qui est très couteux en termes de ressources. Le problème est que la façon de faire comprendre que vous avez de la RAM est très dépendant du fabricant du FPGA. Nous allons nous contenter de présenter les techniques associées au constructeur Xilinx pour le moment.

### RAM prioritaire à l'écriture

Voici comment procéder si l’on veut cibler un composant Spartan3 :
Et maintenant voici comment procéder pour un composant spartan3E. Pour ce composant il semble qu'une RAM à lecture prioritaire soit plus adaptée :
Il serait intéressant de mesurer le coût d'une erreur à ce niveau : que se passe-t-il si l’on ne respecte pas les conseils Xilinx : utiliser un modèle spartan3E sur un spartan3 ?
Nous renvoyons le lecteur à la documentation officielle Xilinx pour qu’il se fasse sa propre idée.

### Une mémoire morte

Une mémoire morte est simplement une RAM initialisée. Cette définition est très discutable, mais ce que l’on cherche à dire, c’est que nous utiliserons ce genre de mémoire pour mettre un programme dedans. Il y a bien longtemps, ce rôle était dévolu aux ROMs ou EPROMs puis aux EEPROMS. Aujourd’hui les processeurs ont des instructions pour écrire dans la mémoire programme : celle-ci est donc, comme une RAM, à écriture ou lecture. Nous donnons un exemple VHDL maintenant :
Le fait que le signal "ram_type" soit déclaré comme "array(63 downto 0)" fait que la ROM est remplie à l’envers : la valeur X"0200A" se trouve en adresse 63 tandis que la valeur X"0400D" se trouve en adresse 0.
La première fois que nous avons implanté ce type de programme nous pensions que ce type de mémoire était forcément implantée en BRAM mais comme nous l'expliquons dans un autre livre, ce n’est pas toujours le cas. Cela peut dépendre en fait de la version de l'ISE utilisée !

### Technologie Xilinx

Tout ce qui a été fait dans ce début de chapitre sur les mémoires est relativement portable : le code est du vrai VHDL et l’on peut donc penser que n’importe quel environnement de développement l'acceptera. Nous allons nous attacher à un cas particulier de mémoires propres à Xilinx. Vous rencontrerez plusieurs fois le bandeau de définition ci-dessous dans ce document mais cette terminologie est très importante.
On appellera dans la suite :
* RAMB4 un RAM/ROM de 4 kbits
* RAMB16 un RAM/ROM de 16 kbits (2 ko)
* RAMB16_S18 une RAMB16 avec 18 bits de données (2 ko) : 16 bits et deux bits de parité non utilisés pour nos besoins mais qui le sont pour le picoBlaze.
* RAMB16_S18_S9 une RAMB16 avec deux PORTs indépendants 18 bit de données (16 kbits) pour le PORT appelé "A" et 9 bits pour le PORT appelé B.
À notre connaissance ces RAMB16 existent encore dans les FPGA plus modernes comme les Spartan 6. Par contre ils peuvent avoir jusqu'à 4 PORTs indépendants mais certains sont limités en lecture seulement. Cette augmentation du nombre de PORTs indépendants permet d’éviter d’utiliser de la DMA. Nous avons eu l’occasion de partager une RAM entre un processeur et une petite carte vidéo par exemple.
À partir de maintenant, nous allons examiner les différentes techniques en les classant par projet. Cela permettra au lecteur intéressé par un projet particulier de pouvoir se déplacer directement à son projet et de lire ce qui a été fait.
* Si vous désirez adapter ce qui est traité ici à un autre cœur non évoqué ici, lisez la section concernant le PIC. Elle n’est pas très étoffée mais elle explique partiellement comment utiliser data2mem à partir de fichier HEX.
* Si vous désirez adapter la programmation par liaison série à un autre fondeur que Xilinx, nous n'avons pas encore grand chose à vous conseiller. Espérons que cela changera dans le futur.

## Projet picoBlaze

Cette section est dédiée au picoBlaze et à toutes les méthodes que l’on peut envisager pour le programmer.

### Utiliser data2mem en sept étapes pour le PicoBlaze

Cette section est une traduction d'un document écrit par Ken Chapman (qui a conçu le picoBlaze) mais nous ne l'avons pas encore testée.
* Faire votre implantation avec l'ISE, cela vous donnera 3 fichiers importants parmi d'autres :
** design_name.ncd - fichier général de données pour le FPGA
** design_name.bit
** program.psm - programme en assembleur pour PicoBlaze.
* Copier les fichiers 'PB_BMM.EXE' et 'change_pb_bits.bat' dans votre répertoire de travail.
* Opérer les changements de votre programme.
* Ouvrir une fenêtre DOS dans votre répertoire de travail.
* Exécuter le fichier bat en utilisant la commande (noter l'absence d'extensions)...
* En supposant un assemblage correct de votre programme, vous verrez apparaître les noms d'instance de tous les blocs mémoire de votre conception. Entrer le nombre de BRAM associé à votre programme.
* Un nouveau fichier .bit sera automatiquement généré mais précédé par 'new_' (e.g. new_design_name.bit) que vous pouvez charger avec Impact (outil de chargement dans le FPGA).
Il est facile de remarquer que la description ci-dessus ne fonctionne que sous Windows. Sous Linux, Nous pensons que tous les outils existent mais probablement pas le fichier batch ! Ce qui est nécessaire est un fichier MEM en fait, cela veut dire que sous Linux quand vous exportez avec kpicosim, il faut choisir le fichier .mem. PB_BMM permet de fabriquer le fichier .BMM à partir du fichier .NCD. Une recherche rapide ne m'a pas permis de trouver PB_BMM sous Linux !!!!

### Utiliser la RS232 pour remplir la mémoire programme du picoBlaze

Nous avons l'intention dans ce chapitre d'examiner la possibilité de remplir la mémoire programme du PicoBlaze à l'aide de la liaison série. Ce problème est très lié à Xilinx et nous utiliserons donc les logicores du chapitre sur la RS232 précédent.
Nous avons fini par abandonner l’utilisation du picoBlaze. Cette section restera donc comme cela sauf si un lecteur veut bien la compléter.

## Projet coreATMega8

Le coreATMega8 est décrit dans le chapitre Embarquer un Atmel ATMega8 et sa chaine de compilation est particulièrement examinée dans Annexe I (transformer un fichier HEX en VHDL). Nous reproduisons sa figure correspondante ici :
Il est facile de rester coi devant cette figure en n'en comprenant pas vraiment le sens. Il y a un aspect séquentiel qu'il est difficile de représenter :
* d'abord j'utilise l'ISE pour réaliser le fichier d'extension .bit
* je compile le code source en c pour faire le fichier .elf
* j'utilise directement data2mem pour insérer le programme .elf au bon endroit dans le fichier .bit (à l'aide du fichier .bmm)
* je télécharge le fichier .bit avec Impact ou autre
Seules les trois dernières étapes qui sont très rapides sont nécessaires pour modifier un programme si la partie matérielle est déjà réalisée.
Le problème évoqué à la section précédente est de réaliser le fichier memory.bmm (qui apparait dans la figure ci-dessus) et qui correspond à notre processeur softcore ! Pour éviter de chercher nous le donnons tout construit :
Ce sont les valeurs derrière le mot clé "PLACED" qu’il faut être capable de retrouver. Nous avons eu l’occasion d’utiliser deux versions de l'outil de synthèse et les deux versions nous ont donné des valeurs de placement différentes (pour un même FPGA). Nous les avons trouvé à l'aide de l'outil PlanAhead (visualisation des cellules FPGA utilisées) en cherchant dans les netlists les RAMB4_S4_S4 correspondant au programme.
Un exemple PlanAhead est présenté ci-contre : le principe est simple vous cherchez les BRAM dans la partie gauche (les netlists) et si vous cliquez dessus on vous présente en blanc dans le FPGA où se trouve le composant en question. Et mieux encore dans une partie non présentée dans la figure, on vous donne la localisation exacte dans le FPGA, autrement dit les valeurs dont vous avez besoin pour mettre après le mot clé "PLACED".
C'est le genre de travail qu’il est difficile à faire quand on débute, surtout que PlanAHaed est, à mon avis, assez mal documenté.
Voici en image la description de ce que je viens de dire : trouvez les bulles dans l'ordre, "je choisis ici", "Je suis ici" et "J’ai besoin de cela". Par rapport à l'exemple précédent on a ajouté la partie très utile pour trouver l'emplacement physique des ROMs qui nous montre que la première "RAMB4_S4_S4" est en "X0Y0".
Plutôt que d’utiliser planAhead vous pouvez tout simplement ajouter le fichier bmm dans votre projet en retirant les LOC= mais en laissant le ";". ISE vous générera un autre fichier bmm (votreFichier.bmm génnérera votreFichier_bd.bmm) avec les locations remplies. Par exemple, dans un autre projet, nous avions utilisé le fichier
et l'ISE nous a généré :
Ce fichier a été réalisé avec les contraintes
dans le fichier UCF.
Il est possible de n'utiliser aucune contrainte dans le fichier ucf, c’est alors le compilateur qui placera où il veut et mettra votre fichier BMM à jour en lui ajoutant, comme dans la remarque précédente, "_bd" avant le ".bmm". Cela est très utile si planHead ne fonctionne pas.
Une fois ce fichier réalisé, il faut le retirer du projet : il n'y a donc pas lieu de garder de fichier bmm dans le projet mais par contre il serait bon de transformer les positions trouvées dans le fichier bmm en contraintes dans le fichier ucf pour éviter toute mauvaise surprise.

### Utiliser des RAMB16_S4_S4 avec data2mem

Finalement après avoir tourné en rond pendant près d'un mois (heureusement pas à plein temps), nous avons abandonné l’utilisation des RAMB4_S4_S4 propres au Spartan2 pour utiliser les RAMB16_S4_S4 du spartan 3 mais ceci a comme conséquence une modification de certains fichiers du cœur. Le fichier BMM donné plus haut correspond à cette option.
En 2011, nous avions laissé les codes sources de cette modification directement dans cette section. Mais nous avons décidé de les retirer car nous avons fait trop de modifications en dehors des fichiers qui étaient ici.
Pour avoir la dernière version, télécharger le fichier arduinoMega8.zip contient la version avec la nouvelle instruction SPM. Vous pouvez l’utiliser normalement (avec data2mem). Nous y avons aussi inséré la possibilité de réaliser une mise à 0 automatique d'un drapeau d'interruption, comme pour l'ATMega16 d'un autre chapitre. Ceci est mis en œuvre dans le fichier io_timer.vhd.
Rappelons encore que c’est la version la plus à jour de l'ATMega8.
Après téléchargement vous trouverez le cœur complet. Il ne doit contenir que 19 fichiers (18 en vhdl et 1 seul ucf) Il y a plus que 19 fichiers fournis, mais vous devrez choisir un seul parmi io.vhd io_timer.vhd io_corcic.vhd à mettre dans le projet. De même un seul ucf sera à utiliser. Dans le sous-répertoire soft vous avez quelques scripts qui utilisent data2mem et des fichiers sources. Il vous faudra adapter les scipts :
* à votre FPGA spartan3, spartan3E ou spartan6 (is sont pour spartan6 par défaut)
* à votre système d'exploitation (ceux fournis ne fonctionnent que pour LINUX).
Si vous ne voulez pas avoir de problème avec votre version d'ISE qui ne placera pas forcément les BRAM au même endroit que pour nous, vous pouvez donner des contraintes de placement dans votre fichier ucf :
Cela vous évitera de changer le fichier bmm donné plus haut !
Ces contraintes fonctionnent pour la carte Spartan3, Spartan3E, Basys2 et probablement Nexys2 de chez Digilent.
Elles ne fonctionnent pas par contre pour la carte Nexys3 (équipée d'un spartan6). Nous les avons changé comme :
ainsi que le fichier memory.bmm correspondant (renommé memoryS6.bmm pour l’occasion) :

### Petit résumé de ce que nous savons faire à ce point

Votre partie matérielle est complètement réalisée et compilée. Vous avez donc à votre disposition un fichier BIT et l'outil data2mem va être chargé de modifier le fichier BIT à partir d'une compilation de votre nouveau programme source supposé être en C pour la suite des explications.
* Nous commençons par compiler notre programme c pour obtenir un fichier au format ELF :
* Nous écrivons notre ficher BMM donné plus haut à l'aide d'un éditeur de texte
* nous lançons data2mem :
Pour nous un nouveau fichier BIT est créé : avr_fpga_rp.bit.
Ce nom est probablement un nom par défaut de data2mem puisque nous n'utilisons pas l'option "-o b" pour donner un nouveau nom. L'option proposée ici "-o h" génère un fichier VHDL qui nous a été utile pour construire le fichier BMM ! Si donc vous remplacez "-o h" par "-o b" votre fichier BIT créé s'appellera "new_avr_fpga.bit".
* Le fichier avr_fpga_rp.bit est chargé dans le FPGA
Un petit script peut faire le travail complet :
à condition d’avoir ADEPT installé (ADEPT est l'utilitaire de Digilent pour télécharger dans le FPGA).
Sur un PC acheté en 2002, la compilation complète de l'AVR prend environ 10 minutes alors que les opérations ci-dessus en prennent à peine une ! Il est probable que certains lecteurs trouvent rétro d’utiliser un PC si vieux... pourtant nous n'aurions jamais écrit cette section si nous ne nous étions pas trouvé avec des temps de compilation énormes. Nous avons eu un tel mal à trouver le bon fichier BMM que nous aurions sans doute abandonné en cours de route si notre seul objectif était de gagner seulement quelques minutes... Mais pour en gagner près de dix, le jeu en valait la chandelle.
Quelle que soit votre opinion, maintenant le travail est fait et, pour ne rien gâcher, publié et accessible gratuitement...

### Utiliser la RS232 et un bootloader pour ATMega8

Note : Cette section ne débouche malheureusement pas sur un ensemble fonctionnant correctement. Nous allons abandonner sa rédaction pour un temps comme nous l'avons fait déjà l'année précédente à la même époque pour nous consacrer à d'autres parties (de ce livre). Ce que nous espérons faire dans le futur :
* Réimplanter l'instruction SPM pour qu'elle soit plus conforme à celle des ATMega (Nous avons fait un pas en avant le 7 juillet 2015 après deux ans (pas à plein temps).... Soyez donc patient)
* Faire fonctionner le bootloader Arduino
Avant de s'intéresser à notre problème particulier, nous allons passer en revue le problème du bootloader dans les Atmel AVR commercialisés. Le mot bootloader est traduit par Chargeur d'amorçage dans Wikipédia mais nous n'utiliserons pas cette terminologie.

#### Le bootloader dans les AVR

L'idée est de réaliser ce que l’on trouve dans le petit système de développement appelé (en) Butterfly ou dans l’Arduino. Ces systèmes très bon marché sont architecturés autour d'un bootloader, c'est-à-dire un petit programme qui démarre le processeur en attendant qu'un ensemble de données lui arrive par la RS232. S'il y a des données, elles correspondent à un programme et le bootloader sera chargé de modifier la mémoire programme en conséquence. Tout ceci est bien plus facile à dire qu’à faire. Nous voyons un certain nombre de questions auxquelles il nous faut répondre pour continuer ce travail :
* le programme bootloader est-il dans un endroit particulier ?
* peut-on envoyer directement un fichier hex ?
Pour la première question, oui le bootloader est dans un endroit particulier qui semble dépendre de la famille des AVR. Un bootloader de l'Arduino pour l'ATMega8 contient
dans son makefile, ce qui montre très clairement qu’il est à un endroit très précis. En général le bootloader est en mémoire haute. Sa taille est classiquement 1 ou 2 ko.
Pour la deuxième question, il n'y a pas de réponse générale. Tout dépend du protocole, donc du code source du bootloader (et du programmateur). Par exemple le protocole STK500 envoie les programmes à écrire dans la mémoire flash en binaire. C'est le programmateur sur PC qui s'occupe de la transformation hex vers binaire (pour nous avrdude)
Nous pensons que les explications données sont suffisantes pour une compréhension de ce qui va suivre. Si vous voulez d'autres informations plus générales nous avons créé un chapitre dédié aux bootloaders dans les AVR (pas très avancé) dans un autre projet : Les microcontrôleurs AVR.

#### Avant Juillet 2015

L'instruction SPM a été implantée en 2011 mais de manière trop différente de celle de l'ATMega8 du commerce pour envisager l’utilisation d'un bootloader existant. Voici comment elle était implantée :
L'instruction SPM implantée dans le cœur et testée par le programme ci-dessus fonctionne de la manière suivante :
* Donnée (8 bits) dans R0
* Adresse d'écriture (16 bits) dans R31:R30
* Le registre SPMCR n’est pas utilisé !
* Le registre R1 n’est pas utilisé !!!
C'est ce dernier point sur le registre R1 qui sera changé un petit peu plus loin dans ce chapitre.
Il faut d’autre part avoir à l'esprit que vous pourrez faire fonctionner un bootloader sur un processeur qu’à une condition : qu’il ait une instruction pour pouvoir écrire dans la mémoire programme. C'est le cas pour l'ATMega8, l'instruction en question s’appelle SPM. La version correspondante du cœur se trouve dans le répertoire /AVRComplet16_S4_S4/25MHzAVR/ du fichier ATMega8_pong_VGA.zip qui contient une version améliorée du processeur, capable de gérer les interruptions et l'instruction SPM.
Mais ...
* La version téléchargeable chez OpenCore ne gère pas l'instruction SPM !!! en tout cas pour le moment (14 juillet 2011 à 14:21 UTC) et c’était encore le cas en Juillet 2015 : Juergen Sauermann, auteur du cœur, nous a dit qu’il ne pouvait plus accéder pour modification au site Opencores.org. Nous continuerons à gérer toutes ces versions avec notre site personnel, mais il nous faudra bien un jour trouver une autre solution !
Intéressons-nous maintenant à la façon dont nous allons réaliser les choses dans notre coreATMega8.

#### Protocole STK500v1 de l'Arduino

Nous avons comme rêve de réaliser un protocole STK500 et un bootloader correspondant dans notre cœur ATMega8. L'intérêt serait de programmer notre FPGA directement à partir de l'environnement Arduino ou même de l'environnement AVRStudio (remplacé par MPLABX depuis l'achat d' ATMEL par Microchip).
À ce jour nous n'avons pas réussi à le réaliser. Mais nous pensons qu’il est intéressant de discuter les raisons de cet échec et ce qu’elles nous ont appris. Et nous n'avons pas abandonné l’idée de le faire fonctionner un jour... et ce projet progresse petit à petit.
Comme base de travail nous nous sommes servi du fichier source "/usr/share/arduino/hardware/arduino/bootloaders/atmega8/ATmegaBOOT.c" C'est un bootloader pour ATMega8. Nous avons réussi à le faire fonctionner mais le programme chargé ne démarre pas convenablement. Ainsi le programmateur avrdude croit discuter avec un Arduino. Dès que l'instruction SPM du bootloader est mise en commentaire, le dialogue entre notre ATMega8 et l'environnement Arduino se passe très bien. D'ailleurs ce même environnement ne sait même pas que rien n'a été écrit en mémoire programme...
Quant à la routine d'écriture en mémoire programme, nous avons déjà réussi à la faire fonctionner dans un autre contexte... mais pas correctement ici. Nous suspectons très fortement l'instruction ATMega8 SPM de ne pas avoir été implantée de manière suffisamment large pour un bootloader.

##### Documentation de l'instruction SPM

L'article AVR106: C functions for reading and writing to Flash memory précise que l'écriture dans la mémoire programme Flash se fait en trois étapes :
* écriture d'une page de 32 mots dans une mémoire tampon
* effacement de la page en mémoire Flash
* transfert du tampon dans la mémoire flash.
Tout ceci est contrôlé par le registre associé à l'instruction SPM : SPMCR.
Nous avons d’autre part trouvé les informations complémentaires suivantes en lisant le code source du bootloader Arduino :
* Une page pour l'ATMega8 est composée de 32 mots (64 octets).
* Le registre Z est R31:R30 (auxquels on ajoute RAMPZ parfois) contient l'adresse où l’on veut écrire.
* Le registre SPMCR est en adresse 0x37(0x57) est à implanter. Il sert à contrôler l'instruction SMP. Par exemple si l’on écrit 3 dedans avant SPM il effacera une page (pointée par Z), si l’on écrit 0x05 il écrira une page (pointée par Z) mais il faut le repasser en mode RWW avec 0x11 (dans SPMCR) auparavant, et en appelant SPM. S'il est mis à 1 cela met R1:R0 dans le registre temporaire de la Flash (pointé par le poids faible de Z).
* R29:R28 contient l'adresse du buffer en RAM
* R25:R24 contient la longueur à écrire
Donc, pour écrire un mot dans le buffer de page, charger le mot dans le registre R1:R0. S'arranger pour que le registre Z pointe sur le mot correct (bits b5 ... b1 [b0 est ignoré car on travaille en mots]) et mettre seulement le bit SPMEN du registre SPMCR et avant 4 cycles appler SPM.
* La gestion des adresses mémoire programme nous a posé des problèmes suffisamment longtemps pour que nous décidions d'y attirer votre attention. Le compilateur C gère les mémoires avec des adresses d'octets. Votre matériel les gère en adresse de mots de 16 bits. Il y a donc un facteur 2 entre les deux. Ainsi une compilation avec l'option de placer le bootloader en 0x1C00 (vu par le compilateur) est associée à une initialisation matérielle en 0x0E00 qui est 0x1C00/2.
* Nous n'avons pas réussi à faire fonctionner data2mem pour qu’il mette le bootloader à l'endroit voulu !!! Il était toujours en adresse 0x0000 malgré la bonne option de compilation. Probablement une option à chercher. Par contre nous avons réussi à le mettre à l'endroit voulu avec l'outil primitif make_mem !

##### Nouvelle instruction SPM (Juillet 2015)

Nous avons modifié l'instruction SPM comme suit :
L'instruction SPM implantée dans le cœur en Juillet 2015 fonctionne de la manière suivante :
* Donnée (16 bits) dans R1:R0
* Adresse d'écriture (16 bits) dans R31:R30 (appelé Z en assembleur)
* Le registre SPMCR n’est pas utilisé !
Cette implantation ne permet pas d’utiliser un bootloader tout fait car on y trouve des attentes sur le registre SPMCR qui ne fonctionne pas dans notre cas.
Le 7 juillet 2015, nous avons enfin réussi à faire fonctionner le programme :
où la seule partie intéressante est le main.
Ce programme écrit d'abord le mot "0xAA55" en mémoire programme dans des adresses consécutives, puis cherche ce même mot en affichant où il l'a trouvé. Excellent donc pour une vérification. Il montre que nous avons enfin réussi à faire cohabiter les instructions SPM et LPM. Cela peut sembler anodin, mais le fait que LPM travaille sur des adresses mémoires (programme) qui adressent des octets tandis que SPM adresse des mots nous a posé beaucoup de soucis. Désolé mais il ne faut pas grand chose parfois pour bloquer la mécanique.
Ce programme fonctionne normalement qu’il soit en adresse basse (comme un programme normal) ou en adresse haute (comme un bootloader). Pour info, la mise de ce programme en adresse haute se fait par :
* mise à true du generic BootLoader dans cpu_core.vhd pour qu'un reset l'emmène dans le bootloader
* compilation totale du projet VHDL
* chargement du fichier .bit
Ainsi, il est possible maintenant d'adapter le bootloader Arduino... Affaire à suivre....

#### Bootloader Arduino modifié

Puisque notre instruction SPM n’est pas conforme à celle de l'ATMega8 du commerce, il nous faut changer le bootloader. Par exemple le bootloader des premiers Arduino ATMega8 attend avant chaque instruction SPM que certains bits du registre de contrôle SPMCR passent à 1. Puisque nous ne gérons absolument pas ce registre, le code du bootloader est à changer... Voici donc une version modifiée du bootloader :
La complexité du code est due à la gestion du protocole STK500.
Le problème est que ce bootloader fonctionne presque en adresse basse 0x0000 mais moins bien si on le met là où il doit être, c’est-à-dire en adresse haute 0x1C00.
* Les fonctionnements à ce jour sont les suivants :
* en adresse haute il semble que le bootloader s'écrase lui-même puisque le protocole STK500 finit par planter. Du coup il finira par exécuter la première instruction de notre programme qui allume des LEDs (le fait qu’il s'écrase lui-même n'est que supposition)
* en adresse basse, on le fait écrire avec un OFFSET de 0x0800 sur les adresses et on branche à l'adresse 0x400, alors le programme ne fonctionne que partiellement : il devrait faire clignoter 8 leds mais n'en fait clignoter qu'une seule ! On change la tempo et c’est observable. Il y a donc une corrélation entre ce que j’écris et ce que je vois, mais pas une corrélation totale comme on l'attends d'une programmation.
* la différence entre les deux valeurs précédentes 0x0800 et 0x0400 est liée au fait qu’il y a des adresses d'octets et des adresses de mots à gérer. Nous avons fait des essais pour trouver les valeurs.

#### Ressource

Le fichier arduinoMega8.zip contient la version avec la nouvelle instruction SPM. Le fichier progmemcontent.vhd contient le bootloader (qui ne fonctionne pas correctement). Cette version a été réalisée avec l'option bootloader à false, ce qui veut dire que vous pouvez l’utiliser normalement (avec data2mem). Nous y avons aussi inséré la possibilité de réaliser une mise à 0 automatique d'un drapeau d'interruption, comme pour l'ATMega16 d'un autre chapitre. Rappelons que c’est la version la plus à jour.
L'obsession que nous avons à essayer de faire fonctionner le bootloader de cette section est liée à ce qu'une réussite permettrait d’utiliser notre FPGA dans une salle d'enseignement où l'environnement Arduino est installé. Il n'y aurait aucune modification à faire pour l'IDE Arduino. Mais bien sûr une modification matérielle nécessiterait l'ISE de Xilinx.
Comme notre objectif n’est pas encore atteint, nous donnons une variante dans la section suivante qui fonctionne, mais nécessite de changer l'IDE Arduino (plus exactement remplacer avrdude).

### Protocole STK500v1 de l'Arduino détourné

En fait ce que nous voulons réaliser est de pouvoir utiliser l'environnement Arduino et le bouton de téléversement pour charger notre programme dans le FPGA. Un moyen évitant de faire fonctionner un bootloader, est d’utiliser data2mem au lieu de avrdude. Notre collègue Bastien Jacquot avait un problème avec des points communs : programmer un Arduino par le réseau. L’idée générale est la suivante :
* ajouter une carte Arduino factice
* remplacer avrdude par un script qui détecte s'il doit lancer avrdude ou s'il lance d'autres commandes
Voici comment on ajoute une carte Arduino.
* Chercher le fichier "/usr/share/arduino/hardware/arduino/boards.txt" et ajouter à la fin par exemple :
* Renommer le fichier "/usr/share/arduino/hardware/tools/avrdude" (qui est un lien) en "/usr/share/arduino/hardware/tools/avrdude.arduino".
* Écrire le script et le mettre à la place de "/usr/share/arduino/hardware/tools/avrdude" :
L'idée générale du script est la suivante :
* repérer l’utilisation d'un programmateur data2mem
* dans ce cas retrouver le fichier qui a l'extension .hex et le remplacer par le même fichier avec l'extension .elf
* lancer data2mem
* lancer ADEPT
* autrement lancer avrdude normalement
Ce script nécessite les fichiers atmega16.bit et memmoryS6.bmm présents dans notre répertoire personnel. Il ne fonctionne que pour une carte nexys3.
La réalisation de de ce travail sous windows doit pouvoir se faire mais nous ne savons pas très bien comment.

### Un autre bootloader qui reçoit directement des fichiers HEX

Voici comment Juergen Sauermann, le concepteur de l'ATMega8 a conçu son propre bootloader. Il a décidé de ne pas modifier le cœur pour ne pas faire de saut (matériel) en mémoire programme au moment du reset.
Notre cœur est forcément particulier par rapport au même composant du commerce : il n'a pas de fusibles par exemple.
Ce que Juergan a tenté de faire est un peu différent de la théorie présentée dans la section précédente à cause de l'absence de fusible. Une autre raison est que dans notre coreATMega8, nous ne sommes pas limité par l'instruction SPM contrairement à ce qui se passe dans l'ATMega8 (où elle fonctionne seulement lorsqu'elle est exécutée dans certaines zones de la mémoire programme).
Nous voyons poindre quelques différences avec la théorie de tout à l’heure, comme montrées en figure ci-contre :
* le bootloader à gauche commence à l'adresse 0 par sa table de vecteurs d'interruption (en bleu clair)
* sa partie donnée en mémoire programme (en gris) est gonflée artificiellement pour laisser de la place au futur programme (une autre façon de présenter les choses est de dire qu'un bootloader est petit en principe, mais que le notre est énorme !)
* le programme commence lui aussi en adresse 0
* l'écriture du programme en mémoire détruit la table de vecteur d'interruptions du bootloader et donc en particulier son vecteur RESET (partie droite de la figure où l’on montre les deux superposés)
* en conséquence, il nous faut trouver un mécanisme pour que le programme soit capable d'appeler le bootloader car la figure de droite montre clairement que le bootloader n'a plus de point d'entrée après son écrasement par le programme : comment se brancher au programme bleu turquoise en fin de mémoire qui représente le bootloader ?
Une autre différence qui n'apparait pas sur le dessin est que nous enverrons par la RS232 des fichiers en format HEX (Intel) et non pas en binaire comme cela se fait traditionnellement. La raison est simple, il est plus facile d'opérer quelques vérifications sur un tel fichier, en particulier de tester si l’on est bien arrivé à la fin (du fichier), de tester s'il y a eu une erreur de transmission dans chacune des lignes... et puis on est prêt à utiliser quelques lignes de code supplémentaires pour notre bootloader en lui demandant de déchiffrer un fichier hex : si l’on manque de place on ira en chercher dans le FPGA.
* Le bootloader donné dans la boîte déroulante ci-dessous ne fonctionne pas encore correctement pour le moment mais Juergen Sauermann nous affirme l'avoir fait fonctionner. Il est donc en cours de modification jusqu'à ce que cette bannière disparaisse.
Ce fichier doit être complété par :
et son fichier d'entête associé :
Nous ne tenterons certainement pas de faire fonctionner ce bootloader sauf si nous n'arrivons pas à faire fonctionner celui de l'Arduino.

### Adaptation au coreATMega8 de l'interface ISP

Comme toute interface, sa connaissance fine nécessite de connaître les deux parties qui échangent leurs informations :
* partie logicielle qui tourne sur le PC
* partie matérielle qui reçoit les données
La partie logicielle existe sous de nombreuses formes (AVRStudio, AVRDude, ...) nous n'aurons donc pas à la créer. La partie matérielle par contre est entièrement à réaliser : comment faire croire à AVRStudio ou AVRDude qu’il discute bien avec une interface ISP connectée à un ATMega8 ?
Il n'y a très peu de chances que la réponse à cette question soit documentée quelque part ! Pour y répondre il nous faudra certainement lire les codes sources d'un programmateur ! Ceci est remis à plus tard pour le moment.
Finalement l'écriture du chapitre Arduino dans un autre projet, et le fait que notre enseignement va se faire à partir de 2013 avec Arduino (plutôt qu'avec les PIC16F) nous a amené à nous intéresser particulièrement à cet Arduino. Nous avons découvert qu'un exemple permet de programmer en ISP un autre processeur AVR. Cherchez ArduinoISP dans le répertoire "examples" de votre installation ARDUINO (sous Ubuntu "/usr/share/doc/arduino-core/examples/ArduinoISP"). Cet exemple qui transforme donc un Arduino en programmateur ISP et vous montrera le protocole de liaison STK500v1 d'un côté, et très partiellement le protocole ISP (car utilise l'interface ISP de l'AVR et il faudra donc lire sa documentation pour tout comprendre). Ce code étant relativement compact nous partirons de celui-ci plutôt que de décortiquer le code d'avrdude par exemple.