Différences entre les architectures ARM du point de vue d'un programmeur C?
Je suis assez nouveau dans la programmation pour ARM. J'ai remarqué qu'il existe plusieurs architectures comme ARMv4, ARMv5, ARMv6, etc. Quelle est la différence entre ces? Ont-ils des ensembles d'instructions ou des comportements différents?
Plus important encore, si je compile du code C pour ARMv6, fonctionnera-t-il sur ARMv5? Qu'en est-il du code ARMv5 fonctionnant sur ARMv6? Ou devrais-je seulement m'inquiéter de la différence si j'écrivais du code d'assemblage du noyau?
5 réponses
Le monde des bras est un peu désordonné.
Pour les programmeurs C, les choses sont simples: toutes les architectures ARM offrent un modèle de programmation À adressage plat 32 bits. Tant que vous restez avec le code source C, la seule différence que vous pouvez voir est sur l'endianness et la performance. La plupart des processeurs ARM (même les anciens modèles) peuvent être à la fois big-endian et little-endian; le choix est alors fait par la carte logique et le système d'exploitation. Bon code C est endian neutre : il compile et fonctionne correctement, quelle que soit la plate-forme endianness (la neutralité endian est bonne pour la fiabilité et la maintenabilité, mais aussi pour les performances: le code non neutre est un code qui accède aux mêmes données via des pointeurs de tailles distinctes, ce qui ravage les règles strictes d'aliasing que le compilateur utilise pour optimiser le code).
La situation est assez différente si l'on considère la compatibilité binaire (c'est-à-dire la réutilisation du code qui a été compilé une fois):
- voilà plusieurs jeux d'instructions:
- le jeu D'instructions ARM d'origine avec un compteur de programme 26 bits (très ancien, très peu susceptible d'être rencontré de nos jours)
- le jeu D'instructions ARM avec un compteur de programme 32 bits (souvent appelé "code ARM")
- le jeu D'instructions Thumb (opcodes simplifiés 16 bits)
- le jeu D'instructions Thumb-2 (pouce avec extensions)
Un processeur donné peut implémenter plusieurs jeux d'instructions. Le plus récent processeur qui sait que le code ARM est le Strongarm, un représentant ARMv4 qui est déjà assez vieux (15 ans). L'ARM7TDMI (architecture ARMv4T) connaît à la fois ARM et Thumb, comme le font presque tous les systèmes ARM suivants, sauf le code Cortex-M. ARM et Thumb peuvent être mélangés ensemble dans la même application, tant que la colle appropriée est insérée là où les conventions changent; cela s'appelle thumb interfworking et peut être géré automatiquement par le compilateur C.
Le Cortex-M0 ne connaît que le pouce instruction. Il connaît quelques extensions, car dans les processeurs ARM "normaux", le système d'exploitation doit utiliser du code ARM (pour gérer les interruptions); ainsi, le Cortex-M0 connaît quelques choses Thumb-for-OS. Cela n'a pas d'importance pour le code d'application.
L'autre Cortex-M ne connaît que Thumb-2. Thumb-2 est principalement rétrocompatible avec Thumb, au moins au niveau de l'assemblage.
- certaines architectures ajoutent des instructions supplémentaires.
Ainsi, si un code est compilé avec un commutateur de compilateur indiquant que c'est pour un ARMv6, alors le compilateur peut utiliser l'une des rares instructions avec L'ARMv6 A mais pas L'ARMv5. C'est une situation courante, rencontrée sur presque toutes les plates-formes: par exemple, si vous compilez du code C sur un PC, avec GCC, en utilisant l'indicateur -march=core2
, le binaire résultant peut ne pas s'exécuter sur un processeur Pentium plus ancien.
- , Il existe plusieurs conventions d'appel.
La convention d'appel est l'ensemble de règles qui spécifient comment les fonctions échangent les paramètres et les valeurs de retour. Le processeur ne connaît que ses registres, et n'a aucune notion de pile. La convention d'appel indique dans quels registres les paramètres vont, et comment ils sont codés (par exemple s'il y a un paramètre char
, Il va dans les 8 bits bas d'un registre, mais l'appelant est-il censé effacer/signer-étendre les 24 bits supérieurs, ou non ?). Il décrit la structure et l'alignement de la pile. Il normalise les conditions d'alignement et le remplissage des champs de structure.
Voilà sont deux conventions principales pour ARM, appelées ATPCS (ancien) et AAPCS (nouveau). Ils sont assez différents sur le sujet des valeurs à virgule flottante. Pour les paramètres entiers, ils sont pour la plupart identiques (mais AAPCS nécessite un alignement de pile plus strict). Bien sûr, les conventions varient en fonction du jeu d'instructions et de la présence d'interfonctionnement du pouce.
Dans certains cas, il est possible d'avoir un code binaire conforme à ATPCS et AAPCS, mais ce n'est pas fiable et il n'y a pas d'avertissement sur incompatibilité. Donc, la ligne de fond est: vous ne pouvez pas avoir une véritable compatibilité binaire entre les systèmes qui utilisent des conventions d'appel distinctes.
- Il existe des coprocesseurs optionnels.
L'architecture ARM peut être étendue avec des éléments optionnels, qui ajoutent leurs propres instructions au jeu d'instructions de base. Le FPU est un coprocesseur optionnel (et il est très rarement rencontré dans la pratique). Un autre coprocesseur est NEON, un jeu D'instructions SIMD trouvé sur certains des plus récents Les processeurs ARM.
Le Code qui utilise un coprocesseur ne fonctionnera pas sur un processeur qui ne comporte pas ce coprocesseur, sauf si le système d'exploitation intercepte les opcodes correspondants et émule le coprocesseur dans le logiciel (c'est plus ou moins ce qui se passe avec les arguments à virgule flottante lors de l'utilisation de la convention D'appel ATPCS, et
Pour résumer, si vous avez du code C, alors recompilez-le. N'essayez pas de réutiliser le code compilé pour une autre architecture ou système.
Pensez à cette chose ARM vs ARM comme un ordinateur Wintel vs un Mac intel. Supposons même que vous ayez la même puce intel (famille) sur les deux ordinateurs, donc des parties de votre code C pourraient être compilées une fois et fonctionner sur les deux processeurs très bien. Où et pourquoi vos programmes varient n'a rien à voir avec le processeur intel, mais tout à voir avec les puces et la carte mère autour d'elle, plus le système d'exploitation dans ce cas.
Avec ARM vs ARM la plupart des différences ne sont pas le noyau mais le logique spécifique au fournisseur qui entoure le noyau. c'est donc une question chargée, si votre code C est une application appelant des appels api standard, alors il devrait compiler sur arm ou intel ou powerpc ou autre. Si votre application commence à parler à des périphériques sur puce ou à bord, quel que soit le type de processeur, une carte, une puce variera et, par conséquent, votre code C doit être écrit pour cette puce ou carte mère. Si vous compilez un binaire pour ARMv6 il peut et aura des instructions considéré comme indéfini sur un ARMv4 et provoquera une exeception. Si vous compilez pour ARMv4, ARMv6 devrait l'exécuter très bien.
Au mieux, si vous êtes dans cet espace d'application, alors ce que vous verrez probablement, ce ne sont que des différences de performance. Certains d'entre eux ont à voir avec votre choix dans les options du compilateur. Et parfois, vous pouvez aider avec votre code. Je recommande d'éviter les divisions et les virgule flottante autant que possible. Je n'aime pas les multiplications mais prendra une multiplication au lieu d'une Division si poussé. x86 nous a gâtés avec des accès non alignés, si vous commencez maintenant avec des e/s alignées, cela vous évitera d'entrer dans d'autres puces qui préfèrent également les accès alignés, et ou vous êtes mordu par les différents systèmes d'exploitation et bootloaders configurez le bras pour réagir, dont aucun n'est ce à quoi vous étiez habitué sur un x86. De même, gardez cette habitude et votre code x86 fonctionnera beaucoup plus rapidement.
Obtenez une copie du bras ARM (google: Arm Architectural Reference Manual, vous pouvez le télécharger gratuitement beaucoup d'endroits, Je ne sais pas ce que le rev actuel est, rev I ou quelque chose peut-être). Parcourez le jeu D'instructions ARM et voyez que la plupart des instructions sont prises en charge sur tous les cœurs, et certaines ont été ajoutées au fil du temps comme divide et byteswap et autres. Vous verrez qu'il n'y a rien à craindre entre les noyaux.
Pensez du point de vue des systèmes, le Wintel vs le Mac intel. ARM ne fait pas de puces, ils font et les noyaux de licence. La plupart des fournisseurs qui utilisent un bras dans leur puce ont leur propre sauce spéciale autour d'elle. Il est donc comme le Wintel vs le mac avec le même processeur au milieu, mais complètement différent quand il s'agit de toutes les choses que le processeur touche et doit utiliser. Il ne s'arrête pas avec le noyau ARM, ARM Vend des périphériques, des unités à virgule flottante, des caches, etc. Donc, peu ou pas ARMv4s sont les mêmes par exemple. Si votre code touche les différences, vous aurez des problèmes si ce n'est pas le cas.
Pour les parties de bras de la puce en plus du bras de bras là sont des crt (manuels de référence technique). mais si vous obtenez le mauvais trm pour le composant que vous utilisez, il peut vous donner des maux de tête. Le TRM peut avoir des descriptions de registre et d'autres choses telles que le bras de bras ne marche pas, mais si vous vivez dans l'espace d'application, vous n'aurez probablement besoin d'aucun d'entre eux, ni du bras de bras. Le bras de bras est bon à des fins éducatives si rien d'autre. Comprendre pourquoi vous pourriez ne pas vouloir diviser ou utiliser des accès non alignés.
ARM its self est assez compatible, à condition de respecter le code utilisateur (le code du noyau est bien sûr différent). Dans un environnement de système d'exploitation hébergé, vous vous en tiendrez probablement à ARMv5 (processeurs ARM926).
La grande différence vient de:
- Le comportement du Cache est très différent. Cache sur certains bras est même pratiquement adressée, ce qui peut rendre les commutateurs de processus douloureux.
- le FPU est disponible en plusieurs saveurs (VFP, néon, et plus encore!). Beaucoup de petits processeurs n'ont même pas un FPU.
- Le mode Thumb a radicalement changé. Le mode Thumb entre ARMv5 n'est pas portable Pour Thumb2 (ARMv6+), ni rétrocompatible.
Si la différence est vraiment importante pour vous, vous devriez être capable de la comprendre à partir de la documentation publique D'ARM.
Mais le but de l'écriture dans un langage de plus haut niveau (même si c'est seulement aussi "élevé" que C) est de Ne pas s'inquiéter à ce sujet . Tout ce que vous faites est recompiler . Même dans le noyau, il ne faut pas vraiment écrire beaucoup en assembly; et quand vous devez écrire quelque chose en assembly (c'est-à-dire pas seulement pour obtenir des performances maximales) ,c'est généralement en raison de plus que le simple choix du processeur (par exemple, ce qui a été directement mappé en mémoire où?).
Liste très rapide et sale des zones à vérifier lors du portage entre architectures en général:
- Endianness : Utilisation de l'union, casting de types de données, champs de bits, partage de données
- Alignement : exigences d'alignement mais aussi caractéristiques de performance d'un accès non aligné possible
- modèle de Mémoire: faible vs forte?
- Multi-core : Comment fonctionne la cohérence?
- Divers : types de données signés et non signés, emballage de structure de données, utilisation de pile, type de données d'enum...