premier tp C NIOS 2014 2015

Download Report

Transcript premier tp C NIOS 2014 2015

Circuits logiques programmables et Microprocesseurs
Deuxième partie: Programmation en C sur microcontrôleur NIOS II
On va écrire des programmes C qui réalisent les mêmes fonctions que les programmes VHDL précédents,
et les exécuter sur la même maquette. On pourra ainsi comparer les 2 options :
• logique programmable programmée en VHDL
• microprocesseur NIOS programmé en C
On utilisera un microcontrôleur tout fait, qui accède à l’ensemble des ressources de la carte DE2 :
Boutons, Leds, mémoires, UART, Port VGA, etc. Le projet QUARTUS qui le contient est situé dans le
dossier c:\veritable_cœur_de nios2011 et s’appelle NIOS_iut_2011
Avant d’écrire le premier programme il faut donner beaucoup d’explications. Vous verrez que les
programmes suivants sembleront faciles à écrire !
I / Ouvrir la chaîne de développement NIOS II EDS et créer un projet
Lancer le logiciel par la commande :
Programme =>ALTERA => NIOS II EDS 9.1=> Legacy NIOS II Tools=> Nios II 9.1 IDE.
En s’ouvrant, il vous demande de choisir un espace de travail. C’est un dossier pour tous les fichiers
associés à un processeur NIOS donné.
S’il ne vous le demande pas, choisir la commande File =>Switch workspace
Choisir le dossier dans lequel est rangé le projet Quartus II, suivi du sous-dossier \programmes
Cliquer alors sur OK. L’écran d’accueil vous propose une visite du logiciel, un tutorial, .. Cliquer sur
WorkBench (espace de travail).
Pour créer un nouveau projet :
• choisir la commande File => New => Nios II C/C++ Application
• Remplir le formulaire comme sur la figure suivante : le nom du projet est premier_projet, le
SOPC Builder System pointe vers le fichier cœur_de_nios_2010.ptf du dossier du projet Quartus,
et le Project Template est un Blank Project (projet vide).
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 1 sur 9
Cliquer sur Next pour continuer.
Dans la dernière boite de dialogue, choisir l’option Create a new system library named :
premier_projet_syslib. Cliquer Finish pour créer et ouvrir le projet.
Chaque système NIOS est unique, avec ses périphériques, ses espaces mémoire, ses interruptions,.. Pour
en tenir compte, l’IDE NIOS crée une bibliothèque système (system library) à partir du cœur de NIOS
que vous avez spécifié (c.a.d. cœur_de_nios_2010.ptf)
Pour cela : dans le panneau C/C++ Projects, situé à gauche de l’écran, faire un click droit sur
premier_projet_syslib et choisir Build Project dans le menu déroulant qui s’ouvre.
Le processus de création de la bibliothèque système est assez long (quelques minutes). Profitez en pour
lire la suite du texte !
Quand il se termine, double-cliquer sur premier_projet_syslib pour regarder les fichiers générés. Ils
sont répartis dans plusieurs dossiers. Parmi ceux-ci, le dossier Includes contient des liens vers les drivers
des périphériques du processeur NIOS que vous utilisez. Le dossier Debug => system description
contient le fichier system.h qui inclut les définitions pour tous les périphériques de ce processeur.
II / Ecriture des programmes
II.1.a /Premier programme : générer un signal carré rapide
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 2 sur 9
On veut créer un signal carré aussi rapide que possible. Pour pouvoir l’observer à l’oscilloscope, on va le
situer sur le bit 0 du port d’entrée/sortie nommé GPIO_0, à droite de la carte. Le bit 0 est le premier en
haut et à gauche.
Sur la carte le connecteur s’appelle JP1. Dans le
microprocesseur NIOS, il s’appelle GPIO_0 (General Purpose
Input Output, = entrées/sorties d’usage général). C’est un mot
de 36 bits, numérotés de 35 à 0.
Les périphériques du NIOS sont ‘memory mapped’, c’est à dire
qu’on accède à leurs registres (de donnée, de commande,
d’état, ..) comme à des mémoires.
On peut y accéder au moyen de macros de lecture et d’écriture
qui sont définies dans les fichiers des drivers des composants.
Il suffit de connaître les adresses.
Exemple : pour un PIO (Parallel Input Output, port de sortie
parallèle, tel que celui du GPIO_0) on dispose des macros :
IOWR_ALTERA_AVALON_PIO_DATA(base, data)
‘base’.
qui écrit la donnée ‘data’ à l’adresse
IORD_ALTERA_AVALON_PIO_DATA(base)
l’adresse ‘base’
qui lit la valeur de la donnée située à
..et bien d’autres..
Ces macros sont disponibles dans le fichier altera_avalon_pio_regs.h Ouvrir ce fichier pour les voir :
dans l’arborescence du NIOS située à gauche de l’écran double cliquer sur
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 3 sur 9
Altera.components\IP Device Drivers\sopc_builder_ip\altera_avalon_pio\inc\
Les adresses de base des composants sont disponibles dans le fichier system.h :
Dans l’arborescence de gauche aller dans
premier_projet_0_syslib[system0]\debug\system_description\system.h
On y trouve, pour le GPIO_0 :
#define GPIO_0_NAME "/dev/GPIO_0"
#define GPIO_0_TYPE "altera_avalon_pio"
#define GPIO_0_BASE 0x00021020
..etc..
L’adresse à utiliser ici est GPIO_0_BASE
Pour créer le programme :choisir la commande File => New => Source File. Saisir, pour le dossier
parent (parent folder) le dossier premier_projet, et pour le nom de fichier, premier.c. Cliquer sur
Finish
Saisir le code du fichier :
#include "system.h"
#include "altera_avalon_pio_regs.h"
//pour les définitions et adresses
//pour les procédures d’entrée-sortie sur
les PIO
int i ;
main()
{
while(1)
//à l’infini..
{i++;
//incrémenter i..
IOWR_ALTERA_AVALON_PIO_DATA(GPIO_0_BASE, i); // l’afficher sur GPIO_0
}
}
II.1.b / Compilation, téléchargement et exécution du programme.
Avant d’exécuter le programme, il faut configurer le FPGA de la carte DE2 avec le microcontrôleur NIOS
du projet Quartus.
• Lancer QUARTUS, ouvrir le projet NIOS_iut_2011
(C:\Projet_Quartus_et_NIOS\Programme_du_NIOS\...)
• choisir la commande Tools => Programmer, et télécharger le fichier NIOS_2010.sof en faisant
start
Revenir à l’IDE NIOS II.
•
•
•
Choisir la commande Project => Build Project Cela lance une compilation complète du projet.
Choisir alors la commande RUN => Run
Choisir NIOS II Hardware. Dans la boite de dialogue qui s’ouvre, choisir dans l’onglet Main le
projet premier_projet, et dans l’onglet Target Connection (connexion avec la carte cible) USBBlaster [USB0]
Le programme se charge, et on observe à l’oscilloscope le signal présent sur GPIO_0. Noter la période de
GPIO_0.0
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 4 sur 9
A mesure que nous complèterons le programme, nous dresserons le tableau des durées d’exécution et nous
essaierons de conclure.
II.2 / Ajout de ET_OU avec les boutons poussoirs KEY1 et KEY2
Nous allons ajouter au programme précédent la fonction et_ou que nous avions réalisée en VHDL. Nous
voulons que la LEDR 0 porte le ET logique des boutons poussoirs KEY1 et KEY2.
II.2.a / En accédant individuellement aux boutons poussoirs
Les LEDS rouges sont connues sous le nom LEDR, c’est un vecteur de 16 bits. Les boutons poussoirs
sont connus respectivement sous le nom de KEY0,.., KEY3.
Pour écrire le ET : si X ,Y, Z sont des variables de type char, les instructions :
X = IORD_ALTERA_AVALON_PIO_DATA(KEY0_BASE); // charge X avec la valeur du port
KEY0, c'est-à-dire avec la valeur du bouton KEY0.
Y = IORD_ALTERA_AVALON_PIO_DATA(KEY1_BASE); // charge Y avec la valeur du port
KEY1, c'est-à-dire avec la valeur du bouton KEY1.
Z = X && Y ; // calcule le et logique des 2 bits
On peut alors afficher Z sur le port LEDR. Vérifier le fonctionnement et mesurer la durée d’une itération
de la boucle infinie (c’est la demi période de GPIO_0.0).
Comment ajouter maintenant sur LEDG0 le OU LOGIQUE de KEY1 et KEY0 ? Faites le et mesurez le
temps d’exécution.
Plus compliqué : comment afficher ce OU logique sur LEDR1, en gardant le ET sur LEDR0?
II.2.b / En utilisant le vecteur « boutons poussoirs »
Les boutons poussoirs sont aussi accessibles au moyen du port BUTTON_PIO, qui les regroupe. C’est un
vecteur de 4 bits. Pour accéder à chaque bit de BUTTON_PIO, il faut passer par des masques.
Charger BUTTON_PIO dans X, masquer les bits qui ne nous intéressent pas, puis tester les 4 valeurs
possibles :
X = IORD_ALTERA_AVALON_PIO_DATA(BUTTON_PIO_BASE); // charge X avec la valeur du
port BUTTON_PIO
si X = 0 alors..
si X = 1 alors.. // etc..
Enfin, on peut remplacer les multiples if par une instruction switch … case. Sa syntaxe est :
switch (variable) //selon la valeur de variable ..
{
case premiere_valeur :
instruction(s) ;
break ;
case deuxieme_valeur :
instruction(s) ;
break ;
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 5 sur 9
default : instruction(s) ;
}
// dans tous les cas non cités..
Comparer les différents temps d’exécution et conclure.
II.3 / Ajout d’un décodeur 7 segments.
Ce module est décrit dans le texte du TP VHDL. On va l’ajouter au programme C. Ses entrées sont les
switches SW3 à SW0 de la carte, connus sous le nom de SWITCH_PIO (cf system.h). Les sorties sont
les 7 segments de l’afficheur HEX0.
L’algorithme de ce morceau de programme est le suivant :
• lire les Switches
• décoder. On utilisera une instruction switch..case
• afficher sur HEX0
Vérifier le fonctionnement et mesurer la durée d’exécution.
II.4 / Ajout d’un comptage de période 1 seconde.
On veut ajouter un compteur décimal dont la fréquence de comptage est de 1 seconde.
La durée d’une itération de la boucle infinie de notre programme a été mesurée à la question précédente.
On sait donc combien d’itérations seront exécutées en 1 seconde.
On va déclarer les variables iterations, pour compter les itérations, et secondes, pour le nombre de
secondes. L’algorithme de cette fonctionnalité est :
•
•
•
... instructions précédentes de la boucle infinie
incrémenter iterations
si iterations atteint la valeur correspondant à 1s :
o incrémenter secondes (modulo 10)
o afficher secondes
o raz iterations
Quelle est la valeur maximale de iterations? quels sont les formats à utiliser pour les variables iterations
et secondes?(char, int, long int...)
Ecrire et tester. Mesurer la durée d’itération de la boucle, faut-il ajuster le maximum de iterations pour
atteindre 1 s ?
II.5 / Précision du comptage : Ajout d’un timer
La méthode précédente ne permet pas d’obtenir une période de comptage précise. On préfère donc utiliser
le Timer 0 pour compter le temps de manière matérielle et non plus logicielle. Son schéma bloc est donné
à la figure suivante, sa documentation complète est dans le dossier documentations.
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 6 sur 9
Il s’agit d’un décompteur 32 bits, qui décompte d’une valeur maximale, period, jusqu’à zéro. A ce point,
selon sa configuration :
•
•
il génère une demande d’interruption (ou pas)
il s’arrête, ou recommence à décompter depuis la valeur period. Cette valeur est elle-même
programmable.
Les programmes y accèdent au moyen des registres de contrôle, d’état, et de données. Tous ces registres
ont une largeur de 16 bits.
Period est un registre 32 bits, on y accède en écrivant ou lisant dans les 2 mots de 16 bits periodh (poids
fort) et periodl (poids faible)
Lire et comprendre dans la documentation le rôle de ces registres.
Le processeur peut écrire et lire par son bus Avalon les registres de contrôle, d’état, de période et de
données.
Dans un premier temps, nous allons le faire fonctionner sans utiliser les interruptions.
Pour réaliser la synchronisation du comptage sur une période de 1 seconde, on va initialiser le compteur
comme suit :
• period = 50.106
// parce que l’horloge est à 50 MHz
// se traduit par periodh = ..., periodl = ... avec les valeurs correctes
• comptage continu, start, pas d’interruption (control = ..)
Puis, à chaque itération de la boucle infinie, on va tester le bit TO (time out) du registre d’état (cf
documentation)
S’il est à 1 (seconde terminée) :
• remise à zéro de TO
• incrémenter et afficher heure
Sinon, ne rien faire
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 7 sur 9
Ecrire et tester ce programme. Inclure dans le programme le fichier Altera_avalon_pio_timer_regs.h
qui contient les macros d’accès au timer . Il est situé dans
Altera.components\IP Device Drivers\altera\sopc_builder_ip\altera_avalon_timer\inc\
Les adresses sont dans system.h.
L’utilisation du timer permet d’obtenir précisément une durée d’une seconde. Quelle est la durée d’une
itération de la boucle infinie ? Conclure.
II.6 / Utilisation du timer en mode interruption
Le fonctionnement du NIOS II en régime d’interruptions est décrit dans les pages 233 et suivantes du
fichier NIOS II Software Developer Handbook, situé à l’endroit habituel.
Le fonctionnement souhaité est celui de la figure suivante : la boucle infinie du main ne s’occupe plus du
tout de la gestion de l’heure ni de son affichage. Lorsque le Timer arrive à zéro (la seconde est donc
écoulée) il pose une demande d’interruption. Le programme interrompt l’exécution de la boucle infinie et
va exécuter la fonction de traitement de cette interruption. Il revient ensuite à la boucle infinie.
en cas de demande
d’interruption
initialisations
Fonction de traitement de
l’interruption
boucle infinie
puis retour à la boucle
infinie
Pour cela il faut :
•
inclure la bibliothèque contenant les fonctions utiles pour les interruptions :
#include « alt_types.h »
•
déclarer en variable globale un pointeur de type void.
void* context ;
•
écrire la fonction de traitement de l’interruption. Elle doit respecter le prototype suivant :
void interruption_du_timer (void* context, alt_u32 id)
•
B. Durand
interruption_du_timer est le nom que nous lui donnons.
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 8 sur 9
•
•
context est le pointeur que nous avons déclaré. Il permettrait de passer un paramètre à la
fonction, nous ne l’utiliserons pas.
id est le paramètre formel qui représente le numéro de l’interruption du Timer
Dans le corps de la fonction écrire les instructions qui :
• incrémentent et affichent secondes
• remettent à zéro la demande d’interruption (bit TO du registre d’état)
•
dans le main :
•
•
initialiser le timer comme précédemment, mais avec interruption autorisée
enregistrer la fonction d’interruption :
alt_irq_register( id , context , interruption_du_timer );Cette instruction indique au système de
gestion des interruptions que la fonction de traitement de l’interruption id est interruption_du_timer.
•
•
•
la valeur de id est dans system.h , sous le nom TIMER_0_IRQ
interruption_du_timer est le nom de la fonction de traitement de l’interruption.
dans la boucle infinie, enlever toute référence à secondes
Ecrire et tester ce programme. L’heure doit s’afficher comme précédemment sur l’afficheur 7 segments.
Mesurer la durée d’une itération de la boucle infinie. Comment a t’elle évolué ? Pourquoi ? Conclure sur
l’intérêt de ce mode de fonctionnement.
Ajouter dans la fonction d’interruption l’affichage de secondes sur le port GPIO_1. Synchroniser
l’oscilloscope sur le bit de poids le plus faible. Que se passe-t-il sur le bit GPIO_0 (0) quand GPIO_1 (0)
est modifié ?
Expliquer et conclure
B. Durand
Licence ISEA
GEII Salon de Provence
Novembre 2014
Page 9 sur 9