Skip to content

M4.1 Architecture

ch2 Architecture minimale

Dans le chapitre précédent nous avons vu que le point de départ du développement des processeurs est la découverte de l'effet transistor. Dans ce chapitre, nous partirons du transistor pour construire pas à pas une architecture minimale de système informatique afin de bien en comprendre le fonctionnement. Nous écrirons ensuite des fonctionnalités très simples avec son langage instruction. Plan du chapitre : La première partie du chapitre, la plus longue, commence au fonctionnement du transistor pour arriver à celui d'un processeur minimaliste. Ensuite, nous verrons comment écrire des programmes pour ces processeurs avec leur langage : l’assembleur Enfin, nous présenterons la différence entre les architectures Von Neumann et Harvard et expliquerons comment on passe d'un langage haut niveau comme python ou le langage C au langage assembleur du processeur. Commençons par le transistor

Diapo 17 Principe du transistor MOSFET - 1 Voici une explication simplifiée du fonctionnement de le brique de base de l'informatique : le transistor MOSFET, suffisante pour l'informaticien. Les curieux pourront consulter la très pédagogique vidéo youtube d'Hervé Discours sur le sujet. Pour réaliser un transistor, on prend un morceau de Silicium ou de Silicium / Germanium mélangé avec quelques atomes de Bore, on dit dopé P, P comme positif. Les atomes de Bore ont un électron en moins sur la couche de valence et leur présence fait donc apparaître des trous mobiles dans la couche externes des électrons. Un trou est une absence de charge négative, et est vu comme une charge positive. Le silicium et le germanium, avec 4 électrons sur la couche de valence, ont les mêmes propriétés semi-conductrices pour nous, on se contentera de parler du silicium à partir de maintenant. On dope ensuite, avec un sorte de pochoir nommé masque, certaines zones avec des atomes de Phosphore. Ces atomes ont un électron de plus que le Silicium sur la couche de valence. Ils ajoute donc des charges négatives mobiles supplémentaires dans la couche externe, d'où le nom de dopage N. Les charges négatives mobiles et les charges positives mobiles se recombinent et apparaît une zone sans charges mobile. Depletion zone en anglais. On place des électrodes métalliques sur les différentes zones, nommées drain, source et substrat, en reliant la source et le substrat. Les zones depletion, sans charges mobiles, font que le courant ne peut circuler entre drain et source, le transistor est un interrupteur ouvert. Pour permettre d'ouvrir un canal pour le passage du courant, on ajoute, toujours avec des masques, une couche d'isolant en Oxyde de Silicium, avec une électrode métallique au-dessus.

Diapo 18 Principe du transistor MOSFET - 2 En l'absence de tension au niveau de la grille, le transistor est bloqué, le courant ne peut passer, il se comporte comme un circuit ouvert. Si on applique une tension entre le substrat et la grille, comme pour un condensateur, des charges négatives et positives vont se déplacer pour atteindre un équilibre électrostatique. Apparaît alors entre les deux zones N un canal peuplé d'électrons mobiles . En fonction du circuit électrique extérieur, le courant peu alors se circuler entre les élctrodes drain et source, le transistor se comporte comme un interrupteur fermé. On a ainsi montré qu'avec le dopage du silicium et le dépôt d'oxyde de silicium et d'électrodes métalliques, on pouvait réaliser des MOSFET, interrupteurs commandés en tension. Le symbole simplifié que nous adopterons pour ce cours est le suivant. 1 est le niveau logique, il correspond à une tension un peu supérieure à 1V.

Diapo 19 Ces deux diapositives ont permis je l'espère de comprendre comment on peut réaliser un interrupteur commandé en tension sur du silicium. Les technologies ont beaucoup évolué et la gravure de transistors de quelques dizaines de nanomètres demande des technologies en conception beaucoup plus évoluées que ces premières réalisation. Cette diapositive montre quelques photos, libres d'utilisation, de transistors réalisés sur du silicium. Le premier est un transistor discret de puissance, le second est un processeur et le troisième est un wafer, disque de silicium sur lequel sont gravés les processeurs.

Diapo 20 Du transistor à la porte logique - 1 Le transistor permet d'avoir un interrupteur commandé en tension. Le niveau logique d'une entrée va ainsi permettre d'ouvrir ou de fermer le transistor. Assemblons 4 transistors de la manière suivante pour voir quelle fonction logique nous obtenons. Les transistors du haut ont une commande inversée, d'où le petit rond au niveau de la grille, c'est-à-dire qu'ils sont fermés quand la commande est au niveau logique 0 et ouvert quand la commande est au niveau logique 1. Les entrées du système sont A et B, la sortie est S. VDD est la tension d'alimentation, aux alentours de 1,5V pour un processeur. Gnd est la tension de référence, 0V. On remplit la table de vérité en étudiant toutes les combinaisons possibles de A et B.

Diapo 21 Du transistor à la porte logique - 2 Pour A = 0 et B = 0, les 2 transistors supérieurs sont fermés et les 2 transistors inférieurs sont ouverts. S est ainsi reliée à VDD, donc au niveau logique 1.

Diapo 22 Du transistor à la porte logique - 3 Pour A = 0 et B = 1, le transistor supérieur gauche reste fermé et l'un des 2 transistors inférieurs est ouvert. S est donc toujours reliée à VDD, donc au niveau logique 1.

Diapo 23 Du transistor à la porte logique - 4 Pour A = 1 et B = 0, nous sommes dans la situation symétrique : le transistor supérieur droit est fermé et l'un des 2 transistors inférieurs est ouvert. S est donc encore reliée à VDD, donc au niveau logique 1.

