Je vois plusieurs problèmes potentiels avec ces sections critiques. Il y a des mises en garde et des solutions à tout cela, mais en résumé:
- Rien n'empêche le compilateur de déplacer du code à travers ces macros, pour une optimisation ou pour d'autres raisons aléatoires.
- Ils enregistrent et restaurent certaines parties de l'état du processeur que le compilateur s'attend à ce que l'assemblage en ligne laisse seul (sauf indication contraire).
- Rien n'empêche une interruption de se produire au milieu de la séquence et de changer l'état entre sa lecture et son écriture.
Tout d'abord, vous avez certainement besoin de barrières de mémoire pour le compilateur . GCC les implémente en tant que clobbers . Fondamentalement, c'est une façon de dire au compilateur "Non, vous ne pouvez pas déplacer les accès à la mémoire à travers ce morceau d'assemblage en ligne car cela pourrait affecter le résultat des accès à la mémoire." Plus précisément, vous avez besoin des deux "memory"
et des "cc"
clobbers, sur les macros de début et de fin. Celles-ci empêcheront également d'autres choses (comme les appels de fonction) d'être réorganisées par rapport à l'assembly en ligne, car le compilateur sait qu'elles peuvent avoir des accès à la mémoire. J'ai vu GCC pour ARM tenir l'état dans les registres de code de condition à travers l'assemblage en ligne avec des "memory"
clobbers, donc vous avez vraiment besoin du "cc"
clobber.
Deuxièmement, ces sections critiques enregistrent et restaurent beaucoup plus que la simple activation des interruptions. Plus précisément, ils enregistrent et restaurent la plupart du CPSR (Current Program Status Register) (le lien est pour Cortex-R4 parce que je n'ai pas pu trouver un joli diagramme pour un A9, mais il devrait être identique). Il y a des restrictions subtiles autour desquelles des morceaux d'état peuvent réellement être modifiés, mais c'est plus que nécessaire ici.
Entre autres choses, cela inclut les codes de condition (où les résultats d'instructions comme cmp
sont stockés afin que les instructions conditionnelles suivantes puissent agir sur le résultat). Le compilateur sera certainement confus par cela. Ceci est facilement résoluble en utilisant le "cc"
clobber comme mentionné ci-dessus. Cependant, cela fera échouer le code à chaque fois, donc cela ne ressemble pas à ce que vous rencontrez des problèmes. Un peu comme une bombe à retardement, dans la mesure où la modification d'un autre code aléatoire pourrait amener le compilateur à faire quelque chose d'un peu différent qui sera rompu par cela.
Cela tentera également de sauvegarder / restaurer les bits informatiques, qui sont utilisés pour implémenter l'exécution conditionnelle Thumb . Notez que si vous n'exécutez jamais de code Thumb, cela n'a pas d'importance. Je n'ai jamais compris comment l'assemblage en ligne de GCC traite les bits informatiques, à part le conclure, ce qui signifie que le compilateur ne doit jamais mettre l'assemblage en ligne dans un bloc informatique et s'attend toujours à ce que l'assemblage se termine en dehors d'un bloc informatique. Je n'ai jamais vu GCC générer de code violant ces hypothèses, et j'ai fait un assemblage en ligne assez complexe avec une optimisation lourde, donc je suis raisonnablement sûr qu'ils tiennent. Cela signifie qu'il n'essaiera probablement pas de changer les bits informatiques, auquel cas tout va bien. Tenter de modifier ces bits est classé comme "imprévisible sur le plan architectural", il pourrait donc faire toutes sortes de mauvaises choses, mais ne ferait probablement rien du tout.
La dernière catégorie de bits qui sera enregistrée / restaurée (en plus de ceux qui désactivent réellement les interruptions) sont les bits de mode. Celles-ci ne changeront probablement pas, donc cela n'aura probablement pas d'importance, mais si vous avez du code qui modifie délibérément les modes, ces sections d'interruption pourraient causer des problèmes. Passer du mode privilégié au mode utilisateur est le seul cas de ce genre auquel je m'attendrais.
Troisièmement, il n'y a rien qui empêche une interruption de changer d' autres parties de CPSR entre le MRS
et MSR
dans ARM_INT_LOCK
. Ces modifications pourraient être remplacées. Dans la plupart des systèmes raisonnables, les interruptions asynchrones ne modifient pas l'état du code qu'elles interrompent (y compris CPSR). S'ils le font, il devient très difficile de raisonner sur ce que le code fera. Cependant, cela est possible (le changement du bit de désactivation FIQ me semble le plus probable), vous devez donc vous demander si votre système le fait.
Voici comment je les implémenterais de manière à résoudre tous les problèmes potentiels que j'ai signalés:
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
Assurez-vous de compiler avec -mcpu=cortex-a9
car au moins certaines versions de GCC (comme la mienne) utilisent par défaut un ancien processeur ARM qui ne prend pas en charge cpsie
et cpsid
.
J'ai utilisé ands
au lieu de juste and
en ARM_INT_LOCK
donc c'est une instruction 16 bits si elle est utilisée dans le code Thumb. Le "cc"
clobber est de toute façon nécessaire, c'est donc strictement un avantage de performance / taille de code.
0
et 1
sont des étiquettes locales , pour référence.
Ceux-ci devraient être utilisables de la même manière que vos versions. Le ARM_INT_LOCK
est aussi rapide / petit que votre original. Malheureusement, je n'ai pas pu trouver un moyen de le faire en ARM_INT_UNLOCK
toute sécurité en autant d'instructions.
Si votre système a des contraintes sur le moment où les IRQ et les FIQ sont désactivées, cela pourrait être simplifié. Par exemple, s'ils sont toujours désactivés ensemble, vous pouvez combiner en un cbz
+ cpsie if
comme ceci:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
Alternativement, si vous ne vous souciez pas du tout des FIQ, il est similaire de simplement laisser complètement les activer / désactiver.
Si vous savez que rien d'autre ne change jamais les autres bits d'état du CPSR entre le verrouillage et le déverrouillage, vous pouvez également utiliser continuer avec quelque chose de très similaire à votre code d'origine, sauf avec les deux "memory"
et les "cc"
clobbers dans les deux ARM_INT_LOCK
etARM_INT_UNLOCK