TP5 : GDB server & architectures multi-processeurs
}}}
[[PageOutline]]
= 1 Objectifs =
Ce TP possède un double objectif. D'une part, on présente l'outil '''GDB Server''' qui est pratiquement indispensable pour déverminer une application logicielle embarquée s'exécutant sur une architecture matérielle prototypée avec SoCLib.
D'autre part, et puisque l'outil GDB Server permet d'analyser le comportement d'architectures multiprocesseurs,
on en profitera pour introduire les architectures clusterisées utilisant deux niveaux d'indexation.
= 2 Outil GDB Server =
L'outil '''GDB Server''' permet d'analyser finement le comportement d'une application logicielle multi-threads s'exécutant sur une architecture matérielle multi-processeur modélisée avec SoCLib.
Cet outil permet à un client GDB (voir [http://www.gnu.org/software/gdb/ Gnu GDB]) s'exécutant sur n'importe quelle station de travail de prendre le contrôle du simulateur d'une plate-forme matérielle modélisée avec SoCLib.
La documentation de l'outil '''GDB Server''' peut être consultée [https://www.soclib.fr/trac/dev/wiki/Tools/GdbServer 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
* de visualiser le contenu des registres de n'importe quel 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 cpu0("cpu0", ...);
}}}
par une instanciation faisant appel au GDB Server
{{{
VciXcacheWrapper > cpu0("cpu0", ...);
}}}
Le GDB server est donc 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 contrôler le processeur
(pour le faire fonctionnner en pas à pas par exemple), et on peut également 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 "gdb_server.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ù la plate-forme est "gelée",
et attend la connexion du GDB Server. Cela peut être réalisé en définissant la variable d'environnement SOCLIB_GDB avant de lancer le simulateur :
{{{
$ export SOCLIB_GDB=START_FROZEN
$ ./simulator.x
}}}
== 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
}}}
La seconde commande à taper dans GDB est la commande permettant d'éviter que le GDB Serverse bloque chaque fois que
le processeur entre dans le noyau du système d'exploitation (adresse 0X80000180) :
{{{
(gdb) monitor exept 0 1
}}}
== 2.4 identification des processeurs ==
L'outil GDB standard permet par 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 (proc_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
}}}
== 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.
= 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)`.
L'espace d'adressage reste partagé par tous les composants du système (quel que soit leur cluster), et n'importe
quel initiateur peut directement adresser n'importe quelle cible. Si l'initiateur et la cible n'appartiennent pas au même
cluster, les paquets VCI (commande et réponse) sont acheminés grace à un interconnect global (généralement un
micro-réseau intégré ou NoC).
[[Image(soclib_tp5_multi.png)]]
Ce regroupement en clusters répond généralement à deux objectifs:
* D'un point de vue architecture, regrouper dans un même cluster les composants qui communiquent beaucoup entre eux permet de réduire la latence des communications, et de minimiser la consommation. Ce découpage permet également de distribuer la mémoire embarquée, et d'éviter le goulot d'étranglement que constituerait un unique banc mémoire sur la puce (même si l'accès à la mémoire externe reste un goulot d'étranglement).
* 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.
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 ||
Cette organisation hiérarchique à deux niveaux impose évidemment que les valeurs des champs GADR 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)
= 4 Travail à réaliser =
L'archive [attachment:soclib_tp5.tgz 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
}}}
== 4.1 GDB Server ==
On va dans un premier temps réutiliser la même architecture mono-processeur que celle modélisée dans le TP4, et on cherche
à exécuter la même application logicielle embarquée. Mais deux ''bugs'' ont été volontairement introduit dans le logiciel, et l'objet de cette première partie est de localiser et de corriger ces deux ''bugs'', en utilisant l'outil '''GDB Server'''.
[[Image(soclib_tp4_cluster.png)]]
Commencer par vous placer dans le répertoire '''soft''', et lancez le Makefile pour générer le fichier '''bin.soft''', ainsi que le
fichier '''bin.soft.txt''' qui contient la version désassemblée (lisible) du logiciel embarqué.
Lancez l'exécution du simulateur dans une première fenêtre... et constatez que vous n'obtenez pas le résultat attendu.
Modifiez le fichier '''tp4_top.cpp''' et le fichier tp5.desc pour introduire le GDB Server dans l'architecture comme indiqué ci-dessus.
Regénérez le simulateur en utilisant soclib-cc. Définissez la variable d'environnement SOCLIB_GDB.
Lancez l'exécution du simulateur dans une première fenêtre de travail.
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 et désactivez le mécanisme de blocage sur Exceptions Interruptions et Trappes, en utilisant les deux commandes ci-dessus. Commencez à exécuter le programme instruction par instruction avec la commande ''stepi''. Le premier dysfonctionnement apparaît assez rapidement...
Quand les deux bugs ont été localisés et corrigés, vous pouvez attaquer l'étape suivante.
== 4.2 architecture à 4 clusters ==
On souhaite maintenant modéliser une architecture quadri-processeurs structurée en quatre clusters presque identiques,
Chaque cluster contiendra un processeur MIPS32, un composant ICU, un timer, et une mémoire. On se dispensera d'instancier le coprocesseur GCD dans cette architecture. On instanciera un seul contrôleur de TTY (contrôlant 4 terminaux numérotés de 0 à3),
et une seule ROM de boot qu'on placera respectivement dans le cluster 0 et dans le cluster 3. On utilisera un composant '''vci_local_crossbar''' comme interconnect local ( voir documentation [https://www.soclib.fr/trac/dev/wiki/Component/VciXcacheWrapper ici]) , et on utilisera le composant '''vci_vgmn''' comme interconnect global ( voir documentation [https://www.soclib.fr/trac/dev/wiki/Component/VciXcacheWrapper ici]) .
Il faut donc écrire les deux fichiers tp5_top.cpp et TP5.desc correspondant à cette architecture,
en veillant à donner des valeurs croissantes (de 0 à 3) à l'argument ''processor-id'' des 4 processeurs.
Il faut définir de nouveaux segments pour les nouveaux périphériques. Il faut également définir 4 segments distincts
pour les piles d'exécution des 4 processeurs. En revanche, les segment seg_reset, seg_kernel et seg_code qui ne contiennent que des instructions peuvent être partagés par les 4 processeurs.
Le code de boot doit également être modifié. Lors du démarrage, les quatre processeurs vont exécuter le même code,
mais les actions réalisées peuvvent 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.
En sortie de la séquence de boot, les quatre processeurs peuvent se brancher au même programme main().
Si tout se passe normalement, les quatre processeurs doivent exécuter le même programme interactif ''Hello World'', chacun
sur son propre terminal.
Si ce n'est pas le cas, il vous reste le '''GDB Server'''...
= 5 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...