Diapo 24 Du transistor à la porte logique - 5 Dernier cas, pour A = 1 et B = 1, les 2 transistors supérieurs sont ouverts et les 2 transistors inférieurs sont fermés. S est donc toujours reliée à Gnd, donc au niveau logique 0.
Le système est combinatoire : A une combinaison des entrées correspond une seule valeur des sorties. L'équation est S = NON (A ET B), qui est l'équation de la porte logique NAND (NON ET en français).

Diapo 25 De la porte NAND à toutes les portes logiques La porte NAND est intéressante car elle permet de reconstituer toutes les portes logiques. on dit que c'est un opérateur complet, comme NOR (NOU OU) par ailleurs) : la porte NON NOT, la porte ET NAND, la porte OU OR, et la porte Ou exclusif, noté XOR en anglais

Diapo 26 Des portes logiques à toutes les fonctions combinatoires - 1 Une fonction combinatoire est une fonction pour laquelle la ou les sorties sont uniquement fonction des entrées. Un additionneur 1 bit est par exemple une fonction combinatoire. S = A ou exclusif B et la retenue Carry = A ET B. On imagine facilement pouvoir réaliser cette fonction avec des portes logiques. L'additionneur 1 bit peut aussi prendre en compte une retenue précédente. . Les sorties sont toujours des fonctions logiques des entrées.

Diapo 27 Des portes logiques à toutes les fonctions combinatoires - 2 Notre additionneur 1 bit combinatoire nous permet de réaliser un additionneur 4 bits.

Diapo 28 Des portes logiques à toutes les fonctions combinatoires - 3 On pourrait ainsi réaliser avec des portes logiques constituées de portes NAND, elles-mêmes réalisées avec des transistors MOSFET : * un additionneur 32 bits * un soustracteur qui n'est autre qu'un additionneur avec des nombres codés en complément à 2 * des opérateurs de décalage * Des opérateurs logiques bit à bit (ET bit à bit sur des nombres de 32 bits, etc...) * des comparateurs * et même un complexe multiplieur de nombres 32 bits. c'est à dire toutes les instructions de traitement de données d'un processeur basique.

On peut aussi réaliser 2 fonctions qui seront bien utiles un peu plus loin : * Un multiplexeur * Un démultiplexeur

Diapo 29 Multiplexeur / Démultiplexeur Le multiplexeur comprend plusieurs entrées, sur la gauche, des entrées de sélection dessous. La sortie est égale à l'entrée dont le numéro est sélectionnée. . C'est en quelque sorte un aiguillage d'une des entrées vers la sortie. Cette fonction est une fonction combinatoire, donc réalisable avec des portes logiques.

De même le démultiplexeur permet d'envoyer une entrée vers une des sorties. Les autres restant à 0.

Diapo 30 La bascule 1 Considérons maintenant le circuit suivant, avec 2 portes NOR ou NON OU et établissons sa table de vérité, en commençant par la 2nde ligne.

Diapo 31 La bascule 2 Pour la porte NOR du bas, S=1, 1 ou Q vaut 1 quelque soit la valeur de Q, donc Qb vaut 0. Pour la porte NOR du haut, R=0, Qb=0, 0 OU 0 = 0 donc Q vaut 1.

Diapo 32 La bascule 3 La 3ème ligne l'état symétrique de la 2ème. Pour la porte NOR du haut, R=1, 1 ou Qb vaut 1 quelque soit la valeur de Qb, donc Q vaut 0. Pour la porte NOR du bas, S=0, Q=0, 0 OU 0 = 0 donc Qb vaut 1.

Diapo 33 La bascule 4 Regardons le comportement du système si R=0 et S=0 Pour la porte NOR du haut, R=0, 0 OU 1 = 1 et 0 OU 0 = 0 donc 0 ou Qb prend la valeur de Qb. Ainsi Q = Qb barre. Pour la porte NOR du bas, de manière symétrique, S=0, 0 OU Q vaut Q donc Qb = Q barre. Si on considère, comme le permettent les 2ème et 3ème lignes que Qb est le complémentaire de Q, Q garde la valeur précédente de Q et Qb garde la valeur précédente de Qb, compément de Q. On a ainsi un état mémorisation, où Q garde sa valeur.

Diapo 34 La bascule 5 Consérons enfin la dernière ligne. Pour la porte NOR du haut, R=1, 1 ou Qb vaut 1 quelque soit la valeur de Qb, donc Q vaut 0
Pour la porte NOR du bas, S=1, 1 ou Q vaut 1 quelque soit la valeur de Q, donc Qb vaut 0. Cet état contredit le fait que Qb soit le complément de Q. On interdit donc cet état, ce qui peut se faire en privilégiant S par exemple dans ce cas.

Le système n'est pas combinatoire puisque la valeur de Q ne dépend pas uniquement de R et S mais aussi de l'état précédent de Q. Le système est dit séquentiel, ses sorties dépendent des entrées et de l'état précédent du système, , ce que montre bien les deux retours des sorties en entrée des portes logiques. Ce système est une bascule R S, avec R pour Reset, mise à 0 de la sortie et S pour Set, mise à 1 de la sortie. En l'absence de commande R ou S, la sortie garde sa valeur.

Diapo 35 La bascule D Une autre bascule, un peu plus complexe à décrire avec des portes logiques, est la bascule D. Lors d'un front montant de l'entrée Clk, la sortie prend la valeur de l'entrée nommée D. Dans tous les autres cas, la sortie garde sa valeur.

Diapo 36 Le buffer 3 états - 1 Pour introduire le processeur, il nous manque un composant, le buffer 3 état. Il est constitué de 2 entrées In (pour Input) et OE (pour Output Enable) reliées chacune à 2 transistors. La sortie est nommée Out (pour Output). Regardons sa table de vérité.

