NIOS II Processor Software Development Réalisé par : BEN HAJJI Anis SMII Mondher Encadré par : M. GLAOUI Mohamed
1. Présentation générale du processeur NIOS II : C’est un processeur embarqué 32 bits conçu spécifiquement pour la famille de FPGA Altera (série Cyclone et série Startix). NIOS II intègre de nombreuses améliorations sur l'architecture d'origine Nios, le rendant plus adapté pour une large gamme d'applications d'informatique embarquée, de DSP au système de contrôle. Son architecture se caractérise par (Figure 1): Processeur RISC 32 bits d'Altera cadencé à 120 MHZ (142 DMIPS), 3 cores possibles : Fast, Economy,Standard , 32 registres généraux,16 registres de control, 32 niveaux d’interruptions, Pipeline à 6 niveaux (Fast), Cache de données et d’instructions dont la taille peut atteindre 64 Bytes, Multiplication et division câblée, 256 instructions personnalisées, Module TCM (Tightly Coupled Memory)
Figure 1 NIOS II Processor Core
Les outils de développement mis à disposition pour ce processeur sont : Plate-forme de développement Quartus II d'Altera, Chaînes d’outils GNU (compilation croisée), IDE Eclipse, Simulateur ModelSim, OS supportés :
μC Linux, portage GPL. 2
Version intégrée à l’IDE : http : //www.niosforum.com/ Version sous Linux : http://nioswiki.jot.com/wikiHome μC/OS II Produit commercial, Noyau Nucleus .Produit commercial.
2. Configuration et implémentation d’un processeur NIOS II sur FPGA 2.1.
Etapes de configuration
1°) Lancer Quartus II : allez au menu Démarrer puis Programmes puis Altera ensuite Quartus II 9.0 et cliquez sur Quartus II. 2°) Créer un nouveau projet : Allez dans le menu File et cliquer New Project Wizard. Vous aurez une page ou vous mettez le nom de votre projet (dans notre cas on a choisi first_nios)
3°) Choisir le composant programmable (device) : Cliquez sur Next sur cette page, vous devez choisir le composant programmable (FPGA, CPLD) sur lequel vous allez implémenter votre circuit numérique. Notre carte est munie d’un FPGA de famille Cyclone III modèle EP3C25F324C6, choisissez ce composant dans la liste des composants proposés comme le montre la figure. 3
4°) choisir d’autres outils logiciels : Cliquez sur Next , dans cette fenêtre vous avez la possibilité de spécifier l’utilisation d’autres outils logiciels (que ceux d’Altera) pour réaliser la simulation ou la synthèse (N.B Nous allons travailler seulement avec la plateforme d’Altera) 5°) Synthétiser le choix : Dans la dernière fenêtre, vous avez une synthèse des choix que vous avez faits dans les fenêtres précédentes, vérifiez que vous vous n’êtes pas trompé et cliquez sur Finish.
4
6°) Diminuer le temps de synthèse : Allez au menu Assignements puis Settings, sélectionnez la section Compilation Process Settings et cochez l’option Use Smart Compilation
5
7°) Créer un système : Après la création de notre projet, on commence à construire le système en utilisant le SOPC Builder se trouvant sous le menu Tools
6
La fenêtre Create New System s’ouvre, remplissez le champ System Name avec le nomde votre système (on va utiliser nios_m). Vous pouvez aussi choisir le langage HDL de votre choix, VHDL dans notre cas. Valider en cliquant sur OK.
Design Specifications : Notre design contient les composants suivants : Nios II processor (NIOS II/e economic core) Avalon-MM Tristate Bridge On-Chip Memory (RAM or ROM) Flash Memory Interface (CFI) JTAG-UART LED Parallel I/O (PIO) Push-button PIO sys_clk_timer (Interval Timer) high_res_timer (Interval Timer)
8°) Ajouter un core NIOS II : Vous allez commencer par ajouter le processeur NIOS II à votre système. Dans la fenêtre de gauche, faites un double click sur le Nios II processor et vous aurez la possibilité de sélectionnez la version du cœur. Economique (NIOS II/e) Standard (NIOS II/s) Rapide (NIOS II/f) En fonction de ce choix, vous avez un processeur plus ou moins puissant mais qui prend aussi plus ou moins de place sur le FPGA. 7
N.B : On va sélectionner la version économique du cœur : NIOS II/e.
8
9°) Choisir le Bridge : Pour choisir le bridge, il suffit d’aller à l’onglet Bridges and Adapters puis Memory Mapped. Ensuite, un double click sur Avalon_MM tristate bridge et enfin cliquez sur Finish.
10°) Ajouter la mémoire : Pour ajouter une mémoire à notre système, allez à l’onglet Memory and Memory Controllers puis On-Chip et ensuite l’élément On-Chip Memory (RAM or ROM). Choisissez la taille de données et la taille totale de la mémoire, cette dernière a un rapport avec la taille du code. Dans notre cas la taille est 40300 Bytes.
9
De même pour la mémoire flash.
11°) Ajouter le module JTAG UART : Cette étape consiste à ajouter l’interface série qui permet de télécharger et débuguer le code sur la cible, cette interface permet aussi de communiquer avec le PC. Dans la catégorie Interface Protocol, allez à Serial, et sélectionnez l’élément JTAG UART puis cliquez sur Finish.
10
12°) Ajouter l’interface des LEDs : Dans la catégorie Peripherals, allez à MIcrocontroller Peripherals, et faites un double clic sur l’élément PIO(Parallel I/O). Choisissez une taille de 4 bits et sélectionnez le mode Output Ports Only, puis cliquez sur Finish.
13°) Ajouter l’interface des boutons (KEY) : Faites un double clicc sur le meme élément PIO (Parallel I/O), mais cette fois choisissez une taille de 4 bits avec le mode Input Ports Only et cliquez sur Finish.
11
14°) Ajouter les Timers : Pour ajouter les temporisateurs (Timers), qui nous permet de faire des opérations séquentielles sur les LEDs, allez à la catégorie Peripherals, ensuite Microcontroller Peripherals, et ensuite faites un double clic sur l’élément Interval Timer. Nous allons ajouter deux timers : Sys_clk_ timer: Timeout Period: 10 ms High_res_timer: Timeout Period: 10 μs Laissez les autres paramètres par défaut et cliquez sur Finish.
12
13
15°) Assignement automatique des adresses : Pour s’assurer que toutes les adresses de base sont valides et bien aligné, allez dans le menu System et cliquez sur Auto-Assign Base Adresses. des interruptions : Pour s’assurer que les IRQ (interruptions) ne sont pas en conflit, allez dans le menu System et cliquez sur Auto-Assign IRQs .
14
16°) Retournant au CPU (NIOS II) déjà rajouté à votre système.Vous allez sélectionner les adresses de reset et d’exception. Sélectionnez ext_flash pour Reset Vector et onchip_memory pour Exception Vector. Ensuite cliquez sur le bouton Finish.
17°) Système Final : Le système devrait ressembler à ceci
15
18°) Générer de système : cliquez sur le bouton Generate. SOPC Builder va vous demander si vous voulez sauvegarder le système, cliquez oui. Ensuite le logiciel va générer votre système. Cette opération consiste à générer tout le code HDL des IP que vous avez instancié dans SOPC Builder. Cette opération peut durer quelques minutes donc il est préférable de passer à l'étape suivante, avant d’attendre la fin de la génération.
16
19°) Instancier le composant créé avec les E/S du FPGA : vous allez utiliser l’outil graphique de Quartus II. Créez un nouveau fichier graphique en allant sur File puis New, choisissez Block Diagram/SChematic File comme type de fichier. Enregistrez le sous le nom first_nios.bdf dans le même répertoire que le projet.
20°) Dès que le SOPC Builder termine la génération de système, instanciez le système dans le fichier first_nios.bdf. Dans ce schéma, double cliquez n’importe où, une fenêtre Symbol s’ouvre. Développer le répertoire Project et sélectionner le symbole nios2, Cliquez sur le bouton OK et placez le symbole.
17
21°) Connecter les entrées du composant NIOS II avec les E/S du FPGA : Tout d’abord, vous allez utiliser les noms attribués dans le fichier d’assignement (donner avec le tuto). Pour cela, on choisit l’onglet Assignments, ensuite, Import Assignments et sélectionnez le chemin du fichier .qsf qui contient les différents pins de la carte avec leurs noms.
Le résultat de cette étape est aperçu lors de l’ouverture du menu Pins. Voici un extrait de l’assignement des pins. On doit connectez les pins dans le bloc schématic avec le nom qui le convient dans le fichier importé.
Exemple : clk_0 (block schematic) va être configuré comme une entrée (input) avec le nom osc_clk (nom du pin dans le fichier importé) et location sur la carte PIN_V9.
Ensuite, double cliquez n’importe où. Ceci ouvre la fenêtre Symbol. Développer le répertoire C:/altera/90/quartus/libraries ensuite Primitives et puis Pin, sélectionnez input et connectez les entrées du CPU de cette manière.
18
Assignement complet
Pour ne pas trouvez un problème d’assignement des Pins, on doit procéder aux configurations suivantes : Allez à l’onglet Assignments ensuite Devices et choisissez l’option Device and Pin Options puis choisissez l’onglet Dual-Purpose Pins et suivez la configuration ci-dessous.
19
22°) Synthèse de design : depuis le menu Processing, sélectionnez Start Compilation.
20
2.2. Etapes d’implémentation Tout d’abord il faut générer le fichier first_nios.sof pour être implémenté sur la carte. Pour cela, nous choisissons l’onglet Processing puis Generte Functional Simulation Netlist.
Assurez-vous que la carte est alimentée. Connectez la carte à votre PC avec le câble USB (connecteur ByteBlaster de la carte). Lancez le programmateur depuis le menu Tools. Une fenêtre apparait, cliquez sur OK. Le nom du type de câble (ByteBlasterMV) doit apparaitre à côtés de Hardware Setup. Le fichier nios2_top.sof devrait déjà être sélectionné dans la fenêtre de programmation. Si ce n‟est pas le cas, cliquez sur le bouton Add File… afin de le sélectionner. Cochez la case Program/configure qui correspond à votre fichier .sof et ensuite cliquez sur le bouton Start afin de programmer le FPGA.
21
2.3.
Programme Hello World avec NIOS II :
1°) Lancement de NIOS II IDE : Démarrer outils Programmes puis Altera et ensuite NIOS II EDS 13.0 et en fin NIOS II IDE 13.0. 2°) Création d’un nouveau projet : sélectionnez File puis New ensuite NIOS II C/C++ Application and BSP from template dans la barre des menus. Dans la fenêtre qui apparait, choisissez le nom de votre projet. Dans la partie SOPC Information File name, vous devez spécifiez le chemin du fichier (.sopc). Il s’agit du fichier que nous venons de créer avec SOPC Builder précédemment (nios_m.sopc). Dans la partie Project Template, choisissez Hello world. Cliquez sur Finish.
22
Voici le code à exécuter : #include <stdio.h> int main() { printf("Hello from Nios II!\n"); return 0; }
Il nous reste à appuyer sur le bouton Run.
23
ANNEXE Allumer LED #include <stdio.h> #include "system.h" #include "altera_avalon_pio_regs.h" int main() { printf("Hello from Nios II!\n"); int count = 0; int delay; while(1) { IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, count & 0x01); delay = 0; while(delay < 2000000) { delay++; } count++; } return 0; }
COMPTEUR #include "alt_types.h" #include "altera_avalon_pio_regs.h" #include "sys/alt_irq.h" #include "system.h" #include <stdio.h> #include <unistd.h> /* A variable to hold the value of the button pio edge capture register. */ volatile int edge_capture; int count = 0; int handle_interrupt(); #ifdef BUTTON_PIO_BASE static void handle_button_interrupts(void* context, alt_u32 id) { /* Cast context to edge_capture's type. It is important that this be * declared volatile to avoid unwanted compiler optimization. */ volatile int* edge_capture_ptr = (volatile int*) context; /* Store the value in the Button's edge capture register in *context. */ *edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE); count += handle_interrupt(*edge_capture_ptr); printf ("Total count = %d\n", count); /* Reset the Button's edge capture register. */ IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0); } /* Initialize the button_pio. */ static void init_button_pio() { /* Recast the edge_capture pointer to match the alt_irq_register() function * prototype. */ volatile void* edge_capture_ptr = (void *) &edge_capture; /* Enable all 4 button interrupts. */ IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf);
24
/* Reset the edge capture register. */ IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0x0); /* Register the interrupt handler. */ alt_irq_register( BUTTON_PIO_IRQ, edge_capture_ptr, handle_button_interrupts ); } #endif static void initial_message() { printf("\n\n**************************\n"); printf("* Hello from Nios II! *\n"); printf("**************************\n"); } int handle_interrupt(int button) { int dcompteur; int compteur; switch (button) { case 0x1: printf( "Button 1 - Added 1\n"); for (dcompteur=0 ; dcompteur < 4 ; dcompteur ++) {IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, dcompteur); usleep(1000000); } return 1; case 0x2: printf( "Button 2 - Added 2\n"); for (compteur=3 ; compteur >= 0 ; compteur --) {IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, compteur); usleep(1000000); } return 2; case 0x4: printf( "Button 3 - Added 3\n"); return 3; case 0x8: printf( "Button 4 - Added 4\n"); return 4; default: printf( "Button press 0x%x UNKNOWN!!\n", button); return 0; } } int main(void) { /* Initialize the button pio. */ #ifdef BUTTON_PIO_BASE init_button_pio(); #endif /* Initial message to output. */ initial_message(); /* Continue counting loop. */ while( 1 ) { usleep(100000); } //handle_interrupt(); return 0; }
25
#include "alt_types.h" #include "sys/alt_stdio.h" #include "altera_avalon_pio_regs.h" #include "sys/alt_irq.h" #include "system.h" #include <stdio.h> #include <unistd.h> /* A variable to hold the value of the button pio edge capture register. */ volatile int edge_capture; int count = 0; int handle_interrupt(); #ifdef BUTTON_BASE static void handle_button_interrupts(void* context, alt_u32 id) { /* Cast context to edge_capture's type. It is important that this be * declared volatile to avoid unwanted compiler optimization. */ volatile int* edge_capture_ptr = (volatile int*) context; /* Store the value in the Button's edge capture register in *context. */ *edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_BASE); count += handle_interrupt(*edge_capture_ptr); alt_printf ("Total count = 0x%x\n", count); /* Reset the Button's edge capture register. */ IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_BASE, 0); } /* Initialize the button_pio. */ static void init_button_pio() { /* Recast the edge_capture pointer to match the alt_irq_register() function * prototype. */ volatile void* edge_capture_ptr = (void *) &edge_capture; /* Enable all 4 button interrupts. */ IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_BASE, 0xf); /* Reset the edge capture register. */ IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_BASE, 0x0); /* Register the interrupt handler. */ alt_irq_register( BUTTON_IRQ, edge_capture_ptr, handle_button_interrupts ); } #endif static void initial_message() { alt_printf("Hello"); // printf("* Hello from Nios II! *\n"); // printf("**************************\n"); } int handle_interrupt(int button) { //int compteur; int dcompteur; int compteur; switch (button) { case 0x1: //alt_printf( "Button 1 - Added 1\n"); for (dcompteur=0x0 ; dcompteur <= 0xf ; dcompteur ++) {IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, dcompteur); usleep(1000000); } return 1; case 0x2:
26
//alt_printf( "Button 2 - Added 2\n"); for (compteur=0xf ; compteur >= 0x0 ; compteur --) {IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, compteur); usleep(1000000); } return 2; case 0x4: //alt_printf( "Button 2 - Added 2\n"); //for (compteur=0xf ; compteur >= 0x0 ; compteur --) //{ IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, 0x0); usleep(1000000); return 3; } } int main(void) { /* Initialize the button pio. */ #ifdef BUTTON_BASE init_button_pio(); #endif /* Initial message to output. */ initial_message(); /* Continue counting loop. */ while( 1 ) { usleep(100000); } //handle_interrupt(); return 0; }
LIENS UTILES www.alteraforum.com/ com.altera.www
27