12
votes

Simulation LDREX / STREX (CHARGEMENT / MAGASIN EXCLUSIF) À CORTEX-M0

Dans l'ensemble d'instructions Cortex-M3, il existe une famille d'instructions LDREX / STREX telles que si un emplacement est lu avec une instruction LDREX, une instruction STREX suivante ne peut écrire à cette adresse que si l'adresse est connue pour avoir été intacte. En règle générale, l'effet est que le STREX réussira si aucune interruption ("exceptions" dans le paron de bras) s'est produite depuis le ldrex, mais échoue autrement.

Quel est le moyen le plus pratique de simuler un tel comportement dans le cortex M0? Je voudrais écrire C code pour le M3 et l'avoir portable au M0. Sur le M3, on peut dire quelque chose comme: xxx

pour effectuer un incrément atomique. Les seules façons que je puisse penser pour réaliser des fonctionnalités similaires sur le cortex-m0 serait de:

  1. avoir "LDRX" désactiver des exceptions et avoir "STREX" et "CLREX" les réactivent, avec l'exigence que chaque "ldrex" doit être suivi prochainement par un "STREX" ou "CLREX".
  2. avez "LDRX", "STREX" et "CLREX" Soyez une très petite routine en RAM, avec une instruction de "LDRX" étant patchée à "STR R1, [R2]" ou "MOV R0, # 1 ". Demandez à la routine «LDREX» branchez une instruction «STOR» dans la routine «STREX» et avons le «CLREX» Bouchon de routine «MOV R0, n ° 1». Dont toutes des exceptions qui pourraient invalider une séquence "LDREX" appel "CLREX".

    Selon la manière dont les fonctions LDREX / STREX sont utilisées, la désactivation des interruptions pourrait fonctionner raisonnablement, mais il semble que Icky modifie la sémantique de "exclusivité de charge" afin de causer de mauvais effets secondaires Si c'est abandonné. L'idée de correction de code semble être comme si elle réaliserait la sémantique désirée, mais il semble maladroit.

    (BTW, question latérale: Je me demande pourquoi Strex sur le M3 stocke le succès / l'indication de défaillance à un registre plutôt que Il suffit de définir un drapeau? Son opération réelle nécessite quatre bits supplémentaires dans l'opcode, nécessite qu'un registre soit disponible pour contenir l'indication de réussite / défaillance et nécessite qu'un "CMP R0, # 0" soit utilisé pour déterminer s'il réussissait. Il s'attendait à ce que les compilateurs ne puissent pas gérer une intrinsèque Strex Sensatiblement si elles n'ont pas obtenu le résultat dans un registre? Obtenir un registre prend deux instructions courtes.)


0 commentaires

3 Réponses :


6
votes

Eh bien ... vous avez toujours SWP restant, mais c'est une instruction atomique moins puissante.

Interruption La désactivation est sûre de fonctionner cependant. : -)

EDIT:

Non SWP sur -M0, désolé Supercat.

OK, semble que vous n'êtes laissé qu'avec une interruption de désactivation. Vous pouvez utiliser GCC-compilable InLine ASM comme guide pour désactiver et restaurer correctement: http: // repo.or.cz/w/cbaos.git/blob/head:/arch/arm-cortex-m0/include/lock.h


2 commentaires

Où est SWP documenté pour le cortex M0? En ce qui concerne les interruptions désactivées, existe-t-il une bonne voie dans C pour enregistrer / restaurer le drapeau d'activation d'interruption, de sorte qu'une séquence LDREX / STREX effectuée avec des interruptions désactivées laisserait des interruptions handicapées?


Bien sûr, vous pouvez pas le faire dans 'c'; Mais l'assembleur en ligne est possible. ASM ("MRS% 0, CPSR \ N Orr% 1,% 0, # 128 \ n msr cpsr_c,% 1 \ n": "= r" (old), "= r" (nouveau): "Mémoire", "CC"); . Vous devez être en mode pour le permettre. Si vous avez le cpsid , il peut être plus facile. Je ne sais pas trop sur le M0.



-2
votes

STREX / LDREX concerne les processeurs multicœurs accédant à des articles partagés dans la mémoire partagée à travers les cœurs. Le bras a fait un travail inhabituellement difficile de documenter que vous devez lire entre les lignes de l'AMBA / Axi et des Documents du bras et de la TRM pour comprendre cela.

