Simulation CPU

Numérique et sciences informatiques

processeur

Instructions machine

Le CPU ne gère que des grandeurs booléennes : les instructions exécutées au niveau du CPU sont donc codées en binaire. L'ensemble des instructions exécutables directement par le microprocesseur constitue ce que l'on appelle le langage machine.

Les langages de programmation « évolués » (Python, C++, …), destinés à être utilisés par des humains, se composent d'instructions complexes, opérant sur des types de données beaucoup plus complexes que des booléens.

Il faudra donc passer par une étape de « conversion » du langage évolué vers le langage machine, chaque instruction du langage « évolué » donnant lieu à un grand nombre d'instructions « élémentaires » du langage machine. On distinguera l'opération de compilation (conversion de tout le code évolué en langage machine) de l'opération d'interprétation (la conversion est réalisée au fur et à mesure du déroulement du code).

Une instruction machine est une chaîne binaire composée principalement de 2 parties :

  • le champ « code opération » qui indique au processeur le type de traitement à réaliser.
    Par exemple le code « 0010 0110 » donne l'ordre au CPU d'effectuer une multiplication.
  • le champ « opérandes » indique la nature des données sur lesquelles l'opération désignée par le « code opération » doit être effectuée.

 

Les instructions machines sont relativement basiques (on parle d'instructions de bas niveau), voici quelques exemples :

  • les instructions arithmétiques (addition, soustraction, multiplication…).
    Par exemple, on peut avoir une instruction qui ressemble à « additionne la valeur contenue dans le registre R1 et le nombre 789 et range le résultat dans le registre R0 ».
  • les instructions de transfert de données qui permettent de transférer une donnée d'un registre du CPU vers la mémoire vive et vice versa.
    Par exemple, on peut avoir une instruction qui ressemble à « prendre la valeur située à l'adresse mémoire 487 et la placer dans la registre R2 » ou encore « prendre la valeur située dans le registre R1 et la placer à l'adresse mémoire 512 ».
  • les instructions de rupture de séquence : les instructions machines sont situées en mémoire vive dans l’ordre :
    Par exemple : si l'instruction n°1 est située à l'adresse mémoire 343, l'instruction n°2 sera située à l'adresse mémoire 344, l'instruction n°3 sera située à l'adresse mémoire 345…
    Au cours de l'exécution d'un programme, le CPU passe d'une instruction à une autre en passant d'une adresse mémoire à l'adresse mémoire immédiatement supérieure :
    Par exemple, après avoir exécuté l'instruction n°2 (situé à l'adresse mémoire 344), le CPU « va chercher » l'instruction suivante à l'adresse mémoire 344+1=345.
    Les instructions de rupture de séquence d'exécution encore appelées instructions de saut ou de branchement permettent d'interrompre l'ordre initial sous certaines conditions en passant à une instruction située une adresse mémoire donnée.
    Par exemple, dans le cas où à l'adresse mémoire 354 nous avons l'instruction suivante : « si la valeur contenue dans le registre R1 est strictement supérieure à 0 alors exécuter l'instruction située à l'adresse mémoire 4521 ».
    Si la valeur contenue dans le registre R1 est strictement supérieure à 0 alors la prochaine instruction à exécuter est l'adresse mémoire 4521, dans le contraire, la prochaine instruction à exécuter est à l'adresse mémoire 355.

 

Comme déjà dit, les opérandes désignent les données sur lesquelles le code opération de l'instruction doit être réalisée. Un opérande peut être de 3 natures différentes :

  1. l'opérande est une valeur immédiate : l'opération est effectuée directement sur la valeur donnée dans l'opérande
  2. l'opérande est un registre du CPU : l'opération est effectuée sur la valeur située dans un des registres (R0, R1, R2,…), l'opérande indique de quel registre il s'agit
  3. l'opérande est une donnée située en mémoire vive : l'opération est effectuée sur la valeur située en mémoire vive à l'adresse XXXXX. Cette adresse est indiquée dans l'opérande.

Exemples :

  • quand on considère l'instruction machine :
    « additionne le nombre 125 et la valeur située dans le registre R2 , range le résultat dans le registre R1 »
    nous avons 2 valeurs : « le nombre 125 » (qui est une valeur immédiate : cas n°1) et « la valeur située dans le registre R2 » (cas n°2)
  • quand on considère l'instruction machine :
    « prendre la valeur située dans le registre R1 et la placer à l'adresse mémoire 512 »
    nous avons 2 valeurs : « à l'adresse mémoire 512 » (cas n°3) et « la valeur située dans le registre R1 » (cas n°2)

 

