# Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/Projets pour ATMEL ATTiny861

L'ATTiny 861 est une architecture simplifiée par rapport à l'ATMega8. Dans ce chapitre, nous allons donc utiliser cette nouvelle architecture. Ce TP cible essentiellement les FPGA de chez Xilinx. Mais le TP suivant est dédié aux FPGA de chez Altera.

## Architecture
### Architecture des registres mémoires

Il existe 3 espaces distincts en mémoire :
* Les 32 premières adresses correspondent aux 32 registres. Ces registres sont directement reliés à l'ALU et tous les calculs ne peuvent être effectués qu’à partir de ces registres (addition, soustraction, multiplication, opérations logiques, tests ).
* L'espace IO de l'adresse 0x20 à 0x5F. Dans cet espace se trouvent beaucoup de registres de gestion du matériel (les ports, Timers, UART,...). Pour accéder à ces registres les instructions in et out utilisent l'adresse 0 pour le premier registre et non l'adresse 0x20. Les instructions cli et sbi permettent de mettre à 0 ou à 1 un bit d'un registre
* Enfin à partir de l'adresse 0x60 se trouve la mémoire à proprement parler (le Tiny861 possède 512 octets de RAM). Pour accéder à tout l'espace mémoire (Registre et zone I/O incluse) les instructions ST,LD et STS et LDS peuvent être utilisées. L'adressage peut être direct (STS ou LDS) ou indirect (ST et LD). Un adressage indirect utilise les registres X, Y ou Z (respectivement les registres R27:R26, R29:R28 et R31:R30) pour pointer sur une zone mémoire.

### Quelques registres importants du banc de registres

Ce tableau (partiel) des registres respecte les fichiers d'inclusion du compilateur avr-gcc. Par exemple, dans la documentation officielle, le bit b0 du PORTB s’appelle PORTB0 tandis que dans le fichier d'inclusion du GNU-C il s’appelle PB0. Comme vous pouvez le constater, chaque registre est caractérisé par deux adresses. Chacune des deux adresses est espacée de 0x20=32. C'est le nombre de registres à usage général. Notez que le fichier d'inclusion avr/io.h que l’on utilisera très vite utilise les nombres qui ne sont pas entre parenthèses :

## Comment mettre un programme en ROM/RAM

Commençons par nous poser la question de savoir comment on peut compiler un programme en langage assembleur ? Le lien suivant décrit plus en détail les problèmes et les solutions : Transformer HEX en VHDL

### L'assemblage

L'assemblage se fait en ligne de commande. Son objectif est de réaliser un fichier au format elf. Voici un exemple de commande :
Nous avons décidé d’utiliser « avr-gcc » pour assembler (au lieu de avr-as). Cette décision a des conséquences importantes :
* Utilisation du pré-processeur qui permet d’utiliser les noms des registres mais aussi les #include traditionnels en langage C
* La table des vecteurs d'interruptions est automatiquement créée même si le RESET seul est utilisé
* Le code doit se situer dans un main

### Programmation de la mémoire programme

