TD+TP : Architecture des Ordinateurs 2016/2017

publicité
Département IF / Architecture Matérielle
TD+TP : Architecture des Ordinateurs 2016/2017
Partie I:
Micromachine
Dans cette série de TP nous allons construire un processeur pur 8-bit, avec les spécifications suivantes :
— ses bus d’adresse et données sont sur 8 bits ;
— le seul type de donnée supporté est l’entier 8 bits signé ;
— il possède deux registres de travail de 8 bits, notés A et B.
Au démarrage du processeur, tous les registres sont initialisés à 0. C’est vrai pour A et B, et aussi pour le
PC : le processeur démarre donc avec le programme à l’adresse 0.
Les instructions offertes par ce processeur sont
Instructions de calcul à un ou deux opérandes par exemple
B -> A
not B -> A
21 -> B
LSR A -> A
A + B -> A
A xor 12 -> A
B xor -42 -> A
B - A -> A ;
Explications :
— la destination (à droite de la flèche) peut être A ou B.
— Pour les instructions à un opérande, celui ci peut être A, B, not A, not B, ou une constante
signée de 8 bits. L’instruction peut être NOT (bit à bit), ou LSR (logical shift right). Remarque : le
shift left se fait par A+A->A.
— Pour les instructions à deux opérandes, le premier opérande peut être A ou B, le second opérande
peut être A ou une constante signée de 8 bits. L’opération peut être +, -, and, or, xor.
Instructions de lecture ou écriture mémoire parmi les 8 suivantes :
*A -> A
*A -> B
A -> *A
B -> *A
*cst -> A
*cst -> B
A -> *cst
B -> *cst
La notation *X désigne le contenu de la case mémoire d’adresse X (comme en C).
Comprenez bien la différence : A désigne le contenu du registre A, alors que *A désigne le contenu de
la case mémoire dont l’adresse est contenue dans le registre A.
Sauts absolus inconditionnels par exemple JA 42 qui met le PC à la valeur 42
Sauts relatifs conditionnels par exemple JR -12 qui enlève 12 au PC
JR offset
JR offset IFZ
JR offset IFC
JR offset IFN
exécutée si Z=1
exécutée si C=1
exécutée si N=1
Cette instruction ajoute au PC un offset qui est une constante signée sur 5 bits (entre -16 et +15).
Précisément, l’offset est relatif à l’adresse de l’instruction JR elle-même. Par exemple, JR 0 est une
boucle infinie, et JR 1 est un NOP (no operation : on passe à l’instruction suivante sans avoir rien fait).
La condition porte sur trois drapeaux (Z,C,N). Ces drapeaux sont mis à jour par les instructions arithmétiques et logiques.
— Z vaut 1 si l’instruction a retourné un résultat nul, et zéro sinon.
— C recoit la retenue sortant de la dernière addition/soustraction, ou le bit perdu lors d’un décalage.
— N retient le bit de signe du résultat d’une opération arithmétique ou logique.
Comparaison arithmétique par exemple B-A? ou A-42?
Cette instruction est en fait identique à la soustraction, mais ne stocke pas son résultat : elle se
contente de positionner les drapeaux.
1
1
Encodage du jeu d’instruction
Les instructions sont toutes encodées en un octet comme indiqué par la table 1. Pour celles qui impliquent une
constante (de 8 bits), cette constante occupe la case mémoire suivant celle de l’instruction.
La table 2 décrit la signification des différentes notations utilisées.
TABLE 1 – Encodage du mot d’instruction
bit
7
instruction autres que JR
saut relatif conditionnel
0
1
6
5
4
3
codeop, voir table 3
cond, voir table 4
2
1
arg2S arg1S
offset signé sur 5 bits
0
destS
TABLE 2 – Signification des différents raccourcis utilisés
Notation
encodé par
dest
arg1
arg2
offset
destS=instr[0]
arg1S=instr[1]
arg2S=instr[2]
instr[5 :0]
valeurs possibles
A si destS=0, B si destS=1
A si arg1S=0, B si arg1S=1
A si arg2S=0, constante 8-bit si arg2S=1
offset signé sur 5 bits
TABLE 3 – Encodage des différentes opérations possibles
codeop mnémonique
remarques
0000
arg1 + arg2 -> dest
addition ; shift left par A+A->A
0001
arg1 - arg2 -> dest
soustraction ; 0 -> A par A-A->A
0010
arg1 and arg2 -> dest
0011
arg1 or arg2 -> dest
0100
arg1 xor arg2 -> dest
0101
LSR arg1 -> dest
logical shift right ; bit sorti dans C ; arg2 inutilisé
0110
arg1 - arg2 ?
comparaison arithmétique ; destS inutilisé
1000
(not) arg1 -> dest
not si arg2S=1, sinon simple copie
1001
arg2 -> dest
arg1 inutilisé
1101
*arg2 -> dest
lecture mémoire ; arg1S inutilisé
1110
arg1 -> *arg2
écriture mémoire ; destS inutilisé
1111
JA cst
saut absolu ; destS, arg1S et arg2S inutilisés
Remarque : les codeop 0111, 1010, 1011, et 1100 sont inutilisés (réservés pour une extension future...).
TABLE 4 – Encodage des conditions du saut relatif conditionnel
cond
mnémonique
00
(toujours)
01
10
11
IFZ
IFC
IFN
si zéro
si carry
si négatif
2
2
Lisons un peu d’assembleur
Que font les programmes suivants ?
Trois petits programmes d’échauffement à gauche, un gros programme à droite.
prog1: 21
42
B+A
-> A
-> B
-> B
prog2: *21
*42
B+A
B
-> A
-> B
-> B
-> *43
prog3: *100 -> A
*101 -> B
B-A ?
JR +2 IFN
B
-> A
A
-> *102
Validez avec un enseignant.
Validez avec un enseignant.
init: -128
-> A
A
-> *98
A-A
-> A
A
-> *255
bcl: 100
-> A
*255 -> B
B+A
-> A
*A
-> B
*98
-> A
B-A?
JR +3 IFN
B
-> *98
*255 -> A
1
-> B
B+A
-> B
B
-> *255
*99
-> A
B-A?
JR +3 IF Z
;
JA bcl
fini:
3
;
;
;
;
;
0->A en 1 octet
index
adresse du tableau T
i
ainsi B contient T[i]
+3 : le JA est en 2 octets
3
Assemblons et désassemblons
La section 1 précise le codage de chaque instruction sous forme binaire.
3.1
Assemblage
Pour chacun de nos programmes d’échauffement, écrivez à droite de chaque instruction son code hexadécimal.
Par exemple, on lit dans les tableaux de la section 1 que le code de 21->A est composé des deux octets :
01001100 (0x4C) suivi de la constante 21 (0x15). N’hésitez pas à commencer par l’écrire en binaire.
Code assembleur
prog1: 21
42
B+A
-> A
-> B
-> B
prog2: *21
*42
B+A
B
-> A
-> B
-> B
-> *43
binaire
hexadécimal
prog3: *100 -> A
*101 -> B
B-A ?
JR +2 IFN
B
-> A
A
-> *102
Validez avec un enseignant.
(Ce travail stupide et dégradant s’appelle l’assemblage (assembly), et vous avez déjà envie d’écrire un programme qui le fait pour vous. Un tel programme s’appelle un assembleur.)
3.2
Désassemblage
Quel est le programme qui a pour codes hexadécimaux 4C, 11, 4D, 47, 32, E2, 42, 80 ?
Quel est le programme qui a pour codes hexadécimaux 11, 4D, 47, 32, E2, 42, 80 ?
Concluez-en qu’il ne faut pas se tromper dans ses calculs d’offset quand on fait un saut.
Validez avec un enseignant.
(Ce travail rébarbatif et vexatoire s’appelle le désassemblage (disassembly), et le programme correspondant
s’appelle un désassembleur – c’est le -d de objdump -d.)
4
4
Architecture du processeur
La micro-architecture de ce processeur est donnée par la figure 1. Nous allons commencer par la passer en
revue. Vous trouverez sur Moodle un squelette de ce processeur.
Quelques remarques :
— Comme d’hab, deux fils qui ont le même nom sur le dessin sont implicitement connectés. N’hésitez pas à
ajouter des fils explicites sur le dessin si cela vous aide. Mais je conseille quand même le crayon à papier...
— Les signaux dont le nom commence par “we” sont les write enable des registres correspondants.
— Quand la taille d’un vecteur de bits n’est pas explicitée, c’est qu’il s’agit d’un vecteur de 8 bits.
— L’automate est chargé de positionner tous les write enable (y compris Mwe pour la mémoire). Vous allez
implémenter cet automate à la partie 6.
Pour chaque question ci-dessous, on vous demande deux choses : 1/ repasser la partie correspondante du
dessin, et 2/ indiquer la partie correspondante dans le circuit Logism.
A la fin de cette partie, vous aurez normalement repassé toute la figure 1 avec de jolies couleurs, et compris le
circuit Logisim...
4.1
Le PC et le cycle de von Neumann
Prenez un crayon rouge.
Ou est le registre PC ?
Comment peut-il être envoyé sur le bus d’adresse de la mémoire ?
Par quel chemin peut-on ajouter 1 au PC ?
Où est le registre d’instruction ?
Où est le registre de constante ?
4.2
Les branchements
Prenez un crayon vert.
Repassez d’abord ce qui permet de réaliser le JA.
Puis ce qui permet de réaliser le JR.
Les deux signaux ja et jr ne sont définis nulle part. Donnez en les équations logiques, à partir des bits du mot
d’instruction.
offset est un entier signé de 5 bits extrait du registre d’instruction. Comment peut-on l’étendre à un mot de 8
bits ayant la même valeur ?
4.3
Les instructions arithmétiques
Prenez un crayon bleu.
Repassez les registres A et B.
Repassez l’ALU.
Comment sont choisis l’operande 1 et l’opérande 2 ?
Comment est choisie la destination ?
4.4
Les accès mémoire
Prenez un crayon mauve ou turquoise ou jaune vif.
Pour faire une lecture mémoire, il faut d’abord positionner l’adresse qu’on veut accéder sur le bus d’adresse.
Montrez par quel chemin cela se fait.
La donnée arrive ensuite sur le bus MDI.
Montrez comment elle va pouvoir rejoindre les registres A ou B.
Pour faire une écriture, c’est pareil mais c’est le contraire.
N’oubliez pas de lever ceM.
Validez avec un enseignant.
5
F IGURE 1 – Les différents blocs fonctionnels de notre processeur
6
5
Construction de l’ALU
Si vous avez le sens de l’observation, vous avez compris que le circuit Logisim téléchargé sur Moodle n’est pas
complet. Vous avez reçu la partie ennuyante et vous réaliserez la partie la plus intéressante : l’ALU et l’automate.
Question 5-1. Pourquoi arg2S intervient-il deux fois dans le calcul des entrées (figure 1).
Afin d’obtenir une ALU complète, vous allez dans un premier temps implémenter les fonctionnalités suivantes
(ne vous occupez pas des drapeaux pour l’instant) :
codeop
0000
0001
0010
0011
0100
0101
1000
1001
mnémonique
arg1 + arg2 -> dest
arg1 - arg2 -> dest
arg1 and arg2 -> dest
arg1 or arg2 -> dest
arg1 xor arg2 -> dest
LSR arg1 -> dest
(not) arg1 -> dest
arg2 -> dest
remarques
addition ; shift left par A+A->A
soustraction ; 0 -> A par A-A->A
logical shift right ; bit sorti dans C ; arg2 inutilisé
not si arg2S=1, sinon simple copie
arg1 inutilisé
Une fois ces opérations disponibles dans votre ALU, vous allez vous charger de donner les bonnes valeurs à
vos drapeaux. On rappelle que les drapeaux sont systématiquement mis à jour dans l’ALU, mais ne sont pas
utilisés/enregistrés plus loin dans le circuit. Les drapeaux Z et N ont un sens pour chacune des instructions du
tableau ci-dessus, en revanche C n’est valable que pour quelques opérations. Vous mettrez donc C à 0 lorsque
l’opération en cours ne peut donner une valeur à C.
Testez votre composant avant de passer à la suite.
6
Construction de l’automate
Dans cette partie, vous allez implémenter un automate qui va piloter votre datapath. Vous allez donc commencer
par implémenter un automate aux fonctionnalités réduites. Il vous servira de base pour la suite.
6.1
Les instructions arithmétiques en 1 octet
Afin d’implémenter les instructions en 1 octet, nous vous donnons un automate représentant le cycle de Von
Neumann (Figure 2).
Pour rappel, le cycle de Von Neumann est le suivant :
— Lire une instruction en mémoire à l’adresse PC, puis l’enregistrer dans le registre d’instruction (InstrFetch)
— Selectionner l’instruction à exécuter (InstrDecode) (ici vous ne ferez rien pour l’instant car vous n’avez
qu’un seul choix possible !)
— Enregistrer le resultat de l’opération (RegWrite), et incrémenter PC.
Question 6-1. Voyez dans le logisim tous les signaux qu’il doit lever, et essayez de les placer dans les patates
au crayon à papier.
Gardez en tête que cet automate ne permet que la réalisation d’opérations de l’ALU sans constantes.
Il faudra donc ajouter plus tard, non seulement la gestion des constantes, mais aussi des accès mémoire.
C’est relativement simple, sauf pour cePC : le plus simple est de le lever juste avant InstrFetch puisque c’est
dans InstrFetch que le PC est utilisé.
Question 6-2. Construisez un automate à patate chaude qui implémente ces trois états et positionne les
signaux nécessaires. Les signaux que votre automate ne lève pas doivent être mis à 0.
Question 6-3. Testez-les sur le programme suivant (que fait-il ?), que vous assemblerez et placerez au début
de la mémoire :
not
b-a
b-a
b-a
b-a
b-a
a -> a
-> b
-> b
-> b
-> b
-> b
7
start
InstrFetch
XXXXXXXXX
RegWrite
XXXXXXXXX
InstrDecode
XXXXXXXXX
F IGURE 2 – Le cycle de von Neumann sans accès mémoire, sans constantes et sans sauts
Question 6-4. Si vous observez la machine de von Neumann, vous verrez que le bus d’adresse fait n’importe
quoi entre deux instructions. Expliquez pourquoi ce n’est pas grave.
6.2
Le saut relatif (inconditionnel pour commencer)
Comme il tient lui aussi en 1 octet, on peut le greffer à notre automate (Figure 3). Dans la même démarche que
pour la question précédente, essayer dans un premier temps de trouver les signaux à lever dans l’état DoJR au
crayon à papier avant de l’implémenter. Attention, votre état InstrDecode possède maintenant deux destinations !
Il vous faut donc mettre des conditions sur les flèches sortantes de cet état.
Ne vous préoccupez pas des sauts conditionnels à ce stade. Testez en remplaçant par une boucle la série de
b-a -> b dans le programme ci-dessus.
start
InstrFetch
XXXXXXXXX
RegWrite
XXXXXXXXX
DoJR
XXXXXXXXX
InstrDecode
XXXXXXXXX
F IGURE 3 – Le cycle de von Neumann sans constantes, sans accès mémoire avec sauts relatifs
6.3
Les instructions arithmétiques en 2 octets
Il s’agit d’ajouter les états IncrPC et CstFetch (Figure 4). Précisez les signaux que doivent positionner les états
IncrPC et CstFetch. N’oubliez pas que PC doit être incrémenté.
8
Testez sur prog1.
start
InstrFetch
XXXXXXXXX
RegWrite
XXXXXXXXX
DoJR
XXXXXXXXX
InstrDecode
XXXXXXXXX
IncrPC
XXXXXXXXX
CstFetch
XXXXXXXXX
F IGURE 4 – Le cycle de von Neumann avec constantes et sauts relatifs, mais sans accès mémoire ni sauts
absolus
6.4
Le saut absolu
Afin d’implémenter le saut absolu, vous devez ajouter un état à l’automate de la Figure 4. Faites valider par un
enseignant avant d’implémenter votre solution. Vous pouvez tester votre implémentation en faisant :
not a -> a
b-a -> b
Ja 1
6.5
Les sauts relatifs conditionnels
Afin de pouvoir faire des sauts conditionnels, on peut regarder la valeur des drapeaux et ainsi faire un saut ou
non. Pour l’instant, à chaque opération de l’ALU, votre processeur écrit le résultat dans un registre. On souhaite
alors ajouter à l’alu une instruction de comparaison qui ne mettra pas à jour les registres mais seulement les
drapeaux. On va donc ajouter l’instruction :
codeop
0110
mnémonique
arg1 - arg2?
remarques
Produit le résultat de arg1-arg2 sur la sortie
mais ce ne sera pas enregistré dans un registre
Il faut donc que cette instruction prenne un chemin différent dans l’automate. On propose alors l’automate de la
Figure 5.
Il faut créer un signal condTrue qui compare l’état des drapeaux avec les bits de condition. Ce signal participera
à choisir si l’automate doit aller de InstrDecode vers DoJR ou NoJR.
Testez votre implémentation sur le programme suivant :
42 -> b
7 -> a
f: b-a?
JR +3 IFN
9
start
InstrFetch
XXXXXXXXX
RegWrite
XXXXXXXXX
DoJR
XXXXXXXXX
NoRegWrite
XXXXXXXXX
InstrDecode
XXXXXXXXX
IncrPC
XXXXXXXXX
NoJR
CstFetch
XXXXXXXXX
XXXXXXXXX
F IGURE 5 – Le cycle de von Neumann avec constantes et sauts relatifs conditionnels, mais sans accès mémoire
ni sauts absolus
b-a -> b
JR -3
Quelle est la fonction f (a, b) calculée par ce programme ?
6.6
Les accès mémoire
Afin de réaliser les accès mémoire, nous vous proposons l’automate de la Figure 6.
Dans l’état MemWrite on se contente de positionner les bus adresse et donnée comme il faut. Dans l’état
MemRead, on positionne l’adresse, puis on lève weDestReg comme dans l’état RegWrite.
10
start
InstrFetch
XXXXXXXXX
RegWrite
XXXXXXXXX
DoJR
XXXXXXXXX
NoRegWrite
XXXXXXXXX
InstrDecode
XXXXXXXXX
IncrPC
XXXXXXXXX
NoJR
CstFetch
XXXXXXXXX
XXXXXXXXX
MemRead
MemWrite
XXXXXXXXX
XXXXXXXXX
F IGURE 6 – Le cycle de von Neumann ressemble plutôt à un plat de nouilles
Partie II:
1
Micromachine - interruptions
Spécification / cahier de charges
Dans cette séance nous reviendrons sur le processeur « Micromachine « que nous avons traité au début de
ce module (voir la Partie I). L’objectif est de réaliser une modification lui permettant de gérer les interruptions.
Une interruption est un événement qui peut être provoqué soit par une cause externe (il y a des broches pour
cela sur la puce du processeur), soit par une cause interne (division par zéro, accès mémoire interdit, accès
d’un adresse mémoire délocalisée sur disque (mémoire virtuelle) etc.). Lorsque survient un tel événement, le
processeur interrompt (temporairement) ce qu’il était en train de faire (son programme) pour sauter à la routine
de traitement des interruptions (ISR = Interrupt Service Routine). Typiquement, c’est un mécanisme en deux
parties :
1. le saut à l’adresse de la routine de traitement.
11
2. A la fin de la routine, le retour au programme interrompu. Ceci est déclenchée par une instruction dédiée.
Le saut va devoir faire deux choses en une instruction (i) sauvegarder l’adresse de retour, c’est à dire l’adresse
à laquelle il faudra reprendre l’exécution du programme une fois l’interruption traitée et (ii) charger l’adresse de
la routine d’interruption dans le registre Program Counter. Il est important que ces deux actions soient faites
en une seule instruction (atomique) pour ne pas permettre qu’une interruption intervienne entre les deux. Plus
précisément, ce qui doit être atomique est la sauvegarde de l’adresse de retour : si on le fait en plus d’une
instruction, une nouvelle interruption qui interviendrait entre ces instructions provoquerait un second déroutement
ce qui deviendrait très compliqué à gérer.
Dans ce TD, nous utiliserons un mécanisme simple pour garantir l’atomicité de l’opération call. Lors qu’une
interruption est détectée par le processeur, l’instruction en cours est terminée. Puis, l’adresse de retour est
enregistrée dans un nouveau registre dédié. Les drapeaux sont également enregistrés dans un registre dédié
(pourquoi ?). Le PC est modifié pour accueillir l’adresse de la routine de traitement. Ensuite, on continue avec le
cycle Fetch.
2
Conception de la solution sur papier
Dans la suite de ce TD, on modifiera notre micromachine de façon à gérer des interruptions déclenchées par
un signal externe supplémentaire nommé IRQ. Ce signal est équivalent à un niveau haut sur un fil reliant le
matériel en question au processeur par une broche dédiée. Le processeur signalera le traitement de l’interruption
pour que le matériel externe (à l’origine de l’interruption) puisse (i) s’assurer que la requête d’interruption a bien
été traitée et (ii) arrêter d’émettre le signal IRQ. Pour ce faire, on ajoutera un signal IntAck (pour Interrupt
Acknowledgement) sortant du chemin de données. Ce signal est controlé par l’automate. Il est à distination du
composant matériel émettant les interruptions et est donc une sortie du processeur.
Important : IRQ et IntAck sont des signaux interfaçant le CPU avec le périphérique en question. Physiquement,
il s’agît donc de broches spécifiques sur la puce du processeur.
2.1
Modification du chemin de données
Reprenez le magnifique dessin dans la figure 1. Prenez un style de votre couleur préférée, et :
1. Ajoutez deux registres nommés SavedPC et SavedFlags accueillant, respectivement, la sauvegarde du
PC et la sauvegarde des drapeaux.
2. Modifiez le circuit pour rendre possible la sauvegarde du PC et des drapeaux. Notez les signaux supplémentaires à gérer par l’automate.
3. Modifiez le circuit pour rendre possible la reprise (restore) des valeurs sauvegardées (PC et drapeaux).
Notez les signaux supplémentaires à gérer par l’automate.
4. Modifiez le circuit pour rendre possible le saut à l’adresse de la routine traitant les interruptions, ce qui
revient à charger une valeur dans le PC. Ici, on supposera que cette adresse est fixe et égale à 128 ==
0x80. Notez le signal supplémentaire à gérer par l’automate.
Validez avec un enseignant.
2.2
Modification de l’automate
Dans la figure 7 vous trouvez une copie de l’automate conçu dans de la partie I.
1. Ajoutez un (seul) état supplémentaire à l’automate, nommé JumpToISR, réalisant la partie call : (i) sauvegarde du PC et des drapeaux et (ii) saut à l’adresse de la routine traitant les interruptions.
2. La sauvegarde du PC et la mise à jour du PC peuvent être réalisées simultanément, c’est-à-dire dans le
même état. Pourquoi ?
3. Ajoutez les transitions allant des autres états vers l’état JumpToISR tel que cet état est atteint à la fin
du cycle de Von Neumann si le signal IRQ est actif. On peut donc supposer que ce signal reste stable
jusqu’au traitement de l’interruption.
4. Ajoutez la transition pour retourner au cycle fetch après déclenchement du traitement.
5. Ajoutez le traitement du signal sortant IntAck signalant que l’interruption a été traitée.
12
start
InstrFetch
XXXXXXXXX
RegWrite
XXXXXXXXX
DoJR
XXXXXXXXX
NoRegWrite
XXXXXXXXX
InstrDecode
XXXXXXXXX
IncrPC
XXXXXXXXX
NoJR
CstFetch
XXXXXXXXX
XXXXXXXXX
MemRead
MemWrite
XXXXXXXXX
XXXXXXXXX
F IGURE 7 – Le cycle de von Neumann (sans gestion des interruptions)
Notre processeur est désormais capable de déclencher une routine traitant des interruptions ... mais pas de
revenir. Pour cela, nous ajouterons une instruction nommée RETI. Elle sera encodée comme 86 = 0x58 =
0101100b. Le lecteur motivé peut vérifier dans la table 3 (partie I) que le code opératoire 1011 n’a pas encore
été utilisé par une instruction existante.
1. Ajoutez un (seul) état supplémentaire à l’automate, nommé doRETI, réalisant le travail demandé, c’est-àdire la restauration des sauvegardes du PC et des drapeaux.
2. Ajoutez les transitions vers cet état et sortant de cet état.
Validez avec un enseignant.
13
3
Réalisation sur logisim
Sur moodle vous trouvez une solution de la micromachine sans gestion des interruptions. Cette solution comprend également un bouton poussoir permettant à l’utilisateur de demander le déclenchement d’une interruption.
Pour le moment, ce signal n’est pas traité par le processeur.
3.1
Modification du chemin de données
1. Ajoutez les deux registres SavedPC et SavedFlags accueillant, respectivement, la sauvegarde du PC et
la sauvegarde des drapeaux.
2. Connectez les registres pour permettre (i) la sauvegarde du PC et des drapeaux, (ii) la reprise des valeurs
sauvegardées (PC et drapeaux). Pre-voyez des signaux pour déclencher ces 4 actions.
3. Ajoutez une possibilité d’enregistrer la valeur 0x80 dans le PC (saut vers la routine de traitement / ISR).
Pre-voyez un signal pour déclencher cette action.
3.2
Modification de l’automate
1. Ajoutez l’état JumpToISR et les transitions le concernant.
2. Ajoutez l’état doRETI et les transitions le concernant. Pour cela il faut une comparaison du code opératoire
de l’instruction (issue du registre IR) avec la constante 1011.
3. Ajoutez les signaux sortants pour ces deux états : sauvegarde PC et drapeaux ; reprise PC et drapeaux ;
saut vers l’ISR ;
Testez votre circuit avec un programme. Sur moodle vous trouvez un code en deux partie : programme principale
démarrant à l’adresse 0 + ISR démarrant à l’adresse 0x80.
14
Partie III:
MSP 430 - Prise en main
Dans cette série de TP «msp430», on va étudier le fonctionnement d’un (petit) ordinateur réel, pour mieux comprendre l’interface entre le logiciel et le matériel. Vous devrez donc faire les diverses manipulations demandées,
et par moment écrire des bouts de programme.
Nous ne ramasserons pas de compte-rendu ; par contre, vous avez intérêt à prendre des notes tout au long du
déroulement du TP pour pouvoir les relire par la suite : dans les TP d’après, mais aussi avant les QCM, et aussi
avant l’examen ! Pour chaque exercice, mettez donc par écrit (sur papier ou sur ordinateur) les manips que vous
faites, les questions que vous vous posez, et les nouvelles notions que vous comprenez.
Comme tout objet technologique, notre plate-forme de TP s’accompagne d’une documentation technique abondante. Pour ne pas vous noyer sous la doc, nous vous en avons copié les extraits essentiels directement dans
le sujet, sous forme d’encadrés. Pour les plus curieux, nous vous avons aussi mis à disposition les documents
sur Moodle :
motherboard.pdf décrit notre carte d’expérimentation et les différents composants présents sur la carte.
msp430x4xx.pdf est le manuel générique de la famille MSP430. Le processeur est documenté au chapitre 3
de ce document.
datasheet-msp430g4618.pdf donne les détails techniques de notre modèle précis de msp430.
1
Découverte de la carte
Pour chaque binôme, allez prendre le matériel nécessaire au TP : une carte d’expérimentation, une sonde JTAG
(le boîtier gris avec une nappe d’un côté), et un câble USB.
Exercice 1 Que signifie l’acronyme USB, au fait ? Expliquez en une phrase la signification du S. Faites valider
cette phrase par un enseignant, mais n’attendez pas qu’il arrive pour passer à la suite.
2 Que
signifie l’acronyme JTAG ? L’explication détaillée est donnée dans l’encadré page 21, que nous
4.Exercice
Functional
Overview
lirons en temps utile.
The MSP430FG4618/F2013 experimenter’s board supports various applications
through the use :ofmotherboard.pdf
the on-chip peripherals
connecting
to a number of on-board
Extrait de la documentation
page
6
components and interfaces as shown in Figure 2.
Wireless
CC1100/
2420/2500
EMK
Interface
Analog
Out
LCD
RS-232
Buzzer
JTAG1
FG4618
JTAG2
Microphone
F2013
Capacitive
Touch
Pad
Buttons
Figure 2: Experimenter’s board block diagram
Wireless communication is possible through the expansion header which is
15
compatible with all Chipcon Wireless Evaluation
Modules from Texas Instruments.
Interface to a 4-mux LCD, UART connection, microphone, audio output jack,
Sur cette carte mère, en plus du msp430, il y a tout un tas de périphériques :
1.
2.
3.
4.
5.
6.
7.
8.
9.
etc.
un écran à cristaux liquides (pour afficher des chiffres et des icônes)
un microphone
un buzzer (pour jouer du son)
une prise casque (pour jouer du son aussi, mais plus joli)
un quartz (pour générer le signal d’horloge)
deux boutons poussoirs
des voyants lumineux (LED)
une roue tactile capacitive (touchpad) en forme de chiffre 4
un port série (RS-232)
...
Exercice 3 Pour chacun de ces éléments, indiquez sur le schéma page précédente son emplacement approximatif. Certains éléments (LEDs, quartz) ne sont pas sur le schéma, vous devrez les chercher directement sur la
carte. Le quartz est repéré X2, et les diodes sont repérées LED1, LED2, LED3 et LED4.
Commentaire La carte comporte deux microcontrôleurs. L’un est un MSP430F2013 (c’est le petit), et l’autre un
MSP430FG4618 (c’est le gros). C’est avec ce second msp430 qu’on va travailler dans ces TP. Les diodes LED1,
LED2, et LED4 y sont connectées par des pistes de la carte mère. La diode LED3, par contre, est connectée au
F2013, qu’on ne va pas utiliser du tout. Vous pouvez dès maintenant oublier son existence, ainsi que celle de la
LED3.
Exercice 4 L’encadré page suivante montre le schéma électrique de la carte mère. Retrouvez les différents
composants vus jusqu’ici, et indiquez leur emplacement sur le schéma.
16
Extrait de la documentation : motherboard.pdf page 19
1
3
5
7
1
3
5
7
H2
H3
H4
H5
H6
H7
UCA0SIMO
UCA0CLK
P7.5
P7.7
P5.7
P5.5
2
4
6
8
UCB0SDA
UCB0CLK
P3.5
P3.7
2
4
2
4
6
8
11
P6.0
P6.2
P6.4
P6.6
VEREFP5.0
P10.6
1
3
5
7
1
3
5
H8
H9
2
4
6
8
2
4
6
P6.1
P6.3
P6.5
P6.7
VEREF+
P5.1
P10.7
VCC
2
1
LCL_PWR1 3
2
FET_PWR1 1
LCL_PWR2
3
2
FET_PWR2 1
VCC_1
VCC_2
14
12
10
8
6
4
2
13
11
9
7
5
3
1
GND
GND
VREG_EN
RESETCC
JTAG1
AVCC_4618
C8
LCL_PWR1
FET_PWR1
C7
10uF 0.1uF
1
3
5
7
9
11
13
15
17
19
FIFO
FIFOP
GDO0
GDO2
P4.2
UCLK1
SIMO1
SOMI1
VCC
C1
0.1uF
GND
1
3
5
7
9
11
13
15
17
19
RF Daughter Card Connect
2
4
6
8
10
12
14
16
18
20
DVCC_4618
RF1
C2
0.1uF
RF2
2
4
6
8
10
12
14
16
18
20
R5
8
7
6
DVCC_4618
2
5
8
7
6
SW1
GND
SW2
R21
0-DNP
0-DNP
R22
1A_1B_1C_1D
1F_1G_1E_DP1
2A_2B_2C_2D
2F_2G_2E_DP2
3A_3B_3C_3D
3F_3G_3E_COL3
4A_4B_4C_4D
4F_4G_4E_DP4
5A_5B_5C_5D
5F_5G_5E_COL5
6A_6B_6C_6D
6F_6G_6E_DP6
7A_7B_7C_7D
7F_7G_7E_DP7
C3
10uF
P3.5
1 JP1
2
SBWTDIO
SBWTCK
DOL_ERR_MINUS_MEM
ENV_TX_RX_8BC
ANT_A2_A1_A0
BT_B1_B0_BB
AU_AR_AD_AL
PL_P0_P1_P2
F1_F2_F3_F4
F5_PR_P4_P3
COM0
COM1
COM2
COM3
SoftBaugh SBLCDA4
(For opt. F2013 programming)
DVCC_4618
P$14
P$13
P$12
P$11
P$10
P$9
P$8
P$7
P$6
P$5
P$4
P$3
P$2
P$1
1N4148
D2
GND
2
5
DVCC_4618
PS8802
3
U2
PS8802
3
U1
Isolated RS232 Communication
100
Q1
MMBT5088
VCC
R9
2k2
GND
UCA0TXD
UCA0RXD
P2.6
P2.7
P3.0
UCB0SDA
UCB0SCL
UCB0CLK
P3.4
P3.5
P3.6
P3.7
UTXD1
C13
URXD1
GND
0.1uF
DVCC_4618
LCDCAP
P5.7
C15
P5.6
P5.5
10uF
COM3
COM2
COM1
GND
COM0
P4.2
UCA0RXD
UCA0TXD
GND GND
75
74
73
72
71
70
69
68
67
66
65
64
63
62
61
60
59
58
57
56
55
54
53
52
51
1k
MSP-EXP430FG4618 PCB Ver 0-00
Document Number:
5
4
3
2
1
RS232
10uF
C4
G1
G2
9
8
7
6
VER:
0-00
S21
S20
S19
S18
S17
S16
S15
S14
COM0
COM1
COM2
COM3
PC_GND
Buzzer
Mute
P$26
P$25
P$24
P$23
P$22
P$21
P$20
P$19
P$18
P$17
P$16
P$15
Sheet: 1/1
MSP430FG4618/F2013 Experimenter's Board
S0
S1
S2
S3
S4
S5
S6
S7
S8
S9
S10
S11
S12
S13
R2
Power Supply Configuration
BATT
GND
C6
VCC_1: FG4618 Supply Config
VCC_2: F2013 Supply Config
C5
DVCC_4618
AVCC_4618
Pos 1-2: FET Powered
Pos 2-3: Battery Powered
VCC
R8
10
R10
10
10uF 0.1uF
GND
Audio output jack
R1
MSP430FG4618 Pin Access
1
3
1
3
5
7
LCDCAP
P5.6
P7.0
UCA0SOMI
P7.4
P7.6
SW2
GDO2
VREG_EN
FIFOP
URXD1
SIMO1
UCLK1
P4.7
2
4
6
8
2
4
6
8
1
3
5
7
11
12
13
2
1
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
P6.5
(A5/OA2O)
A1
R17
SW1
GDO0
RESETCC
FIFO
UTXD1
P4.2
SOMI1
P4.6
P3.0
UCB0SCL
P3.4
P3.6
10
12
13
2
1
3
VREF
C9
VEREF+
VEREFP5.1
P5.0
P10.7
P10.6
S0
S1
S2
S3
S4
S5
S6
S7
S8
S9
DVCC_4618
P6.3
P6.4
C10
P6.5
P6.6
10uF
P6.7
VREF
X2
GND
0.1uF
GND
JP3 1
2
GND
P6.4
(A4/OA1I0)
2
JP4 1
R7
P2.1
P2.3
UCA0RXD
P2.7
10
9
14
15
16
UCB0SDA
UCB0SCL
P3.0
UCB0CLK
Sallen-Key 2nd Order OA1 Active LPF
R25
15.4k
(Output
Attn.)
+
R4
R12
D3
1N4148
+
SP1
9
8
14
15
16
2
4
6
8
P6.0
(A0/OA0I0)
R24
1.4k
C17
3.3nF
R6
470
2
4
6
8
8
470
R20
150k
P6.7
(A7/DAC1)
C16
22nF
LED2
D1
1N4148
Date: 26-Oct-2006
17
2AL60P1
470
470
+
B1
-
2k2
2k2
S2
INNER_GND
2 FET_PWR2
4 LCL_PWR2
6
8
10
12
14
LED3
1k
R29
R30
P6.1
(A1/OA0O)
R11
INNER_GND
JTAG2
GND
SBWTDIO 1
3
5
SBWTCK 7
9
11
13
VSS
VCC
U4
GND
1
14
2
2013_P1.0
3
2013_P1.1
4
2013_P1.2
5
2013_P1.3
6
2013_P1.4
7
2013_P1.5
8
SCL
H1
9 SDA
1
3
5
7
R16
Mic Input Circuitry and
1st Order OA0 Active HPF
P2.3
(Mic Supply)
P1.0/TACLK/ACLK/A0+
P1.1/TA0/A0-/A4+
P1.2/TA1/A1+/A4P1.3/VREF/A1P1.4/SMCLK/A2+/TCK
P1.5/TA0/A2-/SCLK/TMS
P1.6/TA1/A3+/SDO/SCL/TDI/TCLK
P1.7/A3-/SDI/SDA/TDO/TDI
C19
10uF
C14
P6.2
(A2/OA0I1)
PWR1
GND
1
3
5
7
C12
0.1uF
NMI/RST/SBWTDIO
TEST/SBWTCK
XIN/P2.6/TA1
XOUT/P2.7
17
15
13
11
9
7
5
3
1
MSP430F2013PW
18
16
14
12
10
8
6
4
2
2
1
DNP
P2.0
P2.2
UCA0TXD
P2.6
VCC
2
PWR2 1
C11
GND
10
11
13
12
17
15
13
11
9
7
5
3
1
0
S1
100k
2
1
100k
2
1
5
5
LED1
BAND
P$4
RING
P$2
TIP
P$1
C20
5M1
R32
22k
1uF
18
16
14
12
10
8
6
4
2
BB1
R31
Breadboard
17
15
13
11
9
7
5
3
1
BB2
GND
470n
+
P6.3
(A3/OA1O)
R33
GND
R28
47k
2013_P2.6
2013_P2.7
BB3
GND
GND
470k
470
7
7
R3
10k
R23
6
6
GND
47k
100
99
98
97 P6.2
96 P6.1
95 P6.0
94
93
92
91
90
89
88
87
SW1
86
SW2
X1
85
GDO0
84
GDO2
83
RESETCC
82
VREG_EN
81
FIFO
80
FIFOP
79
P2.0
78
P2.1
77
P2.2
76
P2.3
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
S10
S11
S12
S13
S14
S15
S16
S17
S18
S19
S20
S21
P7.7
P7.6
P7.5
P7.4
UCA0CLK
UCA0SOMI
UCA0SIMO
P7.0
P4.7
P4.6
UCLK1
SOMI1
SIMO1
R14
470n
LED4
5M1
C18
5M1
15p
R13
470
R15
C21
VCC
18
16
14
12
10
8
6
4
2
R19
4
4
+
R18
10
5M1
1 JP2
2
R26
M1
1k
3k3
R27
1
2
R34
VCC_2013
1.1
Vous avez dit microcontrôleur ?
Le MSP430FG4618 est un microcontrôleur, c’est à dire un System-on-Chip : une même puce qui contient à la
fois un processeur, de la mémoire, et des contrôleurs de+ périphériques. Si on zoome sur l’intérieur de la puce,
on a donc affaire à l’architecture illustrée ci-dessous.
Extrait de la documentation : datasheet-msp430g4618.pdf page 5
Les flèches repérées MAB et MDB sont respectivement le Memory Address Bus et le Memory Data Bus
(les mêmes que dans la micro-machine). Ce sont eux qui relient le processeur au reste-du-monde, comme
dans toute machine de von Neumann qui se respecte.
Vous pouvez constater qu’ici, le reste du monde ne se limite pas à la mémoire comme dans notre micromachine... Nous allons détailler tout cela.
Exercice 5 Repérez sur ce diagramme le processeur, la RAM, la mémoire flash. Vérifiez que vous connaissez le sens des acronymes RISC, CPU, RAM, ADC, DAC. Sinon, ouvrez le glossaire qui est tout au début de
msp430x4xx.pdf (p. 4). Demandez des explications à un enseignant si nécessaire. Ignorez les autres acronymes
pour le moment.
Exercice 6 Branchez maintenant la sonde JTAG sur la carte. Vous devriez pouvoir choisir sans difficulté entre
les deux connecteurs JTAG en regardant la figure de la page 15.
1.2
Zoom sur le processeur
Si on se rapproche encore, on tombe sur l’architecture suivante :
18
Extrait de la documentation : msp430x4xx.pdf page 44
MDB
MAB
15
0
R0/PC Program Counter
0
R1/SP Stack Pointer
0
R2/SR/CG1 Status
R3/CG2 Constant Generator
R4
General Purpose
R5
General Purpose
R6
General Purpose
R7
General Purpose
R8
General Purpose
R9
General Purpose
R10
General Purpose
R11
General Purpose
R12
General Purpose
R13
General Purpose
R14
General Purpose
R15
General Purpose
16
16
Zero, Z
Carry, C
Overflow, V
Negative, N
dst
src
16−bit ALU
Commentaire Attention, ce schéma ne montre que la vue ISA (instruction-set architecture), c’est à
dire du point de vue de l’utilisateur du processeur. Elle cache les détails de microarchitecture que le
programmeur n’a pas besoin de connaître : l’automate de contrôle, le registre d’instruction, etc.
Les seuls éléments représentés sur le schéma sont donc ceux qui sont accessibles au programmeur :
les 16 registres architecturaux, les drapeaux, ainsi que l’unité arithmétique et logique. Remarquez au
passage que les 4 premiers registres sont spécialisés pour un usage particulier (R0 est le compteur
ordinal, etc). À l’inverse les 12 autres registres sont généraux, on peut y mettre ce qu’on veut.
Nous vous avons reproduit en annexe (page 42 et suivantes) les passages correspondants de la documentation. Vous n’en avez pas besoin pour l’instant, mais pensez à y revenir lorsque vous voudrez des
détails sur l’usage des registres.
Exercice 7
contrôle.
Sur le schéma de la page 18, indiquez où se trouvent nos 16 registres, ainsi que l’automate de
Exercice 8
Explicitez l’acronyme ALU.
Exercice 9
Tiens, il manque les flèches sur les fils entre ALU et les drapeaux. Ajoutez-les.
19
Exercice 10 Allez lire la page https://fr.wikipedia.org/wiki/Registre_de_processeur et résumez, en une
phrase, la différence entre un registre spécialisé et un registre général.
2
Prise en main des outils : mspdebug
Pour communiquer avec notre MSP430 au travers de l’interface USB/JTAG, on va utiliser un programme appelé
mspdebug. Cet outil va nous permettre de charger des programmes dans la mémoire, d’observer et de contrôler
l’exécution du programme, d’inspecter le contenu du CPU et de la mémoire, etc.
Exercice 11
Branchez la carte, et lancez mspdebug en tapant la ligne commande suivante :
mspdebug -j -d /dev/ttyUSB0 uif
L’argument uif est le nom du driver à utiliser, ici celui de notre boîtier JTAG.
Vous devez obtenir une série d’informations techniques compliquées, puis une liste des commandes disponibles,
et enfin un prompt de la forme (mspdebug) en début de ligne. Commencez par effacer complètement la puce
en tapant dans mspdebug la commande erase .
On va maintenant se servir de mspdebug pour allumer et éteindre la diode LED4. Comme le montre la figure
p. 18, tous les périphériques sont mappés sur des adresses mémoire. En écrivant les bonnes valeurs aux bonnes
adresses, on peut contrôler ces périphériques.
Par exemple, pour allumer cette diode, il faut d’abord écrire la valeur 2 à l’adresse 50. Cela fait, on allumera la
diode en écrivant la valeur 2 à l’adresse 49, et on l’éteindra en écrivant 0 à l’adresse 49. Admettons ces valeurs
pour l’instant, nous les expliquerons dans un moment.
Exercice 12
Toujours dans mspdebug, tapez help mw et lisez l’aide de la commande memory write. Remar-
quez au passage que vous pouvez aussi taper help tout court pour obtenir la liste des commandes disponibles,
et help bidule pour obtenir de l’aide sur la commande bidule.
Exercice 13
Faites s’allumer et s’éteindre la diode quelques fois.
20
À savoir : le JTAG
L’acronyme JTAG désigne une méthode permettant de lire ou d’écrire n’importe quel bit de mémoire d’un
circuit séquentiel. Cette méthode nécessite “seulement” des modifications mineures à l’intérieur du circuit,
ainsi qu’une poignée de signaux connectés au monde extérieur (de deux à cinq fils, suivant les variantes
du protocole).
Le principe de base est simple : il s’agit de considérer virtuellement l’ensemble du circuit (ici le MSP 430)
comme un seul gros automate, selon la figure suivante que vous connaissez maintenant bien.
/
s
T
x
s0
/
1000
/
reset
Registre d’état
1000
s
/
F
/
y
Ck
Dans cette figure, le registre d’état est un énorme registre (1000 bits sur notre exemple) qui contient le
registre de l’automate de contrôle, mais aussi tous les registres de la partie «datapath» : les registres de
la boîte à registres, tous les registres de pipeline, la valeur des flags etc. Tout l’état du processeur, quoi.
Si vous n’avez pas compris ce paragraphe, faites-le vous expliquer par un enseignant.
On ajoute (de manière automatique) à chaque flip-flop de cet immense registre un tout petit peu de
circuiterie pour faire de l’ensemble des 1000 registres binaires un unique immense registre à décalage.
C’est une transformation automatique qui est décrite par la figure ci-dessous :
Le registre d’état de la figure ci-dessus, avant...
... et après sa transformation en JTAG
1
R
0
R
test
1
R
0
R
test
1
R
0
21
TestDataIn
test
R
TestDataOut
Le JTAG, suite
Avec tout cela, le circuit fonctionne normallement lorsque test est à 0. Et on peut, en 1000 cycles, mettre
le circuit dans un état quelconque. Il suffit de mettre test à 1, et de pousser l’état qu’on veut dans le grand
registre à décalage ainsi obtenu. Dans le même temps, l’état précédent du circuit sort sur testOut : on
peut également, toujours en 1000 cycles d’horloge, lire l’état complet du processeur. C’est ce qu’on va
faire dans ce TP pour contrôler l’exécution de notre programme en pas-à-pas, mais aussi pour observer
quand on le désire les valeurs des registres.
Mais pourquoi cela s’appele JTAG ? Parce que cela sert surtout à tester chaque puce, y compris les plus
complexes comme votre Pentium, avant de le mettre en boîte. En effet, lors du processus de fabrication,
il arrive souvent qu’une poussière malencontreuse rende un transistor inopérant. Comment détecter cette
situation pour jeter les puces défectueuses au plus tôt ?
Bien sûr, on pourrait lui faire booter Linux puis Windows et jouer un peu à Quake dessus, et on se dirait
qu’on a tout testé. Mais cela prendrait de longues minutes par puce, et le temps c’est de l’argent.
Voici une technique qui permet de tester toute la puce en quelque centaines de milliers de cycles seulement (comptez combien de cycles à 4GHz il faut pour booter Linux en 20s).
— On met test à 1, puis on pousse un état connu, pas forcément utile, dans le processeur.
— Puis on met test à 0, et on fait tourner le processeur pendant quelques centaines de cycles.
— Il fait sans doute n’importe quoi, mais ce n’est pas grave.
— On remet test à 1, et on sort l’état complet du processeur (tout en poussant un nouvel état).
— On compare l’état obtenu avec l’état (obtenu par simulation) dans lequel doit être le processeur si
chacune de ses portes fonctionne correctement. S’il y a une différence, on le jette !
— Et on recommence plusieurs fois, avec des états construits pour faire fonctionner tous les transistors
de l’énorme fonction T – pas forcément des états dans lequel le processeur peut se trouver en
fonctionnement normal.
Tout ceci est même normalisé par le Joint Test Action Group : JTAG.
Et le rapport avec notre interface JTAG ? Eh bien, une fois qu’on a ce mécanisme en place, on peut même
s’en servir pour débugger : on peut aller observer ou changer la valeur de n’importe quel registre du
processeur en quelque dizaines de milliers de cycle. Il suffit de lire l’état, changer les bits qu’on veut, et
réécrire l’état modifié. C’est comme cela que vous pourrez, dans ce TP, observer dans mspdebug ce qui
se passe à l’intérieur de votre MSP430.
Il y a même une boîte nommée JTAG interface qui permet, à travers le JTAG, d’observer aussi tout le
contenu de la RAM et des périphériques.
Bien sûr, le nombre des registres et l’ordre dans lequel ils sont chaînés dépend du microcontrôleur utilisé,
c’est pourquoi on doit passer les bons argumentsà mspdebug.
3
Assemblage et exécution d’un programme
Exercice 14
vant :
Créez un nouveau répertoire TPMSP430, et retapez 1 dans un fichier ex14.s le programme sui-
.section .init9
main:
/* initialisation de la diode rouge */
mov.b #2, &50
/* eteindre */
mov.b #0, &49
/* allumer */
mov.b #2, &49
1. Vous pouvez aussi essayer de copier-coller depuis le PDF, mais il faudra pas venir vous plaindre que ça marche pas (ce qui sera
le cas). Et puis c’est réellement formateur de retaper les exemples (si, si).
22
loop:
jmp loop
Dans ce programme,
— .section .init9 est une commande à destination de msp430-gcc pour lui indiquer où placer ce code –
voir l’encadré ci-dessous.
— mov.b est l’instruction assembleur qui réalise une copie (move) d’un octet (b pour byte).
— en assembleur msp430, #17 désigne la valeur 17, alors que &17 désigne le contenu de la case mémoire
d’adresse 17.
— donc mov.b #2, &49 est une instruction assembleur qui réalise une copie de la valeur constante 2 vers
le contenu de la case mémoire d’adresse 49. Attention, les arguments sont dans l’ordre inverse de la
commande mw de mspdebug... Moyen mnémotechnique : en assembleur MSP430, la virgule se lit “to”.
— jmp est une instruction MSP430 de saut (pour jump)
— main: et loop: sont des définitions d’étiquettes (label). Une étiquette indique une adresse au programme
assembleur. On peut les utiliser ensuite en place de la vraie adresse comme destination de sauts (ou
autres). En cas de saut relatif, le programme assembleur calculera le déplacement par soustraction de
l’adresse du saut à l’adresse de l’étiquette.
— Ici, remarquez qu’on finit notre programme par une boucle infinie dont il ne sortira pas : cela assure que
notre pointeur de programme ne part pas se balader au hasard dans la mémoire...
Exercice 15
Traduisez ce programme en un exécutable en langage machine avec la commande suivante :
msp430-gcc -mmcu=msp430fg4618 -mdisable-watchdog -o prog.elf ex14.s
Les deux options sont importantes. La première, -mmcu=msp430fg4618, indique la puce exacte ciblée. La seconde, -mdisable-watchdog, débranche le watchdog qui fait rebooter le système lorsqu’il est inactif trop longtemps. Allez lire le premier paragraphe de la page wikipedia “watchdog timer” et vous comprendrez par quel
mécanisme votre téléphone reboote lorsqu’il ralentit trop.
Attention, si on fait une faute de frappe dans cette option, il n’y aura pas de message d’erreur mais le programme
fera n’importe quoi, puisqu’il rebootera sans fin.
À savoir : assemblage et éditions de liens
Pour passer d’un programme en langage assembleur à un programme exécutable, il faut réaliser deux
opérations :
1) l’assemblage consiste à convertir un fichier texte contenant des instructions vers un fichier binaire
contenant les même instructions, mais en langage machine. L’outil qui fait ça, l’assembleur, est
typiquement nommé as (et dans notre cas msp430-as), et permet de passer d’un fichier bidule.s
à un fichier bidule.o.
Mais ce n’est pas fini : le programme consiste peut-être en plusieurs morceaux, qu’il faut maintenant
coller ensemble.
2) l’édition de liens consiste à coller ensemble plusieurs fichiers machin.o, et à placer chacun d’entre
eux aux bonnes adresses, par exemple pour s’assurer qu’ils ne se marchent pas les uns sur
les autres. L’outil qui fait ça, l’éditeur de liens, est typiquement nommé ld , et produit un fichier
truc.elf
Invoquer ces différents outils comme il faut avec les bonnes options est compliqué et souvent source
d’erreur. Heureusement, il existe aussi une commande générique gcc qui est beaucoup plus simple
d’usage, et qui se charge d’appeler as et ld dans le bon ordre et avec les bons arguments. Ainsi, vous
pouvez obtenir directement un exécutable avec la commande donnée.
Exercice 16
Désassemblez le programme obtenu par
msp430-objdump -d prog.elf
Cherchez, dans la sortie de cette commande, votre main, et répondez aux questions suivantes :
— Quel est le code binaire de l’instruction jmp loop ?
— A quelle adresse est-elle assemblée ?
23
— Est-ce un saut relatif ou un saut absolu ?
— Qui s’est permis de rajouter toutes ces instructions autour de votre programme ?
Depuis mspdebug, transférez votre programme sur la carte en utilisant la commande
prog prog.elf , puis lancez-le avec la commande run . Constatez que la diode reste toujours allumée (c’est
normal, on ne l’éteint jamais). Interrompez l’exécution en appuyant sur Ctrl+C.
Exercice 17
4
Exécution d’un programme pas à pas
Exercice 18 Copiez ex14.s en un nouveau fichier ex18.s, déplacez les instructions d’allumage et d’extinction
à l’intérieur de la boucle infinie : le but est de faire clignoter la diode. Assemblez par msp430-gcc, chargez par
load puis exécutez de nouveau votre programme par run dans mspdebug.
Si tout va bien, on dirait que la diode reste encore toujours allumée. C’est peut-être que vous vous êtes trompés.
C’est peut-être aussi qu’elle clignote bien, mais trop rapidement pour notre œil. En effet, la fréquence du CPU
est de 1MHz, et chaque instruction prend une poignée de cycles d’horloge, donc notre boucle tout entière tourne
à plus de 100kHz.
Interrompez de nouveau l’exécution, et au lieu de la relancer avec run , utilisez cette fois la commande step
qui exécute une seule instruction machine (faites donc help run et help step au passage).
Constatez qu’en exécutant ainsi le programme en mode pas-à-pas, on arrive maintenant à voir ce qui se passe.
Décidez ainsi si la diode clignote ou si vous vous êtes plantés. Auquel cas, corrigez.
5
Programmation en assembleur : variables et boucles
Vous allez maintenant devoir modifier votre programme un peu plus sérieusement. Pour la syntaxe de l’assembleur MSP430, aidez-vous des explications qui sont données dans les deux encadrés page 26 et page 27.
Débuggage : points d’arrêts
Pour la mise au point, utilisez mspdebug. En plus des commande qu’on a vues jusqu’ici, vous aurez peutêtre besoin de la commande md (memory display) pour lire la mémoire, et de setbreak pour mettre
des points d’arrêt.
Pour plus de détails, help md et help setbreak.
Exercice 19
Introduisons d’abord les registres et les opérations logiques. Modifiez le programme comme suit :
.section .init9
main:
mov.b #2, &50
mov
#2, r15
loop:
mov.b r15, &49
xor #2, r15
jmp loop
/* initialisation de la diode */
/* valeur initiale de la valeur de la diode */
/* transferer r15 vers la diode */
/* que fait cette ligne? */
La nouveauté est l’utilisation l’un des registres introduits dans le dessin de la page 19. L’instruction xor #2, r15
met dans r15 le ou-exclusif (xor), bit à bit, de r15 et de la valeur 2. Remarquez que nous travaillons sur r15 avec
des instruction sans le suffixe .b : ces instructions travaillent sur 16 bits, pas juste 8. Essayez de prédire ce
que fait ce programme. Executez ce programme pas-à-pas, et observez dans la fenêtre mspdebug la valeur du
registre r15 au cours de l’exécution.
Exercice 20 Ajoutez au programme précédent ce qu’il faut pour que, tout en faisant clignoter la diode, il compte
dans le registre r14 (il ajoute à chaque tour de boucle 1 au registre r14). Vérifiez que r14 augmente bien dans
mspdebug.
24
Exercice 21 Modifiez votre programme afin de ralentir suffisamment la boucle infinie pour pouvoir observer le
clignotement à l’oeil nu. Pour cela, vous allez rajouter, à l’intérieur de la boucle existante, une seconde boucle
qui ne fait rien sauf perdre du temps. Ce sera l’équivalent d’une boucle for : elle incrémente un registre jusqu’à atteindre une certaine valeur, par exemple 20000. Attention, 20000 tient sur 16 bits mais pas sur 8 bits :
utilisez des instructions sans l’extension .b. Pour sortir de cette boucle, vous pourrez utiliser une instruction de
comparaison CMP et un saut conditionnel, par exemple JNE.
C’est une question difficile (la première fois, après cela va tout seul) : si la page 26 ne vous suffit pas, ne restez
pas bloqué, faites appel à un enseignant.
25
Survol de la syntaxe assembleur du msp430
On vous présente ici la syntaxe que vous allez devoir utiliser en TP. Elle est en général insensible à la
casse (majuscules ou minuscules, c’est pareil).
Opérations La plupart des instructions est de la forme OPCODE SRC, DST . OPCODE est l’opération souhaitée, par exemple ADD, XOR, MOV, etc. La liste complète est donnée page suivante. SRC et DST indiquent
les opérandes (source et destination) sur lesquels travailler. La destination est aussi le second opérande de l’opération, ainsi la virgule peut souvent se lire “to”. Par exemple ADD #1, R5 peut se lire
“ADD 1 to R5” et, en C++, s’écrirait R5=R5+1;. Une instruction spéciale est l’instruction MOV, par exemple
MOV R7, R5 , qui peut se lire “MOV R7 to R5” et s’écrirait en C++ R5=R7; a
En détail, chaque opérande est de l’une des formes suivantes :
— un nom de registre : R7, R15... (utilisez les numéros, pas de «SP» ni «PC» etc.)
— une constante immédiate, à préfixer par # : #42, #0xB600...
— le contenu d’une case mémoire désignée par son addresse, à préfixer b par & : &1234, &0x3100...
— le contenu d’une case mémoire dont l’adresse est la valeur contenue dans un registre, alors ce
registre est préfixé par @. Par exemple MOV R7, @R5 , s’écrirait en C++ ainsi : *R5=R7;
Par exemple, l’instruction ADD &1000, R5 calcule la somme de R5 et de la valeur contenue dans la
case d’adresse 1000, et range le résultat dans R5. Attention, certaines combinaisons n’ont pas de sens,
et seront rejetées par l’assembleur avec un message d’erreur. Par exemple l’instruction MOV R8, #36 ne
veut rien dire.
Certaines instructions travaillent sur un seul opérande, et ont donc une syntaxe légèrement différente. Par
exemple INV DST inverse chacun des bits de DST, ou CLR DST met DST à zéro. Reportez-vous à la
liste page ci-contre pour plus de détails, et/ou à la doc : msp430x4xx.pdf pages 56 et suivantes.
Drapeaux Certaines instructions, notamment les opérations arithmétiques et logiques, modifient le registre d’état (R2, cf encadré page 44), en particulier les drapeaux Z, N, C, V :
— Z est le Zero bit. Il passe à 1 lorsque le résultat d’une opération est nul, et il passe à 0 lorsqu’un
résultat est non-nul.
— N est le Negative bit. Il passe à 1 lorsque le résultat d’une opération est négatif (en complément à
deux) et il passe à 0 lorsqu’un résultat est non-négatif.
— C est le Carry bit. Il passe à 1 lorsqu’un calcul produit une retenue sortante, et il passe à 0 lorsqu’un
calcul ne produit pas de retenue sortante.
— V est le Overflow bit. Il est mis à 1 lorsque le résultat d’une opération arithmétique déborde de la
fourchette des valeurs signées (en complément à deux), et à 0 sinon.
La liste page ci-contre détaille l’effet de chaque instruction sur les quatre drapeaux : un tiret lorsque le
drapeau n’est pas affecté, un 1 ou un 0 lorsque le drapeau passe toujours à une certaine valeur, et une
étoile lorsque l’effet sur le drapeau dépend du résultat.
Sauts conditionnels Les instructions de branchement sont de la forme JUMP label . Regardez par
exemple le programme page 22. Le saut peut être soit inconditionnel (instruction JMP), soit soumis à une
condition sur les drapeaux. Par exemple, l’instruction JNZ label est un Jump if Non-Zero : elle sautera
vers label si et seulement si le bit Z est faux.
Opérandes «word» ou «byte» Chaque instruction peut travailler sur des mots de 16 bits (par défaut), ou sur des octets (il faut pour cela remplacer OPCODE par OPCODE.B) . Par exemple, l’instruction
MOV.B R10, &42 copie les 8 bits de poids faible de R10 vers l’octet situé à l’adresse 42, alors que l’instruction MOV R10, &42 copie tout le contenu de R10 vers les deux octets situés aux adresses 42 et 43 c .
Reportez-vous à l’encadré page 46 pour plus de détails.
a. Et donc en termes Unix c’est cp, pas mv.
b. Si par mégarde on écrit mov 42, R5 au lieu d’écrire mov #42, R5 alors non seulement ça ne cause aucun message
d’erreur, mais surtout le programme fera n’importe quoi. Vous voila prévenu. Et si vous voulez savoir ce qui se passe dans ce
cas, assemblez puis désassemblez, puis cherchez dans la doc ce qu’on vous a caché.
c. Précision : les 8 bits de poids faible vont en 42, et les 8 bits de poids fort vont en 43. On dit que le msp430 est de
type little-endian. Allez lire https://fr.wikipedia.org/wiki/Endianness si c’est la première fois que vous voyez ce mot.
26
Liste compacte des instructions MSP430
Description
Operation
V
N
Z
ADC(.B)
dst
Add C to destination
dst + C → dst
*
*
*
*
ADD(.B)
src,dst
Add source to destination
src + dst → dst
*
*
*
*
ADDC(.B)
src,dst
Add source and C to destination
src + dst + C → dst
*
*
*
*
AND(.B)
src,dst
AND source and destination
src .and. dst → dst
0
*
*
*
Mnemonic
C
BIC(.B)
src,dst
Clear bits in destination
.not.src .and. dst → dst
−
−
−
−
BIS(.B)
src,dst
Set bits in destination
src .or. dst → dst
−
−
−
−
BIT(.B)
src,dst
Test bits in destination
src .and. dst
0
*
*
*
BR
dst
Branch to destination
dst → PC
−
−
−
−
CALL
dst
Call destination
PC+2 → stack, dst → PC
−
−
−
−
CLR(.B)
dst
Clear destination
0 → dst
−
−
−
−
0
CLRC
Clear C
0→C
−
−
−
CLRN
Clear N
0→N
−
0
−
−
CLRZ
Clear Z
0→Z
−
−
0
−
*
CMP(.B)
src,dst
Compare source and destination
dst − src
*
*
*
DADC(.B)
dst
Add C decimally to destination
dst + C → dst (decimally)
*
*
*
*
DADD(.B)
src,dst
Add source and C decimally to dst.
src + dst + C → dst (decimally)
*
*
*
*
*
DEC(.B)
dst
Decrement destination
dst − 1 → dst
*
*
*
DECD(.B)
dst
Double-decrement destination
dst − 2 → dst
*
*
*
*
Disable interrupts
0 → GIE
−
−
−
−
−
DINT
Enable interrupts
1 → GIE
−
−
−
INC(.B)
dst
Increment destination
dst +1 → dst
*
*
*
*
INCD(.B)
dst
Double-increment destination
dst+2 → dst
*
*
*
*
.not.dst → dst
EINT
INV(.B)
dst
Invert destination
JC/JHS
label
Jump if C set/Jump if higher or same
JEQ/JZ
label
Jump if equal/Jump if Z set
−
−
−
−
JGE
label
Jump if greater or equal
−
−
−
−
−
JL
label
Jump if less
JMP
label
Jump
JN
label
Jump if N set
PC + 2 x offset → PC
*
*
*
*
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
JNC/JLO
label
Jump if C not set/Jump if lower
−
−
−
−
JNE/JNZ
label
Jump if not equal/Jump if Z not set
−
−
−
−
MOV(.B)
src,dst
Move source to destination
−
−
−
−
−
−
−
−
−
src → dst
No operation
NOP
POP(.B)
dst
Pop item from stack to destination
@SP → dst, SP+2 → SP
−
−
−
PUSH(.B)
src
Push source onto stack
SP − 2 → SP, src → @SP
−
−
−
−
Return from subroutine
@SP → PC, SP + 2 → SP
−
−
−
−
*
RET
Return from interrupt
*
*
*
RLA(.B)
dst
Rotate left arithmetically
*
*
*
*
RLC(.B)
dst
Rotate left through C
*
*
*
*
RRA(.B)
dst
Rotate right arithmetically
0
*
*
*
RRC(.B)
dst
Rotate right through C
*
*
*
*
SBC(.B)
dst
RETI
Subtract not(C) from destination
dst + 0FFFFh + C → dst
*
*
*
*
SETC
Set C
1→C
−
−
−
1
SETN
Set N
1→N
−
1
−
−
SETZ
Set Z
1→C
−
−
1
−
*
SUB(.B)
src,dst
Subtract source from destination
dst + .not.src + 1 → dst
*
*
*
SUBC(.B)
src,dst
Subtract source and not(C) from dst.
dst + .not.src + C → dst
*
*
*
*
SWPB
dst
Swap bytes
−
−
−
−
SXT
dst
Extend sign
0
*
*
*
TST(.B)
dst
Test destination
dst + 0FFFFh + 1
0
*
*
1
XOR(.B)
src,dst
Exclusive OR source and destination
src .xor. dst → dst
*
*
*
*
Remarque chacune de ces instructions est documentée en détail dans la doc (msp430x4xx.pdf, section
3.4). Il faut s’y reporter si vous avez besoin de précisions.
27
6
Memory-mapped IO
À savoir : Les entrées-sorties
Du point de vue du processeur, un périphérique se présente comme un ensemble de registres (au sens
du cours d’AC), qui permettent d’échanger de l’information entre le CPU et le périphérique.
On peut distinguer informellement trois sortes de registres dans un périphérique :
— les registres d’état du périphérique fournissent de l’information sur l’état du périphérique : est-il actif,
est-il prêt, a-t-il quelquechose à dire, etc. Ils sont typiquement accessibles en lecture seulement : le
processeur peut lire leur contenu, mais pas le modifier.
— les registres de contrôle ou de configuration du périphérique sont utilisés par le CPU pour configurer
et contrôler le périphérique. Ils seront typiquement accessibles en lecture-écriture, ou parfois en écriture
seulement.
— les registres de données du périphérique permettent de lui envoyer des données (en écrivant dedans
depuis le CPU) ou de recevoir des données de la part d’un périphérique (en lisant dedans).
Contrôleur de périphérique
reg. d’état
CPU
reg. de configuration
Périphérique
(optionnel)
Monde
extérieur
reg. de données
Tout cela est assez informel. Dans certains cas, un même registre peut appartenir à plusieurs de ces
catégories, par exemple s’il contient à la fois des informations d’état (en lecture seule) et des information
de configuration (en lecture/écriture).
La circuiterie contenant ces registres est appelée le contrôleur du périphérique. La plupart des boites sur
la figure de la page 18 sont des contrôleurs de périphériques. Physiquement parlant, le contrôleur est
parfois situé sur le périphérique lui-même, par exemple un contrôleur de disque dur. Parfois au contraire
il est placée plus près du processeur (ceux de la page 18 sont tous intégrés sur la même puce). et reliée
ensuite au périphérique proprement dit par un moyen quelconque. Par exemple, votre carte vidéo est
reliée à votre écran par un câble VGA ou HDMI. L’architecture générique est illustrée ci-dessous :
Les registres matériels doivent pouvoir être accédés individuellement par le CPU. Comme pour les
cases mémoire, on leur donne donc chacun une adresse distincte. Certains processeurs distinguent les
adresses de mémoire et les adresses de registres matériels ; ils offrent alors des instructions distinctes
pour accéder aux uns et aux autres. À l’inverse, la majorité des processeurs, dont notre MSP430, utilisent un unique espace d’adressage : certaines adresses correspondent à de la mémoire, et d’autres
à des registres matériels. Les entrées-sorties se font alors avec les mêmes instructions que les accès
mémoire classiques. De plus, les contrôleurs de périphériques et la mémoire se partagent les mêmes
bus d’adresse et de donnée : à nouveau, voir la figure de la page 18.
On parle alors d’entrées/sorties «projetées en mémoire», ou Memory-Mapped Input/Output.
28
Utile pour le TP : le plan mémoire du msp430
Du point de vue du CPU, la mémoire principale et les périphériques se présentent tous comme des
cases mémoire. Certains registres matériels font 16 bits, et occupent donc deux adresses consécutives
(à gauche sur le schéma ci-dessous). Certains autres registres ne font que 8 bits, et occupent une seule
adresse (à droite sur le schéma ci-dessous). Vous aurez aussi remarqué que la «mémoire» est elle-même
composée d’une région de RAM (en lecture-écriture) et d’une région de mémoire flash (en lecture seule).
Pour s’y retrouver, la documentation technique nous indique le «plan d’adressage» (en VO, la memory
map) c’est à dire une cartographie des différentes régions de l’espace d’adressage de la machine :
Address
FFFFh
Access
Interrupt Vector Table
Word/Byte
Flash/ROM
Word/Byte
RAM
Word/Byte
Reserved
No access
FFC0h
FFDFh
FFBFh
3100h
30FFh
1100h
01FFh
16-Bit Peripheral Modules
Word
8-Bit Peripheral Modules
Byte
Special Function Registers
Byte
0100h
00FFh
0010h
000Fh
0000h
Exercice 22 Pour allumer notre diode on écrivait aux adresses 50 et 49. Traduisez-les en hexa (de tête !) et
placez-les sur le plan mémoire.
Exercice 23 Cherchez la diode LED4 sur le schéma de la page 17. Comment s’appelle la broche du processeur auquel elle est reliée ? La partie intéressante commence par P (comme Port d’entrée-sortie).
Exercice 24
Même question pour les deux autres diodes LED1 et LED2.
Exercice 25
Entourez, parmi les périphériques de la figure de la page 18, ceux qui commandent ces diodes.
Ces broches sont des general purpose input/outputs, ou GPIO.
Allez lire l’introduction du chapitre 11 de msp430x4xx.pdf. Elles peuvent être configurés en entrée (I) ou en sortie
(O). Ce choix se fait par écriture dans un registre de contrôle. Pour le port 5 ce registre s’appelle P5DIR et est
mappé à l’adresse 50. Vous reconnaissez le 50 ? Ces registres font 8 bits, ainsi les GPIO vont par 8.
Exercice 26 Retrouvez l’adresses de P5DIR dans le tableau de la page 413 de msp430x4xx.pdf. Qu’est-ce
qui est mappé à l’adresse 49 ?
Exercice 27
Vous avez à présent tout ce qu’il faut pour savoir comment allumer les deux autres diodes.
Exercice 28 Le buzzer (cherchez-le sur les schémas) se commande comme une diode : pour jouer une note,
il suffit de le faire “clignoter” à la bonne fréquence. Essayez de jouer une note. Vous pouvez faire monter et
descendre la note pour faire des bruits de sirènes... faites un concours de sons et lumières.
29
7
S’il reste du temps
Exercice 29 Écrivez (et testez) un programme qui lit un argument dans R15, et le sort en binaire sur nos deux
diodes : une diode clignote comme une horloge, et une autre diode s’allume pour les bits à 1 (en commençant
par les bits de poids faible).
Exercice 30
Écrivez une procédure qui divise R14 par R15 et renvoie le résultat dans R15.
30
Partie IV:
MSP430 - Convention d’appel
Dans cette partie, on va s’intéresser aux conventions d’appel, c’est à dire à la façon dont les différentes fonctions
d’un programme communiquent entre elles. Créez un nouveau répertoire TP3 et mettez tous vos nouveaux
fichiers là-dedans.
À savoir : le rôle de la convention d’appel
Lorsqu’on se situe au niveau assembleur, le langage reflète directement l’architecture de la machine : registres, instructions, etc. On ne retrouve donc pas les notions classiques des langages de programmation :
variables, boucles, conditionnelles, qu’il faut implémenter explicitement en utilisant le jeu d’instructions
disponibles.
Pour les appels de fonction (aka «procédure», «méthode», «sous-programme», «routine») la plupart des
architectures offrent des instructions dédiées. Ces instructions s’appellent par exemple CALL et RET sur
msp430 (et sur x86), ou BL et BX sur ARM. Ainsi, CALL #func saute vers la fonction func, et RET
retourne vers la fonction appelante. Mais ces instructions ne s’occupent pas particulièrement des arguments ni des résultats. Afin que nos différentes fonctions puissent communiquer entre elles, il faut donc
se mettre d’accord, entre appelant et appelé, sur la façon de se passer les paramètres et de rendre les
résultats : c’est la convention d’appel.
1
Appels de fonctions en assembleur
Exercice 31 Recopiez le programme ci-dessous dans un fichier tp3.s. Identifiez dans le code machine, les
zones qui correspondent aux différentes fonctions.
.section .init9
main:
/* emballage des arguments */
MOV #6, R5
MOV #7, R6
call #mult
/* Le resultat est suppose retourne dans le registre R7 */
MOV R7, R15
/* infinite loop */
done:
jmp done
mult:
MOV #0, R7
ret
Rappel : la chaîne de compilation
Vous avez déjà vu toutes les commandes utiles dans les TP précédents :
— Pour assembler un programme ASM vers du langage machine :
msp430-as -mmcu=msp430fg4618 -o truc.o truc.s
— Pour faire l’édition de liens entre un ou plusieurs modules et obtenir un exécutable :
msp430-gcc -mmcu=msp430fg4618 -mdisable-watchdog -o bidule.elf truc1.o truc2.o
— Pour désassembler un exécutable et obtenir un fichier texte lisible à l’oeil nu :
msp430-objdump -d machin.elf > machin.lst
31
Exercice 32 On veut maintenant que la fonction mult calcule la multiplication a × b. Écrivez le code nécessaire
pour additionner a sur lui-même b fois. Notre convention d’appel sera la suivante :
— à l’entrée dans la fonction, R5 et R6 contiennent les arguments, qui sont supposés strictement positifs et
“pas trop grands” : on suppose que a et b sont assez petits pour que a × b tienne sur 16 bits.
— au retour de la fonction, R7 contient la valeur de retour.
Débuggez ! Puis, faites valider par un enseignant.
2
Étude du fonctionnement détaillé de call et ret
Dans cette partie, on s’intéresse un peu plus en détail à ce qui se passe dans le processeur au moment de
l’exécution des instructions call et ret.
Commencez par lire l’extrait de documentation ci-dessous qui parle de la pile. En particulier vous devez en
déduire que le registre R1 a un rôle particulier et qu’il doit être manipulé avec beaucoup de précaution : en
particulier vous ne devez jamais vous en servir pour garder des résultats intermédiaires de vos calculs.
Exercice 33 Reprenez votre programme main précédent et, avec le débuggeur, placez-vous juste avant l’instruction call #mult. Notez la valeur contenue dans le registre SP. Effectuez le saut (step dans le débuggeur).
Notez la nouvelle valeur contenue dans SP. Observez la mémoire à ces deux adresses et comprenez ce qui y
est stockée. En particulier, comparez ces valeurs avec les adresses des instructions autour du call.
Exercice 34 Continuez d’exécuter votre programme jusqu’à atteindre l’instruction ret. Observez de nouveau
la mémoire autour de l’adresse contenue dans SP avant et après le ret.
Exercice 35 Recopiez le programme ci-dessous dans un nouveau fichier. Recopiez également votre version
mult. Et surtout implémentez la routine sumprod telle que spécifiée.
.section .init9
main:
/* Ici, il y a du travail */
/* TODO: preparer les parametres de sumprod */
call #sumprod
mov r14,r15
/*
sumprod(v1,c1,v2,c2) computes v1*c1+v2*c2.
inputs: in r10,r11,r12,r13 respectively
outputs: in r14
note: uses mult :)
*/
sumprod:
/* Ici, il y a du travail! */
/* TODO: realisez la fonction attendue en vous servant de mult */
mov #0, r14
ret
/*
mult(a,b) computes a*b
inputs: r5,r6
outputs: r7
notes: Votre version copiee-colee ici ! */
mult:
mov #0, r7
ret
Exercice 36 A l’aide du débuggeur, suivez l’évolution des adresses contenue dans SP et de la zone mémoire
correspondante. En particulier, comprenez ce qui s’est passé dans la pile lorsque vous êtes en train d’exécuter
le deuxième appel à mult.
32
Extrait de la documentation : msp430x4xx.pdf page 45
3.2.2
CPU Registers
Stack Pointer (SP)
The stack pointer (SP/R1) is used by the CPU to store the return addresses
of subroutine calls and interrupts. It uses a predecrement, postincrement
scheme. In addition, the SP can be used by software with all instructions and
addressing modes. Figure 3−3 shows the SP. The SP is initialized into RAM
by the user, and is aligned to even addresses.
Figure 3−4 shows stack usage.
Figure 3−3. Stack Pointer
15
1
0
Stack Pointer Bits 15 to 1
MOV
MOV
PUSH
POP
2(SP),R6
R7,0(SP)
#0123h
R8
;
;
;
;
0
Item I2 −> R6
Overwrite TOS with R7
Put 0123h onto TOS
R8 = 0123h
Figure 3−4. Stack Usage
Address
PUSH #0123h
POP R8
0xxxh
I1
I1
I1
0xxxh − 2
I2
I2
I2
0xxxh − 4
I3
I3
I3
SP
SP
0123h
0xxxh − 6
SP
0123h
0xxxh − 8
The special cases of using the SP as an argument to the PUSH and POP
instructions are described and shown in Figure 3−5.
Figure 3−5. PUSH SP - POP SP Sequence
Extrait de la documentation : msp430x4xx.pdf page 69
PUSH SP
POP SP
SPold
SP1
SP1
The stack pointer is changed after
a PUSH SP instruction.
SP2
SP1
The stack pointer is not changed after a POP SP
instruction. The POP SP instruction places SP1 into the
stack pointer SP (SP2=SP1)
RISC 16-Bit CPU
33
3-5
Extrait de la documentation : msp430x4xx.pdf page XX
34
Partie V:
MSP430 - Interruptions
L’objectif de cette est d’étudier un rouage essentiel dans le fonctionnement des ordinateurs, le mécanisme des
interruptions matérielles. Après avoir vu le coté implémentation matérielle lors du TP micro-machine, nous allons
étudiez les aspects logiciels liés.
En particulier, on va s’intéresser aux notions suivantes : requête d’interruption (IRQ), vecteur d’interruption,
masquage d’interruption, routine de traitement d’interruption (ISR), sauvegarde de contexte, acquittement d’interruption.
Mais avant ça, on va prendre en main les bouttons, sans parler d’interruption.
1
Boutons et attente active
Exercice 37 Sur la carte, trouvez à quelles broches du processeur (et donc quel prot GPIO) les deux boutons
sont connectés.
Exercice 38
Recopiez le bout de code suivant dans un nouveau fichier.
.section .init9
main:
/* Ici, il y a du travail */
/* On veut configurer les bons ports en mode ‘‘entree’’*/
/* Et on n’oublie pas de configurer les bons ports pour pouvoir allumer la LED1 */
loop:
/* Puis on veut attendre que l’un des boutons soit appuye*/
/*
/*
/*
/*
Lorsqu’un appui a eu lieu, on veut : */
- allumer LED1 si elle etait eteinte ou */
- l’inverse */
nb: rappelez-vous que ca peut se faire en une seule instruction :) ! */
/* on recommence */
jmp loop
Exercice 39 Commencez par configurer le port que vous avez identifié à la question précédente pour qu’il
agisse comme une entrée.
Exercice 40 Maintenant écrivez le bout de programme qui vous permet d’attendre que le bouton soit appuyé.
Commencez par l’écrire en pseudo-c, puis traduisez-le en assembleur.
Exercice 41 Bien sûr, n’oubliez pas de complétez à la fois la configuration et le code de la boucle principale
pour faire clignoter la led en alternant son état à chaque fois que vous appuyez sur le bouton.
2
Boutons et interruptions
Un inconvénient majeur de ce qu’on a fait jusqu’ici c’est que le processeur est entièrement occupé à attendre
que l’on appuie sur un bouton. C’est cela qu’on appelle attente active (polling en langue de Shakespeare).
Pour remedier à ce problème, on peut configer le processeur pour qu’il réagisse aux appuis boutons à travers
son mécanisme d’interruption. Entre les appuis boutons, le processeur va ainsi être libre d’exécuter autre chose !
Dans un premier temps, lisez les deux encadrés ci-dessous, pour vous rafraîchir la mémoire sur les différentes
notions mises en jeu et pour commencer à comprendre comment les interruptions sont implémentées matériellement dans le cas du msp430 et de notre carte.
À savoir : scrutation VS interruptions, requêtes (IRQ), vecteur, routine de traitement (ISR)
La communication entre un périphérique et le processeur se fait en général au travers des registres
matériels. Un composant qui veut transmettre une information au programme place cette information dans
un de ses registres, et attend que le processeur vienne lire cette valeur. C’est cette technique, appelée
scrutation (en anglais polling) que vous avez utilisée dans un TP précédent pour connaître l’état des
35
boutons. Un inconvénient majeur de cette approche est son incapacité à passer à l’échelle : pour ne pas
rater un évènement, le programme doit continuellement aller scruter l’état du matériel, ce qui monopolise
le processeur.
L’alternative consiste à mettre en place un mécanisme d’interruptions (cf poly page 73). Dans ce cas, le
composant qui veut transmettre une information au programme place cette information dans l’un de ses
registres, puis envoie au processeur une requête d’interruption (en anglais interrupt request ou IRQ). Selon les architectures, ces requêtes peuvent transiter sur le bus principal ou sur des fils dédiés appelés des
lignes d’interruptions. Dans ce cas-là, soit le processeur dispose d’une entrée pour chaque source d’interruptions, soit comme illustré ci-dessous, les lignes d’interruptions sont concentrées par un périphérique
dédié, appelé le contrôleur d’interruption.
IRQ
CPU
Interrupt
controller
Periph
Periph
system bus
Periph
Periph
Periph
Du côté du processeur, la gestion des interruptions est intégrée au cycle de Von Neumann. Lorsqu’il
reçoit une requête, le processeur interrompt automatiquement l’exécution du programme et saute vers
une adresse bien connue, à laquelle il s’attend à trouver une routine de traitement spécifique (en anglais
interrupt service routine ISR, ou interrupt handler ). Chaque ligne d’interruption est ainsi associée à une
routine distincte, ce qui permet au programmeur de prévoir un traitement différent pour chaque type
d’évènement. La correspondance {requête n1 → routine r1 , requête n2 → routine r2 , etc.} est implémentée
par une structure de données appelée la table des vecteurs d’interruption (interrupt vector table ou IVT).
En général il s’agit d’un tableau de pointeurs de fonction, chaque case contenant l’adresse d’une ISR.
Une routine d’interruption est un morceau de code similaire à une fonction, sauf qu’elle est invoquée
automatiquement par le processeur, et non pas par un appel explicite. En plus de traiter l’évènement
proprement dit en allant lire et/ou écrire dans les registres du périphérique concerné, une ISR devra
typiquement accuser réception de l’interruption auprès du périphérique et/ou du contrôleur d’interruption.
Ensuite, elle peut se terminer et restaurer le contexte d’exécution, c’est à dire reprendre l’exécution du
programme interrompu, qui ne se sera aperçu de rien.
36
Utile pour le TP : les interruptions sur le MSP430
Le fonctionnement des interruptions est détaillé au chapitre 2.2 de la documentation (msp430x4xx.pdf
pages 29 et suivantes) et nous en reprenons les grandes lignes ici. Le CPU du MSP430 ne dispose pas
d’une ligne d’interruption distincte pour chaque périphérique, mais d’une seule ligne partagée par tous
les périphériques. Un composant (par exemple le Module 2) qui veut lever une interruption fait passer
son interrupt flag à 1 (dans notre exemple, il s’agit donc du bit M2IFG). Certains modules ont plusieurs
drapeaux, mais le fonctionnement reste similaire (les petits carrés noirs du schéma représentent des bits
accessibles en mémoire dans un registre matériel). Le signal traverse les autres périphériques et atteint
le processeur. Celui-ci perçoit donc une requête d’interruption (IRQ) lorsque :
— au moins un des périphériques a un interrupt flag levé,
— et le bit GIE du registre SR est vrai (bit Global Interrupt Enable du Status Register )
Interrupt Priority
IRQ
High
GIE
CPU
Low
Module 1
Module 2
Module 3
M3IFG1
M3IFG2
M2IFG
M1IFG
Module X
MXIFG
Module Y
MYIFG
Bus Grant
MAB − 5LSBs
Pour savoir de qui vient la requête, le processeur passe alors le signal Bus Grant à 1. Mais il se peut
que plusieurs périphériques aient des flags levés, et dans ce cas-là on veut les départager par ordre de
priorité. Chaque module, grâce à la circuiterie illustrée ci-dessous, émet donc son propre numéro si et
seulement si :
— ce module a une interrution en attente (i.e. son interrupt flag est levé),
— et aucun module plus prioritaire n’a d’interruption en attente (cf fil de gauche sur le schéma),
— et le signal Bus Grant venant du CPU est actif (cf fil venant du bas).
Il y a donc bien un et un seul numéro d’IRQ envoyé au processeur (dans notre exemple, le nombre 2 codé
sur cinq bits : 0b00010).
Module X
IRQ num = X
MXIFG
5
5
MAB
Bus Grant
Lorsqu’il reçoit ce numéro, le CPU l’utilise comme indice dans la table des vecteurs, et charge le vecteur
dans PC, ce qui revient à sauter à l’adresse de l’ISR.La table est située en mémoire flash à l’adresse
0xFFC0, donc le vecteur numéro x est le mot d’adresse 0xFFC0+2x.
Exercice 42 En vous aidant de l’encadré de la présente page, configurez le port P1 pour qu’il génère une
interruption lors de l’appui sur un bouton. Remarque : tant que cette interruption ne sera pas rattrapée
correctement (vers la question 50), vous ne pourrez pas tester par vous-même que vos réponses sont
37
correctes. Du coup, soit vous comprenez ce que vous faites et vous avancez jusqu’à la question 50, soit
vous avez du mal et alors demandez-nous de l’aide pour vous débloquer. Mais ne perdez pas trop de
temps à patauger.
Exercice 43 Écrivez une routine qui inverse l’état de la LED. Utilisez une étiquette temporaire temp_isr:.
Nous verrons plus loin par quoi la remplacer.
Il reste maintenant à “brancher” ce traitant à la requête d’interruption que vous avez configuré juste avant.
3
Interruption sur bouton poussoir
La première chose à faire consiste à faire en sorte que lorsqu’on appuie sur un des boutons, une interruption
soit émise vers le processeur.
Exercice 44
Commencez par lire l’encadré ci-dessous, qui parle de la gestion des interruptions sur le port P1.
38
Extrait de la documentation : msp430x4xx.pdf pages 411 et 412
Digital I/O Operation
11.2.6 P1 and P2 Interrupts
Each pin in ports P1 and P2 have interrupt capability, configured with the
PxIFG, PxIE, and PxIES registers. All P1 pins source a single interrupt vector,
and all P2 pins source a different single interrupt vector. The PxIFG register
can be tested to determine the source of a P1 or P2 interrupt.
Interrupt Flag Registers P1IFG, P2IFG
Each PxIFGx bit is the interrupt flag for its corresponding I/O pin and is set
when the selected input signal edge occurs at the pin. All PxIFGx interrupt
flags request an interrupt when their corresponding PxIE bit and the GIE bit
are set. Each PxIFG flag must be reset with software. Software can also set
each PxIFG flag, providing a way to generate a software-initiated interrupt.
Bit = 0: No interrupt is pending
Bit = 1: An interrupt is pending
Only transitions, not static levels, cause interrupts. If any PxIFGx flag becomes
set during a Px interrupt service routine or is set after the RETI instruction of
a Px interrupt service routine is executed, the set PxIFGx flag generates
another interrupt. This ensures that each transition is acknowledged.
Note:
PxIFG Flags When Changing PxOUT or PxDIR
Writing to P1OUT, P1DIR, P2OUT, or P2DIR can result in setting the
corresponding P1IFG or P2IFG flags.
Note:
Digital I/O Operation
Length of I/O Pin Interrupt Event
Any external interrupt event should be at least 1.5 times MCLK or longer, to
ensure that it is accepted and the corresponding interrupt flag is set.
Interrupt Edge Select Registers P1IES, P2IES
Each PxIES bit selects the interrupt edge for the corresponding I/O pin.
Bit = 0: The PxIFGx flag is set with a low-to-high transition
Bit = 1: The PxIFGx flag is set with a high-to-low transition
Note:
Writing to PxIESx
Writing to P1IES or P2IES can result in setting the corresponding interrupt
flags.
PxIESx
0→1
0→1
1→0
1→0
PxINx
0
1
0
1
PxIFGx
May be set
Unchanged
Unchanged
May be set
Interrupt Enable P1IE, P2IE
Digital I/O
Each PxIE bit enables the associated PxIFG interrupt flag.
Bit = 0: The interrupt is disabled
Bit = 1: The interrupt is enabled
11.2.7 Configuring Unused Port Pins
39
11-5
Exercice 45 Dans votre programme, rajouter les instructions nécessaires pour activer les interruptions du port
1. Rappel, les addresses auxquel les registres permettant de configurer P1 sont données à la page 413 de la
documentation msp430x4xx.
Exercice 46 Par défaut, le processeur n’entend pas les interruptions. Il faut donc activer les interruptions explicitement. Le processeur fournit une instruction pour ça, eint. Allez vérifier son comportement et sa syntaxe,
page 80 de la doc. msp430x4xx.
Il reste maintenant à étiquetter convenablement votre traitant d’interruption pour que le compilateur l’associe au
bon vecteur d’interruption.
Exercice 47 En vous aidant de l’encadré page précédente et de l’extrait de configuration ci-dessous, déterminez le numéro du vecteur d’interruption associé à P1.
Extrait des fichiers de configuration : msp430fg4618.h
/************************************************************
* Interrupt Vectors (offset from 0xFFC0)
************************************************************/
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
DAC12_VECTOR
DMA_VECTOR
BASICTIMER_VECTOR
PORT2_VECTOR
USART1TX_VECTOR
USART1RX_VECTOR
PORT1_VECTOR
TIMERA1_VECTOR
TIMERA0_VECTOR
ADC12_VECTOR
USCIAB0TX_VECTOR
USCIAB0RX_VECTOR
WDT_VECTOR
COMPARATORA_VECTOR
TIMERB1_VECTOR
TIMERB0_VECTOR
NMI_VECTOR
RESET_VECTOR
(0x001C)
(0x001E)
(0x0020)
(0x0022)
(0x0024)
(0x0026)
(0x0028)
(0x002A)
(0x002C)
(0x002E)
(0x0030)
(0x0032)
(0x0034)
(0x0036)
(0x0038)
(0x003A)
(0x003C)
(0x003E)
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
0xFFDC
0xFFDE
0xFFE0
0xFFE2
0xFFE4
0xFFE6
0xFFE8
0xFFEA
0xFFEC
0xFFEE
0xFFF0
0xFFF2
0xFFF4
0xFFF6
0xFFF8
0xFFFA
0xFFFC
0xFFFE
DAC 12 */
DMA */
Basic Timer / RTC */
Port 2 */
USART 1 Transmit */
USART 1 Receive */
Port 1 */
Timer A CC1-2, TA */
Timer A CC0 */
ADC */
USCI A0/B0 Transmit */
USCI A0/B0 Receive */
Watchdog Timer */
Comparator A */
Timer B CC1-2, TB */
Timer B CC0 */
Non-maskable */
Reset [Highest Priority] */
Exercice 48 Soit xx le numéro que vous venez de déterminer. Vous pouvez maintenant remplacer l’étiquette
temp_isr par les deux lignes suivantes :
.global __isr_xx
__isr_xx:
Bien sûr, vous remplacez xx par la valeur trouvée à la question précédente.
Exercice 49 Il faut maintenant faire en sorte que votre traitant d’interruption acquite l’interruption. Pour ça,
relisez la partie concernant P1IFG dans l’encadré de la page page précédente.
Exercice 50 Avez-vous pensé au retour du traitant d’interruption ? Sinon (ou si vous avez utilisé l’instruction
RET) alors allez vite lire l’encadré ci-dessous et corrigez votre programme de façon adéquat.
40
Extrait de la documentation : msp430x4xx.pdf pages 97
Exercice 51 Dans la boucle principale de votre programme, faites de nouveau clignoter une diode (ou mieux :
jouez de la musique sur le buzzer) et constatez avec satisfaction que les deux activités (programme principal et
ISR) s’exécutent maintenant en bonne harmonie.
41
Annexe : Morceaux choisis de la documentation de l’assembleur MSP430
Extrait de la documentation : msp430x4xx.pdf page 44
CPU Registers
3.2 CPU Registers
The CPU incorporates sixteen 16-bit registers. R0, R1, R2 and R3 have
dedicated functions. R4 to R15 are working registers for general use.
3.2.1
Program Counter (PC)
The 16-bit program counter (PC/R0) points to the next instruction to be
executed. Each instruction uses an even number of bytes (two, four, or six),
and the PC is incremented accordingly. Instruction accesses in the 64-KB
address space are performed on word boundaries, and the PC is aligned to
even addresses. Figure 3−2 shows the program counter.
Figure 3−2. Program Counter
15
1
Program Counter Bits 15 to 1
0
0
The PC can be addressed with all instructions and addressing modes. A few
examples:
MOV
MOV
MOV
#LABEL,PC ; Branch to address LABEL
LABEL,PC ; Branch to address contained in LABEL
@R14,PC ; Branch indirect to address in R14
42
Extrait de la documentation : msp430x4xx.pdf page 45
CPU Registers
3.2.2
Stack Pointer (SP)
The stack pointer (SP/R1) is used by the CPU to store the return addresses
of subroutine calls and interrupts. It uses a predecrement, postincrement
scheme. In addition, the SP can be used by software with all instructions and
addressing modes. Figure 3−3 shows the SP. The SP is initialized into RAM
by the user, and is aligned to even addresses.
Figure 3−4 shows stack usage.
Figure 3−3. Stack Pointer
15
1
0
Stack Pointer Bits 15 to 1
MOV
MOV
PUSH
POP
2(SP),R6
R7,0(SP)
#0123h
R8
;
;
;
;
0
Item I2 −> R6
Overwrite TOS with R7
Put 0123h onto TOS
R8 = 0123h
Figure 3−4. Stack Usage
Address
PUSH #0123h
POP R8
0xxxh
I1
I1
I1
0xxxh − 2
I2
I2
I2
0xxxh − 4
I3
I3
I3
SP
SP
0123h
0xxxh − 6
SP
0123h
0xxxh − 8
The special cases of using the SP as an argument to the PUSH and POP
instructions are described and shown in Figure 3−5.
Figure 3−5. PUSH SP - POP SP Sequence
PUSH SP
POP SP
SPold
SP1
SP1
The stack pointer is changed after
a PUSH SP instruction.
SP2
SP1
The stack pointer is not changed after a POP SP
instruction. The POP SP instruction places SP1 into the
stack pointer SP (SP2=SP1)
RISC 16-Bit CPU
43
3-5
Extrait de la documentation : msp430x4xx.pdf page 46
CPU Registers
3.2.3
Status Register (SR)
The status register (SR/R2), used as a source or destination register, can be
used in the register mode only addressed with word instructions. The
remaining combinations of addressing modes are used to support the
constant generator. Figure 3−6 shows the SR bits.
Figure 3−6. Status Register Bits
15
9
8
V
Reserved
7
SCG1
0
OSC CPU
SCG0
GIE
OFF OFF
N
Z C
rw-0
Table 3−1 describes the status register bits.
Table 3−1.Description of Status Register Bits
3-6
Bit
Description
V
Overflow bit. This bit is set when the result of an arithmetic operation
overflows the signed-variable range.
ADD(.B),ADDC(.B)
Set when:
Positive + Positive = Negative
Negative + Negative = Positive,
otherwise reset
SUB(.B),SUBC(.B),CMP(.B)
Set when:
Positive − Negative = Negative
Negative − Positive = Positive,
otherwise reset
SCG1
System clock generator 1. This bit, when set, turns off the DCO dc
generator, if DCOCLK is not used for MCLK or SMCLK.
SCG0
System clock generator 0. This bit, when set, turns off the FLL+ loop
control
OSCOFF
Oscillator Off. This bit, when set, turns off the LFXT1 crystal oscillator,
when LFXT1CLK is not use for MCLK or SMCLK
CPUOFF
CPU off. This bit, when set, turns off the CPU.
GIE
General interrupt enable. This bit, when set, enables maskable
interrupts. When reset, all maskable interrupts are disabled.
N
Negative bit. This bit is set when the result of a byte or word operation
is negative and cleared when the result is not negative.
Word operation:
N is set to the value of bit 15 of the
result
Byte operation:
N is set to the value of bit 7 of the
result
Z
Zero bit. This bit is set when the result of a byte or word operation is 0
and cleared when the result is not 0.
C
Carry bit. This bit is set when the result of a byte or word operation
produced a carry and cleared when no carry occurred.
RISC 16-Bit CPU
44
Extrait de la documentation : msp430x4xx.pdf page 47
CPU Registers
3.2.4
Constant Generator Registers CG1 and CG2
Six commonly-used constants are generated with the constant generator
registers R2 and R3, without requiring an additional 16-bit word of program
code. The constants are selected with the source-register addressing modes
(As), as described in Table 3−2.
Table 3−2.Values of Constant Generators CG1, CG2
Register
As
Constant
Remarks
R2
00
−−−−−
Register mode
R2
01
(0)
Absolute address mode
R2
10
00004h
+4, bit processing
R2
11
00008h
+8, bit processing
R3
00
00000h
0, word processing
R3
01
00001h
+1
R3
10
00002h
+2, bit processing
R3
11
0FFFFh
−1, word processing
The constant generator advantages are:
! No special instructions required
! No additional code word for the six constants
! No code memory access required to retrieve the constant
The assembler uses the constant generator automatically if one of the six
constants is used as an immediate source operand. Registers R2 and R3,
used in the constant mode, cannot be addressed explicitly; they act as
source-only registers.
Constant Generator − Expanded Instruction Set
The RISC instruction set of the MSP430 has only 27 instructions. However, the
constant generator allows the MSP430 assembler to support 24 additional,
emulated instructions. For example, the single-operand instruction:
CLR
dst
is emulated by the double-operand instruction with the same length:
MOV
R3,dst
where the #0 is replaced by the assembler, and R3 is used with As = 00.
INC
dst
is replaced by:
ADD
0(R3),dst
RISC 16-Bit CPU
45
3-7
Extrait de la documentation : msp430x4xx.pdf page 48
CPU Registers
3.2.5
General-Purpose Registers R4 to R15
Twelve registers, R4 to R15, are general-purpose registers. All of these
registers can be used as data registers, address pointers, or index values, and
they can be accessed with byte or word instructions as shown in Figure 3−7.
Figure 3−7. Register-Byte/Byte-Register Operations
Register-Byte Operation
High Byte
Byte-Register Operation
Low Byte
Unused
High Byte
Byte
Register
Byte
Memory
Example Byte-Register Operation
R5 = 0A28Fh
R5 = 01202h
R6 = 0203h
R6 = 0223h
Mem(0203h) = 012h
Mem(0223h) = 05Fh
ADD.B
ADD.B
R5,0(R6)
Memory
Register
0h
Example Register-Byte Operation
08Fh
@R6,R5
05Fh
+ 012h
+ 002h
0A1h
00061h
Mem (0203h) = 0A1h
R5 = 00061h
C = 0, Z = 0, N = 1
C = 0, Z = 0, N = 0
(Low byte of register)
3-8
Low Byte
(Addressed byte)
+ (Addressed byte)
+ (Low byte of register)
−>(Addressed byte)
−>(Low byte of register, zero to High byte)
RISC 16-Bit CPU
46
Téléchargement