Exercices sur le processeur

Exercice 1: Circuit demi-additionneur

{ "width":400, "height":200, "showToolbox":true, "toolbox":[ {"type":"In"}, {"type":"Out"}, {"type":"Joint"}, {"type":"DC"}, {"type":"LED", "color":"#00FF00"}, {"type":"PushOff"}, {"type":"PushOn"}, {"type":"Toggle"}, {"type":"BUF"}, {"type":"NOT"}, {"type":"AND"}, {"type":"NAND"}, {"type":"OR"}, {"type":"NOR"}, {"type":"XOR"}, {"type":"XNOR"}, {"type":"OSC"}, {"type":"7seg"}, {"type":"16seg"}, {"type":"4bit7seg"}, {"type":"RotaryEncoder"}, {"type":"BusIn"}, {"type":"BusOut"}, {"type":"RS-FF"}, {"type":"JK-FF"}, {"type":"T-FF"}, {"type":"D-FF"}, {"type":"8bitCounter"}, {"type":"HalfAdder"}, {"type":"FullAdder"}, {"type":"4bitAdder"}, {"type":"2to4BinaryDecoder"}, {"type":"3to8BinaryDecoder"}, {"type":"4to16BinaryDecoder"}, {"type":"AltFullAdder"}, {"type":"Transmitter"}, {"type":"Delay"}, {"type":"NumSrc"}, {"type":"NumDsp"}, {"type":"DSO"} ], "devices":[ {"type":"XOR","id":"dev0","x":126,"y":48,"label":"XOR"}, {"type":"AND","id":"dev1","x":126,"y":128,"label":"AND"}, {"type":"NumDsp","id":"dev2","x":222,"y":80,"label":"NumDsp","state":{"direction":2}}, {"type":"NumDsp","id":"dev3","x":222,"y":112,"label":"NumDsp","state":{"direction":2}}, {"type":"NumSrc","id":"dev4","x":46,"y":80,"label":"NumSrc","state":{"direction":0,"on":false}}, {"type":"NumSrc","id":"dev5","x":46,"y":112,"label":"NumSrc","state":{"direction":0,"on":true}} ], "connectors":[ {"from":"dev0.in0","to":"dev4.out0"}, {"from":"dev0.in1","to":"dev5.out0"}, {"from":"dev1.in0","to":"dev4.out0"}, {"from":"dev1.in1","to":"dev5.out0"}, {"from":"dev2.in0","to":"dev0.out0"}, {"from":"dev3.in0","to":"dev1.out0"} ] }

Recopier et compléter la table de vérité suivante afin de vérifier les sorties.

\(a\) \(b\) \(a \text{ AND } b\) \(a \text{ XOR } b\)
\(0\) \(0\)
\(1\) \(0\)
\(0\) \(1\)
\(1\) \(1\)

Remarques:

  • expliquer le terme demi-additionneur

  • noter dans simcir (dans la bandeau de gauche) la présence d’un circuit nommé \(\verb|HalfAdder|\). En fait, il s’agit du disposif précédent schématisé.

{ "width":400, "height":200, "showToolbox":true, "toolbox":[ {"type":"In"}, {"type":"Out"}, {"type":"Joint"}, {"type":"DC"}, {"type":"LED"}, {"type":"PushOff"}, {"type":"PushOn"}, {"type":"Toggle"}, {"type":"BUF"}, {"type":"NOT"}, {"type":"AND"}, {"type":"NAND"}, {"type":"OR"}, {"type":"NOR"}, {"type":"XOR"}, {"type":"XNOR"}, {"type":"OSC"}, {"type":"7seg"}, {"type":"16seg"}, {"type":"4bit7seg"}, {"type":"RotaryEncoder"}, {"type":"BusIn"}, {"type":"BusOut"}, {"type":"RS-FF"}, {"type":"JK-FF"}, {"type":"T-FF"}, {"type":"D-FF"}, {"type":"8bitCounter"}, {"type":"HalfAdder"}, {"type":"FullAdder"}, {"type":"4bitAdder"}, {"type":"2to4BinaryDecoder"}, {"type":"3to8BinaryDecoder"}, {"type":"4to16BinaryDecoder"}, {"type":"AltFullAdder"}, {"type":"Transmitter"}, {"type":"Delay"}, {"type":"NumSrc"}, {"type":"NumDsp"}, {"type":"DSO"} ], "devices":[ {"type":"NumDsp","id":"dev0","x":222,"y":80,"label":"NumDsp","state":{"direction":2}}, {"type":"NumDsp","id":"dev1","x":222,"y":112,"label":"NumDsp","state":{"direction":2}}, {"type":"NumSrc","id":"dev2","x":46,"y":80,"label":"NumSrc","state":{"direction":0,"on":false}}, {"type":"NumSrc","id":"dev3","x":46,"y":112,"label":"NumSrc","state":{"direction":0,"on":true}}, {"type":"HalfAdder","id":"dev4","x":110,"y":88,"label":"HalfAdder"} ], "connectors":[ {"from":"dev0.in0","to":"dev4.out0"}, {"from":"dev1.in0","to":"dev4.out1"}, {"from":"dev4.in0","to":"dev2.out0"}, {"from":"dev4.in1","to":"dev3.out0"} ] }