La programmation de la mémoire programme peut être réalisée de plusieurs manières. Si la manière la plus simple reste la transformation du programme en fichier VHDL, nous n'utiliserons pas cette méthode bien trop lente : elle nécessite une recompilation globale du projet, ce qui peut prendre quelques minutes. Nous allons plutôt utiliser un utilitaire data2mem comme indiqué dans la figure ci-après.
La commande qui fait cela est :
qui construit à partir du fichier microcontroleur.bit (résultat de la compilation de l'ISE) un ficher microcontroleur_rp.bit : c’est ce deuxième fichier que l’on va envoyer dans le FPGA. Cette commande nécessite un fichier memory.bmm qui sera fourni et d'ajouter quelques contraintes importantes dans le fichier ucf.

#### Pour la carte Spartan3E

Les contraintes dans le fichier ucf peuvent être :
Ces contraintes sont déjà présentes dans le fichier ucf donné dans les ressources mais sont en commentaires.
Le fichier memory.bmm correspondant contiendra :
Voici donc schématiquement comment cela se passe.

#### Pour la carte Nexys6

Les contraintes peuvent être :
Quant au fichier memory.bmm associé, il doit alors absolument être

## Travail introductif

Comprendre la notion de PORT d'entrée et de PORT de sorties Voici un premier exemple de programme assembleur
Ce programme ne fait que recopier l'état des interrupteurs (PINA) sur les LEDs (PORTA). Un certain nombre d'instructions ont été volontairement laissées en commentaire (qui rappelons-le, commencent par un « ; »). Intéressez-vous seulement à ce qu’il y a entre main : et .END
À ce stade, vous devez franchement être choqué par le fait que l’on ne positionne pas DDRA avant de sortir sur le PORTA. On rappelle donc que dans notre FPGA les PORTs ne seront pas bidirectionnels sauf si nécessaire (comme pour l'i2c par exemple). En clair, les fils qui arrivent sur PINA ne sont pas les mêmes que ceux qui sortent par PORTA. Cette propriété ne peut être vraie que pour un FPGA, pas pour un Tiny861 du commerce.

### Travail à réaliser

Faites tourner le programme ci-dessus dans votre processeur (qui sera dans le FPGA). Si vous avez souvenir de quelques instructions assembleur, vous pouvez essayer de faire autre chose que la recopie sur vos LEDs

## Travaux pratiques d’introduction à l'assembleur
### Travail à réaliser (Exercice 1)

On réalisera un programme qui réalise une addition de 255 et de 1 et qui sort le registre de statut (SREG) sur les Leds (PORTA). Si vous voulez savoir ce qui sort, la seule solution est de sortir dans PORTB sur les afficheurs 7 segments.
Indication 1 : Le registre SREG peut être accédé comme un PORT par les instructions IN et OUT.
Indication 2 : 127 + 1 fait un overflow car on additionne deux nombres positifs et le résultat est négatif (en complément à deux)

### Travail à réaliser (Exercice 2)

Charger R16 et R17 avec deux nombres et étudier les résultats et les bits de statut (drapeaux) pour les opérations suivantes.
Indication : Un des deux registres peut être chargé à l'aide des interrupteurs.

### Travail à réaliser (Exercice 3)

Additionnez un nombre et son complément à 2, et faites la même chose avec son complément à 1 et comparez les résultats

### Travail à réaliser (Exercice 4)

On vous demande de réaliser un compteur binaire qui sort sur des LEDs. La seule difficulté de cet exercice est de réaliser une temporisation.
Indication : essayer de comprendre et d’utiliser ce code :
Montrer qu'un tel delay est encore trop rapide. Pour avoir un temps correct il faudra le faire environ 0x40 fois.

### Travail à réaliser (Exercice 5)

On vous demande de rechercher les instructions à mettre en œuvre pour la réalisation de chenillards et de réaliser les programmes correspondants.
1°) Réaliser un chenillard simple avec l'instruction « lsl » en détectant la disparition du '1'. Indication : Si l'instruction « lsl » est appliquée dans une boucle, le '1' finit par disparaître !!!!
2°) Réaliser ce même chenillard en initialisant la SRAM avec un code du style :
Puis aller chercher sans arrêt ces valeurs dans la RAM avec l'instruction « LD R16,Y+ » sachant que Y=R29:R28.
Indication : La SRAM d'un ATTiny26/46/86 commence à partir de l'adresse 0x0060. Sa fin dépend de la série (128/256/512 octets) 0x0DF/0x015F/025F
Un algorithme est donné sous forme d'organigramme un peu plus loin.
1°) Correction chenillard simple :
"BRNE suite" peut être remplacé par "BRCC suite" mais pas par "BRVC suite".
2°) Correction chenillard simple avec SRAM :

## Réalisation d'une émission RS232

Sur le tiny861 du commerce, il n'y a pas de liaison série. Nous allons donc en ajouter une.

### Un Logicore pour ne pas réinventer la roue

