Bon ! il faut bien un jour se lancer dans la
compréhension et l’exploitation des interruptions. Cela n’a pas été facile mais
le projet que je vais vous présenter fonctionne correctement sur mon raspberry(mais
j’ai encore de nombreuses interrogations non résolues à ce jour). Nous allons
nous servir du timer ARM du BCM2835 (attention il y a plusieurs timers et nous
utiliserons celui de la page 196 de la doc du BCM2835) pour générer une
interruption à intervalle régulier (environ 3s) pour allumer et éteindre la led
activité suivant le même principe que le chapitre précédent.
La théorie est simple !! : le timer génère toutes
les 3 secondes une interruption qui sera gérée par un contrôleur
d’interruption puis prise en compte par le processeur qui changera de mode de
fonctionnement et appellera la bonne routine de traitement de l’interruption.
Donc nous aurons un module qui contiendra les routines du
timer, un autre celles du contrôleur d’interruption (IC) un autre la gestion
des adresses à appeler suivant le type et le mode (appelé vecteur des
interruptions) et bien sûr les modules boot.s et kernel.s comme dans les
exemples précédents. Tout le projet est ici.
Dans le module timer, nous trouvons une fonction
d’initialisation du timer qui fixe la fréquence, et qui autorise l’envoi d’une
interruption. J’ai eu du mal à calculer correctement la fréquence en effet il
faut diviser la fréquence de base de l’horloge soit 250 000 000 hertz
par le facteur de division +1 (ici 7F
soit 127 + 1 = 128) ce qui donne une fréquence de 1 953 125hz
qu’il faut diviser par 256 (indiqué par le code 10 se trouvant sur les bits 2et
3 du registre de contrôle du timer) ce qui donne 7629 hz. Le compteur à une
valeur de 22887 ce qui donne un temps de 22887/7629 = 3 secondes
Nous trouvons aussi une fonction de test de l’interruption
et de sa remise à zéro.
Dans le module du Contrôleur d’Interruption (IC page 109 doc
BCM2835) nous trouvons des fonctions simples d’initialisation, d’activation, de
recherche de l’identification de l’interruption et de la fin de l’interruption.
Voyons la partie la plus compliquée : la mise en place
du vecteur d’interruption module interruptions.s
Le processeur ARM peut fonctionner dans plusieurs
modes : le mode user que nous avons utilisé par défaut dans tous les
premiers chapitres de ce blog, le mode superviseur que nous utilisons depuis
que nous développons sans OS, puis les modes abort, indéfini, irq et fiq. Pour
ces derniers modes, le processeur y passera en fonction du type d’interruptions
reçues : instruction non autorisé, problème mémoire, interruptions générés
par les périphériques (irq=normale, Fiq= rapide ). Notre timer génère une
interruption de type irq.
Pour chaque mode, nous devons prévoir la procédure à appeler
lors d’une interruption. Pour cela le processeur se branche sur les adresses en
début de la mémoire. Dans notre module, nous commençons donc par recopier une
table des adresses des procédures pour tous les modes. Bien sûr pour les modes
autre que irq, nous mettons le minimum d’instructions à exécuter (quoiqu’il
vaudrait mieux envoyer un message pour indiquer ce qui se passe !!!).
Pour écrire ce module, je me suis servi d’un exemple trouvé
dans un livre et cela commence par une instruction que je n’avais jamais
vue : mcr p15,0,r0,c12,c0,0 !!!!
Dans la documentation ARM, on trouve d’autres instructions de ce
type !! qui servent à piloter des fonctions très particulières du
processeur. J’ai mis cette instruction en commentaire car elle est peut être
utile pour d’autres processeurs. Ensuite nous trouvons la recopie à l’adresse zéro de la mémoire de la table des adresses d’interruptions puis pour chaque
mode la procédure à exécuter. Vous remarquerez qu’il y a une procédure reset qui appellera
notre première routine _start.
Vous remarquerez qu’à la place de push et pop, nous
utilisons les instructions stmfd et ldmfd qui font la même chose. Remarquez que
les adresses de retour au programme appelant sont différentes suivant le mode.
Enfin, nous ne complétons que la fonction irq_handler en
appelant les fonctions du contrôleur d’interruptions ou du timer pour gérer l’interruption.
Ce module sera appelé dans le module boot_s car le vecteur
d’interruptions doit être chargé au plus tôt.
Ensuite il nous reste à modifier le module kernel.s pour
modifier l’allumage et l’extinction de la led ; Pour cela on utilise
l’instruction mcr p15,0,r0,c7,c0,4 qui met le processeur en attente d’une
interruption. Hélas, je me suis aperçu que cette instruction ne fonctionne
qu’en mode superviseur. Si on veut utiliser le mode user, il faudra modifier la
partie allumage extinction pour être appelée de la fonction irq_handler ou de
la fonction test_interruption_timer. Je vais essayer cela dans le prochain
post.
La mise au point de tout cela a été laborieuse car en cas de
non fonctionnement, il est pas facile de savoir quelle partie est en
cause : est ce le timer qui ne génère pas l’interruption ou le contrôleur
qui ne la pas autorisé ou le handler qui fonctionne mal ? Donc prudence lors des modifications. De plus
lors de mes premiers tests, j’ai réussi à planter mon portable sous windows10
(soit 10 minutes au moins pour redémarrer !! ) malgré la liaison série
entre les deux. Je pense que la procédure mini_uart d’envoi de caractère ne
vérifiant pas la validité des caractères, j’ai du envoyer des cochonneries qui
ont fait planter window10 !!!
Mes explications sur le sujet des interruptions étant simplistes, je vous
renvoie à toute la documentation sur internet traitant des interruptions des
processeurs ARM
Aucun commentaire:
Enregistrer un commentaire