Aller au contenu

EX06 - Traitement des interruptions – avec solutions

Exercice 1 : Concept général

Décrivez succinctement le concept général d’un interruption, d’exception et de leur traitement.

Solution

Les interruptions sont des événements déclenchés généralement par des périphériques internes ou externes au µP. Elles leur permettent de signaler des changements d’état (p.ex. pression sur un bouton-poussoir, réception de données sur une interface de communication, etc.).

Les exceptions sont généralement dues à des dysfonctionnements du logiciel. Elles servent à détecter des erreurs dans les programmes et permettent d’effectuer un traitement approprié afin de les corriger.

Les interruptions et les exceptions peuvent être vues comme des appels à des fonctions ou des routines de traitement (ISR - Interrupt Service Routine) effectués directement par le µP.

Exercice 2 : Types d’événements

Citez et décrivez les types d’événements pouvant survenir sur un système à µP.
Décrivez la différence entre des événements ou interruptions synchrones et asynchrones.

Solution

Les µP sont capables de traiter :

  • Interruptions matérielles levées par des périphériques d’entrées/sorties internes et/ou externes
  • Interruptions logicielles levées par le programme, tels des appels système (syscall)
  • Exceptions logicielles et matérielles signalant des dysfonctionnements des programmes
  • Reset levé lors de la mise sous tension du système et provoquant la réinitialisation du processeur et de ses périphériques

Les interruptions ou événements synchrones sont générés par le programme lui-même lors de son exécution. Ils sont traités dès leur levée sans retard.

Les interruptions ou événements asynchrones sont générés par des périphériques. Ils sont levés indépendamment au déroulement du programme et sont traités avec un certain retard.

Exercice 3 : Séquence d’interruption

Citez les 4 étapes principales du traitement d’une interruption.

Solution

Les 4 étapes principales:

  • Attente d’autorisation pour le traitement des interruptions
  • Sauvegarde du contexte du programme en cours
  • Exécution de la routine de traitement
  • Restauration du contexte et retour au programme suspendu

Exercice 4 : Table des vecteurs d’interruptions

Décrivez la fonction de la table des vecteurs d’interruptions.
Indiquez son contenu.

Solution

La table des vecteurs d’interruptions sert d’interface entre le µP et le logiciel. Lors de la levée d’une interruption, le CPU détermine la source de l’interruption afin de déterminer le numéro de vecteur d’interruption. Ce numéro de vecteur lui sert à trouver dans la table des vecteurs d’interruptions la routine de traitement correspondante.

La table des vecteurs d’interruptions contient les µP du profil A une instruction permettant d’appeler les routines de traitement. Sur les µC du profil M, elle contient l’adresse des routines de traitements.

Exercice 5 : Commutation de contexte

Expliquez la commutation de contexte d’interruption.
Décrivez la latence d’interruptions.
Décrivez la gigue d’interruptions et donnez quelques exemples.

Solution

La commutation de contexte d’interruption est l’action de sauver les registres du µP avant l’appel de la routine de traitement, respectivement de les restaurer après traitement.

La latence d’interruption correspond au temps écoulé entre la levée de l’interruption et le début de l’exécution de la routine de traitement.

La gigue d’interruptions est la variance de la latence. Elle est due principalement au déclenchement des interruptions ou au traitement d’interruptions plus prioritaires.

Exercice 6 : Interruptions imbriquées

Décrivez le principe d’interruptions imbriquées.

Solution

Sur les systèmes embarqués et plus particulièrement sur les systèmes à puce (SoC), il est courant de devoir traiter une batterie d’événements différents provenant de sources différentes. Les µP assignent à chaque source d’interruptions une priorité. Lors d’une levée simultanée de plusieurs interruptions, le µP va toujours traiter la plus prioritaire.

Durant le traitement d’une interruption, si une nouvelle source lève une nouvelle interruption, le µP va contrôler sa priorité. Si elle est plus prioritaire que celle qui est en cours de traitement, le µP va suspendre son traitement pour traiter la nouvelle. Dans le cas contraire, il va poursuivre le traitement en cours et la traiter plus tard.

Exercice 7 : Section critique

Quel est le résultat de l’instruction ci-dessous si durant l’exécution de l’instruction une interruption matérielle est levée et appelle la fonction “irq_handler” ?

int len = 0;

len += 2;   // <-- irq_handler() est appelée durant l'exécution

void irq_handler(void)
{
  len += 4;
}

Comment procéder pour pallier à ce problème ?

Solution

L’instruction “len+=2;” se décompose comme suit:

        // <-- si irq arrive ici --> len == 6
ldr r0, =len
        // <-- si irq arrive ici --> len == 6
ldr r1, [r0]
        // <-- si irq arrive ici --> len == 2 !!!
add r1, r1, #2
        // <-- si irq arrive ici --> len == 2 !!!
str r1, [r0]
        // <-- si irq arrive ici --> len == 6

Le résultat dépend du moment où le µP commence le traitement de l’interruption. Le résultat peut aussi bien être 2 ou 6.

Pour pallier à ce problème, il suffit sur les µP mono-coeur de bloquer la levée d’interruptions.

Exercice 8 : Interruptions matérielles

Citez les 3 techniques pour connecter des périphériques d’entrées/sorties à un processeur pour un traitement interruptif.

Solution

Les 3 techniques sont :

  • La scrutation logicielle
  • La priorité d’interruption
  • Les interruptions vectorisées

Exercice 9 : Génération d’exceptions

