Aller au contenu

Concept général

Le traitement des interruptions (Interrupt Handling) est un sujet indissociable de la programmation de systèmes embarqués et de systèmes sur puce.

Principe

Une interruption (Interrupt) peut être vue comme un appel à une routine de traitement (ISR - Interrupt Service Routine). Déclenchée par un événement interne ou externe au processeur, elle suspend l’exécution du programme en cours, puis appelle une routine, laquelle traite l’événement pour finalement retourner au programme interrompu. Hormis un délai, le traitement correct d’une interruption ne doit pas altérer le comportement d’un programme en cours d’exécution.

Types d’événements

Les événements externes au CPU, levés par des périphériques, génèrent des interruptions matérielles. Ces interruptions sont dites asynchrones, car elles surviennent indépendamment de l’exécution du programme. Par contre, les événements internes, causés par des interruptions logicielles, des appels système, ou par des exceptions, des dysfonctionnements du programme lors de son exécution,sont dits synchrones. Elles sont dues à l’exécution du code et de ses instructions. Le signal reset est le dernier type d’événements. Il provoque le reset du processeur et de la réinitialisation de ses périphériques.

Interruptions matérielles

Les interruptions permettent de traiter de façon asynchrone des événements levés par le matériel (Hardware Interrupt). Cette capacité à traiter des événements spontanés procure une grande réactivité au système et à ses applications. Elles offrent un traitement rapide et approprié de l’événement.

Interruption matérielle

Il existe une multitude d’exemples pour illustrer leurs utilités. Pour n’en citer qu’un, prenons une interface de communication. Lors d’échanges d’information entre un pilote et un périphérique d’entrées/sorties gérant une interface de communication, interface ayant un comportement non prévisible/aléatoire, il est souhaitable que le périphérique puisse lui-même prendre l’initiative des échanges, en forçant le processeur à suspendre immédiatement l’exécution du programme en cours pour laisser le pilote traiter les requêtes (réceptions ou envois de données).

Des interruptions matérielles peuvent également être mises en œuvre pour faciliter le débogage d’applications logicielles lors de leur réalisation. Certains processeurs implémentent une infrastructure permettant de stopper l’exécution du programme en surveillant le bus d’adresses et/ou de données (Hardware Breakpoint, Watchpoint). Si l’adresse et/ou la donnée passant sur leur bus respectif correspond à un point d’arrêt, le processeur suspend l’exécution du programme et active la session de débogage.

Interruptions logicielles

Les interruptions logicielles (Software Interrupt) permettent à un processus en espace utilisateur de franchir la barrière de protection en quittant son mode non privilégié pour accéder au mode privilégié du µP et ainsi aux fonctions fournies par l’OS dans l’espace noyau. Cette fonctionnalité est largement utilisée par les systèmes d’exploitation (GNU/Linux, Windows, MacOS, etc.) pour les appels système (syscall). Les processeurs ARM proposent l’instruction “SVC” (Supervisor Call) pour générer cette interruption logicielle.

Interruption logicielle

Les interruptions logicielles permettent également à des applications de communiquer avec des programmes (firmware, silicon software) contenus dans une mémoire non volatile du processeur ou de l’ordinateur. BIOS (Basic Input/Output System) des machines “Windows” est un exemple typique.

Elles permettent aussi à des outils de développement de poser des points d’arrêt (Software Breakpoints) facilitant le débogage d’applications. Les processeurs ARM proposent l’instruction “BKPT” (Break Point) pour générer cette interruption logicielle.

Exceptions

Les défaillances et dysfonctionnements dans les logiciels sont monnaie courante. Lors de l’exécution d’un programme, des erreurs peuvent survenir et perturber son bon déroulement. Les µP implémentent tout un arsenal de mécanismes de reconnaissance de ces exceptions et fournissent aux logiciels une série d’interruptions spécifiques permettant un traitement approprié.

Exceptions

Les exceptions logicielles les plus usuelles sont dues à des instructions illégales, des erreurs arithmétiques ou des violations de privilèges.

  • Une instruction illégale (Undefined Instruction) est une instruction pas supportée et pas implémentée par le jeu d’instructions du processeur. La levée de cette exception permet au logiciel de réaliser par exemple des routines émulant le comportement de ces instructions non définies.

  • Les unités de calculs (arithmétique et/ou à virgule flottante) sont conçues pour signaler des erreurs de calcul, telles les divisions par zéro. La levée d’une exception permet de détecter ce type d’erreurs et de réaliser un traitement approprié.

  • Les processeurs implémentant plusieurs modes de fonctionnement avec différents niveaux de privilèges sont capables de détecter des violations de privilèges. Ces violations résultent d’accès à des ressources non autorisées, tel l’usage d’instructions nécessitant un niveau de privilèges supérieur ou des accès à des zones mémoires protégées.