Diapo 37 Le buffer 3 états - 2 Lorsque OE = 1, le transistor supérieur et le transistor inférieur se comportent comme des interrupteurs fermés. Si l'entrée est à 0, la sortie est à connecté à Gnd, donc elle est au niveau logique 0.

Diapo 38 Le buffer 3 états - 3 Si l'entrée est à 1, la sortie est connecté à Vdd donc elle est au niveau logique 1. Ainsi, quand l'entrée OutputEnable est à 1, la sortie recopie l'entrée.

Diapo 39 Le buffer 3 états - 4 Lorsque OE=0, quelque soit la valeur de In, le transistor supérieur et le transistor inférieur sont ouverts. La sortie n'est donc connectée à aucun potentiel. On note cela Z et on dit qu'elle est "en l'air", ou en haute impédance.

Ce circuit est nommé buffer 3 états. On peut dire rapidement que c'est un interrupteur commandé. Lorsque OE = 1, il est fermé, la sortie recopie l'entrée. Cependant l'entrée ne recopie pas la sortie, c'est unidirectionnel, c'est pourquoi on préfère le schéma triangle. Le buffer est très utilisé en informatique, il permet de connecter plusieurs sorties sur un même fil, une seule sortie n'étant pas en haute impédance à la fois.

Diapo 40 Mémoire 1 bit Ainsi, avec une bascule D et une buffer 3 états, on peut faire une mémoire 1 bit. Sur le fil conducteur de gauche, est imposé par un autre composant le niveau logique 1. Sur le buffer 3 états, le signal OE est à 0, la sortie est donc en haute impédance, sans interaction avec le fil conducteur. Si le signal CLK de la bascule D passe à 1, Q recopie D et prend donc la valeur 1 imposée sur le fil. Le signal entrant sur l'entrée CLK de la bascule peut donc être appelée Ecriture. Quand il repasse à O , la valeur reste stockée dans la bascule jusqu'à une nouvelle écriture, quelle que soit la valeur en entrée
Pour relire le contenu de la mémoire sur le fil conducteur, on passe le signal OE du buffer 3 états à 1, la valeur de l'entrée du buffer (qui est aussi la valeur de sortie de la bascule) est recopié sur la sortie et donc sur le fil conducteur. . Le signal connecté à OE peut donc être nommé Lecture.

Diapo 41 Mémoire 8 bits En assemblant 8 mémoires 1 bit, on peut réaliser une mémoire 8 bits, dialoguant avec l'extérieur via 8 fils conducteurs. Le signal écriture permet de copier les valeurs logiques des 8 bits en mémoire. Le signal lecture permet de recopier les valeurs stocker en mémoire sur les fils conducteurs.

Diapo 42 Mémoire 8 bits On peut alors simplifier le circuit de 8 bascules et 8 buffers parallèle par un seul accessible circuit, nommé registre 8 bits, mémoire accessible en lecture et en écriture via un groupe de 8 fils. Il est possible d'ajouter plusieurs registres de ce type pour augmenter les capacités mémoire de 1 à 4 octets.
Plutôt qu'un signal de lecture et un signal d'écriture par circuit, on préfère alors avoir un seul signal de lecture, un seul signal d'écriture et un mot d'adresse qui sélectionne le registre souhaité. On ajoute alors le démultiplexeur présenté précedemment pour générer un signal d'activation (appelé enable) pour le registre sélectionné. Ainsi, écrire sur le registre d'adresse 0 se passera de la manière suivante , le démultiplexeur active enable 0, les portes ET font que seul le signal d'écriture du registre d'adresse 0 passe à 1. , les valeurs d0 à d7 présentes sur les fils sont copiées et stockées dans le registre d'adresse 0. Lire le registre d'adresse 3 se passera de la manière suivante , le démultiplexeur active enable 3 et seul le signal de lecture du registre d'adresse 3 passe à 1. Le contenu du registre d'adresse 3 est recopié sur les fils de gauche. Ces fils désservent tous les registres, pour l'écriture de données comme pour la lecture de données. ils sont appelés bus de données. Ici c'est un bus de données 8 bits. Les fils servant à indiquer l'adresse du registre seront eux appelés bus d'adresse.

Nous avons ainsi réalisé, avec des bascules, des buffers 3 états et un multiplexeurs, tous réalisés uniquement avec des transistors, une mémoire 4 octets adressable. Il nous est donc possible désormais d'utiliser des mémoires de plusieurs registres, ainsi que les blocs opérations combinatoires que nous avons vu précédemment, nous allons créer un processeur.

Diapo 43 La CPU On trouve sur ce système informatique minimaliste : Une mémoire de 8 octets. L'espace adressable est de 8 octets, le bus d'adresse a donc 3 bits Un lot de 4 registres : R0, R1, R2 et R3. R3 est nommé Program Counter, il servira à indiquer l'adresse de la prochaine instruction en mémoire. Un bus de données 8 bits reliant la mémoire et les registres Un bus d'adresse permet d'écrire la valeur d'un registre sur l'adresse de la mémoire Une unité arithmétique et logique UAL (ou ALU Arithmetic and Logic Unit en anglais) regroupe les opérateurs combinatoires présentés avant. Ici, nous n'avons gardé que l'addition, la soustraction et la comparaison, tous opérent sur des mots de 8 bits. Chaque bloc opération est activable par un signal enable. L'unité arithmétique et logique possède deux registres d'entrées, A et B et un registre de sortie, S. Enfin, une unité de contrôle a en charge la collecte et l'exécution des instructions. Pour cela elle possède un séquenceur cadencé par l'horloge du processeur qui active la lecture d'une instruction dans un registre IR (Instrution Register). Une fois l'instruction copiée, l'unité de contrôle décode l'instruction : si c'est un déplacement de données, cela agit seulement sur les signaux adresse, lecture et écriture des registres et mémoire. Si c'est un traitement de données, un simple démultiplexeur active le bloc opération indiqué par le code instruction. Le chargement des opérandes et le stockage du résultat est une copie de données entre les registres et les registres A, B, S de l'UAL. Les registres, l'Unité Arithmétique et logique et l'unité de contrôle forment la CPU, Central Processing Unit. Le terme coeur de processeur désigne une CPU et quelques éléments supplémentaires.

