[Kos-dev] IPL - Niveau de Privilège des Interruptions

Hlide kos-dev@enix.org
Tue, 26 Jun 2001 22:56:39 +0200


Soyons clair, je disais que l'on pouvait simuler les IPL avec le PIC, mais
je ne garantis pas que cela soit rentable ou efficace.

Comment peut-on simuler ? prenons l'exemple d'un niveau de privilège allant
de 0 à 15 (comme sur le SH1) :

1) il faut une table de 16 mots codés sur 16-bit comme suit :

unsigned short
  ipl_mask[16];

Chaque mot de la table code le masque des interruptions sur le PIC (le
premier octet sera pour le "master" et le second pour le "slave"). Le bit 0
étant à 1 pour masquer l'irq #0, le bit 1 à 1 pour masquer l'irq #1, et
ainsi de suite.

2) une fonction irq_set_ipl(int irq,int ipl) comme suit :

void irq_set_ipl(int irq,int ipl)
  {
    unsigned int
      level;
    unsigned int
      mask = 1 << irq;
    for (i = 15; ipl < i; --i)
      ipl_mask[i] |= mask,
    for (mask = ~mask; 0 <= i; --i)
      ipl_mask[i] &= mask,
  }

Ici on place dans les mots d'index supérieur à "ipl" le bit #irq à 1 pour
masquer l'interruption #irq au-desus du niveau de privilège "ipl", puis dans
les mots d'index inférieur ou égal à "ipl" le bit #irq à 0 pour démasquer
l'interruption #irq en dessous ou au niveau de privilège "ipl".

Chaque fois que l'on veut affecter un niveau de privilège à un IRQ on
utilise cette fonction.

3) une fonction irq_set_mask(unsigned short mask) comme suit :

void irq_set_mask(unsigned short mask)
  {
    outb((mask >> 8)&0xff,0xa1); // c'est bien comme ça qu'on masque ?
    outb((mask >> 0)&0xff,0x21); // je ne me souviens plus très bien...
    outb(0x20,0x20);                     // au cas où des interruptions
    outb(0x20,0xa0);                     // sont en attente...
  }

Self-speaking...

4) une variable globale current_ipl :

unsigned short
  current_ipl;

contient le niveau de privilège actuel IPL du code

5) dans le handler du isr #irq :

... isr_handler (int irq,...)
  {
    // on change l'IPL pour empêcher uniquement les interruptions de moindre
priorité d'interrompre l'isr
    unsigned short
      saved_ipl = current_ipl;
    current_ipl = isr[irq].ipl;
    irq_set_mask(ipl_mask[current_ipl] | ~(1 << irq));
    ...
    ... = isr[irq].handler(...);
    ...
    // le traitement étant fini, on restaure l'IPL d'origine
    current_ipl = saved_ipl;
    irq_set_mask(ipl_mask[current_ipl]);
  }

J'ai écrit en C l'ISR générique mais il est bien évident qu'il doit être en
assembleur chez vous ;-).

Par contre, pour les IRQ ayant le même niveau de privilège, l'ordre de
priorité est celle du PIC.

Pire, même si l'IRQ #0 a un IPL plus faible que l'IRQ #1 mais plus fort que
l'IPL courant, si les deux se déclarent simultanément, le PIC donnera
priorité malgré tout à l'IRQ #0. C'est pourquoi je vous disais que ce n'est
pas encore totalement gagné.

Il est tout à fait possible de faire une rotation de l'ordre de priorité sur
le PIC (il existe une commande de registre pour faire ça dans le PIC mais il
faudrait que je retrouve le rare document exhaustif sur le PIC).

A partir de l'IPL courant, on sait quel IRQ a l'IPL le plus proche de lui.
On devrait donc obtenir une rotation possible des priorités des IRQ du PIC
en sorte que c'est cet IRQ qui se retrouve prioritaire sur le PIC. dans le
cas des IRQ #0 et IRQ #1 simultanés, le problème est résolu.

Pas si sûr que ça, prenons IRQ #0, IRQ #4 et IRQ #5. IRQ #0 est le moins
prioritaire en IPL, IRQ #5 le plus prioritaire. Donc on effectue la rotation
de priorité sur le PIC et on obtient la seule chose possible suivante : +
5->6->7->0->1->2->3->4 -. On voit bien que même si l'IPL d'IRQ #0 est
inférieur à l'IPL d'IRQ #4, si IRQ #0 et IRQ #4 se déclare simultanément,
c'est l'IRQ #0 qui l'emporte à cause du PIC :-(.


CONCLUSION :

Avantage de la gestion d'IPL sur les ISR :
- le fait de masquer les interruptions de moindre priorité n'affecte pas le
CPU car le PIC est responsable de les maintenir en attente, avant de les
déclarer au CPU après le démasquage des interruptions. Le CPU tourne donc à
son maximum.

Inconvénient:
- il faut bien choisir les IPL de chaque ISR de sorte que l'on ne se
retrouve pas avec la situation où un ISR risque ne pas s'exécuter en nombre
suffisant parce qu'il y aura eu plusieurs occurence de cet IRQ avant son
démasquage effectif. Imaginez que vous donniez la priorité la plus basse à
l'IRQ #0 et que ce dernier se déclenche à plusieurs reprise avant qu'il
n'ait été démasqué enfin, vous n'exécuterez qu'un seul ISR au lieu du nombre
exact d'ISR qu'il aurait fallu.

Voilà, voilà ;-)