mardi 16 avril 2013

Introduction au PIC32, partie 4, assembleur

Même si c'est beaucoup plus simple et rapide de programmer en 'C', il faut avoir une connaissance minimale des instructions machine et de l'architecture des processeurs sur lesquels on travaille. Je jette toujours un coup d'oeil sur le listing assembleur généré par le compilateur. Dans cette chronique je présente un bref aperçu des particularités de l'architecture MIPS32. A ce sujet le premier programme que j'ai écris dans la partie 1 de cette série me réservais des surprises. Si vous vous intéressez à la progammation assembleur sur les PIC32 vous devez au mininum télécharger les 3 documents suivants:

Particularités

Il y a 32 registres généraux de 32 bits. Le registre R0 par contre est en lecture seul et rapporte '0' à la lecture.
Le registre R31 est utilisé pour conserver l'adresse de retour lors de l'appel d'une sous-routine. Appel qui se fait par l'instruction JAL Jump and Link. L'adresse de retour n'est pas l'instruction qui suis le JAL mais la deuxième.
Il n'y a pas de pointeur de pile, le registre R29 est utilisé à cet effet par le compilateur gcc. Il n'y a pas d'instructions POP et PUSH pas plus qu'il n'y a d'auto-incrément/décrément du pointeur de pile. La pile est donc gérée entièrement en software. Il n'y a pas d'instruction RET pour sortir d'une sous-routine il faut utiliser un J RA où RA est le registre qui contient l'adresse de retour, habituellement R31. C'est cohérent avec les instructions JAL et JALR.
Même si les registres à l'exception de R0 peuvent-être utilisés à n'importe quel usage, il y a une convention à respecter si vous voulez que votre code soi compatible avec celui généré par le compilateur. A ce sujet consultez la carte quick reference

Saute, mais pas tout de suite!

Dans la première partie après avoir écris le premier programme dans lequel il y a une boucle de délais for(i=0;i<16000000;i++);, j'ai jeté un coup d'oeil au résultat en assembleur et quelque chose m'a laissé plutôt perplexe. Ça ressemblais à une erreur d'assemblage et pourtant le programme était fonctionnel!

Voici le code source en 'C'
Et voici le résultat en assembleur:

Ce qui m'a laissé perplexe c'est la traduction de la boucle for:
Le compilateur a inversé ma boucle,au lieu de compter de 0 à 16 000 000 il compte à rebours. Pas de problème avec ça. Par contre l'instruction à l'adresse 9D000B64 ressemble à un boucle infinie. En effet la première fois que cette instruction est exécutée le compteur V0 est à 15 999 999. Donc l'instruction branche à l'adresse 9D000B64 c'est à dire sur elle-même. Boucle infinie donc. Le mystère est resté entier jusqu'à ce que je me mette à la lecture du MIPS32 architecture for programmers Volume 2: instructions set.

Avant de faire un branchement ou un saut les cores MIPS32 exécutent l'instruction qui suis immédiatement l'instruction J. Donc dans cette exemple le compteur V0 est effectivement décrémenté par l'instruction à l'adresse 9D000B68.

Si vous faites la lecture de code assembleur pour l'architecture MIPS32, vous constaterez que le compilateur place toujours une instruction NOP après l'instruction J. Et pour cause puisque cette instruction est exécutée avant de faire le saut. Pour toute instruction B* (branch) ou J* (jump) il faut tenir compte de l'avis suivant:
Processor operation is UNPREDICTABLE if a branch, jump, ERET, DERET, or WAIT instruction is placed in the delay slot of a branch or jump. Si on place une des instructions mentionnée après le mot UNPREDICTABLE après une instruction de branchement conditiel ou un saut le résultat est imprévisible. NOP...NOP...NOP.

Autre particularité, il y a 2 types d'instructions addition, ADD,ADDU,ADDI et ADDIU et soustraction SUB et SUBU. Notez que dans le code assembleur ci-haut le compilateur a utilisé la version ADDIU pour additionner une constante à un registre. Les instructions qui n'ont pas de 'U' à la fin génèrent une exception lorsqu'il y a un débordement arithmétique et le résultat de l'opération n'est pas inscris dans le registre distination. Si vous n'avez pas besoin de gérer ces exceptions faites comme le compilateur 'C' et utilisez les versions se terminant par un 'U'. Le résultat est exactement le même pour les 2 types d'instructions et les indicateurs booléens Z,N et OV seront ajustés de la même façon. Comme mentionné dans la litérature fournis par MIPS le suffixe 'U' pour unsigned a été mal choisi, ça veut plutôt dire pas d'exception.

Fichier en assembleur dans un projet XC32

Pour des raisons de performances il se peut que certaines parties de votre projet soit en assembleur. Si c'est le cas évitez de farie ceci:

Si vous faites ça MPLABX va créé un fichier avec l'extension .s. Si vous voulez vous simplifier la vie créer un fichier vide (empty file...) et nommez le avec une extension .S. Un 'S' majuscule plutôt que minuscule. Les fichiers avec un 'S' majuscule sont traités par le pré-processeur du compilateur ce qui n'est pas le cas des fichiers avec un 's' minuscule. Le traitement par le pré-processeur 'C' va vous permettre d'utiliser les macros et fichiers d'entête des librairie 'C' sans vous casser la tête. Même si votre projet XC32 ne contient qu'un fichier assembleur .S le même d'initialisation du processeur que celui utilisé dans un projet écris en 'C' sera utilisé. Vous n'aurez donc pas plus d'initialisation à faire que si vous écriviez en 'C'.

exemple de projet

Je ne vous présenterai pas de code exemple personnel puisque Microchip en fourni un exemple que vous pouvez télécharger à partir de cette page.

Aucun commentaire:

Publier un commentaire