Le système présenté ici n'utilise que des éléments expliqués avant. Un grand lot de transistors, et un peu de temps, permettrait de le réaliser. Très simplifié, les instructions y sont codées sur un octet et il ne possède que 4 registres internes.

Regardons le fonctionnement de ce système minimaliste pour l'addition de 2 nombres stockés en mémoire aux adresses 5 et 6. Le résultat sera stocké à l'adresse 7.

Diapo 44 La CPU - 2 Au démarrage, le Program Counter est à 0. Les instructions sont stockées dans la mémoire, aux adresses 0 à 3, nous les décoderons au fur et à mesure. Le séquenceur copie la valeur de PC sur le bus de d'adresse, demande une lecture à la mémoire et une écriture au registre IR . L'instruction stockée à la case 0 de la mémoire est alors copiée dans le regristre Instruction de l'unité de contrôle .

Diapo 45 La CPU - 3 Le registre PC indiquant l'instruction suivante est incrémenté. L'instruction est décodée : 00 ne correspond à aucune opération de traitement, c'est un déplacement de données. Le 3ème 0 indique que ce déplacement de données se fait de la mémoire vers les registres. xxx est l'adresse de la case mémoire source et yy l'adresse du registre destination.

Le premier opérande 101, c'est-à-dire 5 est copié sur le bus d'adresse. Le second 00 est copiée sur l'adresse des registres internes. Les signaux lecture de la mémoire et écriture des registres sont activés Le contenu du registre d'adresse 5 de la mémoire est alors copié dans le registre interne R0

Diapo 46 La CPU - 4 L'instruction est terminée. le séquenceur charge l'instruction suivante. Le registre PC indique qu'elle est à l'adresse mémoire 1 . Le séquenceur copie donc le contenu de l'adresse mémoire 1 dans le registre IR de l'unité de contrôle.

Diapo 47 La CPU - 5 L'unité de contrôle décode l'instruction. L'instruction est encore un déplacement de données de la mémoire vers un registre interne. L'adresse source est copiée sur le bus d'adresse L'adresse destination est copiée sur l'adresse des registres internes Les signaux lecture et écriture sont activés Le contenu de la case mémoire 6 est copié dans le registre R1

Diapo 48 La CPU - 6 L'instruction est terminée. le séquenceur charge l'instruction suivante. Le registre PC indique qu'elle est à l'adresse mémoire 2 . Le séquenceur copie donc le contenu de l'adresse mémoire 2 dans le registre IR de l'unité de contrôle.

Diapo 49 La CPU - 7 L'unité de contrôle décode l'instruction. Le code instruction est 10, c'est une addition, les opérandes et le résultat sont des registres internes indiqués dans la suite de l'instruction. L'opérande 1 vient du registre R0, elle est copiée dans le registre A de l'Unité Arithmétique et Logique. L'opérande 2 vient du registre R1, elle est copiée dans le registre B de l'UAL. Le code opération est copié sur le démultiplexeur Sa sortie active le bloc addition Le résultat du bloc addition arrive dans le registre de sortie de l'UAL Enfin, le résultat est stocké dans le registre destination R2 indiqué dans l'instruction.

Diapo 50 La CPU - 8 L'instruction est terminée. le séquenceur charge l'instruction suivante. Le registre PC indique qu'elle est à l'adresse mémoire 3 . Le séquenceur copie donc le contenu de l'adresse mémoire 2 dans le registre IR de l'unité de contrôle.

Diapo 51 La CPU - 9 L'unité de contrôle décode l'instruction. L'instruction est un déplacement de données, cette fois d'un registre interne vers la mémoire. L'adresse source 10 c'est-à-dire 2, est copiée sur l'adresse des registres internes L'adresse destination 111 c'est-à-dire 7, est copiée sur le bus d'adresse Les signaux lecture et écriture sont activés Le contenu du registre R2 est copié dans la case mémoire d'adresse 7

Diapo 52 La CPU - 10 Nous avons ainsi montré le fonctionnement d'un système informatique minimaliste. Il n'est que le résultat du fonctionnement bien ordonné des blocs combinatoires, des mémoires et des registres 3 états présentés avant. Nous n'avons pas présenté le mécanisme permettant de faire des tests (structures IF) ou des boucles (stuctures While). Pour cela, un registre supplémentaire, Status Register, est présent dans la CPU, il comporte les bits indiquant si le résultat de la précédente opération est négatif, nul ou a une retenue. Le bloc comparaison de l'UAL notamment modifie ce registre.

Diapo 53 La CPU - 11 Le jeu d'instruction d'un processeur contient des instructions de sauts, nommées BRANCH. Ces instructions écrivent dans le registre PC, modifiant ainsi l'instruction suivante qui sera lue par le séquenceur. Ici Branch #2 écrit 2 dans PC. . L'instruction suivante serait donc de nouveau l'instruction stockée dans la case mémoire d'adresse 2. Des sauts conditionnels, dépendant des bits du registre SR permettent de faire des sauts en fonction du résultat d'une comparaison.

