Version 27 (modified by 3 years ago) (diff) | ,
---|
- À quelles adresses dans l'espace d'adressage sont placés les registres des 3 contrôleurs de périphériques de la plateforme et comment le kernel les connaît ?
- Que signifie l'acronyme I.R.Q. ?
- Une IRQ est un signal électrique, combien peut-il avoir d'états ?
- Qu'est-ce qui provoque une IRQ ?
- Les IRQ relient des composants sources et des composants destinataires, quels sont ces composants ? Donnez un exemple.
- Que signifie masquer une IRQ ?
- Quels composants peuvent masquer une IRQ ?
- Est-ce qu'une application utilisateur peut demander le masquage d'une IRQ ?
- Que signifie l'acronyme I.S.R. ?
- Dans la plateforme des TPs, sur quelles entrées de l'ICU sont branchées les IRQ venant des TTYs et du TIMER ?
- Quelle valeur faut il avoir dans le registre
ICU_MASK
si on veut recevoir seulement les IRQ venant des 4 TTYs, dans le cas de la plateforme utilisée en TP ? Donnez le nombre en binaire et en hexadécimal. - L'écriture dans
ICU_MASK
n'est pas possible, comment modifier ce registre pour mettre à 1 le bit0
? - Sur une plateforme (autre que celle des TP) sur laquelle on aurait un TTY0 sur l'entrée 5, un TIMER sur l'entrée 2, et un autre TTY1 sur l'entrée 14. Que doit-on faire pour que seuls le TTY1 et le TIMER soient démasqués et que TTY0 soit masqué ?
Si les 3 IRQ se lèvent au même cycle, quelles seront les valeurs des registresICU_STATE
,ICU_MASK
etICU_HIGHEST
? - Dans quel mode est le processeur quand il traite une IRQ ?
- À quel moment doit-on initialiser le vecteur d'interruption ?
- En quoi consiste la liaison des interruptions (interrupt binding en anglais) ?
- Comment le noyau sait-il que la cause de son invocation est une interruption ?
- Quelle instruction permet de sortir du noyau pour revenir dans le code interrompu ? et que fait-elle précisément ?
- Rappeler la différence entre un registre temporaire et un registre persistant.
- Pour qu'une IRQ soit effectivement prise en compte, il faut que le périphérique la lève et qu'elle ne soit pas masquée. Il y a plusieurs endroits où on peut masquer une IRQ, lesquels ?
- Que fait le processeur lorsqu'il reçoit une IRQ masquée ?
- Que signifie acquitter une IRQ ?
- Qui demande l'acquittement à qui ?
- Comment demande-t-on l'acquittement ?
- Est-ce qu'une IRQ peut se désactiver sans intervention du processeur ?
- Est-ce qu'une IRQ peut ne pas être attendue par le noyau ?
- Quelle est la valeur du champ
XCODE
du registrec0_cause
à l'entrée dans le noyau en cas d'interruption ? - Quelle est la valeur écrite dans le registre
c0_EPC
à l'entrée dans le noyau en cas d'interruption ? - Que se passe-t-il dans le registre
c0_status
à l'entrée dans le noyau en cas d'interruption et quelle est la conséquence ? - La routine
kentry
(entrée du kernel à l'adresse0x80000180
) appelle le gestionnaire d'interruption quand le MIPS reçoit une IRQ non masquée, que fait ce gestionnaire d'interruption ? - À l'entrée dans le noyau,
kentry
analyse le champXCODE
du registre dec0_cause
et si c'est0
alors il saute au code donné ci-après (ce n'est pas exactement le code que vous pouvez voir dans les fichiers sources pour que ce soit plus facile à comprendre).Pourquoi, ne pas sauver les registres persistants ?cause_irq: addiu $29, $29, -23*4 // 23 registers to save (18 tmp regs+HI+LO+$31+EPC+SR) mfc0 $27, $14 // $27 <- EPC (addr of syscall instruction) mfc0 $26, $12 // $26 <- SR (status register) sw $31, 22*4($29) // $31 because, it is lost by jal irq_handler sw $27, 21*4($29) // save EPC (return address of IRQ) sw $26, 20*4($29) // save SR (status register) mtc0 $0, $12 // SR <- kernel-mode without INT (UM=0 ERL=0 EXL=0 IE=0) sw $1, 1*4($29) // save all temporary registers including HI and LO sw $2, 2*4($29) [etc. pour les autres sauvegardes des registres temporaires] jal irq_handler // call the irq handler fontion écrite en C lw $1, 1*4($29) // restore all temporary registers including HI and LO lw $2, 2*4($29) [etc. pour les autres restaurations des registres temporaires] lw $26, 20*4($29) // get old SR lw $27, 21*4($29) // get return address of syscall lw $31, 22*4($29) // restore $31 mtc0 $26, $12 // restore SR mtc0 $27, $14 // restore EPC addiu $29, $29, 23*4 // restore the stack pointer eret // jr C0_EPC AND C0_SR.EXL <= 0
- La fonction
irq_handler()
a pour mission d'appeler la bonne ISR. Dans le code qui suit (extrait du fichierkernel/harch.c
), on voit d'abord la déclaration de la structure qui décrit les registres présents dans l'ICU. En fait c'est un tableau de structures parce qu'il y a autant d'instances d'ICU que de processeurs (donné par NCPUS), ici, il y a un seul processeur MIPS, donc NCPUS=1.La déclarationstruct icu_s { int state; // state of all IRQ signals int mask; // IRQ mask to chose what we need for this ICU int set; // IRQ set --> enable specific IRQs for this ICU int clear; // IRQ clear --> disable specific IRQs for this ICU int highest; // highest pritority IRQ number for this ICU int unused[3]; // these 3 registers are not used }; extern volatile struct icu_s __icu_regs_map[NCPUS]; static int icu_get_highest (int icu) { return __icu_regs_map[icu].highest; } static void icu_set_mask (int icu, int irq) { __icu_regs_map[icu].set = 1 << irq; } void irq_handler (void) { int irq = icu_get_highest (cpuid()); irq_vector_isr[irq] (irq_vector_dev[irq]); }
extern volatile struct icu_s __icu_regs_map[NCPUS];
informe le compilateur que le symbole__icu_regs_map
est défini ailleurs et que c'est un tableau de structures de typestruct icu_s
. Ainsi, le compilateurgcc
sait comment utiliser la variable__icu_regs_map
.
Dans quel fichier est défini__icu_regs_map
?
Que font les fonctionsicu_get_highest()
,icu_set_mask()
etirq_handler()
?
Comment s'appelle le couple de tableauxirq_vector_isr[irq]
etirq_vector_dev[irq]
?
Combien ont-il de cases ? - Si
ICU_HIGHEST
contient 10 (dans le cas de notre plateforme) que doit faire la fonctionirq_handler()
- Que fait la fonction
icu_set_mask (int icu, int irq)
? - Les registres du TIMER sont définis dans le code du noyau de la façon suivante :
Écrivez le code de la fonction
struct timer_s { int value; // timer's counter : +1 each cycle, can be written int mode; // timer's mode : bit 0 = ON/OFF ; bit 1 = IRQ enable int period; // timer's period between two IRQ int resetirq; // address to acknowledge the timer's IRQ }; extern volatile struct timer_s __timer_regs_map[NCPUS];
static void timer_init (int timer, int tick)
qui initialise la période du timer n°timer
avec l'entier nommétick
et active les IRQ si la période donnée est non nulle. - La configuration des périphériques et des interruptions est faite dans la fonction
arch_init()
appelée parkinit()
.
Écrivez les instructions C permettant d'ajouter le TIMER dans le noyau avec un tick de 1000000 (1 million de cycles). Il faut (1) initialiser le timer ; (2) démasquer l'IRQ venant du timer dans l'ICU, elle connectée sur son entrée n°0 ; (3) initialiser le vecteur d'interruption avec la fonctiontimer_isr
pour ce timer0
.
Travaux pratiques
La plateforme
Le but de ce TP est d'analyser, de modifier et d'utiliser le gestionnaire d'interruption.
La plateforme que nous allons utiliser contient :
- un processeur
- une mémoire multisegment pour le code et les données du noyau et de l'utilisateur.
- une ROM pour le boot
- un contrôleur MULTITTY
- un timer
- une icu
Sur cette plateforme, les composants produisant des IRQ sont le timer et les 4 ttys. Ces IRQ sont destinées au processeur, mais elles passent par l'ICU. l'ICU permet de masquer individuellement chaque IRQ et si plusieurs sont levées simultanément alors l'ICU permet de dire quelle est celle prioritaire. La manière dont sont routées les IRQ n'est pas modifiable par logiciel, les IRQ sont des signaux électriques câblés par les architectes. Sur almo1
:
- L'IRQ du timer entre sur l'entrée n°1 de l'ICU.
- Les IRQ de TTY entrent respectivement sur les entrées 3, 4, 5 et 6 de l'ICU.
Question : faire un dessin représentant la plateforme avec les signaux IRQ.
Analyse de code
L'archive du code du tp2 (tp2.tgz) doit être placés à coté du code de tp1 et dézippé.
Pour ce TP, il n'y aura qu'une seule étape parce que j'ai voulu ajouter un minimum de code. En effet, la gestion des IRQ est liée à la gestion des périphériques puisque c'est eux c'est essentiellement eux qui les produise. Or, la gestion des périphériques de faire par le mécanisme des pilotes de périphériques. J'en présenterai une version simplifiée lorsque nous verrons les périphériques initiateurs. Pour l'heure, il s'agit de comprendre juste comment sont traités les IRQ et de jouer un peu avec.
Par rapport à l'étape 6 sur tp1, voici les changements
7_isr/ ├── common │ └── syscalls.h : pas de changement parce qu'il y a pas d'ajout d'appel système ├── kernel │ ├── harch.c : ajout des fonctions d'accès aux nouveaux périphériques, des isr et de la fonction arch_init() │ ├── harch.h : ajout de la déclaration de la fonction arch_init, les autres fonctions de harch.c ne sont pas exportés. │ ├── hcpu.h : pas de changement │ ├── hcpu.S : ajout du gestionnaire d'interruption dans kentry │ ├── kernel.ld : ajout des informations sur les segments des nouveaux périphériques │ ├── kinit.c : ajout de l'appel à arch_init() et acceptation des interruptions │ ├── klibc.c : pas de changement │ ├── klibc.h : pas de changement │ ├── kpanic.c : pas de changement │ ├── kpanic.h : pas de changement │ ├── ksyscalls.c : pas de changement │ └── Makefile : ajout des #defines pour indiquer le nombres de périphériques ajoutés ├── Makefile : changement mais rien d'important ├── uapp │ ├── main.c : pas d'appel à exit() parce qu'actuellement le noyau n'est pas interruptible │ └── Makefile : pas de changement └── ulib ├── crt0.c : pas de changement ├── libc.c : pas de changement ├── libc.h : pas de changement ├── Makefile : pas de changement └── user.ld : pas de changement
Exercice
Je vous propose de modifier un peu le code de harch.c pour gérer l'interruption de sur le TTY1 et faire un petit programme de dessin...
- Vous devez modifier la fonction
arch_init()
pour demander démasquer l'interruption du TTY1 dans l'ICU et programmer le vecteur d'interruption. - Vous allez modifier la fonction
isr_tty()
pour y créer un petit jeu de dessin.- L'idée est de gérer les touches de flèche du clavier et de déplacer le curseur sur la fenêtre.
- Ensuite vous pouvez utiliser les touches U (pour UP) et D pour down pour lever ou baisser un crayon.
- Si vous vous déplacez avec le crayon baissé ça écrit des '*'.
- Vous pouvez ajouter d'autres fonctionnalités si ça vous amuse :-)
Pour déplacer le curseur, nous allons utiliser les commandes d'échappement `VT100` comprises par les fenêtres xterm
.
Vous allez aussi devoir créer un automate d'état fini dans la fonction tty_isr
, c'est assez simple ici.
Je vous donne un début de code que vous pouvez analyser et modifier.
Normalement, on ne fait pas un jeu dans une isr, mais un automate oui, c'est possible.
Quand vous tapez sur une flèche ⟶ le clavier envoie 3 code ascii : le code ESC (27), '[', ’C' L'automate a trois états :
- 0 = on attend n'importe quel caractère
- 1 = on a reçu ESC et on attend '['
- 2 = on attend 'A' ou 'B' ou 'C' ou 'D'
static void tty_isr (int irq) { static int state[NTTYS]; char c = __tty_regs_map[ (irq - 3) % NTTYS].read; switch (irq) { case 3: kprintf (0, "code ascii %d", c); if ((c >= 32) && (c <= 126)) kprintf (0, " = %c", c); kprintf (0, "\n"); break; case 4: switch (c) { case 27 : state[1] = 1; break; case '[' : if (state[1] == 1) state[1] = 2; else { state[1] = 0; } break; case 'B' : if (state[1] == 2) { kprintf (1, "%c[B", 27); } state[1] = 0; break; case 'C' : if (state[1] == 2) { kprintf (1, "%c[C", 27); } state[1] = 0; break; } } }