{{{ #!html

TP3 : Processeurs programmables

}}} [[PageOutline]] = 1 Objectif = L'objectif de ce troisième TP est d'introduire des processeurs programmables dans les architectures modélisées. Pour des raisons de flexibilité et de re-utilisation des plate-formes matérielles, les concepteurs de systèmes intégrés essaient de réaliser le plus grand nombre possible de fonctions en logiciel, en utilisant soit des processeurs généralistes de type GPP (General Purpose Processor), soit des processeurs spécialisés de type DSP (Digital Signal Processor). Puisque l'utilisation d'un processeur programmable suppose le déploiement du code sur l'architecture modélisée, on a besoin d'un système d'exploitation minimal pour permettre l'accès aux périphériques partagés, gérer les interruptions en provenance des périphériques, et traiter les exceptions liées aux inévitables erreurs de programmation. On introduit donc dans ce TP le '''GIET''' (Gestionnaire d'Interruptions, Exceptions et Trappes), qui sera utilisé comme OS dans ce TP et dans les suivants. On introduit également dans ce TP l'outil de compilation '''soclib-cc''' qui facilite la génération du prototype virtuel de l'architecture matérielle. = 2 Architecture matérielle = L'architecture matérielle modélisée dans ce TP comporte un seul processeur initiateur à interface VCI, et 4 cibles VCI respectant également le protocole VCI. [[Image(soclib_tp3_archi.png)]] * '''xcache''' est un processeur Mips32 avec ses caches L1. Le composant !VciXcacheWrapper, est un contrôleur de cache à interface VCI qui encapsule un coeur de processeur MIPS32. * '''rom''' est une mémoire non inscriptible à interface VCI contenant le code de boot. On utilise le composant !VciSimpleRam. * '''ram''' est une mémoire inscriptible à interface VCI contenant le code et les données. On utilise également un composant !VciSimpleRam. * '''tty''' est un périphérique adressable de type écran/clavier à interface VCI. On utilise le composant !VciMultiTty. * '''gcd''' est le coprocesseur cible réalisant le calcul du PGCD déjà utilisé dans le TP2. * '''vgsb''' est le bus système déjà utilisé dans le TP2. Les modèles de simulation des composants matériels instanciés dans cette architecture sont disponibles dans la bibliothèque SoCLib. Ils vous sont fournis, et vous n'aurez pas à les reécrire vous-même. Le composant '''!VciXcacheWrapper''' peut encapsuler différents processeurs RISC 32 bits. Le coeur du processeur est modélisé par un ISS (Instruction Set Simulator). Le type du processeur instancié (MIP32, ARM, SPARCV8, PPC405, NIOS, !MicroBlaze, etc.) est défini par un paramètre template du composant `VciXcacheWrapper`. Consultez la documentation [http://www.soclib.fr/trac/dev/wiki/Component/VciXcacheWrapper ici]. Le composant '''!VciSimpleRam''' est utilisé pour modéliser des mémoires inscriptibles embarquées (SRAM). On utilise le même composant pour modéliser des mémoires non inscriptibles (ROM). Ce composant peut contenir un ou plusieurs segments (correspondant à des tranches disjointes de l'espace adressable). Le composant analyse les bits de poids fort de l'adresse VCI pour déterminer le segment désigné. Les bancs de mémoire physique correspondant aux différents segments sont modélisés par des tableaux C++ dont la longueur est définie par les valeurs stockées dans la `MappingTable`. Consultez la documentation [http://www.soclib.fr/trac/dev/wiki/Component/VciSimpleRam ici]. Enfin le composant '''!VciMultiTty''' est un contrôleur de terminaux alphanumériques. Ce composant peut contrôler de 1 à 256 terminaux (un terminal est une paire écran / clavier). Pratiquement, chaque terminal est modélisé par l'ouverture d'une fenêtre XTERM indépendante sur l'écran de la station de travail qui exécute la simulation. Chaque terminal possède 4 registres adressables pour la lecture ou l'écriture, et fonctionne en mode ''caractère'': on ne peut lire ou écrire qu'un seul caractère par transaction VCI. Consultez la documentation [http://www.soclib.fr/trac/dev/wiki/Component/VciMultiTty ici]. = 3 Logiciel embarqué = Les applications logicielles qui s'exécutent sur le (ou les) processeur(s) programmable(s) ont besoin d'accéder à des périphériques ou à des coprocesseurs matériels spécialisés. Ces applications sont souvent multi-tâches et plusieurs tâches s'exécutent en parallèle sur un même processeur en multiplexage temporel. Pour ces raisons, on a généralement besoin d'un système d'exploitation embarqué sur le MPSoC. Les composants de la bibliothèque SoCLib permettent d'exécuter des systèmes d'exploitation généralistes (tels que LINUX ou NetBSD). Pour simplifier les choses, dans ce TP et les suivants, on se contentera d'un système d'exploitation minimal, appelé GIET (Gestionnaire d'Interruptions, Exceptions et Trappes). == 3.1 Gestionnaire d'Interruptions, exceptions et Trappes == Le GIET fournit principalement trois services: * un ''gestionnaire d'appels systèmes'' fournissant en particulier des fonctions d'accès aux périphériques. * un ''gestionnaire d'interruptions'' supportant un mécanisme d'interruptions vectorisées. * un ''gestionnaire d'exceptions'' permettant de traiter les erreurs des programmes utilisateurs. Les deux principales limitations du GIET, qui le différencie d'un ''vrai'' système d'exploitation, sont l'absence de support pour la mémoire virtuelle, et l'absence de support pour la création dynamique de tâches. Le code est donc séparé en deux parties : les fichiers contenant le code système qui s'exécute en mode ''superviseur'' sont contenus dans le répertoire '''sys''', tandis que les fichiers contenant le code applicatif qui s'exécute en mode ''utilisateur'' sont contenus dans le répertoire '''app'''. * Le fichier '''sys_handler.c''' est écrit en C. Il est dans le répertoire '''sys''', et contient le code du gestionnaire d'appels système, chargé d'appeler la fonction système correspondant au service demandé. * Le fichier '''exc_handler.c''' est écrit en C. Il est dans le répertoire '''sys''', et contient le code du gestionnaire d'exceptions, chargé de la signalisation et du traitement des erreurs détectées dans les programmes utilisateurs. * Le fichier '''irq_handler.c''' est écrit en C. Il est dans le répertoire '''sys''', et contient le code du gestionnaire d'interruptions, ainsi que les routines de traitement des interruptions (ISR). * Le fichier '''ctx_handler.c''' est écrit en C. Il est dans le répertoire '''sys''', et contient le code du gestionnaire de changements de contexte, utilisé lorsqu’un processeur exécute plusieurs tâches en multiplexage temporel. * Le fichier '''drivers.c''' contient les fonctions d'accès aux périphériques. Il rassemble donc les ''pilotes'' de tous les périphériques de la machine. * Le fichier '''common.c''' est écrit en C. Il est dans le répertoire '''sys''', et contient les fonctions générales du système d'exploitation. * Le fichier '''giet.s''' est écrit en assembleur MIPS32. Il est dans le répertoire '''sys''', et contient la fonction qui analyse la cause de l'appel au GIET, et la fonction de sauvegarde/restauration de contexte. * Le fichier '''stdio.c''' est écrit en C. Il est dans le répertoire '''app''' car il contient l'ensemble des appels système qui peuvent être utilisés par un programme utilisateur écrit en C. Le nom de ce fichier provient du fait que la plupart des appels système sont utilisés pour accéder aux périphériques. Le code source du GIET est accessible et stocké dans le répertoire suivant: {{{ /users/enseig/alain/giet_2011/ }}} Vous pouvez vous faire une copie privée de ces fichiers pour pouvoir les consulter plus facilement, mais il est fortement déconseillé d'utiliser cette copie locale pour générer le code binaire, le code du GIET peut évoluer, et il faut toujours utiliser la version de référence. == 3.2 Génération du code == Puisque le système et les applications sont écrits en langage C, il faut utiliser un cross-compilateur C spécifique au processeur MIPS32 pour générer d'une part le code applicatif, et d'autre part le code du système d'exploitation embarqué. Le résultat de la compilation consiste en deux fichiers binaires au format ELF, '''sys.bin''' et '''app.bin''', qui devront être chargés dans les mémoires embarquées du MPSoC. == 3.3 Chargement du code == Il existe deux méthodes permettant de charger le code binaire dans les mémoires embarquées du MPSoC: 1. Le code peut être stocké dans des mémoires mortes (ROM). Le contenu de ces mémoires est défini lors de la fabrication de la puce, et n'est plus modifiable. Cette approche est évidemment très peu flexible, et elle n'est généralement utilisée que pour le code de boot. 1. Le code peut être stocké dans des mémoires inscriptibles (SRAM), qui sont chargées lors de la mise sous tension du système à partir d'un périphérique de stockage externe (cela peut être une EPROM externe, une mémoire flash, ou un autre dispositif de stockage. On peut même imaginer qu'on utilise une liaison sans fil pour télécharger du code applicatif disponible sur un serveur distant. Cette approche en deux temps est utilisée pour le code applicatif, mais également pour le système d'exploitation embarqué. C'est pourquoi on appelle souvent ''bootloader'' le code de démarrage qui effectue ce chargement. La phase de chargement du système d'exploitation et du code applicatif est en pratique exécutée à chaque mise sous tension, ou chaque fois qu'on active le signal NRESET. Elle peut être très longue (plusieurs millions de cycles). Une fois que le ''bootloader'' a été validé, cette phase de chargement du code n'apporte plus beaucoup d'information, quand on souhaite mettre au point ou mesurer les performances d'une application logicielle sur une architecture matérielle modélisée avec SoCLib. La plate-forme SoCLib fournit donc un service permettant d'initialiser directement les mémoires embarquées à partir du code contenu dans le fichier ELF. Cette initialisation n'est plus réalisée lors de l'exécution de la simulation (dans la phase de boot), elle est réalisée avant le démarrage de la simulation. En pratique, ce chargement est réalisé par le constructeur du composant !VciSimpleRam, grâce à un argument `loader` qui lui permet d'accéder au contenu du fichier ELF contenant le code binaire. Ce constructeur possède un autre argument lui permettant d'accéder à la `MappingTable`. Il peut donc déterminer quels segments ont été affectés à la RAM (ou à la ROM) et lesquels doivent être initialisés. On économise ainsi plusieurs millions de cycles de simulation, et le code de boot peut être beaucoup plus court (le code de boot utilisé dans ce TP contient moins de 20 lignes d'assembleur). = 4 Travail à réaliser = L'archive [attachment:soclib_tp3.tgz soclib_tp3.tgz] contient différents fichiers dont vous aurez besoin pour ce TP. Créez un répertoire de travail spécifique TP3, recopiez l'archive dans ce répertoire TP3, et décompressez-la: {{{ $ tar xzvf soclib_tp3.tgz }}} Outre les fichiers qui permettent de générer le simulateur de l'architecture matérielle, cette archive contient également un sous-répertoire ''soft'' qui est utilisé pour la génération du logiciel embarqué. == 4.1 segmentation de l'espace adressable == Pour cette architecture, il faut définir 9 segments dans l'espace adressable, dont 3 correspondent à l'application et 6 appartiennent au système d'exploitation (adresses plus grandes que 0x80000000). === segments applicatifs === * '''seg_code''' est le segment contenant le code de l'application logicielle embarquée, qui s'exécute en mode ''user''. Il est assigné à la RAM. On choisira pour adresse de base la valeur 0x00400000, et une capacité de stockage de 64 Koctets. * '''seg_data''' est le segment contenant les données globales de l'application logicielle embarquée. Il est assigné à la RAM. On choisira pour adresse de base la valeur 0x01000000, et une capacité de stockage de 64 Koctets. * '''seg_stack''' est le segment contenant la pile d'exécution de l'application logicielle embarquée. Il est assigné à la RAM. On choisira pour adresse de base la valeur 0x02000000, et une capacité de stockage de 64 Koctets. === segments système === * '''seg_reset''' est le segment contenant le code de ''boot'' exécuté à la mise sous tension. Il est évidemment assigné à la ROM. L'adresse de base 0xBFC00000 est imposée par la spécification du processeur Mips32. On choisira une capacité de stockage de 4 Koctets. * '''seg_kcode''' est le segment contenant le code du système qui s'exécute en mode ''kernel''. Il s'agit principalement du code du Gestionnaire d'Interruptions, Exceptions, et Trappes (GIET), du code des fonctions système, ainsi que du code des routines d'interruption (ISR, pour interrupt Service Routine). Ce segment est assigné à la RAM. L'adresse de base 0x80000000. On choisira une capacité de stockage de 64 Koctets. * '''seg_kdata''' est le segment contenant les données cachables du système d'exploitation. Il est assigné à la RAM. L'adresse de base est égale à 0x81000000. Sa capacité est de 64Koctets. * '''seg_kunc''' est le segment contenant les données non cachables du système d'exploitation. Il est assigné à la RAM. L'adresse de base est égale à 0x82000000. Sa capacité est de 64Koctets. * '''seg_tty''' est le segment associé au contrôleur de terminaux TTY. On prendra pour adresse de base la valeur 0x90000000, et pour longueur 64 octets, ce qui permet d'adresser jusqu'à 4 terminaux indépendants. * '''seg_gcd''' est le segment associé au coprocesseur GCD. On prendra pour adresse de base la valeur 0x95000000. La longueur de 16 octets correspond aux quatre registres adressables de ce composant. Les adresses de base des segments sont utilisées à la fois par le matériel et par le logiciel embarqué. Elles doivent donc être définies à deux endroits : 1. Pour le matériel, les adresses de base et les longueurs des segments doivent être définies dans le fichier '''tp3_top.cpp''' pour être stockées dans la !MappingTable. Elles sont utilisées dans la phase de configuration du matériel par les constructeurs des composants. 1. Pour le logiciel, les adresses de base des segments doivent être définies dans le fichier '''soft/seg.ld''' qui contient les directives pour l'éditeur de liens lors de la compilation du logiciel embarqué. == 4.2 Compilation du logiciel embarqué == Le répertoire '''soft''' de l'archive qui vous est fournie contient les fichiers spécifiques à l'application embarquée : * Le GIET peut supporter des architectures comportant plusieurs processeurs, mais les structures de données utilisées par le système doivent être dimensionnées en fonction du nombre de processeurs et du nombre de tâches parallèles. Ces paramètres sont définis dans le fichier '''soft/config.h'''. * le fichier '''reset.s''' est écrit en assembleur et contient le code de boot qui est exécuté à la mise sous tension, ou lors de l'activation du signal NRESET. Ce code s'exécute en mode ''kernel'', mais il est spécifique à chaque plate-forme matérielle, car il est chargé d'initialiser les périphériques présents dans l'architecture. Il variera donc d'un TP à l'autre. * le fichier '''main.c''' est écrit en C et contient le code de l'application logicielle. Il utilise les appels système définis dans le fichier ''stdio.c''. * les fichiers '''sys.ld''', app.ld''', et '''seg.ld''' contiennent les directives pour l'éditeur de liens, et en particulier les adresses de base des différents segments. * le fichier '''Makefile''' permet de lancer la génération du logiciel embarqué. La chaîne de compilation croisée GCC fournit quatre outils: * mipsel-unknown-elf-gcc : '''compilateur''', pour transformer un fichier source C (extension .c) en fichier assembleur pour MIPS32 (extension .s) * mipsel-unknown-elf-as : '''assembleur''', pour transformer un fichier assembleur (extension .s) en fichier binaire objet (extension .o) * mipsel-unknown-elf-ld : '''éditeur de lien''', pour résoudre les références croisées entre les fichiers objets et produire le fichier binaire exécutable * mipsel-unknown-elf-objdump : '''désassembleur''', pour générer un fichier texte lisible à partir d'un fichier binaire. Assurez-vous que votre PATH contient bien le chemin permettant d'accéder à ces 4 outils en exécutant la commande : {{{ $ source /users/outil/soc/env_soclib.sh }}} '''Question''' : Éditez le fichier '''reset.s''', de façon à définir la taille du segment de pile. On choisira une taille de 64 Koctets. C'est le code de boot qui réalise le branchement vers la première instruction de l'application grâce à l'instruction ''eret''. On rappelle que l'instruction ''eret'' modifie le registre protégé SR (registre 12 du coprocesseur ''système'') pour que le processeur retourne dans le mode ''user'' et effectue un branchement à l'adresse contenue dans le registre protégé EPC (registre 14 du coprocesseur ''système''). Le code de boot fait partie du code système et il est donc défini dans le fichier '''sys.bin'''. Le code de l'application est défini dans le fichier '''app.bin'''. Ces deux fichiers sont générés de façon indépendante, et il faut donc définir un mécanisme permettant au code de boot de déterminer l'adresse de la première instruction de l'application (point d'entrée dans le code utilisateur). '''Question''' : quelle est la convention utilisée par le GIET pour permettre au code de boot de déterminer l'adresse de la première instruction de l'application? Complétez le fichier '''reset.s''' pour initialiser le registre EPC. La première application proposée affiche simplement un message sur le terminal TTY. '''Question''' : Ouvrez le fichier '''main.c'''. Que fait ce programme ? (La fonction ''tty_getc()'' est bloquante, et ne rend pas la main tant qu'un caractère n'a pas été saisi au clavier). Le fichier '''stdio.c''' contient l'ensemble des appels systèmes fournis par le GIET aux applications. '''Question''' : Éditez le fichier '''stdio.c''' contenu dans le répertoire de référence du GIET. Quels sont les appels système qui permettent d'accéder à un terminal TTY ? Que trouve-t-on dans le code de chacun de ces appels système? Le fichier '''seg.ld''' est inclus dans les deux fichiers '''sys.ld''' et '''app.ld''', et définit les adresses de base de tous les segments pour le logiciel. '''Question''' : Complétez le fichier '''seg_ld''' pour définir les adresses de base des différents segments. Lancez l'exécution du Makefile dans le répertoire ''soft''. Quatre fichiers doivent être créés: '''app.bin''' et '''sys.bin''' contiennent le code binaire au format ELF, et les fichiers '''app.bin.txt'' et '''sys.bin.txt''' contiennent une version désassemblée (donc lisible) de ce code binaire. '''Question''' : Éditez le fichier ''app.bin.txt''. Combien d'instructions assembleur ont été générées pour le programme main? Vérifiez que l'adresse du point d'entrée dans le programme utilisateur est bien rangée au début du segment ''seg_data''. == 4.3 Description de l'architecture matérielle == Pour ce qui concerne le matériel, il faut compléter le fichier ''tp3_top.cpp'' qui vous est fourni: '''Question''' : Définissez les adresses de base et les tailles des 9 segments de l'espace adressable. '''Question''' : Complétez le constructeur de la ''!MappingTable''. Consultez la documentation de la !MappingTable [http://www.soclib.fr/trac/dev/wiki/Component/MappingTable ici] pour bien comprendre la signification des 4 arguments du constructeur. Quel est le nombre de bits de poids fort de l'adresse qui doivent être décodés par le contrôleur du bus pour déterminer la cible VCI désignée ? Quels sont les bits d'adresse qui doivent être décodés par le contrôleur du cache, pour déterminer qu'une adresse appartient à un segment non-cachable ? '''Question''' : Parmi les 9 segments utilisés dans cette l'architecture, lesquels doivent être non-cachables ? '''Question''' : Définissez l'argument du composant ''loader'' qui réalise le chargement du code binaire dans les mémoires ROM et RAM. Cet argument est une liste de chaînes de caractères définissant les cheminoms des différents fichiers ELF contenant du code binaire à charger en mémoire. '''Question''' : Définissez les arguments des constructeurs des composants matériels instanciés, ainsi que les valeurs de leurs paramètres template. Vous devez consulter la documentation des composants !VciXcacheWrapper et !VciSimpleRam pour comprendre la signification des arguments. On choisira des caches à correspondance directe (c'est-à-dire un seul niveau d'associativité), ayant une capacité totale de 4 Koctets et des lignes de caches d'une longueur de 16 octets. '''Question''' : Complétez la net-list en connectant les signaux du bus. == 4.4 Génération du simulateur == Les composants matériels modélisés dans les TP1 et TP2 étaient très simples. Vous avez pu décrire vous-même les modèles de simulation de ces composants, et vous avez pu écrire ''à la main'' le Makefile permettant de générer le simulateur. Mais les composants instanciés ici (!VciXcacheWrapper, !VciSimpleRam, et !VciMultiTty) sont des objets nettement plus complexes, qui utilisent eux-mêmes un grand nombre d'autres composants. Tous les fichiers sont accessibles sur le serveur SVN de SoCLib, qui fournit un service de gestion de versions et supporte le développement coopératif de la plate-forme. Mais l'exploitation de cette infrastructure pose (au moins) deux problèmes : 1. Il faut identifier et localiser tous les fichiers nécessaires pour générer le simulateur d'une architecture particulière. L' architecture très simple proposée ici nécessite la compilation d'une centaine de fichiers. D'une façon générale, l'identification des fichiers nécessaires à la compilation est un travail non négligeable, et la construction du Makefile peut devenir assez pénible. 2. Par ailleurs, la plupart des modèles ont des paramètres templates (puisque la plupart des composants ont des interfaces VCI, et que les largeurs des champs VCI sont définis par un paramètre template). Pour chaque composant possédant un paramètre template, il faut modifier le fichier ''.cpp'' pour préciser la valeur des paramètres template avant de lancer la compilation de ce composant (on dit qu'on instancie les ''template''). Vous avez fait ce travail dans le TP2, et c'est très fastidieux dès que les architectures deviennent complexes. La chaîne de compilation '''soclib-cc''' a pour but de résoudre ces deux problèmes dans le cas général, en automatisant la recherche des dépendances, l'instanciation des templates, et l'appel du compilateur. Pour permettre cette automatisation, tout composant logiciel de SoCLib est accompagné d'un fichier de ''metadata'' (fichier possédant le suffixe {{{.sd}}}) qui contient les informations suivantes: * le nom de la classe C++ * les paramètres templates associés, avec leurs types et les valeurs par défaut (si applicable) * les chemins d'accès aux fichiers d'en-tête (.h) et d'implémentation (.cpp) * la liste des ports d'interface du composant * la liste des dépendances vers d'autres composants * les paramètres du constructeur, avec leurs types Ce fichier est écrit dans un langage que Python peut parser nativement, et on trouvera ci-dessous, à titre d'exemple, le fichier ''vci_simple_ram.sd'': {{{ Module('caba:vci_simple_ram', classname = 'soclib::caba::VciSimpleRam', tmpl_parameters = [parameter.Module('vci_param', default = 'caba:vci_param')], header_files = ['../source/include/vci_simple_ram.h',], implementation_files = ['../source/src/vci_simple_ram.cpp'], ports = [ Port('caba:vci_target', 'p_vci'), Port('caba:bit_in', 'p_resetn', auto = 'resetn') Port('caba:clock_in', 'p_clk', auto = 'clock') ], uses = [ Uses('caba:base_module'), Uses('common:linked_access_buffer', addr_t = parameter.StringExt('sc_dt::sc_uint<%d>', parameter.Reference('addr_size')), id_t = parameter.StringExt('sc_dt::sc_uint<%d>', parameter.Reference('srcid_size'))), Uses('common:loader'), Uses('common:mapping_table', ], instance_parameters = [ parameter.IntTab('ident'), parameter.Module('mt', 'common:mapping_table', auto='env:mapping_table'), parameter.Module('loader', 'common:loader', auto='env:loader'), parameter.Int('latency') ], extensions = [ 'dsx:addressable=ident', 'dsx:get_ident=ident:p_vci', 'dsx:mapping_type=memory'], ) }}} Il faut par ailleurs définir les caractéristiques de la top-cell dans un fichier de directives pour soclib-cc. Ce fichier est habituellement suffixé par '''.desc''', mais le nom n'est pas imposé. Ce fichier est également en langage parsable par Python, et contient : le nom de fichier de la top-cell SystemC, la liste des modèles des composants instanciés et les valeurs des paramètres template VCI. Vous trouverez ci-dessous, le fichier '''tp3_top.desc''' décrivant l'architecture du TP3: {{{ todo = Platform('caba', 'tp3_top.cpp', uses = [ Uses('caba:vci_xcache_wrapper', iss_t = 'common:mips32el'), Uses('caba:vci_simple_ram'), Uses('caba:vci_multi_tty'), Uses('caba:vci_vgsb'), Uses('caba:vci_gcd_coprocessor'), Uses('common:mapping_table'), Uses('common:elf_file_loader')], cell_size = 4, plen_size = 8, addr_size = 32, rerror_size = 1, clen_size = 1, rflag_size = 1, srcid_size = 12, pktid_size = 1, trdid_size = 1, wrplen_size = 1 ) }}} '''Question''' : Complétez le fichier ''tp3_top.desc'' qui vous est fourni. Il faut vérifier que le chemin d'accès à à soclib-cc est bien dans votre PATH en exécutant: {{{ $ source /users/outil/soc/env_soclib.sh }}} Il faut enfin lancer la génération du simulateur avec la commande: {{{ $ soclib-cc -p tp3_top.desc -o simulator.x }}} L'exécutable ''simulator.x'' devrait être créé dans le répertoire de travail ''TP3''. == 4.5 Simulation == Lancez la simulation avec la commande : {{{ $ ./simulator.x }}} En cas de problème lors de l'éxécution, vous pouvez relancer le simulateur en activant le mode DEBUG. Il faut ajouter les deux arguments suivants sur la ligne de commande {{{ $ ./simulator.x -DEBUG 0 -NCYCLES 10000 }}} Vous obtiendrez une trace d'exécution entre les cycles 0 et 10000, que vous pouvez rediriger vers un fichier. Pour attirer votre attention sur des erreurs fréquentes, faites les essais suivants : 1. Modifiez l'adresse de base du segment ''seg_gcd'' dans le fichier ''tp3_top.cpp'' pour lui donner la valeur 0xB0000000 au lieu de 0x9000000. Relancez la compilation et la simulation. Expliquez les résultats obtenus. 1. Déclarez le segment correspondant au périphérique GCD (seg_gcd) comme cachable. Relancez la compilation et la simulation. Expliquez les résultats obtenus. == 4.6 Modification du logiciel embarqué == Puisque le logiciel embarqué est chargé dynamiquement dans la RAM et dans la ROM lors du lancement du simulateur, il est possible de modifier le logiciel embarqué (fichier '''app.bin'''), sans modifier l'architecture matérielle et donc sans régénérer le simulateur. On va donc maintenant écrire une application logicielle un peu plus complexe, qui utilise le coprocesseur GCD, simplement en modifiant le fichier '''main.c''' dans le répertoire ''soft'', et en relançant la compilation et la génération du fichier '''bin.soft'''. '''Question:''' Modifiez le fichier '''main.c''', pour que les programmes C exécute une boucle infinie dans laquelle on effectue successivement les opérations suivantes : 1. affichage du numéro de cycle et du numéro d'itération. 1. génération aléatoire de deux variables ''opa'' et ''opb'' de type `int`. 1. écriture de ''opa'' dans un registre du coprocesseur GCD. 1. écriture de ''opb'' dans un autre registre du coprocesseur GCD. 1. écriture dans un troisième registre du coprocesseur GCD, pour démarrer le calcul. 1. lecture du registre d'état du coprocesseur GCD pour tester la fin du calcul. 1. lecture du registre du coprocesseur GCD contenant le résultat. 1. affichage du numéro d'itération, du numéro de cycle, des valeurs des opérandes et du résultat sur le TTY. Pour afficher sur le terminal, on utilisera l'appel système ''tty_printf()''. Pour obtenir le numéro de cycle, on utilisera l'appel système ''proctime()''. Pour la génération aléatoire, on utilisera la fonction ''rand()''. Pour les accès au coprocesseur GCD on utilisera les appels système spécifiques au coprocesseur GCD définis dans le fichier '''stdio.c'''. Pour introduire un peu d'interactivité dans cette application, vous pouvez introduire à la fin de la boucle un appel système ''tty_getc()'' qui lit un caractère au clavier, et bloque l'exécution du programme tant que le caractère n'est pas saisi. = 5 Compte-rendu = Vous devez rédiger un compte-rendu pour ce TP, et une démonstration de votre simulateur au début du TP de la semaine suivante ou plus tard (vous devez organiser vos répertoires de TP proprement...).