Communication bidirectionnelle FPGA - ordinateur
=> Un peu de codesign
: Processeur Nios2 + plateforme de caractérisation d'opérateurs numériques
Si des parties de ce wiki manquent de clarté, ou s'il y a des erreurs, ne pas
hésiter à me contacter : cedric.honnet+wiki.enst@gmail.com
(juillet 2008)
Plan de ce Wiki :
1. Création du Nios,
partie HARD
2. Création du Nios,
partie SOFT
3. Caractérisation d'opérateur
Principe de ce travail :
Effectuer de grandes quantités
d'opérations sur FPGA puis rapatrier les résultats grâce au Nios.
Le Nios permet ici la communication
avec un ordinateur grâce à la console d'exécution du programme embarqué.
Pré-requis:
Connaître un peu quartus,
le VHDL et le langage C. Le pack de logiciels utiles dans notre cas, Nios II Embedded
Design Suite, est disponible à ComElec mais il est aussi téléchargeable sur
leur site :
https://www.altera.com/support/software/download/nios2/dnl-nios2.jsp
1. Création du Nios, partie
HARD
On va partir d'exemples fournis par Altera, récupérables sur leur site, mais depuis ComElec on les trouve à cette adresse :
/comelec/softs/opt/altera/current/nios2eds/examples
On peut utiliser, aux choix, les exemples en Verilog ou VHDL, ça ne sera de toute façon pas compatible avec le langage qu'on utilise.
Dans notre cas on prend un NIOS en VHDL, le modèle standard est un assez bon compromis. Voici la commande à utiliser :
cp -r
/comelec/softs/opt/altera/current/nios2eds/examples/vhdl/niosII_stratixII_2s60/standard ~/NIOS_FOLDER
Première étape, commande d'ouverture du fichier projet de quartus (ou qpf pour quartus project file) :
quartus
~/NIOS_FOLDER/standard/*.qpf &
Créer un fichier de conception graphique (ou bdf pour block design file) :
File => New => Block Diagram/Schematic
File

Double cliquer dans le fichier bdf, puis sur NiosII_stratixII_2s60_standard dans la fenêtre qui s'ouvre:

...cliquer sur le fichier graphique pour y "déposer" le Nios:

Double cliquer sur le Nios pour ouvrir SOPC builder.
Pour ajouter des Entrée/Sorties, sélectionner Peripheral => Microcontroller Peripheral => double cliquer sur PIO (Parallel I/O).
Dans notre cas on a besoin de 4 E/S, dont entre autre une entrée sur 32 bits :

Note: ici on ne se préoccupe pas des options d'entrée.
Après avoir cliqué sur Finish, en bas de la fenêtre d'édition PIO (Parallel I/O), on peut renommer les modules voulus en les sélectionnant et en utilisant la touche f2 :

Facultatif mais bien utile :
Il peut être intéressant d'optimiser le Nios pour gagner en surface utilisée et en temps de compilation mais si on n'est dans l'urgence, on peut sauter ces étapes facultatives.
On peut donc enlever de nombreux modules encombrant et inutiles, voilà ce qu'il reste après les optimisations proposées :

Note: les 5 derniers modules sont les entrées / sorties qu'on a rajouté puis renommé.
Si on veut que ce système fonctionne, quelques étapes sont indispensables :
=> retirer le module onchip_ram en cliquant dessus puis sur Remove (ça utilise 20% des registres du FPGA alors qu'on peut utiliser la SDRAM)
=> faire de même pour ext_flash et ext_ram ainsi que ext_ram_bus, puis spécifier le tout au processeur : (pour qu'il puisse trouver la mémoire pour l'exécution de son programme)
=> double clic sur le module cpu (2ème ligne) => pour les 2 cases intitulées Memory: sélectionner sdram:

On peut aussi sélectionner Nios II/e au lieu de Nios II/s car on divise par 2 l'utilisation de blocs logiques.
Note : on a fsystem: 80,0 MHz grâce à l'utilisation des PLL du FPGA ; étapes pour y parvenir :
=> cliquer sur Finish pour fermer la fenêtre d'édition du module cpu.
=> double clic sur le module pll => simple clic sur Launch Altera's ALTPLL MegaWizard
=> sélectionner 3 Output Clocks et modifier la fréquence à 80.00000000 MHz :

Voici la liste des modules retirés :
onchip_ram
ext_ram_bus
ext_flash
ext_ram
lan91c111
lcd_display
uart1
Les trois derniers n'ont pas étés détaillés mais leur suppression s'est aussi faite avec le bouton Remove.
=> grâce à ces optimisation on compile en 4 minutes au lieu du TRIPLE (idem pour les blocs logiques utilisés sur le FPGA).
Après avoir arrangé notre Nios à notre besoin, cliquer sur System => Auto-assign Base Adress et faire de même pour Auto-assign IRQs :

Maintenant que le Nios est prêt, on le "re-génère" en cliquant sur generate (tout en bas à droite de SOPC Builder)…
…3 à 5 minutes après, on obtient normalement le message : "Info: System generation
was successful."
On clic alors sur Exit, sur Oui puis sur OK pour accepter "l'update du symbol".
On peut maintenant créer automatiquement les pins :
=> clic droit sur le Nios => Generate pins symbol ports
Mais on n'a pas besoin de pins pour toutes nos entrées et sorties. On les remplace par des fils que l'on connectera à notre plateforme de caractérisation, voilà ce qu'on obtient :

Il faut maintenant enregistrer le fichier bdf en prenant soin de l'ajouter au projet :

…et avant de compiler le projet grâce au raccourci clavier Ctrl+L, il faut IMPERATIVEMENT désigner ce fichier bdf comme "maître" :
=> clic droit sur Block1.bdf => Set as Top-Level Entity :

Pour être certain que tout fonctionne, on va charger notre processeur dans le FPGA :
=> Tools => Programmer :

En cliquant sur Hardware Setup… on peut sélectionner le port USB sur
lequel le câble JTAG est branché.
Le chargement du Nios se fait en cliquant sur Start.
On va maintenant tester ce Nios grâce aux exemples de programmes embarqués fournis par Altera.
2. Création du Nios, partie
SOFT
Nous allons utiliser le logiciel appelé Nios2 IDE, il permet entre autre de compiler et d'envoyer le programme embarqué.
Il a pour principal atout, dans notre cas, de posséder une console de contrôle d'exécution du programme embarqué.
Pour lancer Nios2 IDE depuis ComElec, il faut simplement exécuter cette commande :
nios2-ide
Lorsque Nios2 IDE s'ouvre, une fenêtre demande l'adresse à utiliser pour notre espace de travail :

=> La case Use this as the default and do not ask again ne doit pas être cochée :
Si on travail sur plusieurs Nios (utile pour faire des tests), on a souvent besoin de changer de programme (et donc d'espace de travail).
En considérant que l'adresse du dossier contenant les fichiers de description du Nios est la suivante :
~/NIOS_FOLDER/standard
…alors entrer ceci dans la case Workspace :
~/NIOS_FOLDER/standard/software
Nios2 IDE va automatiquement concevoir les librairies qui permettront de faire correspondre les adressages software au hardware (le Nios que l'on vient de finir). On va donc créer une nouvelle application, 1ère étape :

On va utiliser le template "Count Binary" car c'est le plus complet pour notre cas, 2ème étape :

Après avoir cliqué sur Finish, on va préparer le chargement du programme embarqué dans notre Nios sur le FPGA :
=> clic sur Run => Run…
1 => double clic sur Nios
II Hardware
2 => clic sur Common
3 => si on veut écrire dans un fichier les données rapatriées depuis le FPGA, cocher File et y indiquer un fichier de sortie
4 => clic sur Apply

Après avoir cliqué sur le bouton Apply, cliquer sur le bouton Run pour charger le programme dans notre processeur.
Maintenant que ça marche, on peut s'amuser avec la carte de développement, mais on peut aussi s'amuser en travaillant :
3. Caractérisation
d'opérateur
Si l'on reprend le projet qu'on a mis en place (partie HARD) on peut maintenant ajouter notre plateforme de caractérisation au fichier bdf.
Pour cela, il faut créer le symbole graphique correspondant au composant de description de notre plateforme.
=> View => Utility Windows => Project Navigator :

On considère le composant déjà fait et fonctionnel, dans notre cas il s'appelle MULTICORE.vhd :

Maintenant que ce symbole graphique est créé, on peut l'instancier dans notre fichier bdf :
=> Edit => Insert Symbol…
=> Project => double clic sur MULTICORE => "déposer" sur le fichier bdf.

Après quelques customisations on obtient ça :

Il faut maintenant recompiler le projet, puis recharger le FPGA avec l'outil Programmer.
On va pouvoir charger un programme C plus approprié, voici celui utilisé pour le dénombrement des masquages d'erreurs d'un opérateur constitué de 18 bits d'injection d'erreur :
//define the number of coefficients to get back :
(= nb of bits in error vector)
#define coef_nb 18 // case of the RC adder 8bits
#include "count_binary.h"
#include <time.h>
#include <stdio.h>
// A variable to hold the value of the button pio
edge capture register.
volatile int edge_capture;
// used to read the computation state:
static alt_u8 ready;
// used to show the computation state:
static alt_u8 dwn_cpt;
// coefficient calculated by our test bench
static alt_u32 coef;
// adress used to request coefficient from
test bench
static alt_u8 addr_out;
// used to restart the test:
static alt_u8 restart;
// used to count the calculation time:
static clock_t start;
static double the_time;
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);
/* 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.
*/
void* edge_capture_ptr = (void*) &edge_capture;
/* Enable all 4 button interrupts. */
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf);
/* 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 );
}
/* Seven Segment Display PIO Functions */
static void count_sevenseg(int hex)
{
static alt_u8 segments[16] = {
0x81, 0xCF, 0x92, 0x86, 0xCC, 0xA4, 0xA0, 0x8F, 0x80, 0x84, /* 0-9 */
0x88, 0xE0, 0xF2, 0xC2, 0xB0, 0xB8 }; /* a-f */
unsigned int data = segments[hex & 15] | (segments[(hex >> 4) & 15] << 8);
IOWR_ALTERA_AVALON_PIO_DATA(SEVEN_SEG_PIO_BASE, data);
}
/* Illuminate LEDs with the argument given */
static void count_led(alt_u8 b)
{
// make the LED's LSB on the right (= bitwise swap)
IOWR_ALTERA_AVALON_PIO_DATA
(
LED_PIO_BASE,
( (b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU) ) * 0x10101LU >> 16
);
}
static void led_sevenseg(alt_u8 hex)
{
count_sevenseg(hex);
count_led(hex);
}
static void refresh_IO()
{
// refresh "addr_out" to send the
good coef request :
IOWR_ALTERA_AVALON_PIO_DATA( ADRESS_COEF_BASE , addr_out); //
ADRESS_COEF_BASE = output 5bits to RAM
// GET THE COMPUTATION STATE (finish or not):
ready = IORD_ALTERA_AVALON_PIO_DATA( READY_BASE ); // READY_BASE = input 1bit
// GET THE COMPUTATION "TIME" UNTILL THE END:
dwn_cpt = IORD_ALTERA_AVALON_PIO_DATA( DOWN_CPT_BASE ); // DOWN_CPT_BASE = input 8bit
// Get the coefficient from the RAM :
coef = IORD_ALTERA_AVALON_PIO_DATA( COEF_BASE ); // COEF_BASE = input 32bits from RAM
}
static void reset_TEST_BENCH()
{
IOWR_ALTERA_AVALON_PIO_DATA( RESET_TEST_BENCH_BASE , 0); //reset
usleep(10);
//wait 10 µs = 500Tclk
IOWR_ALTERA_AVALON_PIO_DATA( RESET_TEST_BENCH_BASE , 1); //start
}
static void handle_button_press()
{
switch(edge_capture)
{
case 0x1: //button 0 :
start another iteration
reset_TEST_BENCH();
break;
default:
printf("recommencez!!!\r\n");
break;
}
edge_capture = 0; //reinit. interrup.
flag.
}
static void init_main()
{
refresh_IO();
led_sevenseg(0);
addr_out = 0;
dwn_cpt=0xFF;
printf("\r\n\r\n\r\nSTART...\r\n\r\n");
start = clock(); //start stopwatch
}
/*******************************************************************************
* int main()
*
******************************************************************************/
int main(void)
{
//initialisations:
init_button_pio();
init_main();
reset_TEST_BENCH();
// Endless loop:
while( 1 )
{
usleep(100000); //wait 0.1s
if (ready==0) //show if calculation is not yet done:
{
refresh_IO();
led_sevenseg(0xFF - dwn_cpt); //show when it's going to finish:
}
else //if calculation is finished then get results
{
while (addr_out<coef_nb)
{
if (addr_out == 0)
{
//get calculation time:
the_time = (double)(clock()-start)/(double)(CLOCKS_PER_SEC);
//display "the_time"
in elapsed seconds:
printf("...calculation time: %d min & %2.2f sec ( = %4.2f sec
)\r\n\r\n",
(int)(the_time/60), (int)the_time%60 + the_time - (int)the_time, the_time);
}
refresh_IO();
printf("ERROR_RANK(%d)=%u;\r\n",addr_out+1,coef);
addr_out++;
}
printf("\r\n sum(ERROR_RANK)\r\n");
// check for the reset request
restart = 0;
while( restart == 0 )
{
if (edge_capture!=0) //reset requested (push
button interrupt)
{
handle_button_press();
init_main();
restart = 1;
}
else usleep(1000000); //wait
1s
}
}
}
return 0;
}
//////////////////////////////////////////////////////////
// can be useful to send a digit from keyboard to FPGA: //
//////////////////////////////////////////////////////////
/*
static void saisie_clavier()
{
unsigned
int tmp;
printf( "\r\n\r\n Entrez une valeur
hexa à envoyer
au FPGA : \r\n");
scanf("%1x", &tmp);
addr_out=(alt_u8)tmp;
printf( "\r\n Vous avez saisi
: %1x, merci \r\n", addr_out);
}
*/
...et on peut maintenant récupérer nos données comme voulu sur la console ainsi que dans le fichier désigné plus haut :

