Back | << | Index | >> |
L’architecture ARM Cortex est une architecture brevetée, fabriquée par différents fondeurs ( ST, NXP, TI, … ).
Les modèles proposés s’adaptent aux applications en fonction des performances exigées en temps de calcul, consommation, prix, supports pour OS.
Exemple d’Application
Plusieurs solutions sont possibles, je propose d’utiliser l’environnement de développement proposé par ST : STM32CubeIDE
Nous retrouvons en vert les registres de mu0-risc :
L’architecture ARM-cortex comporte 13 registres de calcul ( R0-R12 )
L’objectif de cette partie n’est pas de passer en revue tous les instruction
de l’architecture ARM Cortex, mais simplement d’en voir quelques unes pour coder
des algorithmes simples.
L’assembleur reste uniquement un moyen pour bien comprendre les mécanismes dans un microprocesseur.
Nous passerons par la suite au langage C.
Toutes les instructions de calcul se font entre registres ( caractéristique d’une architecture RISC).
Il est possible dans une instruction d’indiquer une
valeur immédiate ( en utilisant # )
Cette valeur doit tenir sur 8 bits ( ou pouvoir s’exprimer avec un décalage )
Reprenons les instructions évoquées dans la partie précédente, avec quelques suppléments ou variantes.
mov r1, #5 // r1 <- 5
mov r1, r2 // r1 <- R2
add r1,r2,#1 // r1 <- r2+1
add r1,r2,r3 // r1 <- r2+r3
sub r1,r2,#1 // r1 <- r2-1
sub r1,r2,r3 // r1 <- r2-r3
mul r1,r2,r3 // r1 <- r2*r3
and r1,r2,r3 // r1 <- r2 and r3
orr r1,r2,r3 // r1 <- r2 or r3
REMARQUE :
Par défaut l’exécution d’une instruction ne modifie pas les bits d’état NZVC.
le suffixe s permet de modifier les bits d’état NZVC à l’issue d’un calcul :
adds r1,r2,#1 // r1 <- r2+1 ; modification(NZVC) ;
Lecture d’une donnée de 32 bits en mémoire
ldr r0,=val // r0 <- adr(val)
ldr r1, [r0] // r1 <- [r0]
Nous avons toujours le problème qu’une adresse fait 32 bits, et une instruction est codée sur 32 bits.
Je ne peux donc pas indiquer dans une même instruction le nom de l’instruction,
un numéro de registre et une adresse mémoire de 32 bit__s.
La pseudo instruction __ldr r0,=val est remplacé à la compilation
par ldr r0,[PC+offset].
Le compilateur prendra soin lors de la compilation de placer l’adresse de
la variable val à une case mémoire située à offset instructions de ldr r0,[PC+offset].
Lecture d’une donnée de 8 bits en mémoire
Comme chaque octet possède une adresse, l’instruction ldrb permet de rapatrier 8 bits dans un registre de 32 bits.
L’extension de format se fait par défaut en considérant des nombres non signés ( ajout de zéros sur les poids forts )
ldr r0,=val // r0 <- adr(val)
ldrb r1, [r0] // r1 <- [r0]
Ecriture d’une donnée de 32 bits en mémoire
ldr r0,=val
str r1, [r0]
Ecriture d’une donnée de 8 bits en mémoire
ldr r0,=val
strb r1, [r0]
Post Incrémentation
Lors du parcours d’un tableau, au lieu d’utiliser l’instruction add pour faire évoluer la valeur du pointeur, il est possible décrire :
ldr r1,[r0],#4 // r1 <- [r0] ; r0 <- r0+4
b loop // PC <- loop : Saut inconditionnel, dans tous les cas on va à l’adresse loop
Une séquence If then else ou une boucle conditionnelle ( for / while ) en langage C se traduit par un saut conditionnel en Assembleur.
REMARQUE : l’instruction cmp r0, r1 permet de faire la modification des bits NZVC pour une soustraction r0-r1 ( le résultat n’est pas récupéré )
beq loop // PC <- loop si résultat précédent égal à 0 ( on regarde NZVC pour cela )
bne loop // PC <- loop si résultat précédent différent de 0 ( Not Equal )
conditions de saut :
PROJET SOURCE
Pour tester les différents exemples ci-dessous, il faudra modifier le fichier makefile
L’ IDE STM32CUBE comporte :
STM32CUBE est basé sur le logiciel ECLIPSE
Le fichier Makefile donne les conditions de compilation et d’édition de liens.
Reprenons le programme faisant la somme des éléments d’un tableau afin de le tester :
On distingue le segment “.data” et ‘.text” :
Lorsque je compile mon programme ( raccourcis CTRL+B, j’exécute mon makefile.
Le makefile fait référence au compilateur arm-none-eabi-gcc pour transformer des fichiers C/C++ ou Assembleur en
fichiers objets binaires .o
L’éditeur de lien arm-none-eabi-ld permet de réaliser le placement mémoire de ces fichiers objet en tenant compte du
fichier de configuration stm32f411re_flash.lds ( ce fichier indique dans quelle zone mémoire il faut placer les différents segments ( code, datas )).
L’exécutable résultant main.elf peut alors être transmis à la cible.
extrait de stm32f411re_flash.lds :
A l’issue d’une mise sous tension ou d’un reset, PC reçoit l’instruction d’adresse Reset_Handler
On peut noter la boucle pour copier les données d’initialisation de la flash vers la RAM.
La fonction SystemInit() permet de configurer l’horloge du microcontrôleur.
Après avoir compilé et donc généré un exécutable .elf ( à condition qu’il n’y ait pas eu d’erreur de compilation ), nous pouvons charger et debugger notre programme.
La configuration de debug se trouve dans Run –> Debug Configurations ; nous y faisons référence à main.elf.
Accès direct :
Pour placer un point d’arrêt, double cliquer dans la marge ( ou appuyer sur SHIFT+CTRL+B )
Pour aller directement au point d’arrêt, appuyer sur F8 (Resume).
Mode pas à pas pour analyser l’exécution de chaque instruction :
Dans l’environnement de Debug, les registres sont visibles dans la fenêtre Registers ( Window -> Show View -> Registers )
Pour observer la mémoire, utiliser la fenere Memory Browser :
Dans le makefile, effectuer la modification suivante :
ASRC += src/main_calc_2.s ### A MODIFIER ###
Lors de l’étude du langage C, nous avons évoqué l’intérêt du découpage d’un programme en fonctions ( rangement et réutilisation ).
Une fonction reste un bout de code situé à une certaine adresse.
Pour y accéder je dois donc effectuer un saut.
L’instruction b pourrait être utilisée, mais après exécution de la fonction, il faut retourner à l’instruction suivant l’appel de la fonction.
Je doit donc mémoriser l’adresse de retour.
L’instruction bl permet donc de :
Par convention, les paramètres des fonctions sont placés dans R0 et R1 ( puis R2, R3 si nécéssaire ).
Le paramètre de retour de fonction est placé dans R0.
Dans le makefile, effectuer la modification suivante :
ASRC += src/main_calc_3.s ### A MODIFIER ###
Considérons la fonction average qui fait appel à notre fonction sum_tab précédente :
Etant donné que mon 2ème bl a pour effet d’écraser le registre LR contenant le retour à la fonction main, je reste coincé dans la fonction average.
Dans le makefile, effectuer la modification suivante :
ASRC += src/main_calc_4.s ### A MODIFIER ###
Afin de sauvegarder le registre LR en cas d’appels de fonctions imbriqués, nous utilisons un espace en mémoire
appelé la pile. Pour se repérer dans cette zone mémoire, nous utilisons le registre pointeur de pile SP (stack pointer).
En plus du registre LR, il est possible de sauvegarder des registres généraux afin de conserver le contexte de la fonction appelante.
Dans le makefile, effectuer la modification suivante :
SRC += src/main_5.c
ASRC += src/calc_5.s ### A MODIFIER ###
Par la suite nous utiliserons uniquement le langage C.
Le compilateur effectue donc dans un premier temps une conversion du langage C vers l’assembleur.
Lors d’un appel de fonction, le compilateur respecte les règles du standart AAPCS ( R0,R1,R2,R3 pour passer les paramètres, R0 pour la valeur de retour ).
Disassembly :
L’interprétation assembleur du code en C se trouve dans la fenêtre disassembly :
Dans le makefile, effectuer la modification suivante :
ASRC += src/main_str.s ### A MODIFIER ###
Représentation d’une chaine de caractères dans un processeur : le code ASCII
Comme toute donnée dans un système à processeurs, les caractères sont codés avec des 0 et des 1.
Algorithme : passage de minuscules à majuscules
Le codage du caractère ‘a’ est 0x61
Le codage du caractère ‘A’ est 0x41
Pour toute lettre, il y a une différence de 0x20 entre la minuscule et la majuscule.
Considérons la chaîne de caractères “abscefgh”, et observons la en mémoire :
C’est un tableau d’octets, dont l’adresse du premier élément est 0x200000b0
Le dernier élément du tableau est le caractère Nul ( 0x00 ).
L’algorithme consiste donc à :
PROJET SOURCE
WORKSPACE_LAB_F411_ASM_STM32CUBE
Dans le makefile, effectuer la modification suivante :
# SRC += src/main.c
ASRC += src/main_remove_space.s
# ASRC += src/int2ascii.s
Q1. Proposer un codage de la fonction remove_space permettant de remplacer tout espace dans une chaine de caractères ( ch_src ) par un undescore (‘_’).
La chaîne modifiée sera placée à l’adresse ch_dest.
Dans le makefile, effectuer la modification suivante :
SRC += src/main.c
# ASRC += src/main_remove_space.s
ASRC += src/int2ascii.s
On considère le programme c suivant permettant de convertir un entier en ASCII :
On se propose de coder en assembleur le sous programme int2ascii dont la spécification est donnée ci-dessous :
fonction int2ascii : convertit une valeur entière non signée en une chaîne de caractère.
Paramètre 1 : r0 contient la valeur à convertir
Paramètre 2 : r1 contient l’adresse de la chaîne de caractère
On donne l’organigramme de int2ascii :
Q1. Proposer un codage de la fonction int2ascii en assembleur, dans un contexte où la fonction main est en C.
Back | << | Index | >> |