Exercice 2: activité débranchée avec le processeur en papier M99 (imaginé et conçu par Philippe Marquet et Martin Quinson)

Avec uniquement une feuille de papier et un crayon, nous allons pouvoir simuler le fonctionnement d’un véritable processeur en faisant abstraction de toute synchronisation imposée par l’horloge (élément présent dans tout processeur).

Présentation du M99

Le M99 est une machine dotée de 100 cases mémoire (la grille en haut de la feuille), et d’un processeur (en bas de la feuille).

La mémoire est composée de 100 mots mémoire de 3 chiffres (valeur de 000 à 999). Ces 100 mots mémoire sont adressables par des adresses codées sur 2 chiffres. Cette mémoire va contenir données et instructions.

Le processeur dispose de deux registres généraux nommés A et B, et d’un registre accumulateur/résultat nommé R. Ces registres sont de 3 chiffres, mais au contraire de la mémoire, ils ont un signe. Ils peuvent donc contenir des valeurs comprises entre -999 et 999.

Le processeur dispose aussi d’un quatrième registre nommé PC (Program Counter). C’est le pointeur d’instruction, contenant l’adresse mémoire de la prochaine instruction à exécuter. Lorsqu’on utilise le M99, on peut noter le numéro de l’instruction à exécuter dans la case prévue à cette effet, mais en pratique, il est plus simple de le matérialiser avec un “pion” situé sur une des cases de la grille mémoire, ou même de suivre avec son doigt.

Unité arithmétique et logique

L’unité arithmétique et logique – UAL – est en charge d’effectuer les calculs. Les opérandes et résultats sont dans les registres, A et B pour les opérandes, R pour le résultat.

Unité de commande

L’unité de commande pilote l’ordinateur. Son cycle de fonctionnement comporte 3 étapes :

  1. charger l’instruction depuis la case mémoire pointée par PC vers la zone dédiée dans l’UAL (la case sous le registre PC). Incrémenter ensuite le PC.
  2. décoder l’instruction : à partir des 3 chiffres codant l’instruction, identifier quelle est l’opération à réaliser en utilisant le pense-bête à droite de l’UAL.
  3. exécuter l’instruction.

Jeu d’instructions

code mnémonique instruction à réaliser
0 x y STO xy copie le contenu du registre R dans le mot mémoire d’adresse xy
1 x y LDA xy copie le mot mémoire d’adresse xy dans le registre A
2 x y LDB xy copie le mot mémoire d’adresse xy dans le registre B
3 x y MOV x y copie le contenu du registre Rx dans Ry (R0: R; R1: A; R2: B)
4 - - opérations arithmétiques et logiques
4 0 0 ADD ajoute les valeurs des registres A et B, produit le résultat dans R
4 0 1 SUB soustrait la valeur du registre B à celle du registre A, produit le résultat dans R
. . . etc
5 x y JMP x y branche en xy (PC reçoit la valeur xy)
6 x y JPP x y branche en xy si la valeur du registre R est positive
7 x y JEQ x y saute une case (PC += 2) si la valeur du registre R est égale à xy
8 x y JNE x y saute une case (PC += 2) si la valeur du registre R est différent de xy

Boot et arrêt

La machine démarre avec la valeur nulle comme pointeur d’instruction (PC=0) et elle s’arrête si le pointeur d’instruction vaut 99.

On peut donc utiliser le mnémonique HLT comme synonyme de JMP 99.

Entrées/sorties