Assembleur

Le microprocesseur étant incapable d'interpréter la phrase « additionne le nombre 125 et la valeur située dans le registre R2 , range le résultat dans le registre R1 », il faut coder cette instruction sous forme binaire :

« additionne le nombre 125 et la valeur située dans le registre R2 , range le résultat dans le registre R1 »

« 11100010100000100001000001111101 »

Afin de faciliter la lecture et l'écriture d'instructions machine par les informaticiens, on remplace les codes binaires par des symboles mnémoniques, en utilisant la syntaxesyntaxe La syntaxe concerne le signifiant, soit ce qu'est l'énoncé. du langage appelé assembleur.

« additionne le nombre 125 et la valeur située dans le registre R2 , range le résultat dans le registre R1 »

« ADD R1,R2,#125 »

« 11100010 10000010 00010000 01111101 »

Remarque : chaque élément de l'instruction occupe une quantité de mémoire bien définie. Par exemple, en architecture ARM :

  • L'instruction ADD occupe les 12 bits de poids fort
  • Les adresses des registres R1 et R2 occupent chacune 4 bits
  • L’opérande #125 occupe les 12 bits de poids faible

Pour en savoir plus : Présentation Architecture et jeu d’instructions ARM

 

 

Exemples d'instructions en assembleur

Instructions Description
LDR R1,78
Place la valeur stockée à l'adresse mémoire 78 dans le registre R1
STR R3,125
Place la valeur stockée dans le registre R3 en mémoire vive à l'adresse 125
ADD R1,R0,#128
Additionne le nombre 128 (une valeur immédiate est identifiée grâce au symbole # ) et la valeur stockée dans le registre R0, place le résultat dans le registre R1
ADD R0,R1,R2
Additionne la valeur stockée dans le registre R1 et la valeur stockée dans le registre R2, place le résultat dans le registre R0
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
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 (voir ci-dessous)
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
HALT
Arrête l'exécution du programme

Entraînement 1 :

Expliquer brièvement par une phrase, les instructions suivantes :
  • ADD R0, R1, #42
  • LDR R5,98
  • CMP R4, #18
    BGT 77
  • STR R0,15
  • B 100

Entraînement 2 :

Écrire les instructions en assembleur correspondant aux phrases suivantes :
  • « Additionne la valeur stockée dans le registre R0 et la valeur stockée dans le registre R1, le résultat est stocké dans le registre R5 »
  • « Place la valeur stockée à l'adresse mémoire 878 dans le registre R0 »
  • « Place le contenu du registre R0 en mémoire vive à l'adresse 124 »
  • « la prochaine instruction à exécuter se situe en mémoire vive à l'adresse 478 »
  • « Si la valeur stockée dans le registre R0 est égale 42 alors la prochaine instruction à exécuter se situe à l'adresse mémoire 85 »

 

Labels

En réalité, les instructions assembleur B , BEQ , BNE , BGT et BLT n'utilisent pas directement l'adresse mémoire de la prochaine instruction à exécuter, mais des labels.

Un label correspond à une adresse en mémoire vive (c'est l'assembleur qui fera la traduction « label »→ »adresse mémoire »). L'utilisation d'un label évite donc d'avoir à manipuler des adresses mémoires.

Voici un exemple qui montre comment utiliser un label :

CMP R4, #18
BGT monLabel
MOV R0, #14
HALT
monLabel:
MOV R0,#18
HALT

 

Dans l'exemple ci-dessus, nous avons choisi monLabel comme nom de label. La ligne MOV R0,#18 a pour label « monLabel » car elle est située juste après la ligne monLabel: .

Concrètement, voici ce qui se passe avec ce programme : si la valeur stockée dans le registre R4 est supérieure à 18 on place le nombre 18 dans le registre R0 sinon on place le nombre 14 dans le registre R0.

ATTENTION : la présence du HALT juste après la ligne MOV R0,#14 est indispensable, car sinon, la ligne MOV R0,#18 sera aussi exécutée (même si la valeur stockée dans le registre R4 est inférieure à 18 )

Entraînement 3 :

Voici un programme Python très simple :

x = 4
y = 8
if x == 10:
   y = 9
else:
   x = x+1
z = 6

et voici maintenant voici son équivalent en assembleur :

MOV R0, #4
STR R0,30
MOV R0, #8
STR R0,75
LDR R0,30
CMP R0, #10
BNE else
MOV R0, #9
STR R0,75
B endif
else:
LDR R0,30
ADD R0, R0, #1
STR R0,30
endif:
MOV R0, #6
STR R0,23
HALT

Après avoir analysé très attentivement le programme en assembleur ci-dessus, établir une correspondance entre les lignes du programme en Python et les lignes du programme en assembleur.

À quoi sert la ligne B endif ?

À quoi correspondent les adresses mémoires 23, 75 et 30 ?

Nous allons utiliser un simulateur développé par Peter L Higginson. Ce simulateur est basé sur une architecture de Von Neumann. Nous allons trouver dans ce simulateur :

  • une RAM
  • un CPU

Une version en ligne de ce simulateur est disponible ici : http://www.peterhigginson.co.uk/AQA/

Voici ce que vous devriez obtenir en vous rendant à l'adresse indiquée ci-dessus :

sim_cpu
simulateur développé par Peter L Higginson

On peut distinguer les différentes parties du simulateur :

  • à droite, on trouve la mémoire vive ("main memory")
  • au centre, on trouve le microprocesseur
  • à gauche on trouve la zone d'édition ("Assembly Language"), c'est dans cette zone que nous allons saisir nos programmes en assembleur

Revenons sur les parties RAM et CPU

La RAM

Par défaut le contenu des différentes cellules de la mémoire est en base 10 (entier signé), mais d'autres options sont possibles : base 10 (entier non-signé, "unsigned"), base 16 ("hex"), base 2 ("binary"). On accède à ces options à l'aide du bouton "OPTIONS" situé en bas dans la partie gauche du simulateur.

Le CPU

Dans la partie centrale du simulateur, nous allons trouver en allant du haut vers le bas :

  • le bloc "registre" ("Registers") : nous avons 13 registres (R0 à R12) + 1 registre (PC) qui contient l'adresse mémoire de l'instruction en court d'exécution
  • le bloc "unité de commande" ("Control Unit") qui contient l'instruction machine en cours d'exécution (au format hexadécimal)
  • le bloc "unité arithmétique et logique" ("Arithmetic and Logic Unit")

Nous ne nous intéresserons pas aux autres composants de la partie CPU

Programmer en assembleur

Dans la partie "éditeur" ("Assembly Language") saisissez les lignes de codes suivantes

MOV R0,#42
STR R0,150
HALT

En cliquant sur le bouton "submit", vous devriez voir apparaitre des nombres "étranges" dans les cellules mémoires d'adresses 000, 001 et 002 :

L'assembleur a fait son travail, il a converti les 3 lignes de notre programme en instructions machines, la première instruction machine est stockée à l'adresse mémoire 000 (elle correspond à "MOV R0,#42" en assembleur), la deuxième à l'adresse 001 (elle correspond à "STR R0,150" en assembleur) et la troisième à l'adresse 002 (elle correspond à "HALT" en assembleur) Pour avoir une idée des véritables instructions machines, vous devez repasser à un affichage en binaire ((bouton "OPTION"->"binary")). Vous devriez obtenir ceci :

Nous pouvons donc maintenant affirmer que :

  • l'instruction machine "11100011 10100000 00000000 00101010" correspond au code assembleur "MOV R0,#42"
  • l'instruction machine "11100101 10001111 00000010 01001100" correspond au code assembleur "STR R0,150"
  • l'instruction machine "11101111 00000000 00000000 00000000" correspond au code assembleur "HALT"

Au passage, pour l'instruction machine "11100011 10100000 00000000 00101010", vous pouvez remarquer que l'octet le plus à droite, (00101010)2, est bien égale à (42)10 !

En utilisant le bouton RUN Une fois la simulation terminée, vous pouvez constater que la cellule mémoire d'adresse 150, contient bien le nombre 42 (en base 10). Vous pouvez aussi constater que le registre R0 a bien stocké le nombre 42.

ATTENTION : pour relancer la simulation, il est nécessaire d'appuyer sur le bouton "RESET" afin de remettre les registres R0 à R12 à 0, ainsi que le registre PC (il faut que l'unité de commande pointe de nouveau sur l'instruction située à l'adresse mémoire 000). La mémoire n'est pas modifiée par un appui sur le bouton "RESET",pour remettre la mémoire à 0, il faut cliquer sur le bouton "OPTIONS" et choisir "clr memory". Si vous remettez votre mémoire à 0, il faudra cliquer sur le bouton "ASSEMBLE" avant de pouvoir exécuter de nouveau votre programme.

Entraînement 2:

Modifiez le programme précédent pour qu'à la fin de l'exécution on trouve le nombre 19 à l'adresse mémoire 10. On utilisera le registre R1 à la place du registre R0. Testez vos modifications en exécutant la simulation.