On vous donne l’ensemble matériel qui réalise la partie transmission de la liaison série.

### Réaliser la liaison série

Insérer ce logicore dans le composant travail à réaliser comme indiqué dans le schéma ci-dessous :

#### Exercice de préparation

Le compteur mod_m_counter est à configurer. Pour ce faire, il faut savoir que l’on va travailler à 19200 bauds et que la fréquence d'horloge est de 50 MHz. En déduire la valeur à mettre dans le generic de ce compteur.

### Comment interfacer des PORTs/Registres dans l'ATTiny861

Pour information, le cœur original d'Andreas Hilvarsson qui réalise le PORTA est donné maintenant :
Cet ensemble était transformé en composants puis câblé pour réaliser le PORT. Mais nous ne le garderons pas pour deux raisons :
* son côté bidirectionnel est inutile dans un FPGA (en tout cas très souvent)
* nous avons utilisé un ATMega8 très souvent dans ce cours et avons apprécié ses deux process séparés pour les entrées et sorties. Les PORTs et registres de l'ATMega8 restent pour nous une référence.
Nous avons donc essayé de les recopier dans ce projet en retirant donc l'aspect bidirectionnel des PORTs que nous jugeons absolument inutile dans un FPGA. Ainsi voici comment sera réalisée les entrées/sorties avec des PORTs :

### Travail à réaliser (exercice 1)

On testera les communications avec gtkterm (application Linux) configuré comme ci-dessous (à part peut-être le PORT) :
PORT :/dev/ttyS0, vitesse : 19200, Parité : none, Bits 8, Bit de stop : 1, contrôle de flux : none
1°) Réaliser la partie matérielle complète en gérant le bit TXEN du registre UCSRB. L'écriture dans le registre UDR devra envoyer par la liaison série. De plus, les bits UDRE et TXC du registre UCSRA seront reliés au complément de "buffer_full". Pour simplifier, les registres UCSRB et UDR seront en écriture seulement et UCSRA sera en lecture seulement.
2°) Réaliser un programme qui lit sans arrêt les switchs et envoie sur la RS232 leur valeur seulement lorsqu’ils ont changé. Pour réaliser cet exercice vous allez procéder en deux temps :
2-1) Réaliser un programme qui lit sans arrêt les interrupteurs et envoie un '1' lorsqu’ils ont changés (et seulement dans ce cas). L'envoi du '1' se fera par un sous-programme. Bien faire la distinction entre '1' et 1 !!!!
Indication : il faut réserver un registre pour l'ancienne valeur des interrupteurs et un pour la nouvelle de manière à pouvoir comparer. En fin de boucle ne pas oublier de mettre la nouvelle valeur dans l'ancienne. Ceci est expliqué dans l'organigramme ci-dessus. L'envoi d'un caractère par la liaison série se fait par la simple instruction :
2-2) Modifier le sous programme « serial_tx » pour qu’il envoie maintenant deux caractères correspondant à la valeur hexadécimale des interrupteurs. Indication : voici comment on procède à l'envoi d'un caractère hexadécimal correspondant à la valeur des poids faibles :
Faites la même chose avec les poids forts. Le fin du fin serait d'ajouter un espace pour séparer les futures valeurs. Essayez avec :

### Travail à réaliser (exercice 2)

Réaliser un compteur décimal sur deux digits sortant par la liaison série (avec une temporisation bien sûr).

## Réalisation matérielle d'une réception RS232

Par rapport au TP précédent la liaison devra être dans l'autre sens : c’est le PC qui maintenant va fournir des valeurs au FPGA. La réalisation de cette liaison se fait pratiquement comme dans le TP précédent (_rx au lieu de _tx) :

### Travail à réaliser (exercice 1)

Modifier la partie matérielle de l'exercice 1 du TP précédent pour réaliser une réception maintenant. Le bit RXEN du registre UCSRB autorisera la réception. Le registre UDR sera maintenant en écriture pour envoyer par la liaison série, mais aussi en lecture pour lire ce qui est arrivé. De plus, le bit TXC du registre UCSRA sera relié au "data_present".
Indications :

