Académique Documents
Professionnel Documents
Culture Documents
: EXCEPTIONS et INTERRUPTIONS.
Ce sont des mécanismes fournis par la majorité des architectures embarquées à base de processeur.
Elles permettent de détourner le chemin d’exécution normal du processeur. Ce détournement peut etre
déclenché soit :
L’appuie sur un bouton de reset d’une carte déclenche une exception asynchrone (system
reset exception).
La réception de données depuis un élément externe communicant.
Une interruption, parfois appelée interruption externe (external interrupt), est une exception
asynchrone déclenchée par un événement produit par un dispositif hardware. Les interruptions
sont une classe d’exception. Ce qui différencie les interruptions des exceptions, plus précisément
ce qui différencie les exceptions synchrones des exceptions asynchrones, est la source de
l’événement. La source de l’événement pour une exception synchrone est générée de façon
interne par le processeur en raison de l’exécution d’une instruction. La source de l’événement pour
une exception asynchrone est générée par un dispositif hardware externe.
1
Dans la suite du texte, comme dans de nombreux ouvrages, on utilisera le terme exceptions pour les
exceptions synchrones, et interruptions pour les exceptions asynchrones.
Le terme general exceptions englobe les exceptions et interruptions.
Le terme interrupts et external interrupts sont identiques.
Les conditions spéciales sont des exceptions qui sont générées par des instructions
spéciales (comme l’instruction TRAP sur les processeurs de la famille Motorola 68K).
Ces instructions permettent au programme de forcer le processeur à passer dans le mode
d’exécution privilégié, ce qui lui permet d’avoir accès au jeu d’instructions privilégié. Par
exemple, les instructions utilisées pour invalider les interruptions externes doivent être
réalisées en mode privilégié.
2
La solution pour le problème de la concurrence est d’utiliser les interruptions externes.Par
exemple, une application embarquée fonctionnant sur un processeur principal (core processor)
émet une commande à un dispositif, lui demandant de faire un travail. L’application embarquée
continue son exécution, réalisant d’autres fonctions pendant que le dispositif essaye de terminé le
travail demandé. Dès que son travail est terminé, le dispositif déclenche une interruption externe
vers le processeur principal (core processor), ce qui indique que le dispositif est maintenant prêt à
accepter de nouvelles commandes.
Une autre utilisation des interruptions externes est de fournir un mécanisme de communication,
pour signaler ou alerter un processeur embarqué de l’arrivée d’un événement externe nécessitant
un service (par exemple, arrivée d’un paquet de données d’un bus de communication, expiration
d’un timer hardware,…).
Donner des priorités à de multiples sources d’interruptions de telle sorte qu’à n’importe
quel instant la plus haute priorité est présentée au cœur CPU pour le traitement.
Décharger le cœur CPU du traitement qui consiste à déterminer la source exacte de
l’interruption.
Le PIC possède un ensemble de lignes de requêtes d’interruptions. Une source externe génère une
interruption en émettant un signal physique sur une ligne de requête d’interruption. Chaque ligne
de requête d’interruption a une priorité qui lui est assignée.
La figure de la page suivante illustre un PIC avec 4 sources d’interruptions. Chaque source
d’interruption est connectée à une ligne de requête d’interruption : un capteur airbag, un capteur de
frein, un capteur de niveau d’essence, et une RTC ( Real Time Clock ou Horloge temps réel ou
HTR).
3
La figure suivante est une table d’interruptions associée à notre exemple (interruption table).
Elle liste toutes les interruptions disponibles du système embarqué.
Comprendre les priorités des sources d’interruptions permet au programmeur de mieux appréhender le
concept d’interruptions imbriquées (nested interrupts). Ce terme recouvre la possibilité d’une
interruption de priorité supérieure de préempter le traitement d’une interruption de priorité inférieure.
Il est facile de voir comment une source d’interruption de faible priorité est affectée par une
interruption de plus haute priorité, et leurs temps d’exécutions et fréquences si la table est ordonnée
selon les priorités. Cette information aide le programmeur à concevoir et implémenter de meilleures
ISR qui permettent des interruptions imbriquées (nested interrupts).
La colonne de la fréquence maximale de la table d’interruptions spécifie la contrainte du temps de
traitement placée sur toutes les ISR qui ont le plus faible impact sur le système.
La colonne vector adress de la table d’interruptions spécifie l’endroit mémoire où l’ISR doit être
installée (elle dépend du processeur). Le processeur va automatiquement chercher l’instruction à partir
de l’une de ces adresses mémoire basée sur un numéro d’interruption, qui est spécifié dans la colonne
IRQ. Ces instructions commencent la routine spécifique à chaque interruption.
4
3.2. Classifications des exceptions générales.
La majorité des processeurs classent les exceptions de la manière suivante :
Les exceptions synchrones sont classées en exceptions précises et imprécises. Avec une exception
précise, le compteur programme du processeur pointe l’instruction exacte (offending instruction) qui
a provoqué l’exception, et le processeur sait alors à partir de quel endroit reprendre l’exécution
après la fin du traitement de la routine associée à l’exception.
Dans les architectures modernes qui implémentent le pipeline pour les données et les instructions, les
exceptions sont levées dans l’ordre d’écriture des instructions, et pas dans l’ordre d’exécution des
instructions. En particulier, les architecturent assurent que les instructions qui suivent l’offending
instruction et qui ont débuté leur exécution dans le pipeline durant l’exception n’affectent pas l’état de
la CPU. Il devient impossible de déterminer l’instruction exacte qui a provoqué l’exception. On
parle d’exception imprécise, le compteur programme est incapable de pointer sur l’offending
instruction.
Les priorités les plus élevées sont réservées aux reset de systèmes ou aux autres évènements ou erreurs
qui imposent un reset complet du système.
Les deux autres niveaux de priorités intermédiaires concernent l’ensemble des erreurs et des
conditions spéciales d’exécutions internes au processeur. Une exception synchro.ne est générée et
acquittée seulement pour certains états internes du processeur.
5
Le niveau de priorité le plus bas est celui des exceptions asynchrones externes au cœur du processeur.
Vu de l’application, toutes les exceptions ont une priorité supérieure à celle des objets du RTOS, c’est-
à-dire taches, FAM, sémaphores, …Cela est précisé dans la figure suivante.
6
4.1. Installation des routines d’exceptions (execption handlers).
Les ESR (exception service routine) et les ISR (interrupt service routine) doivent être installées
dans le système avant que les exceptions et interruptions puissent être gérées. Cela nécessite la
connaissance d’une table d’exception et d’interruption (general execption table). Nous avons déjà
rencontré ce type de table, rappelée ci-dessous.
Dans cette table, que l’on appelle aussi la table des vecteurs (vector table), chaque ligne de la colonne
vector adresses pointe vers le début d’une ESR ou ISR. L’installation d’une ESR ou ISR nécessite de
remplacer l’entrée appropriée de la table des vecteurs par l’adresse de l’ESR ou ISR.
Le code de démarrage (startup code) du système embarqué installe les ESR au moment de
l’initialisation. Les drivers des dispositifs hardware installent typiquement les ISR au moment de
l’initialisation des drivers.
Si une exception ou bien une interruption apparait alors qu’aucune routine associée n’est installée, le
système souffre d’un dysfonctionnement et peut s’arrêter. Pour éviter ce problème, de nombreux
RTOS embarqués possède une routine par défaut (default handler function) installée dans la table des
vecteurs pour chaque ISR et ESR possibles du système. La routine par défaut (default handler
function) réalise une petite quantité de travail pour assurer une réception propre et un retour propre de
l’exception). De nombreux RTOS fournissent un mécanisme qui permet au programmeur la possibilité
de réécrire la routine par défaut (default handler function) à son gout, ou d’insérer un traitement
supplémentaire à la suite de la routine par défaut (default handler function). Cela permet au
programmeur de coder des actions spécifiques avant et après les actions par défaut.
Dans ce cours, le terme routine de service (service routine) signifie indifféremment ESR et ISR.
Le registre d’état (status register ou SR) qui contient les bits d’état courants du processeur.
Le compteur programme (program counter ou PC) qui contient l’adresse de retour, c’est-à-
dire l’adresse à exécuter après la fin de la routine de service.
D’autres registres pour permettre une reprise correcte du programme préémpté, cela sera
détaillé plus loin dans ce chapitre.
7
Définition : Les piles (stacks) sont utilisées pour le stockage des informations de l’état du
processeur. Dans un environnement avec OS embarqué, la pile est composée d’un bloc mémoire
réservé statiquement et d’un pointeur dynamique, dit pointeur de pile (stack pointer).
Lorsque des données sont sauvées dans la pile, le pointeur de pile est incrémenté pour refléter le
nombre d’octets recopiés dans la pile. Ce procédé est souvent appelé empiler (pushing) des valeurs
dans la pile.
Lorsque des données sont recopiées depuis la pile, le pointeur de pile est décrémenté pour refléter le
nombre d’octets recopiés depuis la pile. Ce procédé est souvent appelé dépiler (popping) des
valeurs dans la pile. Le pointeur de pile pointe toujours vers le premier endroit susceptible
d’enregistrer une donnée dans la pile. Dans notre cours, on suppose que la pile grandit vers le haut
(dans certains autres processeurs, elle peut grandir vers le bas). Notez qu’une pile typique ne stocke
pas d’intensificateurs pour les contenus. Les utilisateurs devront utiliser l’opération d’empilage
(push) et de dépilage (pop) de la pile de manière symétrique. Si cette règle n’est pas
scrupuleusement respectée durant les traitements d’ESR et d’ISR, des résultats inattendus seront
possibles.
Dans ce cours, nous avons précisé que dans les RTOS embarqués, tous les objets taches possèdent un
bloc de contrôle de tache (ou TCB). Durant la création d’une tache, un bloc de mémoire est réservé en
tant que pile pour l’usage de la tâche, comme indiqué à la figure suivante.
8
Les langages de programmation de haut niveau (comme le C et le C++), utilise l’espace de la pile pour
passer les variables entre les fonctions et les objets du langage.
Le pointeur de pile actif (active SP) est réinitialisé vers le SP de la tache active lors de chaque
apparition de changement de contexte. C’est le noyau qui réalise cette opération en arrière-plan.
Comme indiqué précédemment, le processeur utilise n’importe quelle pile vers laquelle le SP pointe
pour stocker son état d’information minimal avant d’invoquer la routine d’interruption.
Bien que des disparités dans l’implémentation des ISR et ESR entre systèmes existent, l’idée générale
pour déterminer et réserver de l’espace mémoire pour la pile lors des interruptions est la même. Dans
de nombreux cas, lorsque des exceptions générales apparaissent et qu’une tache est en exécution, la
pile de la tâche est utilisée pour gérer l’exception ou l’interruption. Si une ESR ou ISR de plus
faible priorité est en exécution au moment de l’arrivée d’une nouvelle exception ou interruption, la
pile utilisée par l’ESR ou l’ISR de plus faible priorité est aussi la pile utilisée par la nouvelle exception
ou interruption. Cette approche peut devenir problématique dans le cas d’exceptions ou interruptions
imbriquées.
4.3. Chargement et appel des routines d’exceptions (execption handlers).
Comme indiqué plus tôt, certaines différences existent entre le travail réalisé en amont par le
processeur pour les ESR et ISR. Cela est dû au fait qu’une interruption externe est seul type
d’exception qui peut être invalidée par software. Dans beaucoup d’architecture à processeur
embarquée, les interruptions externes peuvent être validées ou invalidées à travers un registre de
contrôle du processeur. Ce registre de contrôle contrôle directement les opérations du PIC et détermine
quelles interruptions le PIC lève dans le processeur. Le PIC filtre les interruptions selon la
configuration du registre de contrôle et détermine l’action nécessaire. Dans la suite de ce cours, on
suppose ce modèle d’architecture comme la référence.
Formellement, une interruption peut être :
Invalidée (disabled).
Active (active).
En attente (pending).
Une interruption invalidée (disabled) est aussi appelée interruption masquée (masked interrupt). Le
PIC les ignore.
Une interruption en attente (pending) est une interruption qui n’est pas acquittée, elle apparait alors
que le processeur est en train de traiter une interruption de priorité supérieure. L’interruption en attente
(pending) est acquittée et traitée après que toutes les interruptions de priorités supérieures qui étaient
aussi dans l’état pending aient été traitées.
Une interruption active est une interruption qui est en train d’être acquittée et traitée par le processeur.
Pour les exceptions synchrones, le processeur détermine d’abord la première exception apparue puis
calcule l’index correct dans la table des vecteurs pour retourner l’ESR. Ce calcule dépend de
l’implémentation.
Dans le cas d’une exception asynchrone, une étape supplémentaire est nécessaire. Le PIC doit
déterminer si l’interruption a été invalidée ou masquée (disabled). Si c’est le cas, le PIC ignore
l’interruption et l’état du processeur est inchangé. Si l’interruption n’est pas masquée, le PIC lève
l’interruption dans le processeur et le processeur calcule l’adresse du vecteur puis charge cette adresse
afin d’exécuter la routine de service.
9
La figure suivante présente ces situations.
Certains vendeurs implémentent des tables de correspondance sous forme hardware, d’autres sous
forme software. Mais le mécanisme de fonctionnement reste le même. Lorsqu’une exception
apparait, la valeur de l’index est calculé depuis la table. Le contenu de la table à cet index ou
offset est l’adresse de la routine de service. Le conteur programme est initialisé avec cette adresse
(vector adress) et l’exécution commence à cette adresse.
10
Sur cette précédente figure, une tache s’exécute. A un moment, une interruption de faible priorité
devient active, et la routine de service associée commence à s’exécuter. Alors que cette routine
s’exécute, une priorité de niveau supérieure devient active, la routine de priorité inférieure devient
préemptée. La routine de priorité supérieur s’exécute jusqu’à son terme, puis le contrôle retourne à
la routine de faible priorité. Avant que la routine de faible priorité puisse prendre fin, une autre
interruption devient active. Comme précédemment, la routine de faible priorité est préemptée
pour permettre à la routine de priorité moyenne de s’exécuter complétement. Une fois de plus,
avant que la routine de faible priorité puisse se terminer, une autre priorité de niveau supérieure
devient active et s’exécute jusqu’à son terme. La routine de faible priorité est alors capable de
s’exécuter jusqu’à son terme. A ce moment, la tâche qui était interrompue précédemment peut
reprendre son exécution.
Lorsque les interruptions sont emboitées, la pile (stack) de l’application doit être assez large pour
s’accommoder au maximum d’appels successifs des fonctions imbriquées de l’application, au
maximum d’exceptions et d’interruptions imbriquées possibles, si l’application s’exécute avec
toutes les interruptions autorisées (enabled). Ce problème est fréquemment observé dans les
interruptions imbriquées.
En reprenant l’exemple précédent, N taches ont été créées, chacune avec son propre TCB et sa pile
allouée statiquement. Supposons que la pile de la tâche en exécution est utilisée pour les
exceptions, un scénario simple utilisant la figure suivante peut être :
11
espaces nécessaires (application stack space + execption stack space) est inférieure à la taille de
la pile allouée à la tache 2. En conséquence, lorsque des données sont copiées dans la pile
dépassent les limites définies statiquement, le TCB de la tache 3 devient corrompu, c’est ce qu’on
appelle le stack overflow.
Malheureusement, le TCB corrompu n’est pas indiqué jusqu’à ce que la tache 3 passe en état
d’exécution. Ce type d’erreurs peut être très difficile à détecter. Ces erreurs sont fonctions de la
combinaison des taches en exécution et de leurs fréquences, ainsi que des séquences
d’interruptions. Cette situation donne l’impression à l’utilisateur ou au développeur d’avoir un
système qui fonctionne de manière sporadique.
Deux solutions à ce problème sont possibles :
L’approche commune pour les execption frame est que les ESR ou ISR alloue un block mémoire, soit
statiquement, soit dynamiquement, avant de s’installer dans le système. Le gestionnaire d’exception
(execption handler) sauve alors le pointeur de pile dans une zone mémoire temporaire, réinitialise le
pointeur de pile de cette pile privée (private stack), puis commence le traitement. Cela est décrit à la
figure suivante.
12
Le gestionnaire d’exception (execption handler) peut réaliser d’autres actions, comme
enregistrer des informations supplémentaires sur l’état du processeur, dans sa pile.
13
Une ISR va typiquement utiliser une de ces trois méthodes pour invalider (disable) les
interruptions pour une ou toutes les raisons suivantes :
Sans entrer dans les détails, une ISR, quand elle s’exécute avec les interruptions invalidées
(disable), peut permettre au système de manquer des interruptions (interrupt miss) si l’ISR est
trop longue. Dans cette situation, les interruptions sont levées mais le processeur ne peut
enregistrer leurs occurrences car il est occupé. L’ISR n’est donc pas appelée pour ces
occurrences manquées. Ce problème est souvent présent pour des dispositifs qui utilisent des
mécanismes de déclenchement sur front (edge triggering). Ce point sera abordé plus loin dans
ce polycopié.
14
L’ordonnanceur du noyau RTOS ne peut pas fonctionner lorsqu’une ISR invalide (disable)
toutes les interruptions du système lors de son fonctionnement. Comme indiqué
précédemment, le traitement des interruptions a une priorité supérieure que celui des taches.
Donc, les taches temps réel qui ont des deadlines strictes peuvent aussi être affectées par une
ISR mal conçue.
La figure suivante illustre un nombre de concepts liés à une interruption unique.
La durée prise par le processeur pour acquitter l’interruption et réaliser le travail initial
de sauvegarde.
La présence à ce moment d’une interruption de priorité supérieure.
L’interruption est invalidée puis plus tard revalidée par software.
Le premier cas contribue toujours à la latence de l’interruption. Comme on peut le voir, la
latence de l’interruption peut être non bornée, donc le temps de réponse également. La latence
de l’interruption est hors de contrôle de l’ISR. Le temps de traitement T C, en revanche, est
déterminé par la manière dont est implémentée l’ISR.
Il est possible que le traitement complet soit réalisé dans le contexte de l’interruption, c’est-à-
dire avec les interruptions invalidées. Notez que le temps de traitement d’une interruption
d’une plus haute priorité est une source de latence d’interruption pour l’interruption de plus
basse priorité.
Une autre approche est d’avoir une partie de l’ISR s’exécutant dans le contexte de
l’interruption et une autre partie s’exécutant dans le contexte d’une tache. La première partie
du code de l’ISR sert le dispositif de manière que la requête de service soit acquittée et que le
dispositif soit placé dans un état opérationnel connu de telle sorte qu’il puisse rependre son
opération. Cette portion de l’ISR prépare la requête de service du dispositif puis l’envoie au
15
reste de la section de l’ISR qui l’exécute dans le contexte d’une tache. Cette dernière partie de
l’ISR est typiquement implantée comme une tache daemon (daemon task) dédiée.
Il existe deux raisons principales de partitionner l’ISR en deux pièces :
Les interruptions de plus faibles priorités peuvent être gérées avec moins de priorité
que les taches critiques s’exécutant sur le système.
Cette approche réduit les chances de rater des interruptions.
Cette approche permet plus de concurrence car les dispositifs sont servis de manière
minimale, de telle sorte qu’ils peuvent continuer à fonctionner alors que leurs
précédentes requêtes sont accumulées sans pertes.
16
Le délai d’ordonnancement apparait lorsque d’autres taches de plus haute priorité sont en
exécution po sont ordonnancées pour être exécutées. Ce délai d’ordonnancement inclue
également le temps nécessaire au changement de contexte après que la tache daemon soit
déplacée de la file Ready vers la file en exécution.
En conclusion, la durée d’exécution d’une ISR dans le contexte d’une interruption dépend du
nombre d’interruptions et de la fréquence de chaque source d’interruption existant dans le
système. Bien qu’une approche générale pour concevoir une interruption existe, aucune
solution unique n’existe pour implanter une ISR qui fonctionnera pour tous les systèmes
embarqués. Le développeur devra, pour concevoir son l’ISR, prendre en compte les
considérations présentées dans ce cours.
Recommandations générales.
Dans les architectures où les interruptions imbriquées (nested) sont permises :
Une ISR doit invalider (disable) les interruptions de même niveau si l’ISR est non
réentrante.
Une ISR doit masquer toutes les interruptions si elle a besoin d’exécuter une séquence
de code de façon atomique.
Une ISR doit éviter d’appeler des fonctions non réentrantes. Certaines fonctions de
bibliothèque standard sont non réentrantes, comme beaucoup d’implémentation de
malloc et printf. Comme une interruption peut apparaitre au milieu de l’exécution
d’une tache, et parce qu’une tache peut être au milieu d’un appel de fonction malloc,
le résultat peut être catastrophique si l’ISR appelle cette même fonction réentrante.
Une ISR ne doit jamais faire un appel bloquant ou suspensif (suspend). Cela peut
entrainer l’arrêt complet du système.
Si l’ISR est partitionnée en deux sections avec une section daemon task, la daemon task n’a
pas une priorité élevée par défaut. La priorité doit être fixée en respectant le reste du système.
17
Au contraire, les déclenchements sur niveau (level) sont utilisés avec des signaux analogiques.
La figure suivante en donne un exemple. Il est important de noter que dans ce type de
déclenchement, le PIC ou le microcontrôleur doit définir une valeur de ce seuil de
déclenchement.
Ces signaux, sans anti rebond (debouncing) ou filtrage se comportent comme des sources
sporadiques.
18