Les entrées/sortries sont “mappées” en mémoire: Écrire le mot mémoire 99 écrit sur le terminal, tandis que les valeurs saisies sur le terminal seront lues dans le mot mémoire 99.

Partie 1

Objectifs:

  • prise en main du M99

  • comprendre l’encodage des opcodes dans la mémoire

  • comprendre le cycle fetch/decode/execute

1.1. Que fait le programme chargé à l’adresse 0 ? Écrire la fonction équivalente en Python.

1.2. Que fait le programme débutant à l’adresse 13 ?

1.3. Écrire un programme affichant le maximum de deux entrées clavier en langage assembleur puis en Python.

Partie 2:

Objectifs:

  • Modifier un programme en assembleur

  • Voir l’intérêt d’un compilateur, et d’un langage de haut niveau

2.1. Que fait le programme débutant à l’adresse 40 (pour les entrées 5 et 2)?

2.2. (Bonus) Peut-on raccourcir ce programme ? Donner votre proposition.

2.3. (Bonus) Corrigez ce programme quand la seconde entrée vaut 0

Notes des concepteurs

Le M99 est un ordinateur en papier, assez simple à utiliser avec seulement un crayon, mais il a été pensé pour être relativement réaliste des vrais ordinateurs.

  • La mémoire d’un vrai ordinateur est également découpée en mots mémoires, chacun étant doté d’une adresse unique. En général, les vrais ordinateurs utilisent des mots de 1 octet (8 bits). Les ordinateurs 32bits peuvent avoir jusqu’à 2³² mots (soit un peu plus de 4Go de mémoire) tandis que les ordinateurs 64bits peuvent en avoir jusqu’à 2⁶⁴ en théorie (18 Exaoctets, 18.10¹⁸ octets).

  • Les vrais processeurs ont également des registres afin de gérer au mieux le problème de la barrière mémoire. Ils ont également des caches pour optimiser les échanges entre la mémoire et le CPU. Là où lire en mémoire peut demander une centaine de cycles CPU, lire en cache prend entre 10 et 30 cycles. Le M99 n’a pas de caches pour simplifier.

  • Les vrais programmes sont également écrits sous forme d’opcodes en mémoire des vrais ordinateurs, avec le préfixe indiquant l’opération tandis que le sufixe indique les opérandes. Le jeu d’opérations élémentaires disponibles varie beaucoup d’un processeur à l’autre.

    Pour le M99, nous avons choisi d’utiliser des mots mémoires de trois positions décimales, ce qui contraint fortement le nombre d’instructions disponibles. Ces contraintes sont parfaitement réalistes de celles que doivent résoudre les fabriquants de CPU. Ajouter des instructions simplifie l’écriture de programmes efficaces, mais complique grandement le processeur, qui devient plus cher et plus énergivore.

    Les processeurs de la famille RISC (reduced instruction set CPU) visent la simplicité et n’offrent que peu d’instructions tandis que ceux de la famille CISC (complex instruction set CPU) offrent des opérations optimisées plus rares, comme des opérations vectorielles.

    Il serait faux de dire que l’une des familles est vraiment préférable à l’autre. Il s’agit plutôt de deux compromis différents entre complexité du processeur et complexité des programmes. Les processeurs des téléphones portables sont souvent des RISC (par exemple du constructeur ARM) tandis que ceux des ordinateurs sont souvent des CISC (par exemple des constructeurs Intel ou AMD).

  • Gérer les entrées/sorties au travers d’adresses particulières de l’espace d’adressage du bus mémoire est parfaitement réaliste. En revanche, il est rare d’avoir plusieurs périphériques à la même adresse et on aurait pu séparer les lectures du clavier et les écritures à l’écran dans des zones mémoire différentes. De plus, nous avons ignoré toute la synchronisation qu’un vrai processeur doit faire pour échanger avec les périphériques, souvent bien plus lent.

Extensions (fonctions récursives, …) avec le M999 en Terminale

Pour aller plus loin

Ceux qui aiment bien ces petits jeux autour de l’assembleur devraient regarder du coté des jeux suivants:

  • Core War où des programmes s’exécutent tous ensemble dans la mémoire d’un ordinateur. Chacun tente de stoper les autres en écrivant dans leur mémoire.

  • Human Resource Machine (payant). Un ensemble de défis à résoudre à l’aide de programmes en pseudo-assembleur le plus court possible.

  • TIS-100