### Les chenillards et la RS232

On désire maintenant reprendre le chenillard déjà réalisé en assembleur, particulièrement celui qui utilisait une mémoire RAM pour s'exécuter. Pour simplifier tout sera maintenant réalisé en C.

#### Travail à réaliser (exercice 2)

On cherche maintenant à utiliser la réception RS232. L'objectif est de réaliser le chenillard normalement, sauf quand des données sont envoyées à partir du PC. Elle sont alors disposées dans la mémoire en lieu et place des anciennes valeurs. Pour simplifier votre travail, on adoptera le protocole suivant : quand une donnée arrive alors on sait qu’il y en aura forcément 16 et on les attendra toutes les 16. Une fois terminé on retournera tranquillement à notre chenillard.
N'oubliez pas la temporisation pour voir les chenillards.
Pour passer en mode programmation nous allons utiliser un interrupteur. En début de chaque boucle la position de cet interrupteur est testée et s'il est positionné on se dirige vers le programme de chargement. Un 'L' sera affiché sur les afficheurs dans ce cas mais un 'U' dans le cas d'un non chargement.

#### Exercice 2 en version assembleur

L'organigramme du programme à réaliser est présenté maintenant :

## Réalisation d'une transmission RS232 pour carte Spartan6

Il est impossible d’utiliser les logicores précédents avec la carte Spartan6. La raison est que les logicores déjà utilisés sont certes écrit en VHDL, mais d'une manière très matérielle. En particulier, ils utilisent des LUT4 qui sont disponibles dans les circuits de typa Spartan3, mais pas dans les circuits Spartan6 qui utilisent des LUT6.
Le nouveau logicore adapté à notre problème présent s’appelle uart_tx6 et est disponible chez Xilinx. Il a à peu près les mêmes entrées/sorties que uart_tx, mais quelques noms ont été inversés.

### Schéma de principe

Comme d'habitude, nous allons présenter le travail à réaliser sous forme schématique.

### Réalisation matérielle

La solution présentée ci-dessous gère à la fois la réception et l'émission par la liaison série. Elle utilise par contre les logicores adaptés au FPGA de type Spartan6. Si vous utilisez ceux du spartan3 il vous faudra légèrement adapter le code ci-dessous car quelques entrées ont changé de nom.

## Réalisation partielle du timer 1

Nous allons donc partiellement réaliser le timer 1.
* Partiellement car il est normalement sur 10 bits et nous n'en garderons que 8.
* Partiellement car nous nous contenterons d'une division sur 3 bits (du registre TCCR1B) contre 4 bits dans celui du commerce. Seules les divisions par 0, 1, 2, 4, 8, 16, 32 et 64 seront implantées. Il manque donc les divisions par 128, 256, 512, 1024, 2048, 4096, 8192 et 16384.
* Partiellement car nous n'implémenterons ni le dépassement de capacité ni les
comparaisons....
* Partiellement car donc seuls TCNT1 (0x2E) et TCCR1B (0x2F) seront implémentés.
* écrire dans TCNT1
* régler le pré diviseur en écrivant dans TCCR1B
* mais le bit TOV1 du registre TIFR n’est pas implanté
La dernière remarque peut sembler une limitation importante, mais il vous faut savoir qu’il n'y a aucun mécanisme d'interruption dans ce cœur !

### Indication 1

On prendra soin de définir les deux constantes TCNT1 et TCCR1B. On réalisera ensuite le célèbre :
Le timer pourra être un simple process dans "microcontroleur.vhd"

### Indication 2

La prédivision peut être faite par :
Vous complétez correctement les deux process iowr et iord et c’est tout.
Voici maintenant la solution partielle.

## ANNEXE I : les fichiers nécessaires aux TPs de ce chapitre (Version pour Spartan 6)

