Chapitre 3.2 - Architecture Von Neumann/Assembleur⚓︎
John Von Neumann (1903-1957) est un mathématicien et physicien (et bien d'autres choses) américano-hongrois. Il a le premier théorisé l'architecture des processeurs, tels qu'ils fonctionnent encore aujourd'hui.
1. Architecture von Neumann⚓︎
On distingue 4 zones essentielles :
- le CPU (Central Processing Unit) qui contient lui-mĂŞme :
- l'Unité Arithmétique et Logique (UAL) dans laquelle sont effectuées les opérations de base (addition, multiplication...) Cette zone comporte notamment les registres (peu nombreux, de l'ordre de la dizaine) qui sont les espaces de travail ultra-rapides dans lesquels l'UAL va effectuer ses calculs. Une fois ceux-ci effectués, les valeurs des registres repartent dans la mémoire.
- l'Unité de contrôle, qui va séquencer les opérations. Lorsqu'on parle d'un processeur à 3 GHz, cela signifie (approximativement) que Unité de Contrôle va envoyer l'ordre d'une nouvelle opération à l'UAL 3 milliards de fois par seconde.
- la mémoire, qui contient à la fois les données à traiter et les instructions du programme. Cette idée de stocker au même endroit données et programme est l'idée centrale de l'architecture von Neumann.
- les bus de communication (des fils électriques permettant de transporter les données entre les différents composants).
- les Entrées/Sorties, permettant de gérer les informations avec l'extérieur.
2. Activité : simulation d'un programme en assembleur⚓︎
On l'a vu, dans l'architecture de Von Neumann, la mémoire vive (RAM) contient à la fois les données et les programmes (suite d'instructions).
Dans la mémoire d'un ordinateur on peut donc stocker des données (Entiers, flottants, booléens, caractères,...) sous forme binaire. Par exemple 00100110
correspond au nombre entier 38. Mais on peut également y stocker des instructions à destination de l'unité de traitement (UAL).
Ce mĂŞme code binaire 00100110
pourrait très bien correspondre au code d'une instruction machine, par exemple "Stopper le programme".
C'est le rôle du système d'exploitation (windows,linux,...) et du programmeur de faire en sorte de distinguer en mémoire ce qui correspond à des instructions ou des donnéees.
Les instructions machine⚓︎
Une instruction machine se présente de la façon suivante :
A retenir
Champ de code de l'opération | Champ de l'opérande
Remarque : chaque processeur possède ses propres codes d'opération (opcode). C'est son jeu d'instructions.
Exemple
Additionner | Valeur contenue dans le registre R1, Nombre 37
Chaque instruction peut occuper un ou plusieurs mots dans la mémoire d'un ordinateur.
Remarque : un mot correspond à l'unité de base pouvant être traitée par le processeur. Avec un proceseur 8 bits la taille du mot correspond à 8 bits soit 1 octet. Avec un processeur 64 bits la taille du mots correspond à 64 bits soit 8 octets.
Au début de l'informatique les programmeurs devaient coder leur programme directement en binaire : le langage machine.
Par exemple, le langage machine suivant est une instruction :
Exemple
01001100 00100101
- le premier octet
01001100
correspond au code de l'opération à effectuer (opcode) : "ajouter la valeur suivante au registre R1". - le second octet
00100101
(37 en décimal) est l'opérande : la valeur à ajouter à celle contenue dans le registre R1.
Le langage assembleur⚓︎
La programmation en binaire étant loin d'être évidente pour un humain, on a inventé le langage assembleur qui permet d'écrire les instructions de manière plus compréhensible. Dans notre exemple le code 0100110000100101
est remplacé par :
0100110000100101
ADD R1,37
Ce qui est tout de même déjà beaucoup plus lisible !
Voici un exemple de programme assembleur :
Le langage assembleur est donc une simple traduction brute du langage machine. Pour résumer :
- Le langage machine est une succession de bits qui est directement interprétable par le processeur d'un ordinateur.
- Un langage assembleur est le langage machine où les combinaisons de bits sont représentées par des "symboles" qu'un être humain peut mémoriser.
- Un programme assembleur convertit ces "symboles" en la combinaison de bits correspondante pour que le processeur puisse traiter l'information. Le programme assembleur traduit donc le langage assembleur en langage machine.
Remarques : - Un langage assembleur est souvent spécifique à un type de processeur. - Un langage assembleur est appelé "langage de bas niveau" car il est très proche du langage machine.
Les compilateurs / interpréteurs⚓︎
Le langage assembleur n'est toutefois pas facile à manipuler. C'est pourquoi il a été conçu des langages de programmation plus agréable à utiliser : les langages de haut niveau (Ex : C, Python, Javascript,...).
On parle également de niveau d'abstraction d'un langage. Plus celui-ci est proche de notre langage naturel et plus son niveau d'abstraction est élevé. Plus le langage est proche de la machine (binaire) plus celui-ci est de bas niveau.
Mais tout langage de programmation, pour être exécuté par une machine, doit être à un moment où à un autre traduit en langage binaire.
Il existe plusieurs manières de procéder :
- La première consiste à traduire le programme dans son ensemble une fois pour toute et générer un fichier avec le code binaire prêt à être exécuté. Il s'agit de la méthode dîte de compilation, réalisée par un compilateur. Le langage C est un exemple de langage compilé.
- La deuxième méthode consiste à traduire les instructions en langage binaire au fur et à mesure de la lecture du programme. Il s'agit de la méthode dîte d'interprétation, réalisée par un interpréteur. Le langage Basic est un exemple de langage interpété.
- Enfin il existe des méthodes mixtes qui consistent à traduire le programme en pseudo-code (bytecode). Ce pseudo-code est interprété par une machine virtuelle au moment de l'execution. L'intérêt de cette approche est que l'execution et la traduction du pseudo-code en langage binaire est plus rapide. Mais également, le fait que ce pseudo-code permet une certaine indépendance vis à vis du processeur sur lequel il est exécuté. En effet, il suffit juste de disposer d'une machine virtuelle spécifique au processeur en question. Python et Java sont des exemples de langages utilisant cette technique.
3. L'assembleur, en pratique⚓︎
Présentation du simulateur de processeur AQA⚓︎
Nous allons utiliser un simulateur d’architecture de Von Neumann, réalisé par Peter Higginson pour préparer des étudiants anglais à leur examen de Computer Science. Il se nomme AQUA et on peut l’exécuter en ligne.
Quelques principes de base :
- On ne peut pas définir de variables. Les données manipulées sont soient stockées à un endroit précis en mémoire soit dans un des registres R0 à R12.
- Pour calculer avec une donnée en mémoire, il faut d’abord la transférer dans un registre.
- L’interface se divise verticalement en trois zones :
- À gauche, l’éditeur de programme en assembleur.
- On remplit le formulaire et on le soumet avec submit.
- Puis on assemble le programme en mémoire avec assemble (parfois fait automatiquement).
- On l’exécute avec run (Plusieurs vitesses d’exécution sont disponibles).
- Au centre, le processeur, avec :
- les treize registres de données de R0 à R12.
- le Compteur de Programme PC.
- l’ Unité de Contrôle avec son Registre d’Instruction CIR.
- l’ALU avec ses quatre drapeaux de test (Z pour zéro, N pour négatif, C pour carry, retenue et V pour overflow).
- les bus reliant les différents composants du processeur et la mémoire (en bleu).
- Les registres MAR et MBR servent à transférer des données entre la mémoire et les registres :
- MAR contient l’adresse (en décimal) où l’on veut lire ou écrire.
- MBR la valeur lue ou à écrire (en hexadécimal).
- À droite, la mémoire divisée en mots de largeur 32 bits et dont les adresses commencent à 0. Dans "OPTIONS" on peut choisir le format d’affichage (décimal signé ou non, binaire, hexadécimal).
Remarque : il n’existe pas de structure de contrôle conditionnelle comme le "if... then... else" ou les boucles "while", "for". Pour les implémenter, on utilise des instructions de saut inconditionnel ou conditionnel en fonction du résultat de la comparaison précédente. Les points de chute de saut sont repérés par des étiquettes placées dans le programme.
AQA et le modèle de Von Neumann⚓︎
Ci-dessous, sont encadrés les quatre éléments constitutifs d'une architecture de Von Neumann :
Légende des encadrements : - En rouge, le processeur (CPU), comprenant : - En rose, l'unité de contrôle (UC) - En bleu, l'untité arithmétique et logique (UAL) - En vert, la mémoire vive (RAM)
La RAM⚓︎
Le contenu des différentes cellules de la mémoire peut être affiché dans différents formats ![AQA_options_RAM.
- base 10 (entier signé, signed) – par défaut
- base 10 (entier non-signé, unsigned),
- base 16 (hex),
- base 2 (binary).
- ...
Chaque cellule de la mémoire est accessible par son adresse. Il existe deux formats d’adressage des cellules de la mémoire : - 32 bits – format mot (option word mode) (par défaut) les adresses vont de 000 à 199 (codée en base 10). - 8 bits – format octet (option byte mode) les adresses vont de 000 à 799 (codée en base 10).
Le CPU⚓︎
Dans la partie centrale du simulateur, on trouve les différent composants du microprocesseur : - les registres (Registers) : 13 registres (R0 à R12) - 1 registre spécial (PC) qui contient l’adresse mémoire de l’instruction en cours d’exécution
- L’unité de commande (Control Unit) qui contient l’instruction machine en cours d’exécution (au format hexadécimal)
- l’unité arithmétique et logique (Arithmetic and Logic Unit)
Mon premier programme en assembleur⚓︎
Le jeu d’instructions AQA est précisé dans la documentation.
Remarque préalable : les opérandes ont la syntaxe suivante... - Rn correspond au registre numéro n. - #n correspond à une valeur entière immédiate n (sans passer par une mémoire). - n correspond à une adresse mémoire n (dans la RAM).
Voici quelques exemples d’instructions d’opérations arithmétiques et de transfert de mémoire :
Instruction | Traduction |
---|---|
LDR R1, 78 | Charge dans le registre R1 la valeur stockée en mémoire à l’adresse 78 |
STR R1, 123 | Stocke le contenu du registre R1 à l’adresse 123 en mémoire |
LDR R1, [R2] | Charge dans le registre R1 la valeur stockée en mémoire à l’adresse contenue dans le registre R2 |
ADD R1, R0, #128 | Additionne le nombre 128 et la valeur stockée dans le registre R0. Place le résultat dans le registre R1 |
SUB R1,R0,#128 | Soustrait le nombre 128 de la valeur stockée dans le registre R0, place le résultat dans le registre R1 |
SUB R0,R1,R2 | Soustrait la valeur stockée dans le registre R2 de la valeur stockée dans le registre R1, place le résultat dans le registre R0 |
MOV R1, #23 | Place le nombre 23 dans le registre R1 |
MOV R0, R3 | Place la valeur stockée dans le registre R3 dans le registre R0 |
OUT R1, 4 | Affiche en sortie 4 la valeur contenue dans le registre R1 |
HALT | Symbole de fin de programme, indispensable pour que le programme se termine sans erreur |
-
Ouvrir le simulateur AQUA. Régler la mémoire de sorte d’avoir un affichage hexadécimal, avec des cellules au format 32 bits.
-
Saisir le programme ci-dessous dans la fenêtre d’édition puis le soumettre avec submit .
Le programme vient d’être mis dans les cellules mémoires d’adresses 000, 001 et 002 :
L’assembleur a converti les 3 lignes du programme en instructions machines, chacune occupant une cellule de 32 bits :
Pour exécuter un programme, il suffit de cliquer sur le bouton RUN (exécution en continu) ou STEP (exécution pas à pas).
Par défaut, le simulateur montre comment il « travaille » par une animation. La vitesse d’animation est réglable à l’aide des boutons << et >> apparaissant à coté du bouton STOP qui permet de mettre l’exécution en pause. Tester l’exécution du code, en ralentissant suffisamment la vitesse afin de bien comprendre toutes les étapes de cette exécution.
On constate que deux types de valeurs circulent au sein du système : - des données (valeurs lues/écrites de/vers la mémoire/les registres/l’unité de commande) - des adresses des cellules de mémoire
Une fois la simulation terminée, on peut constater que la cellule mémoire d’adresse 150, contient bien le nombre 42 (en base 10). il en est de même pour le registre R0.
Pour remettre la mémoire à 0, il faut cliquer sur le bouton OPTIONS et choisir clr memory.
Mon deuxième programme en assembleur⚓︎
- Constater que...
- les 5 lignes d'instructions (ligne 0 à 4) sont bien enregistrées dans la RAM aux emplacements mémoire de 0 à 4.
- les emplacements de mémoire de registre sont vides (valeurs nulles).
- Répérer...
- l'Unité de Contrôle (UC) qui décode l'instruction en cours.
- l'Unité Arithmétique et Logique (UAL) qui prend deux opérandes en entrée pour sortir le résultat de son calcul.
- le registre du compteur de programme (PC) qui stocke l'emplacement mémoire de la prochaine instruction à aller chercher.
- Le champ d'entrée clavier (input).
- le champ de sortie qui servira d'affichage (output).
-
Exécuter le programme pas à pas (step) en vitesse lente ("OPTIONS" : "def slow"). A la fin, relancer l'exécution pour essayer de comprendre au mieux les étapes de ce programme.
-
Décrire à l'écrit l’enchaînement des opérations élémentaires effectuées lors de l’exécution des instructions.
- Pour chaque ligne d'instruction, décrire le rôle du PC, de l'UC, de la RAM, du l'UAL. On considérera l'état avant toute exécution de la ligne étudiée (ex: initialement, on étudie l'état des composants avant le début d'exécution de la ligne 0)
- Résumer vos notes dans ce tableau à compléter :
Ligne | Instruction en assembleur | Instruction en hexadécimal | Etat/rôle du PC | Etat/rôle de l'UC | Etat/rôle de l'UAL | Etat/rôle de la RAM | Etat/rôle du registre |
---|---|---|---|---|---|---|---|
0 | MOV R0, #10 | e3a0000a | Prochaine adresse Ă chercher : 0 | Vide | Aucun rĂ´le | Zone 0 en attente de chargement | Vide |
1 | MOV R0, #250 | e3a010fa | Prochaine adresse à chercher : 1 | A décodé l'instruction e3a0000a. En attente de l'instruction e3a010fa | Aucun rôle | Zone 0 chargée. Zone 1 en attente de chargement | valeur 10 affectée au registre R0 |
... | ... | ... | ... | ... | ... | ... | ... |
Mon troisième programme en assembleur⚓︎
Comprendre le programme suivant :
Pour l'étude vous pouvez : Sélectionner l’affichage Unsigned. Exécuter le programme pas à pas (step) en vitesse lente (options puis def slow).
- Analysez l’enchaînement d’opérations élémentaires lors de l’exécution des instructions de transfert de mémoire
MOV R0,#10
puisLDR R1,10
. - Observer l’évolution des registres PC (Compteur de programme), CIR (Registre d’instructions), MAR (adresse d’écriture/lecture en mémoire) et MBR (donnée à lire/écrire).
- Observez pour quelle(s) instruction(s), l’ALU est-elle sollicitée ?
Mon quatrième programme en assembleur⚓︎
Analysez l’enchaînement d’opérations élémentaires lors de l’exécution des instructions
- Où sont stockées dans la mémoire centrale les quatre valeurs calculées par ce programme ? Il s’agit des premières valeurs d’une suite célèbre, laquelle ?
- Rajouter les calculs de deux termes supplémentaires de la suite, par copier-coller (rajouter donc les itérations 5 et 6), puis exécuter le programme dans le simulateur. Observer l’état de la mémoire, expliquer l’erreur signalée par l’Unité de Contrôle et corriger le programme.
//initialisation
MOV R0, #30
MOV R1, #1
STR R1, [R0]
ADD R0, R0, #1
MOV R2, #1
//itération 1
STR R2, [R0]
ADD R2, R2, R1
LDR R1, [R0]
ADD R0, R0, #1
//itération 2
STR R2, [R0]
ADD R2, R2, R1
LDR R1, [R0]
ADD R0, R0, #1
//itération 3
STR R2, [R0]
ADD R2, R2, R1
LDR R1, [R0]
ADD R0, R0, #1
//itération 4
STR R2, [R0]
ADD R2, R2, R1
LDR R1, [R0]
ADD R0, R0, #1
//fin
HALT
Mon cinquième programme en assembleur : Entrées - sorties en assembleur (INP ; OUT)⚓︎
Dans le menu "SELECT", choisir le programme "add".
Le programme va se charger dans la zone d'Ă©dition.
Quelles sont les nouvelles instructions utilisées dans ce programme ?
Comme précédemment, décrire précisément la suite des instructions de ce programme d'addition.
Mon sixième programme en assembleur.⚓︎
En assembleur :
else:
et fin:
sont des étiquettes qui jouent le rôle de repères / points de chute, dans les instructions de
branchement / saut.
B 45 Structure de rupture de séquence, la prochaine instruction à exécuter se situe en mémoire vive à l'adresse 45
CMP R0, #23 Compare la valeur stockée dans le registre R0 et le nombre 23. Cette instruction CMP doit précéder une instruction de branchement conditionnel BEQ, BNE, BGT, BLT
CMP R0, R1 Compare la valeur stockée dans le registre R0 et la valeur stockée dans le registre R1.
CMP R0, #23
BEQ 78 La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 est égale à 23
CMP R0, #23
BNE 78 La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 n'est pas égale à 23
CMP R0, #23
BGT 78 La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 est plus grand que 23
CMP R0, #23
BLT 78 La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 est plus petit que 23
B fin est une instruction de branchement / saut inconditionnel : le programme se poursuit à partir de l’étiquette fin, le flux normal (passage à la ligne suivante) est interrompu.
CMP R0,#0 est une instruction de comparaison qui compare le contenu du registre R0 au nombre 0. Elle est suivie d’une instruction de branchement (ou saut) conditionnel BLT else : le programme se poursuit soit à partir de l’étiquette else si R0 est plus petit que 0, sinon avec l’instruction de la ligne suivante (comportement par défaut).
Mon septième programme en assembleur.⚓︎
On considère le programme Python ci-dessous :
a = int(input()) #entier lu stocké dans le registre R0
b = int(input()) #entier lu stocké dans le registre R1
if a > b:
m = a
else:
m = b
#le maximum m de a et b est stocké dans le registre R2
#et en mémoire centrale à l’adresse 20
print(m)
Résumé
Le langage assembleur est donc une simple traduction brute du langage machine. Pour résumer :
- Le langage machine est une succession de bits qui est directement interprétable par le processeur d'un ordinateur.
- Un langage assembleur est le langage machine où les combinaisons de bits sont représentées par des "symboles" qu'un être humain peut mémoriser.
- Un programme assembleur convertit ces "symboles" en la combinaison de bits correspondante pour que le processeur puisse traiter l'information. Le programme assembleur traduit donc le langage assembleur en langage machine.
Remarques : - Un langage assembleur est souvent spécifique à un type de processeur. - Un langage assembleur est appelé "langage de bas niveau" car il est très proche du langage machine.