Imaginez des petits codes permettant de générer les exceptions suivantes:

  • Une interruption logicielle
  • Une instruction non définie
  • Une exception “data abort”
  • Une exception “prefetch abort”

Indiquez pour chacune de ces exceptions le numéro de vecteur pour un µC du profil M.

Solution
#include <cstdio>

extern "C" {
void sv_call_handler(void)  { printf("  11: sv_call_handler raised\n"); }
void hard_handler(void* ra) { printf("   3: hard_fault_handler raised: ra=%p\n", ra); }
}

int main()
{
    printf("generate software interrupt\n");
    asm volatile ("svc #1");

    printf("generate undefined instruction\n");
    asm volatile (".short 0xffff");

    printf("generate exception \"data abort\"\n");
    asm volatile ("ldr r0,=0xffffffff"); 
    asm volatile ("ldr r0,[r0]");

    printf("generate exception \"prefetch abort\"\n");
    asm volatile ("mov r0,pc");
    asm volatile ("blx r0");
    asm volatile ("nop");

    return 0;
}

Code assembleur pour une traitement des “hard faults” permettant un retour au programme principal.

    .text
    .thumb
    .syntax unified
    .align 2

    .global hard_fault_handler
    .type hard_fault_handler, %function
hard_fault_handler:
    push {lr}
    ldr  r0, [sp, #4*6+4]
    bl   hard_handler
    ldr  r0, [sp, #4*6+4]
    adds r0, #2
    orrs r0, #1
    str  r0, [sp, #4*6+4]
    pop  {pc}
    .size hard_fault_handler,.-hard_fault_handler

Exercice 10 : Gestion de la levée d’interruptions

Implémentez en assembleur les deux fonctions ci-dessous permettant de bloquer et d’autoriser les interruptions au niveau du CPU. Réalisez ces fonctions pour les µC du profil M.

void interrupt_enable(void);
void interrupt_disable(void);
Solution
void interrupt_enable(void)  { asm volatile ("cpsie i"); }
void interrupt_disable(void) { asm volatile ("cpsid i"); }

Exercice 11 : Priorité d’interruptions

Démontez à l’aide de “timers” la priorité d’interruptions et la préemption.

Solution
#include <cstdio>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>

static volatile unsigned long systick_millis;
static volatile unsigned long timer5_millis;

void sys_tick_handler(void) 
{ 
    systick_millis++; 
}

void tim2_isr(void)
{
    timer_clear_flag(TIM2, TIM_SR_UIF);  // acknowledge interrupt

    printf ("timer2 enters...\n");
    auto st = timer5_millis;
    while(1) {
        auto sp = timer5_millis;
        if ((sp - st) > 5) break;
    }
    printf ("timer2 leaves...\n");

}

void tim5_isr(void) 
{
    timer_clear_flag(TIM5, TIM_SR_UIF);  // acknowledge interrupt

    printf ("  timer5 enters...\n");
    timer5_millis++; 
    auto st = systick_millis;
    while(1) {
        auto sp = systick_millis;
        if ((sp - st) > 10) break;
    }
    printf ("  timer5 leaves...\n");
}

void TimerSetup()
{
    // configure priority grouping: 16 groups supported
    scb_set_priority_grouping(0<<8);   

    // systick config @1kHz
    systick_set_reload(rcc_ahb_frequency / 1000);   
    systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
    systick_counter_enable();

    // timer 2 config @1kHz
    rcc_periph_clock_enable(RCC_TIM2);
    rcc_periph_reset_pulse(RST_TIM2);
    timer_set_period(TIM2, rcc_apb2_frequency / 1000);
    timer_enable_irq(TIM2, TIM_DIER_UIE);
    timer_enable_counter(TIM2);

    // timer 5 config @10Hz
    rcc_periph_clock_enable(RCC_TIM5);
    rcc_periph_reset_pulse(RST_TIM5);
    timer_set_period(TIM5, rcc_apb2_frequency / 10);
    timer_enable_irq(TIM5, TIM_DIER_UIE);
    timer_enable_counter(TIM5);

    // set timer priorities (higher prio first)
    nvic_set_priority(NVIC_SYSTICK_IRQ, 0x50);
    nvic_set_priority(NVIC_TIM5_IRQ,    0x60);
    nvic_set_priority(NVIC_TIM2_IRQ,    0x70);

    // enables timers
    systick_interrupt_enable();
    nvic_enable_irq(NVIC_TIM2_IRQ);    
    nvic_enable_irq(NVIC_TIM5_IRQ);    
}

int main()
{
    TimerSetup();

    while(1) { asm volatile ("nop"); }

    return 0;
}

Exercice 12 : Traitement d’un bouton-poussoir par interruption

Détectez les changements d’état d’un bouton-poussoir à l’aide d’interruptions.

Solution
#include <cstdio>

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/cm3/nvic.h>

volatile unsigned long exti_count=0;
void exti1_isr(void) 
{
    exti_count++;
    exti_reset_request(EXTI1);
}

void EXTISetup()
{
    // configure disco click board on cape 2
    // rotary channel B
    rcc_periph_clock_enable(RCC_GPIOC);
    gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO1);
    exti_select_source(EXTI1, GPIOC);

    exti_set_trigger(EXTI1, EXTI_TRIGGER_FALLING);
    exti_enable_request(EXTI1);
    nvic_enable_irq(NVIC_EXTI1_IRQ);    
}


int main()
{
    EXTISetup();

    unsigned long count=0;
    while(1) {
        if (count != exti_count) {
            count = exti_count;
            printf ("exit-count: %lu\n", count);
        }
    }

    return 0;
}