Le cœur ATTiny861 est disponible ICI chez Opencores.org. Nous en donnons une version modifiée pour un fonctionnement avec data2mem ce qui simplifie sa programmation mais le rend très dépendant des outils Xilinx.

### La mémoire programme modifiée

Nous avons modifié la mémoire programme par rapport au cœur original. En voici la nouvelle version destinée à réaliser 8 ko pour la version ATTiny861 donc.

### La mémoire donnée modifiée

Nous avons modifié la mémoire donnée par rapport au cœur original. En voici la nouvelle version destinée à réaliser 512 octets pour la version ATTiny861 donc.
Notre enseignement nous a obligé à cibler un FPGA spartan3E500 ce qui a nécessité 10 min de compilation sur un PC récent, pour nous entendre dire qu’il n'y avait pas assez de ressource dans ce FPGA pour la compilation correspondante !
Le problème est tout simplement que la mémoire donnée est compilée dans des LUTs ! Il existe des options pour l'obliger à inférer des BROM mais nous ne voulions pas modifier les options de compilation. Nous avons décidé d’utiliser aussi directement des BRAM pour la RAM (voir un peu plus loin).

### Cœur complet

Recherchez ICI chez Opencores.org et prenez le fichier mcu_core.vhd
Nous ne le reproduisons pas ici car nous n'y avons fait aucune modification.

### Microcontroleur complet

Il ne vous manque maintenant que les outils pour travailler du point de vue logicielle.

### Outils pour programmer

Si vous voulez utiliser l'outil Xilinx data2mem, il vous faut le fichier bmm. En voici un pour la carte Nexys 3 :
et voici un exemple de script pour assembler et télécharger :
que vous devrez adapter à votre configuration. Nous travaillons toujours dans un sous répertoire du projet que l’on appelle soft. Cela explique pourquoi le script commence par aller chercher le fichier .bit dans un répertoire parent (cp ../microcontroleur.bit .). Ce script compile (assemble plutôt) un fichier appelé Td7exo5.S met le programme dans le fichier .bit pour le télécharger avec l'outil de Digilent djtgcfg.

## ANNEXE II : les fichiers nécessaires aux TPs de ce chapitre (Version pour Spartan 3E)

Le fichier global microcontroleur.vhd de la section précédente est identique.

### Notre mémoire donnée

La mémoire donnée VHDL ci-dessous peut être utilisée sans problème. Elle est réalisée avec des RAMB16_S4 propre à Xilinx et ne peut donc pas être portée telle quelle.
Cette mémoire de données est bien plus grande que celle du 861 du commerce (512 octets) puisqu'elle a une taille de 4 ko. Est-ce gênant ? Il est clair que c’est du gaspillage. Pour information, nous avons déjà utilisé des surplus de mémoire RAM à l'aide de pointeurs que l’on initialise nous-même. Si l’on tente de réaliser cela, il nous faudra modifier la première ligne de l'architecture :
qui limite la mémoire RAM à 512 octets. Ceci est laissé comme exercice pour le lecteur.
Venons-en à la mémoire programme.

### Notre mémoire programme

Commençons par donner la mémoire programme pour la carte Spartan 3E :
Cette mémoire programme a une taille de 8 ko, ce qui est sa taille dans le Tiny861 du commerce.
Les contraintes dans le fichier ucf peuvent être :
et elles doivent être associées au fichier attiny861.bmm suivant :

### Cœur complet

Recherchez ICI chez Opencores.org et prenez le fichier mcu_core.vhd
Nous ne le reproduisons pas ici car nous n'y avons fait aucune modification.

### Script d'assemblage

Nous avons laissé un certain nombre de commentaires qui montrent que nous utilisons une carte Nexys3 et l'ISE 14.5, mais que pour les besoins de l'enseignement, l'ISE 11.1 est utilisé avec une carte Spartan3E500
Commentez la dernière ligne si vous n'avez pas le câble USB JTAG et utilisez alors IMPACT pour télécharger!
| Projets Hardware pour ATMEL ATMega16 | TPs ATTiny861 avec Altera |