Comment cela fonctionne est si vous avez un noyau qui prend en charge STREX / LDRX et si vous avez un contrôleur de mémoire prenant en charge l'accès exclusif, alors si le contrôleur de mémoire voit la paire d'opérations exclusives sans autre coeur accessible à la mémoire entre vous. retour ex_okay plutôt que d'accord. Les documents du bras indiquent aux concepteurs de la puce s'il s'agit d'uniprocesseur (ne mettant pas la mise en œuvre de la fonctionnalité Multicore), alors vous n'avez pas à prendre en charge EXOKAY, il suffit de revenir d'accord, qui, à partir d'une perspective de logiciel, la paire LDREX / STREX permet d'accéder à cette logique (le logiciel). Spins dans une boucle infinie car il ne reviendra jamais de succès), le cache L1 soutient cependant que cela semble que cela fonctionne.

Pour uniprocesseur et pour les cas où vous n'avez pas accès à la mémoire partagée à travers les noyaux, utilisez SWP.

Le -M0 ne prend pas en charge LDREX / STREX ni SWP, mais que sont ceux qui vous obtiennent essentiellement? Ils vous obtiennent simplement un accès qui n'est pas affecté par vous faire un accès. Pour vous empêcher de piétinez vous-même, il suffit de désactiver les interruptions pendant la durée, la façon dont nous avons effectué des accès atomiques depuis les âges sombres. Si vous souhaitez une protection contre vous et un périphérique si vous avez un périphérique qui peut interférer, il n'ya aucun moyen de contourner cela et même un échange peut ne pas avoir aidé.

alors il suffit de désactiver les interruptions autour de la section critique.


11 commentaires

Depuis l'écriture de la question et avoir à traiter avec le M0, j'ai pris pour sauver l'état d'interruption, désactiver les interruptions, faire l'action souhaitée et restaurer l'état d'interruption, mais le LDREX / STREX fonctionne bien sur le M3; Il dit essentiellement "Effectuer le magasin si aucune interruption n'est arrivée depuis la charge". Sauf s'il y a tellement d'interruptions que la boucle LDREX / STREX échouerait à plusieurs reprises, je pense que c'est moins cher de dire: Réessayer: LDREX R1, [R0] / Ajouter R1, R1, N ° 1 / Strex R2, R1, [R0 ] / CMP R2, # 0 / boucle réessaye que ...


faire Mme R2, primask / CPSID I / LDR R1, [R0] / Ajouter R1, R1, # 1 / STR R1, [R0] / MSR Primask, R2 , bien que j'avouerai je Je ne sais pas vraiment comment les horaires fonctionnent. Lorsque j'avais initialement écrit la question, je pensais qu'il était nécessaire de gérer l'état d'interruption en utilisant l'approche beaucoup plus compliquée employée par les bibliothèques EFM32; Le LDREX / STREX est une amélioration massive sur l'utilisation des routines EFM32.


Geez, je n'ai même pas regardé la date, je pensais que c'était une nouvelle question, désolé ... il est toujours très étrange pour moi que le M3, Armv7m est sorti en premier et le M0 Armv6m est sorti plus tard. Je ne sais pas si le bras vient de les offrir dans cet ordre ou de trouver un vendeur à puce pour les utiliser, c'est pourquoi on est sorti avant l'autre, il a certainement confondé beaucoup de gens qui ont sauté sur les extensions de pouce seulement pour découvrir que l'ARMV6 n'a eu que quelques-uns et armv7 avaient cent cinquante environ.


Le cortex-m0 est censé être moins cher et moins cher que le M3; Je suis en quelque sorte aimé le Arm7-TDMI, ce qui est ce que je me suis coupé les dents. Le M3 est presque aussi bon que le mode d'instruction 32 bits d'Arm7-TDMI, mais le mode d'instruction 32 bits pourrait faire quelques choses que le M3 ne peut pas (par exemple, je pense que j'ai fait quelque chose comme ldrh r0, [r10 ], # 2 / ldrb r1, [PC, R0 LSR # 12] / Ajouter un PC, PC, R0 ASL # 2 Pour passer à l'une des 16 routines avec une zone de 1 km basée sur les quatre bits supérieurs d'une récupération Word 16 bits). Le changement de vitesse dans ldrb est inhabituel, mais était pratique; L'ensemble de l'instruction Thumb2 manque cette option, cependant.


BTW, si je me souviens bien, la séquence a exploité le fait que lors de l'exécution de l'instruction "LDRB", PC lue comme l'emplacement actuel plus 4, de sorte que la table de saut a démarré immédiatement après l'instruction Ajouter un PC . C'était dans le code qui devait observer une valeur sur le bus d'adresse d'un autre processeur et répondre avec des données dans le délai de cycle de cet autre processeur. Je ne voulais donc gaspiller de cycles inutilement.