Les exceptions matérielles sont dues généralement à des erreurs du logiciel, mais détectées par des unités externes à l’unité centrale de traitement du processeur (CPU). Les exceptions matérielles les plus courantes sont des erreurs d’accès sur le bus de données (Bus Error) ou sur le bus d’adresses (Address Error) ainsi que le “reset” du processeur.

  • Des contrôleurs de périphériques ne répondant pas aux cycles d’accès dans les temps spécifiés soit par configuration, soit par le matériel sont en principe à l’origine des erreurs sur le bus de données et détectées par l’unité d’interface du bus processeur (BIU - Bus Interface Unit). La cause de ces erreurs est généralement la non-activation de l’horloge cadençant les contrôleurs.

  • Deux causes principales sont à l’origine des erreurs sur le bus d’adresses, les accès non alignés et les accès non autorisés. La levée d’exceptions pour des accès non alignées dépend de l’implémentation de la BIU et de sa capacité à effectuer des accès multiples pour lire ou écrire des données non alignées. Les MMU/MPU (Memory Management Unit / Memory Protection Unit) fournissent les outils indispensables pour la protection des zones mémoires et l’isolation des processus. Elles sont conçues en autre pour détecter des violations d’accès à la mémoire, telle que des accès en lecture ou écriture non autorisés, l’exécution de code non autorisé, la protection de zones mémoire protégées pour un processus.

Reset du processeur

De multiples causes peuvent être à l’origine du reset du processeur, de sa réinitialisation. La première est bien naturellement la mise sous tension de la carte processeur. Cette mise sous tension est cruciale. Une fois stabilisé, le signal reset est levé. Détecté par le processeur et l’ensemble de ses unités et contrôleurs, il sert à leur réinitialisation. Les chiens de garde (Watchdog) sont la deuxième cause importante. Ils servent à surveiller des interblocages logiciels (Deadlock). En cas de blocage, les chiens de garde activent le signal reset et forcent la réinitialisation du système.

Séquence d’interruption

Lors de la levée d’une interruption suite à un événement interne ou externe, le processeur suspend l’exécution du programme en cours et effectue un traitement approprié à l’événement. Ce traitement se déroule en quatre étapes principales.

Séquence d'interruption
  1. Attente d’autorisation pour le traitement des interruptions
  2. Sauvegarde du contexte du programme en cours
  3. Exécution de la routine de traitement
  4. Restauration du contexte et retour au programme suspendu

La première étape n’est présente que lors de requêtes d’interruptions levées par des événements asynchrones. Lors de tels événements, le CPU ne peut effectuer le traitement que si le traitement des interruptions est autorisé. La première cause de désactivation est le traitement d’une interruption. Celui-ci bloque en effet le traitement d’autres interruptions tant que le traitement de l’interruption active n’est pas terminé. La désactivation des interruptions est également un mécanisme indispensable à la conception d’applications logicielles de bas niveau, proche du CPU et de ses périphériques. Il permet en effet de protéger certaines sections critiques et éviter ainsi des conditions de concurrence (Race Conditions) entre différentes routines de traitement. Un des exemples typiques sont des données partagées et accédées par une routine de traitement d’interruption (ISR) et des routines exécutées par une tâche (Thread). Lors d’interruptions synchrones, cette étape n’existe pas, car la cause de l’événement est l’exécution de l’instruction. Dans de tels cas, le traitement de l’événement s’effectue immédiatement.

La deuxième étape commence aussitôt le traitement des interruptions autorisé. Le CPU suspend immédiatement le programme en cours, effectue une commutation de contexte avant de pouvoir appeler la routine de traitement d’interruption. Dans une première phase, le CPU sauve l’état courant du programme, l’adresse de retour vers le programme en cours d’exécution et les registres du processeur. Puis, il bloque les interruptions et change son mode d’opération pour un mode privilégié adapté au traitement de l’événement. Avant de chercher dans la table des vecteurs d’interruptions la routine de traitement à exécuter. Selon l’architecture du processeur, ces phases sont effectuées complètement par le CPU ou réparties entre CPU et logiciel.

En troisième étape, l’identité de la source de l’événement déterminée, son traitement peut s’effectuer.

En quatrième étape, une fois l’exécution de la routine de traitement terminée, il ne reste qu’à restaurer le contenu des registres du CPU pour rétablir le contexte du programme suspendu afin qu’il puisse poursuivre son exécution où il a été interrompu. Selon l’architecture du processeur, le CPU effectue directement toutes les opérations nécessaires lors du retour de la routine de traitement. Avec d’autres architectures, cette tâche est déléguée au logiciel.

Table des vecteurs d’interruptions

La table des vecteurs d’interruptions sert d’interface entre le HW et le SW. Le logiciel l’utilise pour configurer le CPU afin qu’il puisse appeler la routine d’interruption appropriée à l’événement à traiter. Cette table est indispensable pour adapter le µP aux besoins spécifiques du système. Selon les architectures, l’emplacement de cette table peut être fixe ou laissé au libre choix du logiciel et se trouver aussi bien en mémoire vive que morte.

Traitement des interruptions au niveau du microprocesseur

Lors de la levée d’une interruption, le CPU identifie la source d’interruption à laquelle un numéro fixe de vecteur d’interruption lui est associé. Avec ce numéro, le CPU accède à la table des vecteurs d’interruption afin d’obtenir le vecteur d’interruption et appeler la routine de traitement. Ce vecteur correspond, selon l’architecture du µP, soit à une instruction, soit à l’adresse de la routine de traitement.

