Accueil
Démo
Exercices formatifs
Travaux pratiques
Simulation libre
Sessions sauvegardées

TP4: Gestion des interruptions et ordonnancement de processus

Ce travail pratique vaut 4% de la note totale du cours. Comme le stipule le plan de cours, le travail doit être fait individuellement.

Objectifs

Ce travail pratique vise les objectifs suivants:

  1. Se familiariser avec les interruptions
  2. Comprendre le fonctionnement d'une table des vecteurs d'interruption;
  3. Mettre en place une table des vecteurs d'interruption rudimentaire en assembleur ARM;
  4. Implanter une routine de traitement d'interruption en assembleur ARM;
  5. Comprendre la sauvegarde de contexte effectuée par un système d'exploitation;

Préparation

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:

  • Cochez la case « Activer »;
  • Sélectionnez le type « FIQ »;
  • Entrez « 90 » dans la case « cycles »;
  • Entrez « 250 » dans la case « cycles (premier) »;

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.

Étapes

Ce TP s'effectue en deux étapes: écrire du code (pour 50% de la note du TP) et répondre à des questions (pour 50% de la note).

Étape 1: code (75%)

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 devez implémenter trois sections de code distinctes :

  1. La table des vecteurs d'interruption;
  2. Le code initialisant le système et les processus;
  3. La routine d'interruption permettant au système de changer le processus en cours d'exécution tout en sauvegardant son contexte.

Vous trouverez plus d'informations sur chacune de ces sections dans la section détails ci-bas.

Lorsque vous avez terminé, téléchargez tout d'abord votre fichier source.txt sur votre ordinateur grâce au bouton « sauvegarder » 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.

Étape 2: questions (25%)

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.

Détails sur le code

Important: n'écrivez votre code qu'aux endroits indiqués, sans toucher au reste. Toute autre modification au code pourrait causer des problèmes!

1. La table des vecteurs d'interruption

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.

AdresseCorrespond à l'interruption...
0x00000000Reset
0x00000004Instruction invalide
0x00000008SVC (interruption logicielle)
0x0000000CPrefetch Abort
0x00000010Data Abort
0x00000014Inutilisé
0x00000018IRQ (Interruption Request)
0x0000001CFIQ (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.

2. Initialisation du système et des processus

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 :

  • Les registres R0 à R7 inclusivement (ne sauvegardez pas les registres R8 à R14);
  • Les drapeaux de l'ALU;
  • La valeur courante de PC.

Pour sauvegarder ces données, votre système dispose de deux piles différentes : Pile1, qui contient les informations nécessaires au processus 1, et Pile2 pour le processus 2. 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 Pile1 et Pile2. Pour ce faire, vous devrez accomplir les deux étapes suivantes:

  1. Initialiser la pile du processus 1;
  2. Initialiser la pile du processus 2;

Pour ce faire, utilisez la fonction initialiserProcessus, 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).

3. Routine de traitement de l'interruption

Votre routine de traitement de l'interruption correspond à l'étiquette moninterruption. Lors d'une interruption, votre routine doit, dans l'ordre :

  1. Déterminer le processus qui s'exécute présentement. Vous pouvez utiliser la variable ProcessusEnCours pour mémoriser cette valeur;
  2. Sélectionner la pile du processus courant. Il vous faudra donc modifier le pointeur de pile SP selon l'identifiant de ce processus.
  3. Sauvegarder les registres R0 à R7, les drapeaux et PC (qui est automatiquement transféré dans LR par le processeur avant de commencer l'interruption) sur la pile du processus courant;
  4. Déterminer le prochain processus à exécuter. Dans le cadre de ce TP, il n'y a que deux processus, il suffit donc d'alterner entre les deux;
  5. Restaurer les registres R0 à R7 et les drapeaux du processus à reprendre à partir de sa pile;
  6. Sortir de l'interruption, en revenant dans le nouveau processus à exécuter. Pour revenir dans le bon processus, quel registre devrez-vous modifier?

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.

Tester et valider le bon fonctionnement du système

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 1 et 2 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. Si la valeur de R7 est toujours à 0 à la fin du code de processus1 et 2 à la fin du code de processus2 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.

N'utilisez pas le registre R7 pour vos calculs car il vous servira à valider votre code. Cependant, vous devez le sauvegarder (et le restaurer) comme les autres (R0 à R7) sur la pile.

Ressources

Remerciements

Merci à Marc-André Gardner pour la création de ce TP!