On peut modifier le nombre (coef_nb) de coefficients à rapatrier grâce à la première ligne de code du fichier count_binary.c (que l'on a vu juste avant) :
#define coef_nb 18 // case of the RC adder 8bits
...pour le Nios avec la plateforme de caractérisation on peut normalement tout trouver ici :
/comelec/users/honnet/NIOS/
=> Pour lancer le projet de la plateforme de caractérisation à 1 et 2
erreurs :
cp -r /comelec/users/honnet/NIOS/1et2erreurs
~/TON_DOSSIER/1et2erreurs
quartus ~/TON_DOSSIER/1et2erreurs/standard/*.qpf &
Pour les autres projets, idem mais ils se trouvent là :
/comelec/users/honnet/NIOS/1erreur/standard/*.qpf
/comelec/users/honnet/NIOS/exhaustif/standard/*.qpf
/comelec/users/honnet/NIOS/random/standard/*.qpf
(Fichiers MULTICORE.vhd disponible aussi en annexe de mon Mémoire)
Si on change l'opérateur à tester, attention
:

1 => il faut que l'opérateur soit dans le projet ; moi je les ai mis dans le dossier :
/comelec/users/honnet/NIOS/ANY_COMPONENTS
2 => dans le fichier bdf
il faut changer la taille des entrées (k=8 par défaut), la taille du vecteur fault (w=18 par défaut), et le nombre de réplications
voulues (0 par défaut) => 2 et 3 étant les meilleurs compromis pour des gros
opérateurs (ça fait 4 et 8 c½urs) :
3 => dans le fichier MULTICORE.vhd il faut modifier
le nom de l'opérateur à instancier, j'ai laissé une note de ce genre aux 1ères
lignes :
--*************************************************************************
--*************************************************************************
--*** to change the name of
the tested components look arround line 130 ***
--*************************************************************************
--*************************************************************************
...
Quelques micro-modifications sont à faire en cas de caractérisation d'opérateurs autotestables mais ce n'est vraiment pas du VHDL compliqué.
Résumé :
- workspaces software (avec nios2-ide) :
/comelec/users/honnet/NIOS/ 1et2erreurs/software
1erreur/software
exhaustif/software
random/software
- projets hardware (avec quartus) :
/comelec/users/honnet/NIOS/1et2erreurs/standard/*.qpf
1erreur/standard/*.qpf
exhaustif/standard/*.qpf
random/standard/*.qpf
- dossier contenant les opérateurs à caractériser ainsi que la librairie cadence :
/comelec/users/honnet/NIOS/ANY_COMPONENTS
…et
2 types de fichiers importants :
- fichiers c de contrôle des Nios :
/comelec/users/honnet/NIOS/1et2erreurs/count_binary.c
1erreur/count_binary.c
exhaustif/count_binary.c
random/count_binary.c
- fichiers vhdl des test benchs :
/comelec/users/honnet/NIOS/1et2erreurs/MULTICORE.vhd
1erreur/MULTICORE.vhd
exhaustif/MULTICORE.vhd
random/MULTICORE.vhd
Le travail qui a été rapidement présenté dans ce Wiki est un peu plus développé dans le mémoire téléchargeable à cette adresse :
http://perso.telecom-paristech.fr/~honnet/MEMOIRE.pdf
Une fois
de plus, en cas de problème quelconque, ne pas hésiter à me contacter : cedric.honnet+wiki.enst@gmail.com

Comme toujours, si vous ou l'un de vos équipiers étiez capturés
ou tués, le département d'état nierait avoir eu connaissance de vos agissements.
Ce Wiki ne s'autodétruira pas dans 5 secondes, heureusement.