Je suis d'accord, je suppose que le M3 est un remplacement des microcontrôleurs basés sur Arm7, le -M0 est là pour pousser les bords de puissance et de taille. juste d'avis aucune preuve.


Pourriez-vous regarder meta.stackexchange.com/questions/185837/... et donner quelques commentaires sur Cortex-M3, Cortex-M0, etc. J'ai proposé que nous devrions simplement vous en débarrasser et utiliser uniquement Cortex-M; Mais si vous avez un bon argument autrement?


Notez qu'il existe des microcontrôleurs multicœurs avec Cortex-M0, par ex. LPC43XX ou LPC541XX avec un cortex-m4 et un ou deux cortex-m0. Une option d'avoir LDRex et des amis pour de tels coprocesseurs seraient très utiles, il sera difficile de les remplacer par quelque chose en utilisant uniquement des accès à la mémoire ordinaire.


Ce serait, mais le bras n'a pas nécessairement conçu pour celui qui est une chose spécifique au fournisseur, pour laquelle ils pourraient mettre en œuvre quelque chose, mais cela ne serait pas une nouvelle instruction ...


Désolé, c'est un problème clair: Strex / LDRX concerne les processeurs multicœurs - il suffit de lire le manuel de référence M3 et M4. ldrex et Strex conviennent parfaitement à la mise en œuvre d'opérations atomiques non bloquantes sur le M3 et M4.


@Venemo a accepté. Strex / LDRX sont beaucoup plus efficaces que SWP, même sur un seul noyau, également (c'est pourquoi SWP ne fait pas partie du jeu d'instructions Thumb-2).



3
votes

Le cortex-m3 a été conçu pour une latence laisse-latence faible et un multitâche à faible gigue, c'est-à-dire que le contrôleur d'interruption coopère avec le noyau afin de conserver des garanties sur le nombre de cycles, car l'interruption de la tricage pour interrompre la manipulation. Le LDREX / STREX a été mis en œuvre comme moyen de coopérer avec tout cela (de tout ce que je veux dire interrompre le masquage et d'autres détails tels que le réglage de bits atomiques via des alias de bande de bandes de bit), comme autrement, une seule noyau, non-MMU, non-cache μC μC aurait peu utilisé pour cela. Si cela ne l'a pas impliqué cependant, une tâche de priorité faible devra contenir une serrure et pourrait générer des inversions de petites priorités, avec la latence et la gigue qu'un système de temps réel difficile ne peut pas faire face, du moins pas dans l'ordre de La magnitude autorisée par la sémantique "Réessayer" qu'un LDREX / STREX échoué a.

sur une note latérale, et parlant strictement en termes de timings et de gigue, le cortex-m0 a un profil de chronométrage d'interruption plus traditionnel (c'est-à-dire qu'il ne sera pas Abandonner des instructions sur le noyau lorsqu'une interruption arrive), être sujette à bien plus de gigue et de latence. Sur cette question (à nouveau, strictement chronométrage), il est plus comparable aux modèles plus anciens (c.-à-d. L'ARM7TDMI), qui manque également de chargement atomique / de stockage / de stockage ainsi que des incréments atomiques et des décréments atomiques et d'autres instructions de coopération à faible latence, nécessitant une interruption désactivée / Activer plus souvent. P>

J'utilise quelque chose comme ceci à Cortex-M3: P>

static inline int atomic_CAS(volatile void *addr, int32_t expected,
        int32_t store) {
  int ret = 1;

   __interrupt_disable();
   if (*(volatile uint32_t *)addr) == expected) {
      *addr = store;
      ret = 0;
   }
   __interrupt_enable();
   return ret;
}


3 commentaires

Je suis surpris que vous décriviez le modèle d'interruption pour M0 aussi plus proche de l'armv4t que d'ARMV7-M. Ma vue simpliste est que ARMV6-M est un sous-ensemble d'ARMV7-M (fabrication de code M0 compatible avec M3).


Le diable est dans les détails. Le M0 est venu après la M3, avec la promesse de marketing que vous pouvez compiler du code pour cela et fonctionner sur le M3. C'est presque vrai: le code de bibliothèque de mode utilisateur générique est compatible; Mais vous avez besoin d'un HAL pour faire de tout autre chose utilisable, IIRC Même l'interruption de désactivation / active n'est pas compatible binaire.


Ne change pas le fait que M0 a le même modèle d'exception, l'empilement de comportement que M3, pas l'approche du registre bancaire.