{{{ #!html

TP4 : Architectures multi-processeurs et périphériques

}}} [[PageOutline]] = 1 Objectif = Le but de ce quatrième TP est double : D'un côté, on souhaite introduire les différents périphériques qui seront utilisés dans la suite de ces TPs, en insistant sur les mécanismes de communication entre les périphériques et le système d'exploitation. D'un autre côté, on souhaite modéliser des architectures multi-processeurs génériques, telles qu'on puisse facilement faire varier le nombre de processeurs. . = 2. Périphériques = Il existe deux types de périphériques: * Un périphérique ''orienté caractère'' (tel qu'un contrôleur TTY) supporte des requêtes de lecture ou d'écriture d'un petit nombre de caractères. Ce type de périphérique se comporte généralement comme une cible sur le bus, puisqu'il ne peut que recevoir des commandes provenant d'un processeur, et qu'il n'a pas la possibilité de lire ou d'écrire lui-même en mémoire. Le contrôleur de périphérique utilise une interruption pour signaler au système d'exploitation qu'un événement (tel qu'un caractère frappé au clavier) s'est produit du côté du périphérique. * Un périphérique orienté ''bloc'', tel qu'un contrôleur de disque, doit tranférer de grosses quantités de données entre la mémoire et le disque. Les transferts se font par blocs (un bloc contenant généralement 512 octets), et ces périphériques ont généralement une capacité DMA : Ils sont à la fois maître et cible sur le bus, car ils reçoivent des commandes définissant le transfert à effectuer, mais peuvent ensuite directement lire ou écrire en mémoire. Le contrôleur de périphérique utilise une interruption pour signaler au système d'exploitation la fin du transfert. == 2.1 composants matériels == Lorsque le nombre de périphériques augmente, le nombre de lignes d'interruption augmente également, et il faut un mécanisme permettant de concentrer plusieurs dizaines de requêtes d'interruption vers un seul signal IRQ connecté au processeur. C'est en interrogeant le composant matériel ICU (Interrupt Controler Unit) que le système d'exploitation (en pratique le gestionnaire d'interruption) peut obtenir le numéro de la ligne d'interruption active la plus prioritaire. Le composant '''vci_icu''' est un contrôleur d'interruptions vectorisées. C'est une cible VCI dont vous trouverez la spécification fonctionnelle [http://www.soclib.fr/trac/dev/wiki/Component/VciIcu ici]. Prenez le temps de la lire. Le composant '''vci_multi_timer''' est également une cible VCI contenant un nombre queconque de timers programmables capables de générer des interruptions périodiques. On trouvera la spécification fonctionnelle de ce composant [http://www.soclib.fr/trac/dev/wiki/Component/VciMultiTimer ici]. Le composant '''vci_block_device''' est un contrôleur de périphérique de stockage externe (disque ou mémoire flash). Ce composant IOC est à la fois un initiateur VCI, capable de lire et d'écrire dans la mémoire, et une cible qui peut recevoir des commandes de configuration. Puisque nous sommes en simulation, le composant IOC gère un unique fichier, stocké sur le disque de la station de travail qui exécute le simulateur. Le nom de ce fichier est un argument du constructeur. On trouvera la spécification fonctionnelle de ce composant [http://www.soclib.fr/trac/dev/wiki/Component/VciBlockDevice ici]. Le composant '''vci_frame_buffer''' est un contrôleur d'écran graphique. C'est une cible VCI qui est vue comme un tampon mémoire directement adressable de M lignes de N pixels, dans lequel le logiciel peut lire ou écrire. Le contenu de ce buffer est parcouru périodiquement, et son contenu est affiché sur l'écran graphique externe. On trouvera la spécification fonctionnelle de ce composant [http://www.soclib.fr/trac/dev/wiki/Component/VciFrameBuffer ici]. Le composant '''vci_dma''' est un composant matériel qui peut être programmé par le système d'exploitation pour éffectuer de gros transferts de données, tel que la copie d'une image d'un tampon mémoire situé dans l'espace utilisateur vers le tampon mémoire du composant FBF. On trouvera la spécification fonctionnelle de ce composant DMA [http://www.soclib.fr/trac/dev/wiki/Component/VciDma ici]. Les deux composants IOC et DMA étant à la fois initiateur et cible, on obtient finalement une architecture possédant trois initiateurs VCI (indexés de 0 à 2), et 9 cibles VCI (indexées de 0 à 8), conformément au schéma ci-dessous. [[Image(soclib_tp4_mono.png)]] Les lignes d'interruptions ne passent pas par le réseau VCI : chaque ligne d'interruption d'un périphérique est directement connectée aux ports d'entrée '''p_irq_in[i]''' du composant ICU : * La ligne d'interruption du TIMER est connectée au port '''p_irq_in[0]''' * la ligne d'interruption du TTY est connectée au port '''p_irq_in[1]''' * la ligne d'interruption du contrôleur IOC est connectée au port '''p_irq_in[2]''' * la ligne d'interruption du contrôleur DMA est connectée au port '''p_irq_in[3]''' Le système d'exploitation (GIET) associe à chaque ligne d'interruption une routine de traitement spécifique, appelée ISR (Interrupt Service Routine), qui est exécutée par le processeur lorsque la ligne d'interruption est activée par le périphérique, et que les interruptions ne sont pas masquées. Il s'agit donc pour le périphérique de "voler" quelques cycles du processeur pour lui permettre d'exécuter un peu de code. L'ISR permet généralement au périphérique de signaler un événement en allant écrire dans un emplacement prédéfini en mémoire. == 2.2 Communication entre l'OS et le contrôleur TTY == Dans le TP3, le programme utilisateur utilise l'appel système '''tty_getc()''' pour lire un caracère. Cet appel système contient une boucle de scrutation dans laquelle, à chaque tour de boucle, le processeur effectue une transaction sur le bus pour lire la valeur du registre STATUS du terminal TTY concerné. On ne sort de cette boucle que lorsque la valeur lue indique qu'un caractère a effectivement été saisi au clavier. Dans ce TP4, le programme utilisateur utilisera l'appel système '''tty_getc_irq()''' pour lire un caractère. Cet appel système utilise un tampon mémoire partagé '''_tty_get_buf''', protégé par une variable de synchronisation '''_tty_get_full'''. Ces deux variables appartiennent au système d'exploitation et sont stockées en mémoire dans le segment ''seg_kunc'', qui est à la fois protégé (non directement accessible par un programmes utilisateur) et non cachable. Plutôt que d'accéder directement au registre STATUS du contrôleur TTY, l'appel système '''tty_getc_irq()''' teste la variable '''_tty_get_full''' rangée en mémoire pour savoir si un caractère est disponible. C'est la routine d'interruption (ISR) associée au terminal TTY qui se charge d'écrire le code ASCII du caractère dans le tampon '''_tty_get_buf''', et de forcer à 1 la variable de synchronsation '''_tty_get_full''' pour signaler que le tampon est plein. Cette variable de synchronisation est remise à 0 par l'appel système '''tty_getc_irq()''' lorsque le caractère est transféré du tampon système '''tty_get_buf''' vers le tampon mémoire défini par l'utilisateur. Une architecture peut contenir plusieurs processeurs et chaque processeur peut exécuter plusieurs tâches (plusieurs programmes utilisateurs) en pseudo-paralléliseme, par multiplexage temporel. Le GIET supporte au plus 8 processeurs, et au plus 4 tâches par processeur, soit 32 tâches au maximum. Comme chaque tâche possède son propre terminal écran/clavier, Il peut exister jusque 32 terminaux indépendants, qui sont tous contrôlés par le même contrôleur TTY : Chaque terminal possède donc son propre jeu de 4 registres, pour communiquer avec l'OS. On dit que le contrôleur TTY est un périphérique multi-canaux. Le GIET définit donc deux tableaux '''_tty_get_buf[32]''' et '''_tty_get_full[32]''', indexés par le numéro du terminal concerné. '''Question''' : Comment les appels système '''tty_getc_irq()''' et '''tty_puts()''' calculent-ils l'index du terminal associé à la tâche qui effectue l'appel système ? La réponse se trouve dans le fichier '''sys/drivers.c'''. '''Question''' : Comment le code de l'ISR associée à l'interruption générée par le TTY calcule-il l'index de l'entrée qui doit être modifiée dans les tableaux '''_tty_get_buf[32]''' et '''_tty_get_full[32] ? La réponse se trouve dans le fichier '''sys/irq_handler.c'''. '''Question''' : Que fait la routine d'interruption ISR déclenchée par le périphérique TTY lorsqu'un caractère est frappé au clavier et que la variable '''_tty_get_full[i]''' vaut 1 (ce qui signifie que le tampon '''_tty_get_buf[i]''' est plein ? La réponse se trouve dans le fichier '''sys/irq_handler.c'''. '''Question''' : Quel est l'avantage de ce type de communication par interruption, comparé au mécanisme de scrutation utilisé dans le TP3 ? == 2.2 Communication entre l'OS et le contrôleur IOC == Les deux appels système '''ioc_read()''' et '''ioc_write()''' permettent à un programme utilisateur de demander au GIET de réaliser un transfert de données entre un tampon mémoire utilisateur et le périphérique de stockage externe. Tous les transferts se font par blocs de 512 octets. '''Question''' : Editez le fichier '''stdio.c''' pour déterminer la signification des trois arguments de ces appels système. Editez le fichier '''drivers.c''' pour voir ce que font réellement les deux fonctions système '''_ioc_read()''' et '''_ioc_write()''' associées. Quand ces fonctions rendent-elle la main au programme utilisateur? L'appel système '''ioc_completed()''', qui appelle lui-même la fonction système '''_ioc_completed()''' permet au programme utilisateur de se mettre en attente sur la fin d'un transfert. C'est donc une fonction bloquante qui ne rend la main au programme utilisateur que lorsque le transfert est effectivement terminé. '''Question''' : Le contrôleur de disque utilisé ici ne peut effectuer qu'un seul transfert de données à la fois. Ce n'est donc pas un périphérique multi-canaux. Si deux programmes utilisateurs s'exécutant en parallèle sur deux processeurs différents demandent simultanément à utiliser le contrôleur de disque, comment le GIET peut-il garantir que le périphérique ne sera alloué qu'à un seul des deux demandeurs? '''Question''': Expliquez précisément le rôle des deux variables _ioc_lock et _ioc_done utilisées par le GIET pour contrôler l'accès au contrôleur de disque. = 3 Modélisation de l'architecture matérielle = L'archive [attachment:soclib_tp4.tgz soclib_tp4.tgz] contient différents fichiers dont vous aurez besoin pour ce TP. Créez un répertoire de travail spécifique TP4, recopiez l'archive dans ce répertoire, et décompressez-la: {{{ $ tar xzvf soclib_tp4.tgz }}} Outre les fichiers qui permettent de générer le simulateur de l'architecture matérielle, cette archive contient également le sous-répertoire '''soft''' qui est utilisé pour la génération du logiciel embarqué. '''Question''' : Complêtez le fichier '''tp4_top.cpp''' pour définir les adresses de base et les tailles des segments associés aux composants TTY, GCD, ICU, TIMER, IOC, FBF et DMA, et pour introduire ces 7 segments dans la table des segments. Ces segments sont-ils cachables ou non cachables? Les adresses de base des segments sont définis dans le fichier '''soft/seg.ld'''. Les tailles minimales des segments sont définies dans la documentation des composants matèriels sur le site SoCLib. '''Question''' : Complétez le fichier '''tp4_top.cpp''' pour définir les arguments des constructeurs des composants ICU, TIMER, IOC, FBF, DMA et BUS. Pour le composant IOC on fera en sorte que le cheminom désignant le fichier externe représentant le disque puisse être redéfini par un paramètre sur la ligne de commande au lancement du simulateur. Pour le composant FBF, on choisira une taille d'écran de 128 lignes de 128 pixels. '''Question''' : Complétez la net-list dans le fichier '''tp4_top.cpp''' pour connecter sur les 4 premières entrées du composant ICU les 4 lignes d'interruption '''irq_tim''', '''irq_tty''', '''irq_ioc''', '''irq_dma''' utilisées dans cette architecture, dans cet ordre. '''Question''' Complétez la net-list pour connecter sur le bus les 3 initiateurs et les 9 cibles. '''Question''' : Complétez le fichier '''tp4_top.desc''' pour pouvoir utiliser soclib-cc, et utilisez le Makefile pour générer le simulateur. = 4. Logiciel embarqué = Le répertoire ''soft'' contient les sept fichiers '''app.ld''', '''sys.ld''', '''seg.ld''', '''config.h''', '''reset.s''', '''main.c''', et '''Makefile''', permettant de générer le logiciel embarqué. == 4.1 Code de boot == Puisqu'on utilise des interruptions, le ''code de boot'', défini dans le fichier '''soft/reset.s''', doit initialiser le vecteur d'interruption (c'est à dire le tableau indexé par le numéro d'interruption, et contenant les adresses des différentes routines d'interruption). Il doit également initialiser le composant ICU, pour démasquer les interruptions qu'on veut autoriser. '''Question''' : En ouvrant le fichier '''sys/irq_handler.c''', déterminez les nom des quatre ISRs associées aux composants TIMER, TTY, IOC et DMA. Modifiez le fichier '''reset.s''' pour initialiser les 4 premières entrées du vecteur d'interruption dans cet ordre. '''Question''' : Le fichier '''sys/hwr_mapping.h''' contient la carte des registres adressables des périphériques supportés par le GIET. Ouvrez ce fichier pour déterminer l'offset du registre ICU_MASK_SET, et complétez le fichier '''reset.s''' pour configurer le composant ICU de façon à autoriser les quatre lignes d'interruption utilisées : irq_in[0], irq_in[1], irq_in[2], irq_in[3]. == 4.2 Activation du TIMER == On va commencer par exécuter un programme très simple, qui se contente d'activer la génération d'interruptions périodiques par le TIMER. Consultez le fichier '''app/stdio.c'' pour déterminer quels sont les deux appels système qui permettent de définir la période et d'autoriser le TIMER à générer les interruptions périodiques. '''Question''' : modifiez le fichier '''main.c''' pour que le TIMER génère des interruptions avec une période de 500000 cycles, compilez le logiciel embarqué, et lancez la simulation. N'oubliez pas de modifier préalablement la variable d'environnement PATH de la fenêtre dans laquelle vous lancez le simulateur, en exécutant la commande: {{{ $ source /users/outil/soc/env_soclib.sh }}} == 4.3 Utilisation du contrôleur TTY == On veut maintenant utiliser le contrôleur TTY en écrivant un petit interprêteur de commandes, qui exécute une boucle infinie dans laquelle il lit des commandes tapées au clavier (un seul caractère à la fois) et les exécute. On traitera au minimum les trois commandes suivantes: * '''a''' : activation des interruptions générées par le TIMER * '''d''' : desactivation des interruptions générées par le TIMER * '''q''' : sortie de l'interprêteur par l'appel système exit() '''Question''' : Complétez le fichier '''main.c''' pour coder cet interprêteur de commandes comme une boucle infinie contenant les appels système '''tty_getc_irq()''' et '''tty_puts()'''. Compilez en utilisant le Makefile du répertoire soft, et exécutez ce programme interactif sur le simulateur. == 4.4 Contrôleur IOC et contrôleur d'écran graphique == On veut utiliser le contrôleur IOC pour charger dans un tampon mémoire du programme utilisateur une séquence d'images stockées dans le fichier '''images.raw''', avant d'afficher ces images sur l'écran graphique contrôlé par le composant FBF. Le fichier attaché '''images.tgz''', que vous pouvez télécharger [attachment:images.tgz ici], est une version compressée du fichier '''images.raw'''. Ce fichier conient une séquence d'images de 128 lignes de 128 pixels, codées en 256 niveaux de gris (un octet par pixel). Vous devez décompresser le fichier '''images.tgz''' en utilisant la commande : {{{ $ tar -xvfz images.tgz }}} '''Question''' : Quel est l'encombrement d'une image en nombre de blocs? '''Question''' : Ecrivez un nouveau programme dans un fichier '''main_display.c'''. Vous utiliserez les appels système '''ioc_read()''' et '''ioc_completed()''' pour charger une image dans un tampon mémoire de 128*128 octets tab[128][128], déclaré dans la fonction main_display(). Utilisez l'appel système '''fb_write()''' pour afficher cette image sur l'écran graphique. N'oubliez pas de tester systématiquement la valeur du code de retour chaque fois que vous utilisez un appel système. Modifiez le Makefile pour utiliser main_display.c au lieu de main.c, compilez le logiciel embarqué, et exécutez-le sur le simulateur. '''Question''' : Dans quel segment sera rangéé ce tableau tab[128][128]? '''Question''' : Complétez le fichier '''main_display.c''' pour encapsuler cette séquence chargement / affichage dans une boucle de façon à afficher successivement les images du fichier '''images.raw'''. On pourra utiliser l'appel système '''tty_getc_irq()''' pour rendre interactif le passage à l'image suivante. = 5. Architecture multi-processeurs générique = On veut maintenant modéliser une architecture matérielle multi-processeur générique, où le nombre de processeurs peut varier entre 1 et 4. Ce paramètre NPROCS sera défini sur la ligne de commande. == 5.1 architecture matérielle == Vous devez modifier le fichier '''tp4_top.cpp''' pour créer un nouveau fichier '''tp4_top_multi.cpp''', en suivant les recommandations suivantes. Lisez toutes les recommandations avant de commencer à coder... * On ajoutera le paramètre NPROCS dans la liste des paramètres qu'on peut modifier sur la ligne de commande, en vérifiant que le paramètre NPROCS est plus grand que 0 et inférieur ou égal à 4. * Pour les composants répliqués PROC[i] ainsi que pour les signaux connectés à ces composants, vous définirez des tableaux de pointeurs indexés par l'index du processeurs (proc_id). * Vous utiliserez une boucle indexée par l'index du processeur pour les constructeurs des composants PROC[i]. * Chaque processeur a besoin de son propre concentrateur d'interruption. Le composant matériel !VciIcu est un composant mono-canal, qui ne peut servir qu'un seul processeur. Il faut donc utiliser le composant matériel multi-canaux !VciMultiIcu, qui peut servir jusqu'à 8 processeurs. Vous pouvez consulter la documentation [http://www.soclib.fr/trac/dev/wiki/Component/VciMultiIcu ici]. * De même, pour ce qui concerne le contrôleur DMA, on souhaite avoir un canal DMA indépendant pour chaque processeur. Il faut donc utiliser le composant matériel multi-canaux !VciMultiDma dont vous pouvez consulter la documentation [http://www.soclib.fr/trac/dev/wiki/Component/VciMultiDma ici]. Attention: Le composant !VciMultiDma utilise le champs VCI TRDID, qui doit avoir au moins 4 bits. Il faudra donc modifier la valeur du champs ''trdid_size'' dans les deux fichier '''tp4_top.cpp''' et '''tp4_top.desc'''. * Pour ce qui concerne les timers, on souhaite avoir un timer indépendant pour chaque processeur, mais puisque le composant !VciTimer peut contenir jusque 256 timers indépendants, on utilisera un seul composant !VciTimer. * Puisque chaque processeur exécutera une seule tâche, et que chaque tâche doit disposer de son propre terminal écran/clavier, le nombre de terminaux controlés par le composant !VciMultiTty est égal à NPROCS. * Pour ce qui concerne l'accès au disque, on aura un seul contrôleur de disque !VciBlockDevice partagé par toutes les applications. * Pour les périphériques multi-canaux qui génèrent des interruptions (TTY, TIMER, DMA,ICU), le nombre de de lignes d'interruption dépend du nombre de processeurs. Il faut donc utiliser des tableaux de signaux. * Pour les tableaux de signaux, vous utiliserez le constructeur générique de tableaux ''alloc_elems(name, size)''. Ce constructeur est défini dans le fichier ''alloc_elems.h'', qu'il ne faut pas oublier d'inclure dans la top-cell. * Pour l'index des initiateurs (SRCID), vous utiliserez les valeurs 0 à (NPROCS-1) pour les processeurs, et les valeurs NPROCS et (NPROCS+1) pour le composant IOC et pour le composant DMA respectivement. * Pour la net-list, vous utiliserez des boucles indexées par l'index du processeur pour connecter les composants répliqués et signaux répliqués. * Puisque l'architecture contient maintenant plusieurs périphériques multi-canaux (ICU, TTY, TIMER, DMA) les longueurs des segments alloués à ces périphériques doivent être ajustées en conséquence. * Pour ce qui concerne le cablage des interruptions, vous utiliserez un composant ICU possédant 32 lignes d'interruption entrantes. L'interruption IRQ_IOC sera connectée à l'entrée p_irq_in[0] du composant ICU. Les 4 interruptions IRQ_DMA[0:3] seront connectée aux entrées p_irq_in[4:7]. Les 4 interruptions IRQ_TIM[0:3] seront connectées aux entrées p_irq_in[8:11]. Les 4 interruptions IRQ_TTY[0:3] seront connectées aux entrées p_irq_in[12:15]. Les ports d'entrée p_irq_in[i] non utilisés devront impérativement être connectés à un signal ayant la valeur ''false''... '''Question''' : Pour quelle raison les valeurs stockées dans les 4 registres de masque ICU_MASK[k] associés aux 4 canaux du composant !VciMultiIcu doivent-elles être non-recouvrantes? '''Question''' : Puisqu'il n'existe qu'un seul contrôleur de disque, qui peut - en principe - être utilisé par toutes les applications, comment le GIET doit-il gèrer les accès concurrents? '''Question''' : Modifiez le fichier '''tp4_top.desc''' pour créer un fichier '''tp4_top_multi.desc'''. * Puisque deux nouveaux composants (!VciMultiDma, et !VciMultiIcu) ont été introduits dans l'architecture, modifiez le fichier le fichier '''tp4_top.desc''' pour créez un fichier '''tp4_top_multi.desc'''. Modifiez le fichier '''Makefile''' en conséquence pour générer un simulateur générique '''simulator_multi.x'''. == 5.2 Code de boot multi-processeurs == Un vrai système d'exploitation tel que LINUX permet de lancer dynamiquement de nouvelles tâches (i.e. de nouveaux programmes utilisateurs) alors que la machine est déjà démarrée (mécanisme ''pthread_create()'' pour les threads POSIX, ou mécanisme fork/exec pour les processus UNIX). Le GIET n'est pas un vrai système d'exploitation, car il ne supporte pas la création ou la destruction dynamique de tâches : Les tâches doivent être créées une fois pour toute au démarrage de la machine, c'est à dire dans le code de boot. On va utiliser ici un mécanisme simple où chaque processeur n'exécute qu'une seule tâche, qui lui est assignée une fois pour toutes dans le code de boot. Le mécanisme général est le suivant : * Tous les processeurs exécutent le même code de boot, mais l'exécution de ce code dépend de l'index du processeur. L'index du processeur (''proc_id'') étant stocké dans le registre système ($15, 1) du processeur MIPS32, sa valeur peut être testée par le logiciel. Remarquez que la valeur de ce registre est initialisée par le constructeur C++, ce qui modélise une valeur ''cablée'' lors de la fabrication de la puce. * Les tâches s'exécutant en parallèle, chaque tâche (et donc chaque processeur) doit disposer de sa propre pile d'exécution. On définit un seul segment pour les différentes piles d'exécution, mais les pointeurs de pile des différents processeurs doivent être initialisés à des valeurs différentes en fonction du ''proc_id''. * Chaque processeur exécutant un programme utilisateur différent, le points d'entrée du programme (registre EPC) doit être initialisé à une valeur dépendant du ''proc_id''. Pour cela, on demande à GCC de construire une table de sauts au début du segment ''seg_data'', contenant les adressesdes 4 points d'entrée dans les 4 tâches allouées aux 4 processeurs. == 5.3 Exécution parallèle sur architecture bi-processeurs == On commence par générer le code binaire pour une architecture comportant seulement 2 processeurs, de façon à exécuter en parallèle les deux programmes définis par les deux fonctions ''main()'' et ''main_display()'' (utilisées au début de ce TP) sur les deux processeurs P0 et P1. Créez un nouveau répertoire '''soft_multi''', et recopiez dans ce répertoire les fichiers contenus dans le répertoire '''soft''', car plusieurs fichiers doivent être modifiés. '''Question''' : Modifiez le fichier '''reset.s''' pour initialiser le pointeur de pile ($29 dans le cas du MIPS32) à une valeur dépendant du ''proc_id''. Chaque tâche disposera d'une pile de 64 Koctets. Quelle doit être la longueur du segment de pile défini dans la table des segments de la top-cell pour 2 processeurs? Pour 4 processeurs? '''Question''' : Modifiez le fichier '''reset.s''' pour que chaque processor PROC[i] initialize le registre de masque du composant ICU[i] qui lui est associé. A la fois la valeur du masque, et les adresses des registres dépendent du canaL: On fera en sorte que le processeur [i] reçoive les interruptions IRQ_TTY[i], IRQ_TIM[i], IRQ_DMA[i]. Le processeur [0] recevra en plus l'interruption IRQ_IOC. '''Question''' : Modifiez le fichier '''reset.s''' pour que chaque processeur initialise son registre EPC à une valeur dépendant du ''proc_id''. '''Question''' : Modifiez le fichier '''reset.s''' pour que tous les processeurs initialisent le vecteur d'interruption pour toutes les lignes d'interruptions existant dans l'architecture. Pourquoi a-ton choisi que cette initialisation soit réalisée par tous les processeurs, ce qui peut sembler redondant ? '''Question''' : Modifiez le fichier '''Makefile''' du répertoire '''soft''' pour que le code des deux fonctions ''main()'' et main_display() soient intégré dans le même fichier '''app.bin'''. Vérifiez dans le fichier '''app.bin.txt''' que les adresses des deux fonctions ''main()'' et ''main_display()'' sont bien rangées au début du segment seg_data. '''Question''' : Modifiez dans le fichier '''config.h''' la valeur de la variable qui informe le système d'exploitation du nombre de processeurs présents dans l'architecture matérielle. '''Question''' : Compilez le logiciel embarqué, puis lancez l'exécution sur le simulateur '''simulator_multi.x''', avec -NPROCS = 2 processeurs. == 5.4 Exécution parallèle sur architecture quadri-processeurs == On souhaite maintenant exécuter 4 programmes en parallèle sur 4 processeurs. '''Question''' : Ecrivez deux autres petits programmes qui s'exécuteront sur les deux autres processeurs. Si vous manquez d'imagination, vous pouvez vous contenter de répliquer le code du programme ''main.c''. Si ces programmes utilisent les interruptions générées par le timer, n'oubliez pas que chaque processeur ne doit configurer que le timer qui lui appartient. '''Question''' : Modifiez les fichiers '''reset.s''', '''config.h''', et '''Makefile''' du répertoire pour les adapter au cas de 4 processeurs, compilez le logiciel embarqué puis lancez l'exécution sur le simulateur '''simulator_multi.x''', avec 4 processeurs. '''Question''' : Le GIET supporte des architectures contenant jusque 8 processeurs. Nous avons cependant limité l'architecture générique à 4 processeurs. Quel est le facteur limitant qui justifie cette restriction? = 6 Compte-rendu = Vous devez rédiger un compte-rendu résumant les étapes et répondant aux, vous pourrez faire une démonstration de votre simulateur la semaine prochaine (ou un peu plus tard, donc organisez bien vos répertoires de TP...).