La taille du bus de données indique le nombre de bits traités en parallèle. Des bus 8 bits sont utilisés pour les très petits microcontrôleurs, la plupart utilisant aujourd'hui des bus 32 bits. Les processeurs puissants sont des architectures 64 bits. Ils traitent donc des nombres de 8 octets à chaque instruction. La taille du bus mémoire donne l'espace adressable, dans lequel on peut mettre différents types de mémoires et périphériques. Le bus mémoire de cet exemple est de 3 bits, pou 2 exposant 3 = 8 adresses mémoire. Un microcontrôleur a habituellement un bus d'adresses de 32 bits donc 2 exposant 32 = 4 Mds d'adresses possibles, 4 Go. Un processeurs puissant a un bus d'adresse de 64 bits, donc 16 Billiards d'adresses possibles. Nous verrons au chapitre 4 comment c'est utilisé.

Ainsi, la suite du cours nous amènera à voir étape par étape d'autres fonctionnalités et des améliorations du processeur minimaliste présenté ici.

Diapo 54 Intro partie 2

Nous avons étudié précedemment comment une CPU formées de blocs combinatoires et séquentiels construits à base de transistors pouvait exécuter des instructions. Les codes instructions étant peu lisibles, très vite est apparu un système de mnémonique pour chaque instruction. C'est le langage assembleur, langage du processeur. Nous verrons par la suite comment les programmes python et C font pour être exécuter par un processeur ne comprenant que les instructions assembleur. Commençons par lire et écrire quelques lignes d'assembleurs

Diapo 55 Le langage assembleur

L'objectif du cours est de comprendre un minimum le fonctionnement de ce langage pour comprendre le fonctionnement du processeur. Le langage assembleur, simple traduction des codes opérations de la machine pour les processeurs historiques, est spécifique à chaque famille de processeur. Ce langage est donc décrit dans la documentation des familles de processeurs, on y trouve des invariants : Des instructions de déplacement de données : MOVE, LOAD REGISTER, STORE REGISTER Des instructions de traitement de données, par exemple : Addition, Comparaison, opérateur logique ET, Décalage à gauche (Logical Shift Right) Des instructions de saut : Branch avec par exemple le saut inconditionnel BRANCH ALWAYS ou le saut si le résultat de l'opération précédente est positive BRANCH PLUS

Pour les instructions, différents modes d’adressage sont possible pour indiquer l’origine et/ou la destination des données/opérandes. Voyons quelle possibilité nous avons pour copier une valeur dans le registre interne R2. On indique ainsi dans l’instruction : La valeur, souvent précédée d'un #, c'est adressage immédiat -> par exemple : MOV R2 #3 copiera la valeur 3 dans le registre R2. On peut indiquer L’adresse d'un registre interne, c'est adressage inhérent, par exemple : MOV R2 R3 copiera le contenu de R3 dans R2. On peut indiquer l’adresse de la case mémoire, c'est l'adressage direct, par exemple : LDR R2 7, le contenu de la case mémoire d'adresse 7 est copié dans R2 On peut indiquer le nom du registre qui contient l’adresse de la case mémoire visée, c'est l'adressage indirect, par exemple LDR R2 0(R3) copiera dans R2 le contenu de la case mémoire dont l'adresse est stockée dans R3 Le nom du registre qui contient l’adresse et le nom du registre qui contient le décalage : adressage indexé -> ex : LDR R2 R1[R3]

Diapo 56 Le langage assembleur La diapositive présente le jeu d'instructions de coeurs de processeurs ARM. ARM est une société anglaise dessinant des coeurs de processeurs vendus à de nombreux fabricants de microcontrôleurs et de microprocesseurs. Les téléphones portables, les nouveaux PC Apple, de nombreux objets connectés et même le supercalculateur le plus puissant actuellement utilisent des coeurs ARM. En 1936, Alan Turing et Alonzo Church présentaient le concept de machine de turing universelle : l’ensemble des opérations arithmétiques peuvent être réalisées par l'utilisation d'un petit sous-ensemble d’opérations simples. Il faut donc faire un compromis entre Simplicité des instructions et Temps de calcul. Une division de nombre réels se fait en une instruction pour un gros coeur ayant un bloc division de réels. Par contre, pour un petit coeur faisant juste des calculs sur des entiers, la division de réels sera décomposées en un millier d'opérations simples et donc beaucoup plus lente. Ainsi, pour répondre aux différents besoin, ARM propose des coeurs ayant plus ou moins d'instructions disponibles. On montre ici les jeux d'instructions des coeurs Cortex-M, destinés aux objets connectés ou au traitement d'entrées/sorties. Les petits coeurs Cortex M0+ se contentent de 57 instructions : les déplacements de données, les opérations classiques sur des entiers et les sauts. Il est très petit et consomme très peu. Un coeur plus gros a des instructions plus efficace. Le coeur cortex M3 propose notamment l'instruction UMLAL qui fait en un cycle multiplication et addition, combinaison d'opérations très utile pour le filtrage numérique. Le coeur Cortex M4 propose des instructions SIMD (Single Instruction Multiple Data), par exemple l'instruction QADD fait 4 additions d'entiers 8 bits en un cycle d'horloge. L'ajout d'une unité de calcul en virgule flottante permet de faire les complexes calculs sur les nombres réels en un cycle instruction. Ce type de coeur de processeurs avec une centaine d'instructions est dit à architecture RISC (Reduced Instruction Set Computer). Chaque instruction se fait en un cycle instruction. Plus complexe, les architectures x86 ou amd64 comporte plusieurs milliers d'instructions, dont certaines très complexes. Les processeurs sont alors plus gros mais les instructions, nécessitant pour certaines plusieurs cycles, plus efficaces. On parle d'architecture CISC (Complex Instruction Set Computer)

