Version 38 (modified by 14 years ago) (diff) | ,
---|
TP5 : GDB server & architectures clusterisées
1 Objectifs
Ce cinquième TP a un double objectif : D'une part, on présente l'outil GDB Server qui est le principal outil permettant de déverminer une application logicielle s'exécutant sur une architecture matérielle prototypée avec SoCLib. D'autre part, on présente les outils permettant de décrire des architectures clusterisées utilisant deux niveaux d'interconnexion et donc deux niveaux d'indexation.
2 Outil GDB Server
L'outil GDB est un outil de debug très utilisé, qui fait partie de la même famille d'outils logiciels libres que le compilateur GCC que vous connaissez déjà (voir Gnu GDB).
Le GDB Server peut être décrit comme un composant matériel qui vient s'interfacer entre le processeur et le contrôleur de cache. Dans cette position stratégique, il peut surveiller et contrôler toutes les communications entre le processeur et le reste de la plate-forme matérielle (principalement la mémoire).
Le comportement du composant matériel GDB server est lui-même contrôlé par une application logicielle interactive, appelée GDB clientqui peut s'exécuter sur une autre station de travail que la station de travail qui simule l'exécution de la plate-forme modélisée avec SoCLib.
La documentation de l'outil GDB Server peut être consultée ici.
Le GDB Server permet:
- de poser des points d'arrêt dans le programme en cours d'exécution
- d'exécuter le programme pas à pas (instruction par instruction)
- de visualiser le contenu des registres du processeur
- de visualiser la valeur stockée à n'importe quelle adresse dans l'espace adressable
- de modifier le contenu de la mémoire ou des registres des processeurs
Le simulateur (qui contient le serveur GDB) et le client GDB peuvent s'exécuter sur deux stations de travail différentes, puisque les communications entre le client GDB et le serveur GDB utilisent un canal TCP.
2.1 Modification de la top-cell
Pour utiliser le GDB Server, tous les processeurs dont on souhaite prendre le contrôle doivent être instanciés dans un mode particulier lorsqu'on définit l'architecture de la top-cell. Il faut remplacer l'instanciation habituelle du processeur:
VciXcacheWrapper<vci_param, Mips32ElIss> proc("proc", ...);
par une instanciation faisant appel au GDB Server
VciXcacheWrapper<vci_param, GdbServer<Mips32ElIss> > proc("proc", ...);
Le GDB server est un pseudo-composant matériel, qui s'interface entre le processeur et le cache. En prenant le contrôle du GDB Server (par l'intermédiaire du client GDB), on peut donc faire deux choses :
- contrôler le processeur (pour le faire fonctionnner en pas à pas par exemple),
- contrôler le reste du système (en effectuant directement des commandes de lecture ou d'écriture vers la mémoire).
Il ne fautpas oublier d'inclure le "header" dans la top-cell
#include "gdbserver.h"
Il faut également complêter le fichier de description de l'architecture utilisé par soclib-cc (fichier platform.desc
):
Uses('caba:iss_wrapper', iss_t = 'common:gdb_iss', gdb_iss_t = 'common:mips32el')
2.2 lancement de la simulation
Pour utiliser GDB Server, il est généralement préférable de lancer le simulateur dans un mode où le processeur est "gelé". Dans ce mode, le GDB serveur ne transmet aucune requêtedu processeur vers le cache, et attend la connexion du client GDB. Cela peut être réalisé en définissant la variable d'environnement SOCLIB_GDB avant de lancer le simulateur :
$ export SOCLIB_GDB=F $ ./simulator.x
Lorsqu'on a fini d'utiliser le GDB server, et qu'on veut revenir dans le mode ou le simulateur démarre normalement, il faut re-modifier la variable d'environnement:
$ export SOCLIB_GDB=
2.3 lancement du client GDB
Une fois que le simulateur est lancé, il faut lancer dans une autre fenêtre, l'exécution du client GDB adapté au type de processeur instancié dans la plate-forme, en lui passant en argument le nom du fichier contenant le code binaire exécuté par les processeurs de l'architecture. Pour un processeur MIPS32:
mipsel-unknown-elf-gdb soft/bin.soft
La première commande à taper dans GDB est la commande permettant de connecter le client GDB au GDB Server :
(gdb) target remote localhost:2346
2.4 identification des processeurs
L'outil GDB standard permet d'analyser le comportement d'applications multi-threads s'exécutant sur une architecture monoprocesseur. Nous souhaitons ici contrôler une architecture multi-processeurs. Pour pouvoir réutiliser le client GDB standard, le GDB Server considère chaque processeur comme un thread. Pour obtenir le nombre de processeurs contrôlables par GDB, il faut taper la commande :
(gdb) info threads
Attention : la numérotation des threads ne correspond pas nécessairement à la numérotation des processors (processor_id)
2.5 Points d'arrêt
La commande break (br) permet de poser des points d'arrêt correspondant à la détection d'une adresse particulière dans le registre PC d'un processeur quelconque de l'architecture simulée :
(gdb) br *0x8000180
Dans ce cas, tous les processeurs de la plate-forme s'arrêtent simultanément.
On peut également utiliser un nom de fonction plutôt qu'une adresse hexadécimale
(gdb) br function_name
Pour supprimer un point d'arrêt, il faut lancer la commande qui retourne la liste des points d'arrêts enregistrés avec leur numéro:
(gdb) info br
Puis lancer la commande de destruction, en désignant le point d'arrêt par son numéro :
(gdb) delete n
2.6 Exécution
Lorsque l'exécution est arrété, la commande continue (c) permet de relancer l'exécution jusqu'au prochain point d'arrêt. Tous les processeurs reprennent leur exécution.
(gdb) c
La commande stepi permet de relancer l'exécution d'une seule instruction assembleur. Seul le processeur qui a détecté un point d'arrêt est concerné. Les autres processeurs restent bloqués:
(gdb) stepi
2.7 Observation des registres
La commande info permet d'afficher différentes informations.
(gdb) info
Sans argument, cette commande donne la liste des informations disponibles.
(gdb) info reg
Avec l'argument reg, elle affiche le contenu des registres du processeur qui a détecté un point d'arrêt. Si on veut observer les valeurs contenues dans les registres d'un autre processeur, il faut préalablement utiliser la commande thread n (où n est le numéro du thread représentant le processeur).
(gdb) thread n (gdb) info reg
2.8 Observation/modification de la mémoire
Pour observer les valeurs contenues en mémoire, on peut utiliser la commande examine (x) :
(gdb) x/10x 0x400000
Cette commande va afficher en hexadécimal 10 mots de 32 bits à partir de l'adresse 0x400000.
Pour modifier une valeur en mémoire, on peut utiliser la commande set :
(gdb) set *(int*)0x400000 = val
Cette commande écrit la valeur val (de type int) à l'adresse 0x400000.
L'archive soclib_tp3.tgz contient différents fichiers dont vous aurez besoin pour ce TP. Créez un répertoire de travail spécifique TP5, recopiez l'archive dans ce répertoire TP5, et décompressez-la:
$ tar xzvf soclib_tp3.tgz
2.9 Utilisation
A titre d'exercice, vous allez utiliser le GDB server sur l'architecture mono-processeur du TP4, sur laquelle vous exécuterez successivement deux applications logicielles contenant chacune un bug que vous devez localiser puis corriger.
Outre les fichiers permettant de regénérer l'architecture monoprocesseur du TP4, l'archive soclib_tp5.tgz contient deux répertoires soft_gcd et soft_prime. Le répertoire soft_gcd contient tous les fichiers nécessaires à la génération du code binaire de l'application de calcul du PGCD que vous connaissez déjà. Le répertoire soft_prime contient tous les fichiers nécessaires à la génération du code binaire d'une autre application logicielle réalisant le calcul des nombres premiers. Les deux bugs (différents) qui ont été introduits dans chacune de ces deux applications ne sont pas visibles à la compilation, et n'apparaissent qu'à l'exécution.
Créez un répertoire de travail TP5, puis téléchargez et décompressez l'archive dans ce tépertoire.
$ tar xzvf soclib_tp5.tgz
Question : Placez-vous dans le répertoire soft_gcd, compilez l'application logicielle en utilisant le Makefile qui vous est fourni. Retourner dans le répertoire TP4_etudiant pour générer le simulateur simulator.x. Lancez l'exécution du simulateur ... et constatez que vous n'obtenez pas le résultat attendu. Il faut évidemment redéfinir le chemin d'accès au code binaire:
$ ./simulator.x -SOFT soft_gcd/bin.soft
Modifiez le fichier tp4_top.cpp et le fichier tp4.desc, ainsi que le fichier Makefile pour introduire le GDB Server dans l'architecture comme indiqué ci-dessus, en remplaçant partout tp4 par tp5 dans les noms de fichier.
Regénérez le simulateur en utilisant soclib-cc.
Lancez l'exécution du simulateur dans une première fenêtre de travail, après avoir redéfini la variable d'environnement SOCLIB_GDB comme indiqué ci-dessus.
Ouvrez dans une seconde fenêtre le fichier bin.soft.txt, de façon à pouvoir suivre - instruction par instruction - le programme en cours d'exécution, depuis la première instruction du code de boot (adresse Oxbfc00000).
Lancez le client GDB dans une troisième fenêtre, connectez-le au simulateur. Commencez à exécuter le programme instruction par instruction avec la commande stepi. Après chaque commande stepi, il faut lancer une commande info r pour afficher le contenu des registres internes du processeur, et vérifier que le comportement de la machine est conforme à ce qu'on attend. Le premier dysfonctionnement apparaît assez rapidement...
Question : Après avoir localisé le bug, corrigez-le et vérifiez que le calcul du PGCD s'exécute correctement.
Question : Refaites le même exercice avec l'application logicielle contenue dans le répertoire soft_prime.
Quand les deux bugs ont été localisés et corrigés, vous pouvez attaquer l'étape suivante.
3 Architectures Clusterisées
Nous appellerons architecture clusterisée une architecture dans laquelle on utilise un double système d'index
pour repérer les initiateurs et les cibles VCI. Un cluster est un sous-système regroupant généralement plusieurs
initiateurs et plusieurs cibles VCI, communiquant entre eux par un interconnect local (bus ou crossbar).
Chaque composant est donc repèré par un couple (cluster_index, local_index)
.
Les architectures clusterisées sont généralement des architectures NUMA (Non Uniform Memory Access). On trouve en effet un banc mémoire physique par cluster, afin d'éviter de remplacer le goulot d'étranglement du bus partagé par le goulot d'étranglement de l'accès à un unique banc mémoire. La mémoire est donc physiquement distribuée, même si l'espace d'adressage reste partagé par tous les composants du système (quel que soit leur cluster).
NUMA signifie donc deux chose :
- n'importe quel initiateur peut directement adresser n'importe quelle cible,
- la latence et la consommation énérgétique seront beaucoup plus faible lorsque qu'un processeur accède à la mémoire locale (dans le même cluster) que lorsqu'il accède à une mémoire distante (dans un autre cluster).
On a généralement un mécanisme d'interconnexion local dans chaque cluster (bus, anneau ou crossbar) pour les communications locales, et un interconnect global (généralement un micro-réseau intégré sur puce) pour les communications inter-clusters.
Les architectures NUMA clusterisées visent donc deux objectifs:
- D'un point de vue performance, on peut réduire la consommation énergétique et la latence des communications en regroupant dans un même cluster les composants qui communiquent beaucoup entre eux.
- D'un point de vue électrique, le découpage en clusters permet de résoudre en partie les problèmes d'horlogerie, puisque chaque cluster peut être implanté dans un domaine d'horloge séparé (approche GALS : Globally Asynchronous / Locally Synchronous). Le franchissement des frontières d'horlogre est alors la responsabilité du micro-réseau assurant les communications inter-clusters.
Remarquez cependant quela capacité de stockage de la mémoire embarquée n'est jamais suffisante, et que l'accès à la mémoire externe reste un goulot d'étranglement.
3.1 structuration des adresses et des identifiants
Pour faciliter le décodage des adresses, on décompose les bits de poids fort de l'adresse VCI en deux champs GADR et LADR, de telle sorte que le décodage du champs GADR définisse complêtement le numéro du cluster cible. Le décodage du champs LADR permet lui de déterminer l'index local de la cible dans un cluster. Le nombre de bits des champs GADR et LADR est une caractéristique de chaque architecture:
GADR LADR OFFSET
De la même façon, les identifiants des composants (SRCID ou TGTID) sont également décomposés en deux champs CID (cluster_index) et LID (local_index):
CID LID
Cette organisation hiérarchique à deux niveaux impose évidemment que les valeurs des champs GADR des adresses de base des segments associés aux cibles d'un même cluster soient égales entre elles (ou appartiennent à un même ensemble de valeurs caractéristiques de ce cluster)
3.2 architecture à 4 clusters
On souhaite modéliser une architecture structurée en quatre clusters presque identiques, Chaque cluster contiendra un processeur MIPS32, un composant ICU, un contrôleur TTY, un TIMER et une mémoire RAM.
- On se dispensera d'instancier le coprocesseur GCD dans cette architecture.
- On placera le contrôleur de disque IOC dans le cluster 0.
- On placera le contrôleur DMA dans le cluster 1.
- On placera le contrôleur d'écran graphique FBF dans le cluster 2.
- On placera la ROM de boot dans le cluster 3.
On utilisera un composant vci_local_crossbar comme interconnect local ( voir documentation ici) , et on utilisera le composant vci_vgmn comme interconnect global ( voir documentation ici) .
Pour ce qui concerne les interruptions:
- Dans chaque cluster (i), la ligne d'interruption du TIMER sera connectée à l'entrée IRQ_IN[0] du composant ICU[i].
- Dans chaque cluster (i), la ligne d'interruptiondu TTY sera connectée à l'entrée IRQ_IN[1] du composant ICU[i].
- La ligne d'interruption du contrôleur IOC sera connectée à l'entrée IRQ_IN[2] du composant ICU[0].
- La ligne d'interruption du contrôleur DMA sera connectée à l'entrée IRQ_IN[3] du composant ICU[1].
On définit 25 segments pour cette plate-forme :
- 4 segments seg_icu_i associés aux 4 composants ICU[i],
- 4 segments seg_tty_i associés aux 4 composants TTY[i]
- 4 segments seg_tim_i associés aux 4 composants TIMER[i]
- 4 segments seg_stack_i pour les 4 piles d'exécution des 4 processeurs
- 1 segment seg_reset pour le code de RESET à l'adresse imposée 0xBFC00000
- 1 segment seg_kcode pour le code système à l'adresse imposée 0x80000000
- 1 segment seg_kdata pour les données cachables du système d'exploitation.
- 1 segment seg_kunc pour les données non-cachables du système d'exploitation.
- 1 segment seg_text pour le code des programmes utilisateur
- 1 segment seg_data pour les données globale des programmes utilisateur
- 1 segment seg_fbf associé au frame buffer
- 1 segment seg_ioc associé au contrôleur de disque
- 1 segment seg_dma associé au contrôleur DMA
Question : Pourquoi faut-il des segments distincts pour les 4 piles d'exécution ?
Question : Proposez des adresses de base pour ces 25 segments, en tenant compte du fait que le crossbar local ne doit décoder que les bits (LADR) de l'adresse, et que le réseau global ne doit décoder que les bits (GADR) de l'adresse. Tenez compte du fait que dans chaque cluster, il existe au moins un segment appartenant à l'espace utilisteur (le segment de pile), et trois segments appartenant à l' espace superviseur (les segments associés au TTY, au TIMER et à l'ICU). Recommandation : on peut utiliser les 4 bits A[31:28] pour le champs GADR, en considérant que seuls les 2 bits A[29:28] sont réellement discriminants pour désigner le cluster visé.
Question : Modifiez le fichier ldscript pour définir ces adresses de bases, et pour préciser le nombre de processeurs.
Question : Complétez les deux fichiers tp5_top_cluster.cpp et tp5_top_cluster.desc correspondant à cette architecture. n'oubliez pas de donner des valeurs croissantes (de 0 à 3) à l'argument processor-id des 4 processeurs.
3.3 Logiciel embarqué
Le logiciel doit être assez profondément modifié :
- code de boot (fichier reset.s) : Lors du démarrage, les quatre processeurs vont exécuter le même code (puisqu'ils se branchent à la même adresse 0xBFC00000), mais les actions réalisées peuvent dépendre du processor_id : En particulier, les pointeur de pile des quatre processeurs doivent être initialisés à des valeurs différentes puisque chaque processeur travaille dans son propre segment de pile.
- code des ISR (fichier isr.s) : Les deux ISR _isr_timer et _isr_tty_get doivent être modifiées pour tenir compte du processor_id : chaque processeur doit adresser le TIMER et le terminal TTY qui lui appartient.
- code du GIET (fichier giet.s) : Le gestionnaire d'interrutions lui aussi doit être modifié, puisqu'il doit maintenant utiliser le processor_id pour interroger le bon contrôleur ICU.
- code des appels systèmes (fichier syscalls.c) : Les deux fonctions système _icu_write() et _icu_read() doivent être modifiées pour tenir compte du processor_id lors des accès aux registres du composant ICU.
- segmentation (fichier ldscript) : Il ne faut pas oublier de modifier le fichier ldscript pour que les adresses de base des segments soient cohérentes avec ce qui a été défini dans le fichier tp5_top.cpp.
En sortie de la séquence de boot, les quatre processeurs se branchent à la même fonction main(), qui doit elle aussi être modifiée pour tenir compte du processor_id, puisque le numéro du timer est un argument des fonctions timer_set_period() et timer_set_mode().
Si tout se passe normalement, les quatre processeurs doivent exécuter le même programme interactif Hello World (avec affichage des interruptions générées par les timers), chacun sur son propre terminal TTY.
Si ce n'est pas le cas, il vous reste le GDB Server...
4 Compte_rendu
Il ne vous est pas demandé de compte-rendu pour ce TP, mais on vous demandera une démonstration de votre simulateur au début du TP de la semaine suivante...
Attachments (4)
- soclib_tp5_gdb_server.png (37.3 KB) - added by 14 years ago.
- soclib_tp5_transpose.png (19.8 KB) - added by 14 years ago.
- soclib_tp5_archi_clusters.png (36.6 KB) - added by 11 years ago.
- soclib_tp5.tgz (241.9 KB) - added by 11 years ago.
Download all attachments as: .zip