{{{ #!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, puisque, 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, sur des processeurs généralistes, ou sur des processeurs de traitement du signal. On utilisera des processeurs RISC 32 bits, car ce type de processeur possède un très bon rapport (puissance de calcul) / (consommation énergétique). On introduira également dans l'architecture les mémoires embarquées contenant le code binaire et les données de l'application logicielle. = 2 Architecture matérielle cible = L'architecture matérielle modélisée dans ce TP comporte un seul initiateur VCI et 4 cibles VCI : [[Image(soclib_tp3_archi.png)]] * '''mips32''' est un processeur MIPS32 avec ses caches L1. On utilise le composant ''!VciXcacheWrapper''. * '''rom''' est une mémoire non inscriptible contenant le code de boot. On utilise le composant ''!VciSimpleRam''. * '''ram''' est une mémoire inscriptible contenant le code et les données. On utilise également un composant ''!VciSimpleRam''. * '''tty''' est un périphérique adressable de type écran/clavier. On utilise le composant ''!VciMultiTty''. * '''lcd''' est le coprocesseur cible réalisant le calcul du PGCD. On utilise évidemment le composant ''!VciLcdCoprocessor''. * '''vgsb''' est le bus système déjà utilsé dans le TP2. On utilise le composant ''!VciVgsb''. 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. Vous pouvez obtenir une description fonctionnelle détaillée pour chacun de ces composants sur le site WEB de !oCLib : [https://www.soclib.fr/trac/dev/wiki/Component] Le composant ''!VciXcacheWrapper'' est un contrôleur de cache générique à interface VCI, qui peut être utilisé pour interfacer différents coeurs de processeur avec le reste du système. Le coeur du processeur est modélisé par un ISS (Instruction Set Simulateur). Le type du proceseur instancié (MIP32, ARM, SPARCV8, PPC405, NIOS, !MicroBlaze, etc.) est défini par un paramètre template du composant ''!VciXcacheWrapper''. Le composant ''!VciSimpleRam'' est utilisé pour modéliser des mémoires inscriptibles embarquées (SRAM), ou pour modéliser des mémoires non inscriptibles (ROM) dont le contenu est ''cablé''. Ce composants peut contenir un ou plusieurs segments (correspondant à des tranches disjointes de l'espace addressable). Cela signifie que ce composant décode les bits de poids fort de l'adresse VCI pour adresser le segment désigné. Les dimensions des tableaux qui implémentent les bancs mémoire physique sont définis par les longueurs des segments définis dans la MappingTable. = 3 Génération et chargement du logiciel embarqué = Il existe plusieurs façons de définir et de générer le code binaire qui sera exécuté par le (ou les) processeur(s) du MPSoC. Si on part d'une application logicielle écrite en langage C, il faut utiliser un cross-compilateur spécifique pour le processeur choisi. Le résultat est un fichier binaire au format ELF. Le code binaire correspondant doit être chargé dans les mémoires embarquées du MPSoC. Il y a donc deux étapes bien distinctes, qui sont la génération du fichier ELF, et le chargement. == 3.1 Génération du code == A détailler... == 3.2 Chargement du code == Il existe deux méthode méthodes permettant de charger le code binaire dans les mémoires embarquées sur la puce: * 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. * Le code est 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 ROM 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 approache est généralement utilisée pour le code applicatif, mais également pour le système d'exploitation embarqué. Le code qui exécute ce chargement de code s'appelle un ''bootloader''. La phase de chargement du code applicatif et du système d'exploitation est exécutée à chaque mise sous tension. Elle peut être très longue (plusieurs millions de cycles). Un fois que le ''bootloader'' a été validé cette phase de chargement n'apporte plus beaucoup d'information, quand on souhaite mettre au point 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 par le constructeur des composants modélisant des mémoires embarquées (ROM ou RAM). Le constructeur du composant ''!VciSimpleRam'' possède un argument ''loader'' qui lui permet d'accéder au contenu du fichier ELF contenant le code binaire. Le constructeur possédant un autre argument lui permettant d'accéder à la MappingTable, il peut déterminer quels segments de la RAM (ou de la ROM) doivent être initialisés. = 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 }}} Cette archive contient les fichiers généraux suivants, extraits de la plate-forme SoCLib : * `vci_param.h` : definition des paramètres VCI. * `vci_signals.h` : definition d'un canal VCI. * `vci_initiator.h` : définition d'un port VCI initiateur. * `vci_target.h` : définition d'un port VCI cible. * `int_tab.h` : définition des index composites. * `segment.h` : définition d'un segment de l'espace adressable. * `segment.cpp` : implémentation des méthodes du segment. * `mapping_table.h` : définition de la mapping table. * `mapping_table.cpp` : implémentation des méthodes de la mapping table. * `address_decoding_table.h` : table indexée par une partie de l'adresse. * `address_decoding_table.cpp` : implémentation des méthodes de la table indexée. * `alloc_elems.h` : allocation de tableaux d'objets complexes L'archive contient également les fichiers suivants : * `vci_xcache_wrapper.h` : définition du composant `VciXcacheWrapper` * `vci_xcache_wrapper.cpp` : méthodes associées * `vci_lcd_coprocessor.h` : définition du composant `VciLcdCoprocessor` * `vci_lcd_coprocessor.cpp` : méthodes associées * `vci_simple_ram.h` : définition du composant `VciSimpleRam` * `vci_simple_ram.cpp` : méthodes associées * `vci_multi_tty.h` : définition du composant `VciMultiTty` * `vci_multi_tty.cpp` : méthodes associées * `vci_vgsb.h` : définition du composant `VciVgsb` * `vci_vgsb.cpp` : méthodes associées * `tp3_top.cpp` : top-cell de l'architecture * `tp3_segmentation.h` : adresses de base et des longueurs des segments. Le répertoire TP3 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 == Cette architecture nécessite la définition de 6 segments: * '''seg_tty''' est le segment associé au contrôleur de terinaux TTY. On prendra pour adresse de base la valeur 0xC0000000, et pour longueur 64 octets, ce qui permet d'adresser jusqu'à 4 terminaux indépendants (consulter la spécification fonctionnelle du composant VciMultiTty). * '''seg_lcd''' est le segment associé au coprocesseur LCD. On prendra pour adresse de base la valeur 0xD0000000. La longueur de 16 octets correspond aux quatre registres adressables de ce composant. * '''seg_reset''' est le segment contenant contient 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 4Koctets. * '''seg_giet''' est le segment contenant le code du Gestionnaire d'Interruptions, Exceptions, et Trappes (GIET). Il est assigné à la RAM. L'adresse de base 0x80000000 est imposée par la spécification du processeur MIPS32. On choisira une capacité de stockage de 4 Koctets. * '''seg_code''' est le segment contenant le code de l'application logicielle embarquée. Il est assigné à la RAM. On choisira pour adresse de base la valeur 0x00400000, et une capacité de stockage de 16 Koctets. * '''seg_data''' est le segment contenant les données globales et la pile d'exécution de l'application logicielle embarquée. Il est assigné à la RAM. On choisira pour adresse de base la valeur 0x10000000, et une capacité de stockage de 64 Koctets. Contrairement à ce que nous avons fait dans le TP2, on ne définit pas les caractéristiques des segments (adresse de base et longueur) directement dans le fichier ''tp3_top.cpp'', car ces valeurs sont maintenant utilisées à la fois par le matériel et par le logiciel embarqué, qui a besoin des adresses de base des différents périphériques. On définit donc ces valeurs dans un séparé fichier ''tp3_segmentation.h'', qui sera inclus dans tous les fichiers qui ont besoin de cette information. Complêtez le fichier ''tp3_segmentation.h'' qui se trouve dans le répertoire TP3. == 4.2 Génération du logiciel embarqué == Puisqu'on dispose d'un contrôleur de terminal dans l'architecture, le premier programme que vous allez exécuter se contente d'afficher le célèbre ''hello world'' sur le terminal. Ce programme est écrit en langage C. Placez-vous dans le sous-répertoire ''soft''. Ce répertoire contient les fichiers suivants : * '''reset.s''' : ce fichier contient le code de boot. * '''giet.s''' : * '''main.c''' : ce fichier contient le programme principal. * '''Makefile''' : Lancez l'exécution du Makefile. Un fichier ''soft.bin'' doit doit être créé dans le répertoire ''soft''. == 4.3 Instanciation des modèles des composants == Comme dans le TP2, il faut modifier tous les fichiers des composants qui possèdent des paramètres templates pour définir les valeurs de ces paramètres templates. Il faut ajouter à la fin de chaque fichier une ligne du type : {{{ template class VciLcdCoprocessor >; }}} == 4.4 Définition de la Top-cell == Il faut compléter le fichier ''tp3_top.cpp'', pour définir complêtement la MappingTable, définir les arguments des constructeurs et les valeurs des paramètres template des différents composants matériels instanciés, et définir le cheminom permettant au ''loader'' d'accéder au fichier ELF. == 4.5 Compilation et génération du simulateur == Complêtez le Makefile qui vous est fourni dans le répertoire TP3, pour définir l'ensemble de tous les fichiers objet utilisés pour construire le simulateur ''tp3_mono_simulator.x''. Lancez la compilation, puis exécutez la simulation. == 4.6 Modification du logiciel embarqué == Il est maintenant possible de modifier le logiciel embarqué (fichier ''main.c''), sans modifier l'architecture matérielle (fichier ''tp3_top.cpp''). L'application logicielle ''hello world'' n'utilisait pas le coprocesseur LCD. Modifiez le fichier ''main.c'', pour que les programme C exécute une boucle infinie dans laquelle on effectue successivement les opérations six 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 le registre ''r_opa'' du coprocesseur LCD. .1 écriture de OPB dans le registre ''r_opb'' du coprocesseur LCD. .1 écriture dans le pseudo-registre ''r_start'' du coprocesseur LCD, pour démarrer la simulation. .1 lecture dans le registre ''r_res'' du coprocesseur LCD pour récupérer 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 évidemment la fonction ''printf()''. Pour obtenir le numéro de cycle, on utilisera la fonction... Pour la génération aléatoire, on utilisera la fonction ''rand()''. Pour les accès au coprocesseur LCD on utilisera les fonctions... Le code de ces fonctions est défini dans le fichier... = 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...