Diapo 57 Simulateur de processeur Peter Higginson, ancien professeur d'informatique au University College London, propose un très pédagogique simulateur de processeur, idéal pour lire et écrire quelques lignes d'assembleur. On y trouve une mémoire, les adresses correspondent à des cases de 16 bits alors qu'une adresse mémoire correspond normalement à une adresse 8 bits. Les registres internes Le bus de données Le bus d'adresses L'Unité Arithmétique et Logique L'unité de contrôle avec le registre Instruction Register et le registre de statut, nommé FLAGS ici. On retrouve les bits N pour négative, Z pour 0, C pour Carry et V pour Overflow. Les cases Input et Output peuvent être vues comme des registres de périphériques d'entrées clavier et de sortie écran. Le jeu d'instructions du processeur simulé, proche de celui du ARM Cortex M0, est fourni dans l'aide. Le simulateur fonctionne mieux sur Chrome ou sur Chromium que sur Firefox. Un simulateur un peu plus complet et un peu plus complexe est aussi disponible sur le site.

Diapo 58 Faisons un essai On charge l'exemple max * Le code assembleur apparaît sur la gauche, on peut cliquer pour le modifier. les codes instructions résultants sont stockés en mémoire. * On peut afficher le contenu de la mémoire en hexadécimal * La lecture du jeu d'instructions renvoyé par le bouton HELP nous informe par exemple sur le code opération 0x768 de la comparaison, auxquels se rajoutent les numéros des registres concernés, 1 et 0 pour nous. * Il est possible de modifier la vitesse d'exécution. * Le séquenceur commence par aller chercher l'instruction stockée à l'adresse 0 et la décode * L'instruction, de manière un peu moins réaliste, attend un contenu dans le registre Input et le stocke en R0 * Le séquenceur va chercher l'instruction stockée à l'adresse 1 * L'instruction, attend un contenu dans le registre Input et le stocke en R1 * Le séquenceur charge l'instruction suivante à l'adresse 1. * Cette instruction comparaison demande comme opérandes les registres 1 et 0 qui sont alors chargés dans l'ALU. R1 est supérieur à R2, c'est donc le bit Carry du registre de statut qui passe à 1. * Le séquenceur charge l'instruction suivante. * C'est un saut conditionnel Branch Bigger or Equal. Il s'appuie les bits Z et C. Comme C est à 1, le saut est effectué vers l'instruction de l'adresse 6, l'unité de contrôle modifie donc PC. * Le séquenceur charge l'instruction 6. Les instructions 4 et 5 n'ont pas été exécutées. * Le résultat est affiché dans la zone Output.

Manipuler ce simulateur aide à bien comprendre le fonctionnement de la CPU d'un processeur.

Diapo 59 Intro partie 3 Nous avons vu dans les parties précédentes comment fonctionne une CPU minimaliste et écrit quelques lignes d'assembleurs. Avant de voir comment on passe d'un langage évolué à l'assembleur, il reste à ajouter un mot sur la distinction entre architectures Von Neumann et Harvard.

Diapo 60 2.3 Architecture Von Neuman / Harvard Dans la CPU minimaliste créé dans la partie 1 et sur le processeur simulé de Peter Higginson, le programme était stocké dans la même mémoire que les données. C'est l'architecture Von Neumann. C'est celle utilisée sur les coeur ARM Cortex M0 par exemple. Le type de mémoire utilisée par le programme peut être différent (la mémoire nommée ROM ici pour le programme et la mémoire nommée RAM pour les données), mais il occupe le même espace adressable et utilise le même bus que la mémoire données. Les bits de poids fort de l'adresse déterminent alors si on pointe sur la mémoire ROM ou la mémoire RAM. D'autres architectures, nommées Harvard, ont une mémoire et un bus dédiés pour les instructions du programme et une mémoire et un bus pour les données. On présente ici le petit microcontrôleur Microchip PIC16F720 avec sa mémoire programme 14 bits séparée de sa plus classique mémoire données 8 bits.

Diapo 61 2.3 Architecture Von Neuman / Harvard 2 L'architecture Von Neumann présente des avantages certains :

Un seul bus de données (pour les données et les instructions) et un seul bus d’adresses => C’est plus simple. C'est l'architecture retenue pour l'économique coeur de processeur Cortex M0+. Le code peut être stocké dans la mémoire RAM des données pour un accès plus rapide. L'architecture offre plus de souplesse sur la localisation du code : pour les machines virtuelles, les machines java, le code tournant sur la machine virtuelle est vue comme les données d'une application. De plus, on peut concevoir des programmes capables de s’auto-modifier.

L'architecture Harvard a d'autres avantages : La mémoire programme peut être optimisée pour des mots de 16 bits, ou même 14 bits comme sur le PIC16F720, et la mémoire données pour des mots de 32 bits. On peut charger les instructions et manipuler les données en même temps, ce qui permet de gagner en rapidité d'exécution du code. La séparation des espaces protège mieux contre l’arrivée d’instructions fallacieuses.

Les deux architectures ayant des avantages, les PCs modernes reprennent le meilleur des deux. * La mémoire principale d'un PC, la RAM externe, sert à stocker les programmes en cours d'exécution et les données, * Le processeur dispose d'une mémoire cache interne pour stocker les prochaines instructions et d'une mémoire cache pour manipuler les données à l'accès fréquent. On parle d’architecture Harvard modifiée