Quelques définitions utiles

  • La source d’interruption (Interrupt Source) est le signal ou l’événement capable de lever une interruption ou de générer une exception
  • La table des vecteurs d’interruptions (Interrupt Vector Table) contient les instructions ou les adresses des routines d’interruptions permettant de traiter les événements
  • Le vecteur d’interruption (Interrupt Vector), contenu de la table des vecteurs, sur les processeurs ARM il correspond soit à une instruction, soit à l’adresse de la routine de traitement
  • Le numéro du vecteur d’interruption (Interrupt Vector Number) est l’index dans la table des vecteurs d’interruptions

Commutation de contexte

L’action de sauver les registres du µP pour appeler la routine de traitement lorsqu’une interruption est levée, ou de les restaurer à la fin du traitement de l’interruption, s’appelle la commutation de contexte (Context Switching). Ces deux commutations de contexte s’effectuent lors de la deuxième et quatrième étape de la séquence de traitement.

Séquence de traitement

Le temps qui s’écoule entre la levée de l’interruption et l’exécution de la première instruction de la routine de traitement d’interruption est appelé latence d’interruption (Interrupt Latency). Elle est due à un facteur principal, le temps de désactivation des interruptions, temps durant lequel le µP n’est pas autorisé à traiter des événements externes. Dans un système fréquemment interrompu, la valeur de cette latence influence considérablement le comportement du système et peut dégrader fortement ses performances. Afin de réduire la latence, les routines de traitement ne doivent effectuer que le strict minimum d’opérations afin de satisfaire le µP et ses contrôleurs et déléguer le maximum du traitement dans des tâches (Thread).

Latence et gigue d'interruption

La variation de la latence d’interruption est appelée gigue d’interruption (Interrupt Jitter). Son amplitude est principalement due aux variations de la durée de désactivation des interruptions. La gigue peut poser des problèmes sérieux dans des systèmes temps réel ayant des contraintes de temps exigeantes.

Interruptions imbriquées

Les systèmes embarqués et les systèmes sur puce (Soc) sont généralement confrontés à devoir piloter simultanément un grand nombre de périphériques différents et gérer une multitude de sources d’interruptions. Si les systèmes d’exploitation riche ne permettent en principe qu’un traitement séquentiel des interruptions, les OS temps réel (RTOS) proposent généralement un support logiciel pour un traitement imbriqué des interruptions (Nested Interrupt Handling) pour les processeurs disposant de cette capacité de traitement.

Dans un système de traitement d’interruptions imbriquées, chaque source d’interruptions reçoit un niveau de priorité. Ce niveau de priorité est généralement configurable et peut être unique ou partagé entre plusieurs sources d’interruptions. Lors du traitement d’une interruption (\(1\)), si une source avec un niveau de priorité supérieure lève une interruption, le CPU suspend la routine en cours de traitement pour traiter la source plus prioritaire (\(2\)).

Traitement imbriqué pour une source prioritaire

Par contre lors du traitement d’une interruption, si une source avec un niveau de priorité inférieure lève une interruption, le CPU termine d’abord le traitement de la routine en cours (\(1\)) avant de traiter la nouvelle source (\(2\)).

Traitement retardé pour une source secondaire

Ce mécanisme est très intéressant, car il permet de réduire le temps de latence pour les sources d’interruptions prioritaires. Par contre, il peut rallonger le temps de traitement des sources secondaires.

Note

Lors de la conception des routines de traitement d’interruptions, il est important d’effectuer le minimum d’opérations, ceci afin de garantir de bon temps de réaction du système. Si certains événements demandent de longs traitements, il est plus judicieux de les déléguer à des tâches d’arrière-plan (Background Task).

Gestion de la levée des interruptions

La conception d’applications mettant en œuvre des tâches de fond (Background Tasks) coopérant avec des tâches événementielles (Event-Driven Tasks) requiert un soin tout particulier. Dans de tels programmes, il est courant d’être confronté à des situations de concurrence lors d’accès à des ressources partagées entre les différentes tâches (Race Condition).

Pour protéger de telles ressources, il est souvent nécessaire d’interdire la levée des interruptions le temps du traitement. Dans une telle situation, il est impératif de garantir à une tâche de fond qu’elle ne soit pas interrompue par une tâche événementielle concurrente, une interruption, lorsqu’elle accède aux données. La portion de code impliquée dans ce traitement est nommée en programmation concurrente section critique (Critical Sections).

Selon la nature de la ressource à protéger, les interruptions sont bloquées à des niveaux différents. Si la ressource est globale, tel le compteur d’un sémaphore sur un µP mono-coeur, la protection s’effectue généralement au niveau du CPU en inhibant toutes les interruptions. Par contre, s’il s’agit d’une ressource proche d’un périphérique, il est usuel de masquer les interruptions au niveau du contrôleur du périphérique. Le CPU ainsi que les contrôleurs placés dans la chaîne de traitement des interruptions disposent de registres spéciaux permettant au logiciel de gérer la levée des interruptions asynchrones.