Ce travail pratique vaut 4% de la note totale du cours. À faire individuellement, il est à remettre au plus tard mardi le 21 mars 2017 à 23h59.
Ce travail pratique vise les objectifs suivants:
Le simulateur ARM utilisé pour le TP4 est disponible en cliquant sur ce lien. Prenez un moment pour vous familiariser avec le simulateur avant de commencer le TP! Une lecture préalable du guide de l'utilisateur vous sera aussi fort utile.
Pour ce TP, nous allons mettre en place une interruption périodique de type FIQ dans le simulateur. Cela permettra de simuler l'utilisation d'une horloge interne au processeur, capable d'interrompre régulièrement son travail. Pour ce faire, cliquez sur le bouton « Configurations » en bas à gauche. Entrez les informations suivantes:
Une fois ces étapes complétées, vous devriez obtenir la configuration suivante:
Cliquez sur le « x » en haut à droite du panneau de configuration pour retourner au simulateur. Notez que vous pouvez également modifier la « Vitesse d'exécution » dans ce même panneau pour effectuer vos simulations plus ou moins rapidement.
Ce TP s'effectue en deux étapes, qui vous demanderont respectivement d'écrire du code (pour 70% de la note du TP), et de répondre à des questions (pour 30% de la note).
Premièrement, il vous faudra coder un système d'exploitation (très) rudimentaire, capable de gérer l'exécution alternative de deux processus distincts, sans que ces derniers ne soient affectés. Pour ce faire, le processeur génère périodiquement une interruption, indépendamment de ce que font les processus. Cette interruption est gérée par le système d'exploitation, qui peut alors décider d'arrêter temporairement un processus pour laisser la place à un autre. Ce système est qualifié de préemptif, puisqu'il peut interrompre un processus à n'importe quel moment, même si ce dernier est dans une boucle infinie. Nous verrons plus en détail les notions reliées à l'ordonnancement des processus dans la suite du cours, mais sachez pour l'instant que cette gestion des processus est tout à fait similaire à ce que fait votre ordinateur lorsque plusieurs programmes requièrent simultanément du temps de calcul.
Vous devrez coder trois sections de code distinctes :
Vous trouverez plus d'informations sur chacune de ces sections dans la section détails ci-bas.
Le code de départ fourni dans le simulateur contient plus de précisions quant à la tâche exacte qui vous est demandée pour chacune de ces sections.
Lorsque vous avez terminé, téléchargez tout d'abord votre fichier asm.txt
sur votre ordinateur grâce au bouton « télécharger » dans le simulateur, puis téléversez ce même fichier dans la boîte de dépôt qui est disponible sur le portail des cours.
Deuxièmement, une fois votre code implanté et testé (nous verrons comment vous pouvez tester son bon fonctionnement), vous devez répondre à dix (10) questions sur le portail des cours. Notez que contrairement aux travaux pratiques précédents, ces questions ne portent pas sur des lignes en particulier, mais bien sur le fonctionnement global de votre programme et des interruptions en ARM.
Ces questions sont disponibles dans la section « Questionnaires » du portail des cours.
La politique des retards mentionnée dans le plan de cours sera appliquée. Comme il y a un fichier à téléverser et des questions à répondre, la date la plus tardive d'entre les deux sera utilisée pour le calcul des retards. Pour toutes questions concernant la procédure de remise ou le travail lui-même, posez vos questions sur Piazza!
Comme beaucoup d'architectures, l'ARM utilise une table des vecteurs d'interruption pour permettre au programmeur de spécifier quoi faire pour chaque type d'interruption. Cette table est située au tout début de la mémoire, à partir de l'adresse 0x00
. Le tableau suivant présente le détail de ces vecteurs d'interruption.
Adresse | Correspond à l'interruption... |
---|---|
0x00000000 | Reset |
0x00000004 | Instruction invalide |
0x00000008 | SVC (interruption logicielle) |
0x0000000C | Prefetch Abort |
0x00000010 | Data Abort |
0x00000014 | Inutilisé |
0x00000018 | IRQ (Interruption Request) |
0x0000001C | FIQ (Fast Interrupt Request) |
Comme vous pouvez le constater, vous utilisiez déjà une interruption sans le savoir, soit l'interruption Reset, en écrivant B main
au tout début du programme. Cette instruction se retrouvait à l'adresse 0x00
et indiquait au processeur quoi faire lors d'un reset, à savoir aller à l'étiquette main
.
Dans ce TP, nous avons implanté l'entrée de la table pour l'interruption Reset (voir B moninitialisation
). Vous devrez écrire le code correspondant à l'interruption FIQ, qui devrait brancher vers votre routine de traitement de l'interruption, soit l'étiquette moninterruption
.
Lorsqu'une interruption survient, le processus en cours d'exécution peut être n'importe où dans son code assembleur. Il nous faut par conséquent être capables d'enregistrer l'état du processus afin d'être en mesure de le restaurer plus tard, sans que ce dernier ne « remarque » qu'il a été interrompu. Plus précisément, dans ce TP, vous devrez sauvegarder :
Pour sauvegarder ces données, votre système dispose de deux piles différentes : Pile0
, qui contient les informations nécessaires au processus 0, et Pile1
pour le processus 1. La figure suivante montre l'organisation mémoire déjà préparée pour vous dans le code fourni. Sur la pile de chaque processus se trouve l'adresse de retour, suivie de l'état des drapeaux, suivi des registres R7 à R0, dans cet ordre. R0 est donc le registre qui est le premier élément de la pile lorsque cette dernière est remplie. N'oubliez pas que la pile fonctionne de bas en haut!
Il vous faudra tout d'abord initialiser les piles Pile0
et Pile1
. Pour ce faire, vous devrez accomplir les trois étapes suivantes:
ProcessusCourant
pour indiquer au système que c'est le processus 0 qui est exécuté en premier.Pour les deux premières étapes, utilisez la fonction initialiseProcessus
, qui fait tout le travail pour vous. Vous n'avez qu'à l'appeler avec les bons paramètres (voyez sa documentation dans le code fourni pour plus de détails).
Votre routine de traitement de l'interruption correspond à l'étiquette moninterruption
. Lors d'une interruption, votre routine doit, dans l'ordre :
ProcessusCourant
pour mémoriser cette valeur;Comme nous utilisons une interruption de type FIQ, votre routine de traitement de l'interruption peut utiliser les registres R8 à R15 sans interférer avec les processus. Pour vous simplifier la vie, servez-vous de ces registres comme registres de travail dans votre fonction moninterruption
, ce qui vous évitera d'avoir à sauvegarder les registres que vous utiliserez dans moninterruption
.
Afin de vous aider à déboguer votre code, il est recommandé d'utiliser les points d'arrêt (« breakpoints »), comme au TP3. Vous pouvez par exemple mettre un de ces points d'arrêt dans la routine d'interruption elle-même, afin de vérifier son bon fonctionnement. Une autre vérification simple est de vous assurer que les blocs correspondant aux processus 0 et 1 sont bel et bien exécutés en alternance à chaque interruption.
En ce qui concerne la validation de votre code, le code fourni prévoit un système capable de détecter certaines erreurs. Il fonctionne en écrivant un code correspondant à cette erreur dans le registre R7, nous vous demandons donc de vous abstenir d'utiliser ce registre. Au début du programme, la valeur de R7 est non-nulle, mais doit le devenir et le rester après la première exécution des deux processus. Si la valeur de R7 est toujours à 0 après plusieurs centaines de cycles, vous pouvez considérer votre système comme suffisamment stable. Dans le cas contraire, cela indique un problème dans votre procédure de sauvegarde/restauration de l'état des processus.
Merci à Marc-André Gardner pour la création de ce TP!