Diapo 62 Intro partie 2.4 Du langage haut niveau à l’assembleur Dans la partie 2, nous avons vu que le langage du processeur est l'assembleur. Très proche des instructions de la machine, il est spécifique à chaque processeur et est très bas niveau, ce qui rendrait l'écriture de codes complexes très fastidieuse. C'est pourquoi aujourd'hui, presque tous les programmes sont écrits avec des langages haut niveau.

Diapo 63 2.4 Du langage évolué à l’assembleur Le processeur ne comprend que les codes opérations. L'assembleur est une simple traduction de ces codes opérations en mnémoniques plus faciles à manipuler. La question se pose alors de savoir comment le programme écrit en python finit par s'exécuter sur un processeur ne comprenant que le code assembleur.

Diapo 64 Le langage C Pour cela nous faisons un détour par le langage C pour parler compilation. En 1972, dans le but de réécrire le système d'exploitation UNIX dans un langage haut niveau, Dennis Ritchie, chercheur des laboratoires Bell, développe le langage C. Celui-ci est amélioré par Brian Kernighan. C'est le premier langage haut niveau utilisé à grande échelle et reste un langage très utilisé, avec ses dérivés C++ et C#. Le code est écrit dans le langage C, dit de haut niveau. Le code est ensuite compilé, c'est-à-dire converti en une suite d'instructions processeurs. Pour cela le compilateur doit connaître le processeur qui va exécuter le code. Ici, c'est le compilateur pour coeur arm avec l'option CORE_MOPLUS qui est utilisé. Une fois le code compilé, le linker fait le lien avec les codes d'autres fichiers ou d'éventuelles bibliothèques, ici pour la fonction printf Le loader enfin, connaissant les zones mémoires utilisables par le processeur, charge les instructions dans la zone appropriée de la mémoire.

Avec la compilation, qui plus est en utilisant des options d'optimisation, le code généré est directement prêt pour être exécuté par le processeur. Il est donc peu gourmand en mémoire et son exécution est rapide.

Diapo 65 code interprété, code compilé Le langage python n'est pas un langage compilé. Quelle est la différence, comment est-il exécuté sur le processeur ? Un code python ne peut fonctionner seul. Il s'exécute dans un interpréteur. C'est ce programme souvent écrit en C qui possède des portions de code assembleurs qui sont exécutées à la lecture des lignes du programme python. L'interpréteur doit donc avoir été compilé pour la famille de processeurs sur laquelle il fonctionne. Le programme python est interprété ligne par ligne, sans passer par une étape de compilation.

L'avantage majeur est qu'une fois l'interpréteur installé sur la machine, l'interprétation du code python n'a pas à se préoccuper de l'architecture de cette machine. C'est très portable. De plus, le code python n'accédant à la machine que via l'interpréteur, il ne peut faire des opérations dangereuses pour l'intégrité de celle-ci. On peut ajouter aussi qu'il est pratique de pouvoir exécuter une ligne de python indépendamment du reste du programme.

L'inconvénient est que la couche intermédiaire "interpréteur" nécessite des ressources mémoire et CPU et que le code étant exécuté ligne par ligne, il ne peut être optimisé. Un programme python est donc souvent plus gourmand en ressources et plus lent qu'un programme compilé.

Le code compilé est lui optimisé pour la cible. Cela nécessite de compiler le programme pour chaque famille de processeurs. L'avantage est un programme moins gourmand en ressource et plus rapide. C'est pourquoi le langage C reste très utilisé en informatique embarquée, pour des systèmes ayant des processeurs peu puissants et peu de mémoire. Très proche de la machine, le C permet des écritures directes en mémoire, à des adresses pas forcément prévues pour recevoir des données. D'autres langages compilés conçus récemment, RUST et GO, tentent de garder ces performances tout en générant un code plus sûr.

Certaines bibliothèques de python, pour tirer partie des performances des langages compilés, sont écrites en langage C et appelées en python. C'est le cas de la bibliothèque de traitement d'image OpenCV par exemple.

Notons que Java est un langage compilé pour la CPU virtuelle d'une Machine virtuelle java fonctionnant sur la machine. Ses avantages et inconvénients se rapprochent des langages interprétés.

Diapo 66 code interprété, code compilé - 2 Le site benchmarkgame propose des comparaisons de performances de l'exécution de différents algorithmes avec des langages compilés et des langages interprétés. Plusieurs algorithmes sont testés. On présente ici seulement les résultats du calcul de la fractale de MandelBrot et du caclul d'une fonction de hashage. La première colonne indique le temps d'exécution, la seconde présente la taille de la mémoire utilisée et la dernière la taille du code en octets après un compression minimale Gzip. On peut noter la performance meilleure des langages compilés et la grande utilisation de ressources de java et javascript. Après 2 décennies de programmation sans tenir compte de l'utilisation des ressources, toujours disponibles en plus grande quantité, les préoccupations environnementales et la facturation des ressources utilisés par les serveurs cloud amorcent un relatif retour en grâce des langages compilés face à java et javascript, pour les logiciels sur PCs et serveurs.

Memoire donc tout binaire -> float ascii adressage en octet

exo sequentiel / combin exo taille des nombres

CPU Bus 8 bits bus adresse ?

Prog Binaire puis ASM puis C Pour ASM, on trouve Lecture/ecriture Mem ou dépalcement données calcul arithmétique calcul logique comparaisons branchement (if et boucles, appels sous-programmes)

Compilation : Convertit code source en code objet (langage machine) Lienn ave Machine du turing universelle Editions de liens : Rassemble les différents modules d'un prog

Systèmes embarqués

fonctions La pile Les interruptions Les Entrées sorties

IT Bien expliquer l'importance des registres et donc de la sauvegarde du contexte lorsqu'on change de tâche ou lorsqu'apparaît une interruption Matérielles et logicielles IRQ pour Interruption ReQuest

Pipeline issu de Intel : Each processor contains from one to hundreds of cores. Each core contains hardware to:

Fetch instructions.
Decode those instructions.
Schedule the instructions for execution.
Execute the instructions.
Fetch the data the instructions need.
Store the data the instructions produce.

mémoire le processeur lit la donnée dans le cache ; – si la donnée n’est pas présente, un défaut 5 de cache a lieu (cache fault 6 en anglais) ; – ce défaut de cache déclenche alors une action prédéterminée, en l’occurrence la copie de la donnée de la mémoire principale vers la mémoire cache ; – la donnée peut alors être lue par le processeur. L’échange de données entre la mémoire cache et la mémoire principale est simi- laire à l’échange de données entre la mémoire principale et la mémoire secondaire (généralement le disque dur) qui sera abordé dans le chapitre sur la gestion de la mémoire. Le même principe est aussi employé entre le cache primaire, logé directement au sein du processeur, et le cache secondaire, logé à l’extérieur. La plupart des processeurs possèdent même plusieurs zones de mémoire cache interne, parfois appelées cache de niveau 1, cache de niveau 2, etc. La plupart des périphériques et des contrôleurs de périphériques possèdent aussi de la mémoire cache pour accélérer les transferts et ce principe est donc très générique. Pourquoi utiliser cinq zones différentes de mémoire (registres, cache primaire, cache secondaire, mémoire principale et mémoire secondaire) ? On pourrait en effet imaginer que le processeur qui a besoin d’une donnée aille directement la chercher dans

Adresses physiques Nous avons défini à la section 1.2 le terme « adresse » comme une indexation des octets disponibles dans la mémoire principale de l’ordinateur. Nous appellerons désormais ce type d’adresse des « adresses physiques » dans la mesure où elles font directement référence à la réalité physique de la machine. Une adresse physique est donc un nombre qu’il suffit de passer au contrôleur de mémoire pour désigner une donnée stockée en mémoire. Bus adresse 64 bits ?

Pour la protection des zones mémoires : Adresses virtuelles En pratique, les ordinateurs n’ont jamais 4 Go de mémoire principale (adresses sur 32 bits) et une grande partie des adresses ne sert à rien. Une idée intéressante est d’utiliser tout l’espace d’adressage pour effectuer la gestion de la mémoire, puis de transformer au dernier moment l’adresse utilisée en une adresse physique. Par exemple, utilisons les 2 premiers bits d’adresse pour réserver des zones de mémoire au système. Ces 2 bits désignent 4 zones de 1 Go et nous allons réserver la zone 3 (11 en binaire) pour le système. Ainsi, toutes les données concernant le système d’exploitation auront une adresse de 32 bits débutant par 11 et il suffira donc de tester ces 2 premiers bits pour savoir si une donnée appartient au système ou pas. L’avantage de ce système est évident ! Mais la correspondance entre les adresses ainsi composées et les adresses physiques n’est plus assurée : les adresses de 32 bits commençant par 11 correspondent à des données stockées après les 3 premiers Go de mémoire... De telles adresses n’ayant pas de correspondance physique s’appellent des adresses virtuelles. Une adresse virtuelle est donc une représentation particulière de la zone occupée par un octet qui nécessite une transformation adaptée pour correspondre à une adresse physique. Cette transformation s’appelle la traduction d’adresses ou, par abus de langage, translation d’adresses. La conversion des adresses virtuelles en adresses physiques connues par les couches matérielles a lieu constamment pendant l’exécution des différents programmes utilisés sur l’ordinateur. Cette conversion doit donc être très rapide car elle conditionne la vitesse d’exécution des programmes. C’est pourquoi cette traduction est directement assurée par un composant électronique logé dans le processeur : la MMU

Chipset Les contrôleurs de périphériques En réalité, le processeur ne communique pas directement avec les périphériques : ceux-ci sont reliés à des contrôleurs de périphériques et c’est avec eux que le processeur dialogue. Un contrôleur peut gérer plusieurs périphériques et le processeur n’a pas besoin de connaître précisément ces périphériques : s’il souhaite, par exemple, écrire une donnée sur le disque dur, il le demande au contrôleur de disque dur et c’est ce dernier qui se débrouille pour effectivement satisfaire la demande du processeur. Le processeur transmet alors la donnée à écrire au contrôleur, le contrôleur la stocke dans une mémoire tampon qu’il possède et la transmettra au disque dur

Les bus de communication Le bus système, ou bus processeur, ou encore bus mémoire, souvent connu sous le nom de FSB (Front Side Bus) permet de connecter le processeur à la mémoire de type RAM ainsi qu’à d’autres composants (voir fig. 1.3). Les communications sur ce bus système sont gérées par un composant particulier de la carte mère : le NorthBridge. C’est ce composant qui conditionne souvent le chipset de la carte mère. Les communications avec des périphériques plus lents tels que les disques durs par exemple sont gérées par un autre composant : le SouthBridge. Le bus de cache ou BSB (Back Side Bus) réalise la connexion entre le processeur et la mémoire cache secondaire (cache L2 et cache L3) quand celle-ci n’est pas directement intégrée au processeur. Le bus PCIe (Peripheral Component Interconnect Express) permet la connexion de périphériques variés comme une carte video,. . . Cette nouvelle technologie, compatible avec l’ancien bus PCI, adopte un modèle de transmission par commutation de paquets (très proche du modèle réseau TCP/IP) sur une ligne série à la différence de l’ancien protocole parallèle dans lequel un composant monopolisait le bus pendant sa transaction