## Ordinateur et logiciel Les technologies numériques sont maintenant omniprésentes. - Elles sont le moteur et l'objet de ce qu'on appelle la "révolution numérique" Elles sont basées sur l'interaction entre : - Des programmes, aussi appelés logiciels, décrivant des processus de traitement de l'information : biens immatériels - Des ordinateurs, capables d'exécuter ces programmes : biens matériels ## Représentation de l'information L'information est représentée au sein des composants de l'ordinateur sous forme de différents états de la matière : - "Trou" ou "pas trou" sur la surface d'un cédérom ou DVD ; - Orientation "nord" ou "sud" d'un matériau magnétique ; - Lumière ou absence de lumière émise par un laser ; - Courant électrique ou non ; - etc. Ce sont souvent des représentations à deux états, c'est-à-dire "binaires". ## Constituants élémentaires (1) Presque tous les ordinateurs sont construits à base de circuits électroniques. Les circuits électroniques sont réalisés au moyen de transistors : - Composant élémentaire, dont le courant de sortie dépend de deux valeurs d'entrée. . Un transistor a donc trois "pattes" . Appelées : base, émetteur et collecteur . Analogue à un "robinet à électricité" : plus il arrive de courant sur la base, plus le courant circule de l'émetteur vers le collecteur ## Constituants élémentaires (2) Dans les ordinateurs, on utilise les transistors en mode saturé, c'est-à-dire "tout ou rien". - Fonctionnement analogue à celui d'un interrupteur . Robinet fermé ou ouvert en grand . Soit le courant passe, soit il ne passe pas du tout - Représentation des valeurs binaires "0" et "1" En combinant plusieurs transistors, on peut effectuer des calculs complexes : - Sur la base de montages en série ou en parallèle - Regroupement au sein de "circuits intégrés" ## Performance (1) 1 kHz (kilo-Hertz) = 10^3 Hz 1 MHz (méga-Hertz) = 10^6 Hz 1 GHz (giga-Hertz) = 10^9 Hz 1 THz (téra-Hertz) = 10^12 Hz ## Performance (2) En fait, ce qui importe aux usagers, c'est le nombre d'opérations (plus généralement, "d'instructions") qu'un ordinateur est capable d'effectuer par seconde. On la mesure en MIPS, pour "millions d'instructions par seconde". On pense souvent que la puissance d'un ordinateur dépend de sa fréquence de fonctionnement - C'est loin d'être toujours vrai ! ## Évolutions architecturales (1) 1946 : Ordinateur ENIAC - Architecture à base de lampes et tubes à vide : 30 tonnes, 170 m^2 au sol, 5000 additions par seconde - 0,005 MIPS, dirons-nous... 1947 : Invention du transistor 1958 : Invention du circuit intégré sur silicium - Multiples transistors agencés sur le même substrat ## Évolutions architecturales (2) 1971 : Processeur Intel 4004 - 2300 transistors dans un unique circuit intégré - Fréquence de 740 kHz, 0,092 MIPS ...40 ans d'une histoire très riche... 2011 : Processeur Intel Core i7 2600K - Plus de 1,4 milliards de transistors - Fréquence de 3,4 GHz - 4 coeurs, 8 threads - 128300 MIPS ## Évolutions architecturales (3) Entre le 4004 et le Core i7 2600K : - La fréquence a été multipliée par 4600 - La puissance en MIPS a été multipliée par 1,4 million La puissance d'un ordinateur ne dépend clairement pas que de sa fréquence ! Intérêt d'étudier l'architecture des ordinateurs pour comprendre : - Où les gains se sont opérés ; - Ce qu'on peut attendre dans le futur proche. ## Barrière de la chaleur (1) Plus on a de transistors par unité de surface, plus on a d'énergie à évacuer. La dissipation thermique évolue de façon proportionnelle à V^{2}*F - La tension de fonctionnement des circuits a été abaissée . De 5V pour les premières générations à 0,9V maintenant - Il n'est plus vraiment possible de la diminuer avec les technologies actuelles . Le bruit thermique causerait trop d'erreurs ## Barrière de la chaleur (2) La fréquence ne peut raisonnablement augmenter au delà des 5 GHz - "Barrière de la chaleur" La tendance est plutôt à la réduction - "Green computing" - On s'intéresse maintenant à maximiser le nombre d'opérations par Watt - Mais on veut toujours plus de puissance de calcul ! ## Barrière de la complexité (1) À surface constante, le nombre de transistors double tous les 2 ans - "Loi de Moore", du nom de Gordon Moore, co-fondateur d'Intel, énoncée en 1965 - Diminution continuelle de la taille de gravage des transistors et circuits sur les puces de silicium . On grave actuellement avec un pas de 14 nm - Limites atomiques bientôt atteintes... . Donc plus possible d'intégrer plus . Mais on veut toujours plus de puissance de calcul ! ## Barrière de la complexité (2) Que faire de tous ces transistors ? - On ne voit plus trop comment utiliser ces transistors pour améliorer individuellement les processeurs - Des processeurs trop complexes consomment trop d'énergie sans aller beaucoup plus vite Seule solution actuellement : faire plus de processeurs sur la même puce ! - Processeurs bi-coeurs, quadri-coeurs, octo-coeurs, ... déjà jusqu'à 128 coeurs ! - Mais comment les programmer efficacement ?! ## Barrière de la complexité (3) L'architecture des ordinateurs a été l'un des secteurs de l'informatique qui a fait le plus de progrès. Les ordinateurs d'aujourd'hui sont très complexes - Plus d'un milliard de transistors dans un processeur Nécessité d'étudier leur fonctionnement à différents niveaux d'abstraction : - Du composant au module, du module au système - Multiples niveaux de hiérarchie ## Structure d'un ordinateur (1) Un ordinateur est une machine programmable universelle de traitement de l'information. Pour accomplir sa fonction, il doit pouvoir : - Acquérir de l'information de l'extérieur ; - Stocker en son sein ces informations ; - Combiner entre elles les informations à sa disposition ; - Restituer ces informations à l'extérieur. ## Structure d'un ordinateur (2) L'ordinateur doit donc posséder : - Une ou plusieurs unités de stockage, pour mémoriser le programme en cours d'exécution ainsi que les données qu'il manipule - Une unité de traitement permettant l'exécution des instructions du programme et des calculs sur les données qu'elles spécifient - Différents dispositifs « périphériques » servant à interagir avec l'extérieur : clavier, écran, souris, carte graphique, carte réseau, etc. ## Structure d'un ordinateur (3) Les constituants de l'ordinateur sont reliés par un ou plusieurs bus, ensembles de fils parallèles servant à la transmission des adresses, des données, et des signaux de contrôle. Le schéma montre comment les "boîtes" fonctionnelles (CPU, mémoire, périphériques, etc.) sont reliées par des bus, c'est-à-dire des liaisons physiques et fonctionnelles : - le processeur, la mémoire et les contrôleurs de média de stockage (disque dur) sont reliés par un bus local à très grande vitesse et très grande "largeur" (nombre de fils de données en parallèle), pour avoir un débit maximal ; - les périphériques à basse vitesse (clavier, souris, etc.) sont connectés sur un bus spécialisé (tel que l'USB : "universal serial bus"). Le contrôleur maître de ce bus à basse vitesse est connecté au bus local à grande vitesse ; - il peut exister des bus spécialisés, comme celui liant le contrôleur graphique à la mémoire centrale, pour charger les textures par exemple. Le contrôleur esclave de ce bus est relié lui aussi au bus local. ## Unité de traitement (1) L'unité de traitement (ou CPU, pour "Central Processing Unit"), aussi appelée "processeur", est le cœur de l'ordinateur. Elle exécute les programmes chargés en mémoire centrale en extrayant l'une après l'autre leurs instructions, en les analysant, et en les exécutant. ## Unité de traitement (2) L'unité de traitement est composé de plusieurs sous-ensembles distincts : - L'unité de contrôle, qui est responsable de la recherche des instructions à partir de la mémoire centrale et du décodage de leur type ; - L'unité arithmétique et logique (UAL), qui effectue les opérations spécifiées par les instructions ; - Un ensemble de registres, zones mémoires rapides servant au stockage temporaire des données en cours de traitement par l'unité centrale. ## Registres Chaque registre peut stocker une valeur entière distincte, bornée par la taille des registres (nombre de bits). Certains registres sont spécialisés, comme : - Le compteur ordinal ("program counter") qui stocke l'adresse de la prochaine instruction à exécuter ; - Le registre d'instruction (« instruction register »), qui stocke l'instruction en cours d'exécution ; - L'accumulateur, registre résultat de l'UAL, etc. ## Chemin de données (1) Le chemin de données représente la structure interne de l'unité de traitement : - Comprend les registres, l'UAL, et un ensemble de bus internes dédiés ; - L'UAL peut posséder ses propres registres destinés à mémoriser les données d'entrées afin de stabiliser leurs signaux pendant que l'UAL calcule. Le chemin des données conditionne fortement la puissance des machines : - Pipe-line, superscalarité, etc. ## Chemin de données (2) Voir fichier embossé [[von_neumann]] : - Les rectangles sont les registres. - Le groupe de rectangles en haut est la banque de registres. - La "banane" en bas est l'UAL. - Les registres intermédiaires d'entrée et de sortie sont des rectangles. ## Exécution d'une instruction (1) L'exécution d'une instruction par l'unité centrale s'effectue selon les étapes suivantes : 1- Charger la prochaine instruction à exécuter depuis la mémoire vers le registre d'instruction ; 2- Décoder (analyser) l'instruction venant d'être lue ; 3- Faire pointer le compteur ordinal vers l'instruction suivante (y compris dans le cas de branchements) ; 4- Localiser en mémoire les données nécessaires ; 5- Charger si nécessaire les données dans l'UAL ; 6- Exécuter l'instruction, puis recommencer. ## Architecture des ordinateurs Les ordinateurs modernes sont conçus comme un ensemble de couches. Chaque couche représente une abstraction différente, capable d'effectuer des opérations et de manipuler des objets spécifiques. L'ensemble des types de données, des opérations, et des fonctionnalités de chaque couche est appelée son architecture. L'étude de la conception de ces parties est appelée "architecture des ordinateurs". ## Machines multi-couches actuelles On représente ici un empilement de couches, identifiées par leur numéro, allant de 0, le plus bas niveau, à 5, le plus haut niveau. Entre les couches est exposé le moyen de passer de l'une à l'autre, de façon descendante. 5- Langages d'application . Traduction 4- Langage d'assemblage . Traduction 3- Système d'exploitation . Interprétation partielle (par le système d'exploitation) 2- Jeu d'instructions . Interprétation par micro-code ou exécution 1- Microarchitecture . Matériel 0- Logique numérique ## Couche logique numérique Les objets considérés à ce niveau sont les portes logiques, chacune construite à partir de quelques transistors. Chaque porte prend en entrée des signaux numériques (0 ou 1) et calcule en sortie une fonction logique simple (ET, OU, NON). De petits assemblages de portes peuvent servir à réaliser des fonctions logiques telles que mémoire, additionneur, ainsi que la logique de contrôle de l'ordinateur. ## Couche microarchitecture On dispose à ce niveau de plusieurs registres mémoire et d'un circuit appelé UAL (Unité Arithmétique et Logique, ALU) capable de réaliser des opérations arithmétiques élémentaires. Les registres sont reliés à l'UAL par un chemin de données permettant d'effectuer des opérations arithmétiques entre registres. Le contrôle du chemin de données est soit microprogrammé, soit matériel. ## Couche jeu d'instruction La couche de l'architecture du jeu d'instructions (Instruction Set Architecture, ISA) est définie par le jeu des instructions disponibles sur la machine. Ces instructions peuvent être exécutées par microprogramme ou bien directement. ## Couche système d'exploitation Cette couche permet de bénéficier des services offerts par le système d'exploitation - Organisation mémoire, exécution concurrente. La plupart des instructions disponibles à ce niveau sont directement traitées par les couches inférieures. Les instructions spécifiques au système font l'objet d'une interprétation partielle (appels système). ## Couche langage d'assemblage Offre une forme symbolique aux langages des couches inférieures. Permet à des humains d'interagir avec les couches inférieures. ## Couche langages d'application Met à la disposition des programmeurs d'applications un ensemble de langages adaptés à leurs besoins. Langages dits "de haut niveau". ## Comment aborder tout cela ? Approches courante : de bas en haut pour les besoins, puis de haut en bas pour les solutions : - Travaux pratiques difficiles au début... Par deux fronts à la fois : - À partir de la couche ISA . Programmation en langage machine : y86 - À partir des transistors et portes logiques . Construction de circuits "sur papier" ## Circuits logiques Un circuit logique est un circuit qui ne manipule que deux valeurs logiques : 0 et 1. À l'intérieur des circuits, on représente typiquement un état 0 par un signal de basse tension (proche de 0V) et un état 1 par un signal de haute tension (5V, 3,3V, 2,5V, 1,8V ou 0,9V selon les technologies). De minuscules dispositifs électroniques, appelées "portes", peuvent calculer différentes fonctions à partir de ces signaux. ## Transistors (1) L'électronique numérique repose sur le fait qu'un transistor peut servir de commutateur logique extrêmement rapide. Deux technologies majeures : - Bipolaire : temps de commutation très rapide mais consommation élevée . Registres, SRAM, circuits spécialisés - CMOS : temps de commutation moins rapide mais consommation beaucoup moins élevée . 90 % des circuits sont réalisés en CMOS . Possibilité de mixage bipolaire-CMOS : BiCMOS ## Transistors (2) Notations : Vcc : source de tension (vaut toujours 1) V0 : masse = tension de référence nulle (vaut toujours 0) R : résistance T : transistor bipolaire : b : base c : collecteur e : émetteur L'émetteur e reçoit la somme de (b + c) La base se déverse dans l'émetteur : consommation élevée par courant de fuite T : transistor CMOS : g : grille (analogue à la base) s : source (analogue au collecteur) d : drain (analogue à l'émetteur) Peu de fuite du courant de grille : plus économe que le bipolaire Deux sortes de dopage de transistors : - NPN (robinet "Normal") : - e/d déconnecté de c/s si base = VO - e/d connecté à c/s si base = Vcc - PNP (robinet "Pas normal") : - e/d connecté à c/s si base à V0 - e/d déconnecté de c/s si base à Vcc Avec un transistor bipolaire ou deux transistors CMOS, on peut créer un premier circuit combinatoire, comme suit : * Circuit bipolaire : Contient : - 1 transistor : TN (NPN) - 1 résistance : R (a, b) - 1 entrée : Va (vaut V0 ou Vcc) - 1 sortie : Vs (vaut V0 ou Vcc) Câblage : Vcc <-> Ra (donc Ra = Vcc) Rb <-> TNc <-> Vs (donc TNc = VS = Rb) Va <-> TNb (donc TNb = Va) V0 <-> TNe (donc TNe = V0) Voir fichier embossé [[transistor_not_bipolaire]] : - La résistance R est le "zig-zag" d'en haut - Le transistor est en bas : . Le rond n'est pas toujours représenté. . La barre épaisse est la "base" (pour le bipolaire) ou la "grille" (pour le C-MOS, voir plus bas). . La flèche indique si le transistor est de type P ou de type N. Si Va = V0 : TNe déconnecté de TNc Ra = Vcc Rb = Vcc Vs = Vcc Si Va = Vcc : TNe connecté à TNc VS = TNc = TNe = V0 Ra = Vcc Rb = TNc La résistance empêche le court-circuit entre Ra et Rb. Du courant se déverse en permanence de Vcc = Ra vers Rb = VO : fuite. C'est pour cela que les circuits bipolaires sont énergivores. Au final : Si Va = V0, Vs = Vcc Si Va = Vcc, Vs = V0 C'est un inverseur. * Circuit CMOS : Contient : - 2 transistors : TP (type PNP) et TN (type NPN) - 1 entrée : Va (vaut V0 ou Vcc) - 1 sortie : Vs (vaut V0 ou Vcc) Câblage : Vcc <-> TPs (donc TPs = Vcc) Va <-> TPg <-> TNg (donc TPg = TNg = Va) V0 <-> TNs (donc TNs = V0) Vs <-> TNd <-> TPd (donc Vs = TNd = TPd) Voir fichier embossé [[transistor_not_cmos]] : - TP est le transistor du haut - TN est le transistor du bas La flèche indique si le transistor est de type P ou N : - Quand la flèche va vers la grille, cela veut dire que le courant peut traverser par défaut le transistor, qui est passant au repos, mais que lorsque la grille est chargée, elle s'y oppose, en "bloquant" la flèche, et le transistor n'est alors plus passant. - Quand la flèche va vers l'extérieur, cela veut dire que le transistor est bloquant au repos, et que quand la grille est chargée elle "aide" le courant à passer, c'est-à-dire que le transistor devient passant. Si Va = V0 : TPs connecté à TPd TNs déconnecté de TNd Vs = TPd = TPs = Vcc Si Va = Vcc : TPs déconnecté de TPd TNs connecté à TNd Vs = TNd = TNs = V0 Au final : Si Va = V0, Vs = Vcc Si Va = Vcc, Vs = V0 C'est aussi un inverseur. ## Transistors (5) En combinant quatre transistors CMOS, on peut obtenir un circuit tel que la sortie Vs n'est dans l'état bas que quand les deux entrées Va et Vb sont toutes les deux dans l'état haut. Contient : - 4 transistors : TPa et TPb (type PNP) et TNa et TNb (type NPN) Les transistors TPa et TNa sont commandés par Va Les transistors TPb et TNb sont commandés par Vb - 2 entrées : Va et Vb (valent V0 ou Vcc) - 1 sortie : Vs (vaut V0 ou Vcc) Voir fichier embossé [[transistor_nand_cmos]] : Montage série-parallèle avec : . Les deux transistors TPa et TPb montés en parallèle entre Vcc et Vs en haut de la figure ; - Les deux transistors TNa et TNb montés en série entre Vs et V0 en bas de la figure. Si Va ou Vb ou les deux valent V0 : - ou bien TPa ou bien TPb ou les deux sont passants dans le montage en parallèle, et Vs = Vcc - le montage série avec TNa et TNb est non passant, donc Vs est isolé de V0 Pas de court-circuit entre Vcc et V0. Si Va et Vb valent Vcc : - TPa et TPb sont tous les deux bloquants dans le montage en parallèle, donc Vs est isolé de Vcc - TNa et TNb sont tous les deux passants dans le montage en série, donc Vs = V0 Pas de court-circuit entre Vcc et V0. Au final : Vs ne vaut V0 que si Va et Vb valent tous deux Vcc, et Vcc sinon. ## Transistors (6) En combinant quatre transistors CMOS, on peut obtenir un circuit tel que Vs est dans l'état bas si Va ou Vb, ou bien les deux, sont dans l'état haut. Contient exactement les mêmes éléments que pour le montage précédent : - 4 transistors : TPa et TPb (type PNP) et TNa et TNb (type NPN) Les transistors TPa et TNa sont commandés par Va Les transistors TPb et TNb sont commandés par Vb - 2 entrées : Va et Vb (valent V0 ou Vcc) - 1 sortie : Vs (vaut V0 ou Vcc) Voir fichier embossé [[transistor_nor_cmos]] : Câblage par montage série-parallèle également, mais les blocs "série" et "parallèle" sont inversés par rapport à la version "NAND" de la diapositive précédente : - Les deux transistors TPa et TPb montés en série entre Vcc et Vs en haut de la figure ; . Les deux transistors TNa et TNb montés en parallèle entre Vs et V0 en bas de la figure. Si Va et Vb valent V0 : - TPa et TPb sont tous les deux passants dans le montage en série, donc Vs = Vcc - TNa et TNb sont tous les deux bloquants dans le montage en parallèle, donc Vs est isolé de V0 Pas de court-circuit entre Vcc et V0. Si Va ou Vb valent Vcc : - le montage série avec TNa et TNb est non passant, donc Vs est isolé de Vcc - ou bien TNa ou bien TNb (ou les deux) sont passants dans le montage en parallèle, donc Vs = V0 Pas de court-circuit entre Vcc et V0. Au final : Vs ne vaut Vcc que si Va et Vb valent tous deux V0, et V0 sinon. ## Portes logiques (1) Fonction NOT : A S 0 1 1 0 Fonction NAND : A B S 0 0 1 0 1 1 1 0 1 1 1 0 Fonction NOR : A B S 0 0 1 0 1 0 1 0 0 1 1 0 ## Fonctions booléennes (2) Une fonction booléenne à n variables a seulement 2^n combinaisons d'entrées possibles. Elle peut être complètement décrite par une table à 2^n lignes donnant la valeur de la fonction pour chaque combinaison d'entrées. C'est la table de vérité de la fonction. Elle peut aussi être décrite par le nombre à 2^n bits correspondant à la lecture verticale de la colonne de sortie de la table : NAND : 1110 NOR : 1000 AND : 0001 ## Fonctions booléennes (4) En notant : A\ le NOT de A A + B le OR de A et B A.B ou AB le AND de A et B on peut représenter une fonction comme somme logique de produits logiques Par exemple : - AB\C vaut 1 seulement si A = 1 et B = 0 et C = 1 - AB\ + BC\ vaut 1 si et seulement si (A = 1 et B = 0) ou bien (B = 1 et C = 0) ## Fonctions booléennes (5) Exemple : la fonction majorité M A B C M 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 1 1 0 0 0 1 0 1 1 1 1 0 1 1 1 1 1 Pour la câbler, on procède de la façon suivante : - À partir des fils A, B, C et d'inverseurs, on construit les fils A\, B\ et C\ - Pour chaque ligne de la table ci-dessus valant 1, on fait une porte AND dont les entrées sont les termes de la ligne. Par exemple : - pour la première ligne à 1, qui est codée par "0 1 1", on relie les entrées de la porte AND aux fils A\, B et C. - pour la deuxième ligne à 1, qui est codée par "1 0 1", on relie les entrées de la porte AND aux fils A, B\ et C. - pour la troisième ligne à 1, qui est codée par "1 1 0", on relie les entrées de la porte AND aux fils A, B et C\. - pour la quiatrième et dernière ligne à 1, qui est codée par "1 1 1", on relie les entrées de la porte AND aux fils A, B et C. - Les sorties de toutes les portes AND sont les entrées d'une porte OR globale donnant le résultat de la fonction : le résultat de cette porte OR est à 1 si l'une au moins (en fait : l'une seulement) des combinaisons AND qui donnent 1 est celle fournie en entrée. ## Fonctions booléennes (6) Toute fonction logique de n variables peut donc être décrite sous la forme d'une somme logique d'au plus 2^n produits de termes. Par exemple : M = A\BC + AB\C + ABC\ + ABC Cette formulation fournit une méthode directe pour implanter n'importe quelle fonction booléenne. ## Identités booléennes (1) Nom Forme AND Forme OR Identité 1A = A 0 + A = A Nul 0A = 0 1 + A = 1 Idempotence AA = A A + A = A Inverse AA\ = 0 A + A\ = 1 Commutativité AB = BA A + B = B + A Associativité (AB)C = A(BC) (A + B) + C = A + (B + C) Distributivité A + BC = (A + B)(A + C) A(B + C) = AB + AC Absorbtion A(A + B) = A A + AB = A De Morgan (AB)\ = A\ + B\ (A + B)\ = A\B\ ## Identités booléennes (2) Chaque loi a deux formes, qui sont duales si on échange les rôles respectifs de AND et OR et de 0 et 1 La loi de De Morgan peut être étendue à plus de deux termes : (ABC)\ = A\ + B\ + C\ Notation alternative des portes logiques : - Une porte OR avec ses deux entrées inversées est équivalente à une porte NAND - Une porte NOR peut être dessinée comme une porte AND avec ses deux entrées inversées ## Porte XOR (1) Grâce aux identités, il est facile de convertir la représentation en somme de produits en une forme purement NAND ou NOR. Exemple : la fonction "ou exclusif" ou XOR, parfois noté "^" comme en langage C. XOR = AB\ + A\B A B X 0 0 0 0 1 1 1 0 1 1 1 0 ## Porte XOR (2) A ^ B = (A B\) + (A\ B) = (A B\)\\ + (A\ B)\\ # Double NOT avant le calcul du OR = ((A B\)\ * (A\ B)\)\ # De Morgan pour transformer le OR en NAND On peut donc représenter une fonction XOR avec trois NAND (fonction simple à câbler), en plus des inverseurs (fonction simple à câbler aussi). ## Décodeur (1) Un décodeur est une fonction qui prend un nombre binaire C à n bits en entrée et se sert de celui-ci pour sélectionner (ou "allumer") l'une de ses 2^n sorties S_i. Chaque sortie est une fonction booléenne indépendante, qui code l’une des combinaisons binaires des entrées. ## Décodeur (2) Pour n=1 entrée, on a 2^1 = 2 sorties : - S0 = C_0\ - S1 = C_0 ## Décodeur (3) Pour n=2 entrées, on a 2^2 = 4 sorties : - S0 = C_1\ C_0\ - S1 = C_1\ C_0 - S2 = C_1 C_0\ - S3 = C_1 C_0 ## Décodeur (4) Pour câbler un décodeur, on se sert de la même technique que décrite précédemment : - pour chaque fil d'entrée C_j, on calcule (C_j)\ au moyen d'un inverseur ; - chaque fil de sortie S_i est câblé comme la sortie d'une porte AND prenant en entrée le code binaire de la valeur de i, codé au moyen des fils d'entrée C_j et (C_j)\. Par exemple, pour n = 3, donc trois fils d'entrée C_0, C_1 et C_2, et huit fils de sortie S_0 à S_7 : - S_0 = C_2\ C_1\ C_0\ - S_1 = C_2\ C_1\ C_0 - S_2 = C_2\ C_1 C_0\ - S_3 = C_2\ C_1 C_0 - S_4 = C_2 C_1\ C_0\ - S_5 = C_2 C_1\ C_0 - S_6 = C_2 C_1 C_0\ - S_7 = C_2 C_1 C_0 ## Multiplexeur (1) Un multiplexeur est une fonction possédant 2^n entrées de données D_i, une unique sortie F et n entrées de contrôle C_j servant à sélectionner l'une des entrées. La valeur de l'entrée sélectionnée est répercutée (routée) sur la sortie. Les n entrées de contrôle codent un nombre binaire à n bits C spécifiant le numéro de l'entrée sélectionnée. ## Multiplexeur (2) Pour constuire un multiplexeur, on a besoin : - de "filtrer" (donc par une porte "AND") la "bonne" entrée ; et donc - de disposer pour chaque entrée d'un fil qui dise à chacune des entrées si elle est la bonne ou pas ; et au final - d'agréger toutes les entrées filtrées vers la sortie (donc par une grande porte "OR"). Les deux premières caractéristiques sont exactement celles d'un décodeur. Le schéma de câblage d'un multiplexeur à 2^n entrées de données D_i, une unique sortie F et n entrées de contrôle C_j, est donc le suivant : - un décodeur prend les C_j entrées pour sélectionner ("allumer") l'une de ses 2^n sorties S_i ; - chacune des S_i sorties est combinée par un AND avec l'entrée D_i correspondante ; - l'ensemble des sorties de ces 2^n portes AND est agrégée par une grande porte OR dont la sortie est la sortie F du multiplexeur. Ainsi, seule l'entrée D_i verra sa valeur propagée sur F, les autres entrées étant filtrées par les portes AND dont la valeur S_i est égale à 0. ## Multiplexeur (3) Dans la pratique, on peut simplifier le câblage décrit dans la diapositive précédente, en fusionnant le niveau de porte AND du décodeur et celui de filtrage des entrées. Par exemple, pour n = 3, donc trois fils d'entrée C_0, C_1 et C_2, et huit fils de sortie S_0 à S_7 : - S_0 = C_2\ C_1\ C_0\ D_0 - S_1 = C_2\ C_1\ C_0 D_1 - S_2 = C_2\ C_1 C_0\ D_2 - S_3 = C_2\ C_1 C_0 D_3 - S_4 = C_2 C_1\ C_0\ D_4 - S_5 = C_2 C_1\ C_0 D_5 - S_6 = C_2 C_1 C_0\ D_6 - S_7 = C_2 C_1 C_0 D_7 et : F = S_0 + S_1 + S_2 + S_3 + S_4 + S_5 + S_6 + S_7 Cela permet de supprimer un niveau de portes logiques, et donc d'accélérer le temps de réponse du circuit et de réduire sa consommation électrique (par la réduction du nombre de transistors). ## Multiplexeur (4) Un multiplexeur peut être utilisé de façon "bourrine" pour câbler sans réfléchir n'importe quelle fonction logique. Il suffit de câbler chacune des entrées dudit multiplexeur, soit sur Vcc si la sortie vaut 1, soit sur la masse si la sortie vaut 0. Par exemple, pour la fonction majorité : D_0 = 0 D_1 = 0 D_2 = 0 D_3 = 1 D_4 = 0 D_5 = 1 D_6 = 1 D_7 = 1 Les fils C_2, C_1 et C_0 servent à sélectionner la bonne valeur de sortie en fonction de la valeur qu'ils codent. C'est le principe d'une mémoire ROM : la valeur de sortie est codée "en dur" pour l'adresse d'entrée (numéro de cellule) correspondante. Bien sûr, lors de l'implantation du circuit, le câblage pourra être simplifié : - les fils à 0 branchés sur une porte AND conduiront à la suppression de cette dernière avant le OR, puisque sa valeur sera toujours à 0 ; - les fils à 1 branchés sur une porte AND seront supprimés, puisqu'ils n'interviennent pas dans le mode passant ou non de ladite porte. ## Multiplexeur (5) En informatique, lors d’un test, seulement l’une des branches donne lieu à un calcul. Par exemple, en langage C, on écrira : F = (test) ? f1 () : f0 (); En électronique, les circuits sont pré-câblés. Les résultats des deux fonctions sont calculés en parallèle, et sont multiplexés selon le résultat du test. ## Comparateur (2) Pour câbler une fonction comparateur, on veut un circuit dont la sortie C renvoie la valeur 1 lorsque deux valeurs A et B sur n bits sont égales, et 0 sinon. Pour cela : - on utilise autant de portes XOR que d'entrées, c'est à dire n, telles que : X_i = A_i ^ B_i - on agrège toutes les valeurs X_i au moyen d'une porte NOR : C = (X_0 X_1 X_2 ... X_n)\ En effet : - si tous les A_i sont égaux aux B_i, alors tous les X_i valent 0, et donc C = 1 ; - s'il existe un i tel que A_i != B_i, alors X_i = 1, donc le OR vaut 1 et donc le NOR vaut 0. On a bien le comportement attendu. ## Fonctions arithmétiques Les fonctions arithmétiques sont des fonctions logiques représentant des opérations arithmétiques simples telles que : - Additionneur - Décaleur - Unité arithmétique et logique ## Additionneur (1) Tous les processeurs disposent d'un ou plusieurs circuits additionneurs. Ces additionneurs sont implantés à partir de fonctions appelées "demi-additionneurs". A B C S 0 0 0 0 0 1 0 1 1 0 0 1 1 1 1 0 Donc : S = A ^ B C = A.B ## Additionneur (2) En fait, pour additionner deux bits situés au milieu d'un mot, il faut aussi prendre en compte la retenue provenant de l'addition du bit précédent (Cin, pour "carry in") et propager sa retenue au bit suivant (Cout, pour "carry out"). 1 0 0 1 1 0 1 0 # A + 0 1 0 1 1 0 0 1 # B 1 1 # Retenue 1 1 1 1 0 0 1 1 # A+B On additionne au plus 3 bits, donc le résultat tient toujours dans un bit de somme et un bit de retenue. ## Additionneur (3) On utilise donc deux demi-additionneurs pour réaliser une tranche d'additionneur complet. Entrées : A, B, Cin Sorties : Cout, S A B Cin Cout S 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 1 0 1 0 0 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 1 1 Le schéma de câblage est le suivant : Sp = A ^ B # Premier demi-additionneur (entrées A et B) Cp = A.B S = Sp ^ Cin # Deuxième demi-additionneur (entrées Sp et Cin) Cq = Sp.Cin Cout = Cp.Cq # Retenue globale De fait, on a une fonction à trois entrées et deux sorties : (Cout, S) = ADD (A, B, Cin) ## Unité arithmétique et logique (1) La plupart des ordinateurs combinent au sein d'un même circuit les fonctions arithmétiques et logiques permettant de calculer l'addition, la soustraction, le AND ou le OU de deux mots machines : c'est l'Unité Arithmétique et Logique (UAL) Le type de la fonction a calculer est déterminé par des entrées de contrôle. ## Unité arithmétique et logique (2) Cette diapositive présente le schéma d'une "tranche" d'UAL à 1 bit. Entrées : A : valeur d'entrée A B : valeur d'entrée B ENA : "enable A" : rend l'entrée A active ENB : "enable B" : rend l'entrée B active INVA : "invert A" : inverse la valeur de l'entrée A Cin : retenue d'entrée F1, F0 : codage sur deux bits du type de fonction à appliquer Sorties : X : valeur de sortie de l'UAL Cout : retenue de sortie de l'UAL Schéma de câblage : Va = (A.ENA) ^ INVA Va est une valeur intermédiaire calculée en filtrant A avec ENA, puis en inversant éventuellement le résultat (la valeur de A) si INVA = 1, au moyen d'une fonction XOR. En effet, X ^ 0 = X et X ^ 1 = X\. Vb = B.ENB Vb est une valeur intermédiaire calculée en filtrant B avec ENB. (Cout, Vs) = ADD (Va, B, Cin) La retenue Cout et la valeur intermédiaire Vs (somme) sont le résultat de l'addition des entrées Va, B et Cin au moyen d'un additionneur complet. On n'utilse pas l'entrée A, mais la valeur intermédiaire Va, qui peut valoir l'inverse de A. Ce sera utile plus tard, quand on verra le câblage de la soustraction. Vp = Va.Vb # Fonction AND ("p" comme "produit") Vo = Va + Vb # Fonction OR Vn = Vb\ # Fonction NOT de B On a ici trois fonctions logiques élémentaires (AND, OR et NOT) applicables aux entrées Va et Vb, donc en pratique aux entrées A (potentiellement A\ si INVA = 1) et B. X = MULT2 (Vp, Vn, Vo, Vs, F1, F0) Le résultat X de la tranche d'ALU est le multiplexage à deux bits F1 et F0 des quatre valeurs d'entée Vp, Vn, Vo et Vs. De fait, F1 et F0 codent le type de fonction que l'ALU met en oeuvre : F1 F0 X 0 0 A.B # AND 0 1 B\ # NOT 1 0 A+B # OR 1 1 Vs # ADD On a donc câblé une fonction : (Cout, X) = UAL (A, B, Cin, INVA, F1, F0) ## Unité arithmétique et logique (3) Pour opérer sur des mots de n bits, l'UAL est constituée de la mise en série de n tranches d'UAL de 1 bit. Pour les opérations logiques bit à bit (AND, OR, NOT), chaque tranche d'ALU travaille de façon totalement indépendante des autres. Pour câbler l'addition sur n bits, on chaîne les retenues et on injecte un 0 dans l'additionneur de poids faible. On appelle ce circuit "Ripple Carry Adder", ou "additionneur à propagation de retenue", en fait peu efficace car le temps mis pour additionner dépend linéairement du nombre de bits à additionner. Câblage et paramétrage d'une UAL à n bits capable d'effectuer les opérations AND, NOT, OR et ADD en fonction d': (C0, X0) = UAL (A0, B0, Cin = 0, INVA = 0, F1, F0) (C1, X1) = UAL (A1, B1, C0, INVA = 0, F1, F0) (C2, X2) = UAL (A2, B2, C1, INVA = 0, F1, F0) (C3, X3) = UAL (A3, B3, C2, INVA = 0, F1, F0) ... (Cout, X_(n-1)) = UAL (A_(n-1), B_(n-1), C_(n-2), INVA = 0, F1, F0) ## Additionneur - Soustracteur (1) En notation "complément à deux", l'opposé d'un nombre est obtenu : - En complémentant tous les bits de ce nombre - En ajoutant 1 au résultat Soustraire A à B revient à calculer B+(-A), ce qui peut se faire : - En ajoutant B au complément de A - En ajoutant 1 au résultat ## Additionneur - Soustracteur (2) L'unité arithmétique et logique dispose déjà de toute la circuiterie nécessaire ! En effet : - la broche INVA permet d'effectuer la complémentation des bits de A avant l'addition ; - il suffit d'injecter un 1 au lieu d'un 0 dans la retenue de l'additionneur de poids faible. On n'a donc pas de deuxième addition complète à effectuer. Paramétrage de l'UAL pour effectuer une soustraction : (C0, X0) = UAL (A0, B0, Cin = 1, INVA = 1, F1 = 1, F0 = 1) (C1, X1) = UAL (A1, B1, C0, INVA = 1, F1 = 1, F0 = 1) (C2, X2) = UAL (A2, B2, C1, INVA = 1, F1 = 1, F0 = 1) (C3, X3) = UAL (A3, B3, C2, INVA = 1, F1 = 1, F0 = 1) ... (Cout, X_(n-1)) = UAL (A_(n-1), B_(n-1), C_(n-2), INVA = 1, F1 = 1, F0 = 1) ## Horloge (1) Dans de nombreux circuits numériques, il est essentiel de pouvoir garantir l'ordre dans lequel certains événements se produisent : - deux événements doivent absolument avoir lieu en même temps ; - deux événements doivent absolument se produire l'un après l'autre. 98 % des circuits numériques sont synchrones. Nécessité de disposer d'une horloge pour synchroniser les événements entre eux. ## Horloge (2) Une horloge est un circuit qui émet de façon continue une série d'impulsions caractérisées par : - la longueur de l'impulsion ; - l'intervalle entre deux pulsations successives, appelé temps de cycle de l'horloge. Exemple d'un signal : 000000111000000111000000111000000111000000111... La durée des "111" est la longueur d'impulsion. La durée d'une séquence "000000111" est le temps de cycle. ## Cycles et sous-cycles (1) Dans un ordinateur, de nombreux événements ont à se produire au cours d'un cycle d'horloge. Si ces événements doivent être séquencés dans un ordre précis, le cycle d'horloge doit être décomposé en sous-cycles. Un moyen classique pour cela consiste à retarder la copie d'un signal d'horloge primaire afin d'obtenir un signal secondaire décalé en phase. Exemple avec un signal régulier C1 issu d'une horloge H, et d'un signal décalé C2 issu d'un retard de temps égal à la moitié de la longueur d'impulsion : C1 = 000000111111000000111111000000111111... C2 = 000000111111000000111111000000111... ## Cycles et sous-cycles (1) On dispose alors de quatre bases de temps au lieu de deux : - fronts montant et descendant de C1 ; - fronts montant et descendant de C2. Front montant : quand le signal passe de 0 à 1. Front descendant : quand le signal passe de 1 à 0. C1 = 000000111111000000111111000000111111... C2 = 000000111111000000111111000000111... ## Cycles et sous-cycles (3) Pour certaines fonctions, on s'intéressera plutôt aux intervalles qu'à des instants précis, pas exemple lorsqu'une action est possible seulement lorsque C1 est haut. On peut alors construire des sous-intervalles en s'appuyant sur les signaux original et retardé. C3 = C1 ^ C2 C1 = 000000111111000000111111000000111111... C2 = 000000111111000000111111000000111... C3 = 000111000111000111000111000111000... ## Mémoire (1) La mémoire principale sert au stockage des programmes et de leurs données. L'unité élémentaire de mémoire est le bit, pour "binary digit" ("chiffre binaire"), prenant deux valeurs, 0 ou 1. Le stockage physique des bits dépend des technologies employées : différentiels de tension, moments magnétiques, cuvettes ou surfaces planes, émission de photons ou non, etc. ## Mémoire (2) Pour stocker les informations, il faut un circuit capable de "se souvenir" de la dernière valeur d'entrée qui lui a été fournie. À la différence d'une fonction combinatoire, sa valeur ne dépend donc pas que de ses valeurs d'entrée courantes. Présence de boucles de rétroaction pour préserver l'état courant. On peut construire un tel circuit à partir de deux portes NAND ou deux portes NOR rebouclées. ## Bascule SR Une bascule SR est une fonction qui a deux entrées et deux sorties : - Une entrée S pour positionner la bascule ; - Une entrée R pour réinitialiser la bascule ; - Deux sorties Q et Q\ complémentaires l'une de l'autre. Q\ = NOR (S, Q) Q = NOR (R, Q\) ## Bascule SR - État 0 Si S et R valent 0, et que Q vaut 0, alors : - Q\ vaut 1 ; - Les deux entrées de la porte du bas sont 0 et 1, donc Q vaut 0. Cette configuration est cohérente et stable (Q\ = 1) = NAND (S = 0, Q = 0) (Q = 0) = NAND (R = 0, Q\ = 1) ## Bascule SR - État 1 Si S et R valent 0, et que Q vaut 1, alors : - Q\ vaut 0 ; - Les deux entrées de la porte du bas sont 0 et 0, donc Q vaut 1. Cette configuration est cohérente et stable (Q\ = 0) = NOR (S = 0, Q = 1) (Q = 1) = NOR (R = 0, Q\ = 0) ## Bascule SR - États stables Lorsque S et R valent 0, les "NOR" se transforment en "NOT" de l'autre entrée : Q\ = NOR (S = 0, Q) = NOT (Q) Q = NOR (R = 0, Q\) = NOT (Q\) Q et Q\ ne peuvent jamais avoir la même valeur. La bascule possède deux états stables, tels que Q = 0 ou Q = 1. ## Bascule SR - Mise à 1 Si l'on part de l'état stable où Q = 0 et Q\ = 1 : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 0, Q\ = 1) = 0 Lorsque S passe à 1, la sortie Q\ de la NOR du haut change et devient Q\ = 0, qui "descend" dans la NOR du bas : Q\ = NOR (S = 1, Q = 0) = 0 Q = NOR (R = 0, Q\ = 0) = 1 et la nouvelle valeur Q = 1 "remonte" alors dans la NOR du haut : Q\ = NOR (S = 1, Q = 1) = 0 Q = NOR (R = 0, Q\ = 0) = 1 Cet état est stable. Si l'on part de l'état stable où Q = 1 et Q\ = 0 : Q\ = NOR (S = 0, Q = 1) = 0 Q = NOR (R = 0, Q\ = 0) = 1 Lorsque S passe à 1, la valeur Q\ = 0 issue de la NOR du haut ne change pas : Q\ = NOR (S = 1, Q = 1) = 0 Q = NOR (R = 0, Q\ = 0) = 1 Cet état est stable. Donc lorsque S vaut 1, que Q vaille 0 ou 1, Q\ = 0 et Q = 1 : Q\ = NOR (S = 1, Q = 1) = 0 Q = NOR (R = 0, Q\ = 0) = 1 Cet état est stable. Même lorsque S repasse à 0, Q reste à 1 : Q\ = NOR (S = 0, Q = 1) = 0 Q = NOR (R = 0, Q\ = 0) = 1 ## Bascule SR - Mise à 0 Si l'on part de l'état stable où Q = 0 et Q\ = 1 : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 0, Q\ = 1) = 0 Lorsque R passe à 1, la sortie Q = 0 issue de la NOR du bas ne change pas : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 1, Q\ = 1) = 0 Cet état est stable. Si l'on part de l'état stable où Q = 1 et Q\ = 0 : Q\ = NOR (S = 0, Q = 1) = 0 Q = NOR (R = 0, Q\ = 0) = 1 Lorsque R passe à 1, la sortie Q de la NOR du bas change et devient Q = 0, qui "remonte" dans la NOR du haut : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 1, Q\ = 0) = 0 et la nouvelle valeur Q\ = 1 "redescend" alors dans la NOR du bas : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 1, Q\ = 1) = 0 Cet état est stable. Donc lorsque R vaut 1, que Q vaille 0 ou 1 on a Q = 0 et Q\ = 1 : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 1, Q\ = 1) = 0 Cet état est stable. Même lorsque R repasse à 0, Q reste à 0 : Q\ = NOR (S = 0, Q = 0) = 1 Q = NOR (R = 0, Q\ = 1) = 0 ## Bascule SR - Résumé (1) Lorsque S vaut temporairement 1, la bascule se stabilise dans l'état Q = 1, quel que soit son état antérieur. Lorsque R vaut temporairement 1, la bascule se stabilise dans l'état Q = 0, quel que soit son état antérieur. La fonction mémorise laquelle des entrées S ou R a été activée en dernier. Cette fonction peut servir de base à la création de mémoires. ## Bascule SR - Résumé (2) Cependant, l'état de la bascule peut être indéterminé. Lorsque S et R sont simultanément à 1, on est dans un état stable dans lequel Q et Q valent 0 : Q\ = NOR (S = 1, Q = 0) = 0 Q = NOR (R = 1, Q\ = 0) = 0 Lorsque S et R repassent simultanément à 0, l'état final de la bascule est non prévisible : on retombe dans un état mémorisé Q = 0 ou bien Q = 1 en fonction des aléas du circuit. ## Bascule D (1) Pour éviter cela, on n'a qu'un seul signal appelé D. Destiné à l'entrée S, et que l'on inverse pour R. On commande la bascule par un signal d'activation CK ("clock"). Contient : - 2 portes AND : Ar et As - 1 inverseur NOT : N - 2 portes NOR rebouclées en bascule SR Câblage : S = AND (D, CK) R = AND (NOT (D), CK) Tant que CK = 0, on a S = 0 et R = 0 : état stable. Dès que CK = 1 : - si D = 0, on a S = 0 et R = 1 : état stable Q = 0 - si D = 1, on a S = 1 et R = 0 : état stable Q = 1 On a une mémoire à 1 bit. ## Bascule D (2) Pour que l'on soit sûr que la valeur conservée en mémoire soit bien celle présente en début de cycle d'écriture, il faudrait n'autoriser l'écriture qu'au début du cycle, sur le front montant du signal d'écriture. Pour cela, on crée un circuit qui transforme un niveau haut en un "glitch" : une impulsion haute de très courte durée. On se base sur l'effet retard du passage d'une porte NOT par rapport à la circulation directe du courant. Contient : - 1 inverseur NOT : N - 1 porte AND : A Câblage : a = CK b = a c = NOT (a) d = AND (b, c) Avec les décalages dus au temps de parcours du signal dans le circuit : a = 000000111111000000111111000000111111... b = 00000011111100000011111100000011111... c = 1111110000001111110000001111110000... d = 000010000000000010000000000010000... Le signal "d" est un "glitch" qui n'est à 1 qu'une fraction du temps, au moment du passage de CK de l'état bas à l'état haut : il matérialise le front montant du signal de référence, c'est-à-dire où il passe de 0 à 1. ## Bascule D (3) Il existe ainsi plusieurs types de bascules D : - Activée par CK à l'état haut - Activée par CK à l'état bas - Activée sur front montant - Activée sur front descendant En termes de représentation graphique, les bascules sont représentées par des "boîtes" rectangulaires avec deux entrées D et CK et deux sorties Q et Q\. Les bascules opérant sur un front possèdent un petit ">" accolé dans la boîte à l'entrée CK. Les bascules activées par un état "bas" ou "descendant" font passer l'entrée CK dans un inverseur stylisé, représenté comme un triangle mais sans le petit rond du not au bout du triangle qui caractérise typiquement l'inverseur. La pointe de ce triangle est collée à l'entrée CK. Le triangle est un triangle rectangle dont le grand côté longe le fil d'entrée CK. C'est donc une sorte de "triangle aplati" qui ne va que d'un seul côté du fil, par rapport au triangle standard qui va des deux côtés du fil. ## Adressage mémoire (1) Les mémoires informatiques sont organisées comme un ensemble de cellules pouvant chacune stocker une valeur numérique. Chaque cellule possède un numéro unique, appelé adresse, auquel les programmes peuvent se référer. Toutes les cellules d'une mémoire contiennent le même nombre de bits. Une cellule de n bits peut stocker 2n valeurs numériques différentes. ## Adressage mémoire (2) Deux cellules mémoire adjacentes ont des adresses mémoires consécutives. Les ordinateurs, basés sur le système binaire, représentent également les adresses sous forme binaire. Une adresse sur m bits peut adresser 2^m cellules distinctes, indépendamment du nombre de bits contenus dans chaque cellule. ## Adressage mémoire (3) La cellule est la plus petite unité mémoire pouvant être adressée. Il y a maintenant consensus autour d'une cellule à 8 bits, appelée "octet" ("byte" en anglais). Afin d'être plus efficaces, les unités de traitement ne manipulent plus des octets individuels mais des mots de plusieurs octets : 4 octets par mot pour une machine 32 bits. La plupart des mémoires travaillent aussi par mots. ## Principe d'un circuit mémoire (1) Il s'agit d'un schéma disposant d'une structure en lignes et colonnes très régulière, qui met en oeuvre une mémoire à 4 mots de 3 bits. Entrées : - A0, A1 : fils d'adresse servant à sélectionner l'un des quatre mots de la mémoire. - I0, I1, I2 ("input") : valeur d'entrée d'un mot, à stocker dans la mémoire. - CS ("chip select") : actif pour sélectionner ce circuit mémoire - RD ("read") : positionné à 1 si l'on souhaite réaliser une lecture, et à 0 si l'on souhaite une écriture. - OE ("output enable") : positionné à 1 pour activer les lignes de sortie. Sorties : - O0, O1, O2 ("output") : valeur de sortie d'un mot, issue de la mémoire. Contient : - 12 bascules D, pour les 12 bits que contient la mémoire, organisés en 4 mots de 3 bits. - des portes AND, OR et NOT pour construire des bouts de décodeurs et de multiplexeurs. - des interrupteurs INT à trois états (0, 1, déconnecté), pour que les sorties O0...02 de la mémoire n'aillent pas interférer avec le bus lorsque la mémoire est inactive ou est en mode écriture. Les bascules D sont organisées selon une disposition rectangulaire. On a donc 4 "lignes" et 3 "colonnes" de boîtes. Une ligne représente un mot, et une colonne un bit de poids donné (0, 1 ou 2). Les deux adresses A0 et A1 sont décodées pour allumer l'une des quatre sorties M0, M1, M2, M3 du décodeur. Chaque entrée CK d'une bascule D d'un mot Mx est issue d'une fonction : AND (Mx, AND (CS, NOT (RD))) Ainsi, la cellule ne pourra être en mode écriture que si : - CS est à 1 - RD est à 0 (donc on demande une écriture) - la bonne ligne est activée par l'entrée Mx, correspondant au bon mot. Chaque sortie Sy d'une colonne est le OR des AND (Mx, Q) de toutes les cellules de la colonne. Ainsi, en lecture, on ne sélectionnera que les valeurs du mot Mx. Les sorties Oy sont câblées selon la fonction : Oy = INT (Sy, AND (CS, RD, OE)) Les valeurs ne seront sorties que si : - CS est à 1 - RD est à 1 (donc on demande une lecture) - OE est à 1 (on veut écrire la valeur sur le bus) ## Principe d'un circuit mémoire (2) Mémoire à 4 mots de 3 bits. Ce circuit possède trois fils de commande : - CS ("chip select") : actif pour sélectionner ce circuit mémoire - RD ("read") : positionné à 1 si l'on souhaite réaliser une lecture, et à 0 si l'on souhaite une écriture. - OE ("output enable") : positionné à 1 pour activer les lignes de sortie. Utilise des interrupteurs à trois états (0, 1, déconnecté) Permet de connecter I0...I2 et O0...O2 sur les mêmes lignes de données de bus (bus servant à la lecture et à l'écriture). ## Types de mémoire Plusieurs critères caractérisent les mémoires: Type d'accès: - Accès arbitraire : RAM R/W, (((E)E)P)ROM, Flash - FIFO : registres à décalage Possibilité d'écriture: - Pas : ROM - Unique : PROM - Multiple : RAM R/W, (E)EPROM, Flash Volatilité: - Les données stockées ne sont conservées que tant que la mémoire est alimentée électriquement ## Mémoire RAM (1) "Random Access Memory" : les mots de la mémoire peuvent être accédés sur demande dans n'importe quel ordre. Cette catégorie comprend en théorie toutes les mémoires à accès aléatoire telles que mémoires volatiles, (((E)E)P)ROM, Flash, etc. Dans le langage courant, ce terme est utilisé pour désigner uniquement la mémoire volatile. ## Mémoire RAM (2) Variétés principales de RAM R/W volatiles : RAM statique : - Circuits actifs à base de portes logiques rebouclées - Conservent leurs valeurs sans intervention particulière tant que le circuit est alimenté en énergie RAM dynamique : - Basée sur des petits condensateurs, moins gourmands en place et en consommation électrique - Nécessite un rafraîchissement régulier des charges Variétés principales de RAM R/W non volatiles : - EEPROM, Flash : Stockage par charges électriques - M-RAM : Stockage magnétique ## Mémoire ROM "Read Only Memory" ("Mémoire morte") Les données stockées perdurent même quand la mémoire n'est pas alimentée. Le contenu, figé à la fabrication, ne peut plus être modifié d'aucune façon. Analogue à l'implantation d'une fonction booléenne dépendant des valeurs d'adresses fournies. Coûteuse du fait de la fabrication en petite série ## Mémoire PROM "Programmable ROM" Les ROMs sont trop longues à faire fabriquer par rapport aux cycles de développement des équipements. La PROM, livrée vierge (tous bits à 1), peut être programmée avec un équipement adapté : par destruction de mini-fusibles par surtension. Une seule écriture est donc possible, la destruction des fusibles n'étant pas réversible. ## Mémoire EPROM Les mémoires PROM sont encore trop chères. En particulier, on les consommerait en grandes quantités lors des phases de développement. Les mémoires EPROM peuvent être réutilisées en les réinitialisant par exposition aux rayons ultra-violets, par l'intermédiaire d'une petite fenêtre en mica sur le boîtier (mais pastille adhésive pour éviter les UV des tubes fluorescents) . Les mémoires EEPROM et Flash sont effaçables électriquement. ## Hiérarchie mémoire (1) La mémoire rapide est très chère, consomme beaucoup et est donc de taille limitée. Pour disposer de plus de mémoire, il faut mettre en œuvre une hiérarchie mémoire. Elle prend la forme d'une pyramide : - plus on monte et plus on a des mémoires rapides et chères (et qui consomment de l'énergie), mais de plus petite capacité ; - plus on descend et plus on a des mémoires de grande capacité et économiques, mais plus lentes. Cette pyramide est constituée de : - Processeur - Registres - Cache(s) - Mémoire Centrale - Mémoire de masse Le déclenchement de la remontée des données d'un étage de la pyramide à celui immédiatement au dessus peut être géré soit de manière logicielle, soit matérielle. Logicielle : - Mémoire de masse -> mémoire centrale : pagination et swap - Caches -> registres : chargement des registres (mrmovl) Métérielle : - Mémoire centrale -> cache(s) : logique de gestion des caches (LRU, etc.) ## Hiérarchie mémoire (2) La hiérarchie mémoire fonctionne grâce aux principes de localité : - Localité temporelle : plus un mot mémoire a été accédé récemment, plus il est probable qu'il soit ré-accédé à nouveau. - Localité spatiale : plus un mot mémoire est proche du dernier mot mémoire accédé, plus il est probable qu'il soit accédé. Les caches tirent parti de ce principe. Sauvegardent les informations les plus récemment accédées, en cas de ré-accès. ## Paradigmes architecturaux L'augmentation continuelle de la vitesse de traitement du cycle du chemin de données provient de la mise en oeuvre d'un ensemble de principes généraux de conception efficaces : - Simplification des jeux d'instructions - Utilisation du parallélisme au niveau des instructions ("Instruction-Level Parallelism", ou ILP) - Apparition des architectures multi-coeurs ## RISC et CISC (1) Plus les instructions sont simples à décoder, plus elles pourront être exécutées rapidement. Après une tendance à la complexification des jeux d'instructions ("Complex Instruction Set Computer", ou CISC), pour économiser la mémoire, on a conçu à nouveau des processeurs au jeu d'instructions moins expressif mais pouvant s'exécuter beaucoup plus rapidement ("Reduced Instruction Set Computer", ou RISC) ## RISC et CISC (2) Les architectures RISC se distinguent par un certain nombre de choix de conception : - Toute instruction est traitée directement par des composants matériels (pas de micro-code) - Le format des instructions est simple (même taille, peu de types différents) - Seules les instructions de chargement et de sauvegarde peuvent accéder à la mémoire - Présence d'un grand nombre de registres - Architecture orthogonale : toute instruction peut utiliser tout registre : que des registres généralistes ## Micro-architecture (1) La couche micro-architecture implémente le jeu d'instructions spécifié par la couche d'architecture du jeu d'instructions (ISA) en s'appuyant sur la couche la logique numérique. La conception de la micro-architecture dépend du jeu d'instruction à implémenter, mais aussi du coût et des performances souhaités : - Jeux d'instructions plus ou moins complexes (RISC/CISC) ; - Utilisation de l'ILP ("Instruction-Level Parallelism"). ## Micro-architecture (2) L'exécution d'une instruction peut se décomposer en plusieurs sous-étapes : - Recherche ("Fetch") : Étant donné l'adresse de la prochaine instruction à exécuter, récupération de l'instruction - Decodage ("Decode") : Détermination du type et de la nature des opérandes - Exécution ("Execute") : Mise en oeuvre des unités fonctionnelles - Terminaison ("Complete") : Modification en retour des registres ou de la mémoire ## Micro-architecture (3) On peut imaginer la conception du niveau micro-architecture comme un problème de programmation : - Chaque instruction du niveau ISA est une fonction - Le programme maître (micro-programme) est une boucle infinie qui détermine à chaque tour la bonne fonction à appeler et l'exécute - Le micro-programme dispose de variables d'état accessibles par chacune des fonctions, et modifiées spécifiquement selon la nature de la fonction : compteur ordinal, registres généraux, etc. ## Schéma d'un processeur élémentaire Ce schéma fonctionnel est constitué des blocs suivants : - le registre de compteur ordinal ; - un circuit calculant l'adresse de l'instruction suivante ; - le chemin de données, incluant un sous-bloc représentant la mémoire (registres et mémoire centrale), implanté pour fonctionner en un seul cycle ; - le registre d'instruction ; - la mémoire ROM contenant le programme du micro-code, implantée pour fonctionner en un seul cycle. Fonctionnement global : 1- Le registre d'instruction est chargé avec l'instruction située dans la mémoire à l'adresse indiquée par le compteur ordinal ; 2- le registre d'instruction permet de savoir quel micro-programme du micro-code exécuter pour l'instruction en question ; 3- à chaque micro-cycle du micro-programme, le chemin de données est piloté par le micro-code pour exécuter la micro-instruction correspondante ; 4- l'état d'avancement du micro-programme est suivi pas à pas par un micro-compteur ordinal, participant à définir l'état courant du processeur, associé au registre d'instruction puisque c'est ce dernier qui donne l'adresse courante dans la ROM du micro-code ; 5- Lorsque le micro-programme de l'instruction courante termine, le circuit calculant l'adresse suivante est activé en fonction du type de l'instruction (branchement ou passage à l'instruction suivante) ; 6- le compteur ordinal est modifié. ## Instructions (1) Chaque instruction est composée d'un ou plusieurs champs. Le premier, appelé "opcode", code le type d'opération réalisée par l'instruction Opération arithmétique, branchement, etc. Les autres champs, optionnels, spécifient les opérandes de l'instruction : - Registres source et destination des données à traiter - Adresse mémoire des données à lire ou écrire, etc. ## Instructions (2) Exemple : format des instructions RISC MIPS. Ces instructions sont toutes sur 32 bits. Les champs sont : O : opcode S : numéro de registre source T : numéro de second registre source D : numéro de registre destination M : numéro de sous-fonction F : numéro de fonction I : valeur immédiate A : adresse Type R (registre) : OOOOOOSSSSSTTTTTDDDDDMMMMMFFFFFF Type I (donnée immédiate) OOOOOOSSSSSTTTTTIIIIIIIIIIIIIIII Type J (branchement) OOOOOOAAAAAAAAAAAAAAAAAAAAAAAAAA ## Contrôle du chemin de données (1) Le chemin de données du processeur MIPS est constitué des éléments suivants : Banque de 32 registres sur 32 bits - Entrées : . Xra, Yra et Zra (5 bits chacun) : numéros des registres source et du registre destination ; . WriteEnable (1 bit) ; . Zdi (32 bits) : donnée immédiate à sauvegarder ; - Sorties : . Xdo et Ydo (32 bits chacun) : sortie des valeurs correspondant aux registres de numéros Xra et Yra. UAL : - Entrées : . A et B (32 bits chacun) : valeurs d'entrée . ALS (2 bits), LF (4 bits), ST (2 bits), SD (1 bit), AS (1 bit) : fils de commande du fonctionnement de l'UAL ; - Sorties : . R : résultat du calcul Mémoire : - Entrées / sorties : . Ad : adresse (32 bits) . Do : donnée (32 bits) . ms : activateur de la mémoire (1 bit) Multiplexeur : - Entrées : . Xdo : valeur issue de la banque de registres ; . Imm : valeur immédiate issue du registre d'instructions, étendue signée sur 32 bits ; . IE (ImmediateEnable, 1 bit) : commande du multiplexeux ; - Sortie : . V : valeur registre ou immédiate Câblage : - Ydo et V alimentent les entrées A et B du multiplexeur ; - Ydo est connecté sur Ad - V est connecté sur Do via un interrupteur (st enable) ; - R est connecté sur Zdi ; - D est connecté sur Zdi via un interrupteur (ld enable) ; Fils de commande de l'ALU : ALS : 00 : Arithmétique 01 : Logique 10 : Décalage 11 : Desactivée LF : 0001 : AND 0011 : A 0101 : B 0110 : XOR 0111 : OR ST : 00 : Pas de décalage 01 : Arithmétique 10 : Logique 11 : Rotation SD 0 : Décalage à gauche 1 : Décalage à droite A/S 0 : Ajoute 1 : Soustrait ## Contrôle du chemin de données (2) La logique de contrôle du micro-code associe à chaque (micro-)instruction un mot binaire commandant le chemin de données. Exemple : mise à zéro des mots mémoire situés aux adresses 0x0100 et 0x0104. Instruction X Y Z we ie Imm ALS a/s LF ST SD le se r/w ms li r1,100 x x 00001 1 1 0x0100 01 x 0101 x x 0 0 x 0 sw r0,(r1) 00000 00001 x 0 0 x 11 x x x x 0 1 1 1 add r1,r1,4 x 00001 00001 1 1 0x0004 00 0 x x x 0 0 x 0 sw r0,(r1) 00001 00000 x 0 0 x 11 x x x x 0 1 1 1 ## Interprétation du micro-code (1) Dans le cas d'un jeu d'instructions de type CISC, une instruction ISA doit être traduite en plusieurs micro-instructions : - Cas des instructions "REP SCAS" des x86 Chaque micro-instruction : - S'exécute en un cycle élémentaire - Spécifie exactement les signaux de contrôle des différentes unités fonctionnelles Nécessité d'un séquenceur de micro-instructions, mise en oeuvre sous la forme d'une machine d'états finis implémentable en ROM. ## Interprétation du micro-code (2) Exemple figuratif de micro-codage pour l'instruction "PUSH ri" du processeur 8086. Le code machine de cette instruction est "0x50 + numéro du registre sur 3 bits". De même, le code de "POP ri" est 0x58. Le schéma explicatif montre comment l'instruction PUSHL peut être interprétée dans le cadre d'un micro-programme. - L'opcode de cette instruction est donc : 01010xxx, où xxx est le numéro du registre. - Ce numéro xxx va être fourni comme adresse à la banque de registres, pour récupérer la valeur à empiler. - En supposant que chaque instruction ISA soit implémentable en au plus quatre micro-instructions, les concepteurs du processeur ont créé une ROM contenant, pour chaque instruction ISA, quatre micro-instructions. - On accède à cette ROM par l'adresse 0x50* 4 = 0x140 À cette adresse, la mémoire contient les informations suivantes : Instruction Fin? 0x140 : ST [RSP],R[xxx] N 0x141 : ADDI SP,2 Y 0x142 : x x 0x143 : x x ## Interprétation du micro-code (3) La logique de commande du micro-processeur est donc constituée d'une automate à états finis, dans laquelle l'état courant est constitué des registres du processeur, du compteur ordinal, du registre d'instruction, ainsi que du micro-compteur ordinal servant à exécuter le micro-code de l'instruction et des registres d'état éventuels. Comme pour un processeur non micro-codé, cette logique de contrôle pilote le chemin de données du processeur, afin d'activer les circuits nécessaires au bon déroulement de chaque micro-instruction. ## Pile (1) Presque tous les langages de programmation incluent le concept de procédure disposant de paramètres d'appel et de variables locales : - Ces variables peuvent être accédées pendant l'exécution de la procédure mais pas depuis la procédure appelante ; - Elles ne peuvent résider à une adresse absolue en mémoire, car cela empêcherait la réentrance. Nécessité de créer dynamiquement des instances de ces variables lors des appels de procédures et de les supprimer à la fin. ## Pile (2) Une pile est une zone de la mémoire que l'on n'accède jamais de façon absolue mais toujours relativement à un registre. - Gérée au moyen d'un registre dédié, le pointeur de pile ("Stack Pointer", ou SP) : - Pointe sur le dernier mot mémoire alloué - Instructions dédiées à l'empilage et au dépilage : . De données : push/pop . D'adresses de retour : call/ret ## Architecture du jeu d'instructions (1) La couche ISA ("Instruction Set Architecture") définit l'architecture fonctionnelle de l'ordinateur. Sert d'interface entre les couches logicielles et le matériel sous-jacent. Définit le jeu d'instructions utilisable pour coder les programmes, qui peut être : - Directement implémenté de façon matérielle ; - Pas de registre d'état interne servant de compteur ordinal pour l'exécution des micro-instructions. Implémenté sous forme micro-programmée. ## Architecture du jeu d'instructions (2) Le jeu d'instructions est indépendant de considérations d'implémentation telles que superscalarité, pipe-lining, etc. - Liberté d'implémentation en fonction des coûts de conception et de fabrication, de la complexité de réalisation, et donc du coût souhaité - Définition de familles de processeurs en fonction des applications visées (du téléphone portable au super-calculateur) - Nécessité pour le compilateur de connaître l'implémentation de la machine cible pour générer du code efficace ## Types de données (1) Le niveau ISA définit les types de données gérés nativement par le jeu d'instructions. - Autorise l'implémentation matérielle des types considérés - Définit la nature (entier, flottant, caractère) et la précision des types supportés Le programmeur n'est pas libre de choisir le format de ses données s'il veut bénéficier du support matériel offert par la couche ISA. ## Types de données (2) Les types de données les plus couramment implémentés dans les jeux d'instructions sont : - Type entier - Type flottant - Type caractère ## Types de données entiers (1) Le type entier est toujours disponible - Sert au fonctionnement de la couche micro-architecture Toutes les architectures disposent de types entiers signés - Presque toujours codés en complément à deux - Il existe aussi souvent des types non signés Disponibles en plusieurs tailles - Quelques unes choisies parmi les tailles classiques de 8, 16, 32, 64 bits (jamais de type booléen) ## Types de données entiers (2) Les types entiers non supportés : - Soit doivent être émulés de façon logicielle Cas du type caractère (8 bits) sur le CRAY-1 (mots de 64 bits) au moyen de décalages et masquages de bits Exemple de l'écriture du caractère "cc" comme troisième octet d'un mot de huit octets servant au stockage d'un fragment de chaîne de caractères : Char à écrire : 00000000000000cc dans un registre à 64 bits Lecture : xxxxxxxxxxxxxxxx à partir de la mémoire Masquage AND : ffffffffff00ffff du mot Résultat : xxxxxxxxxx00xxxx du masquage AND Décalage cc : 0000000000cc0000 du caractère à écrire Opération OR : xxxxxxxxxxccxxxx du mot à écrire - Soit font l'objet d'un support partiel par le matériel Cas des instructions ADD/ADC ("add with carry") sur le 8080 pour faire des additions sur plus d'un octet Exemple de la somme de deux nombres x et y sur 4 octets (H,h,l,L) quand on n'a qu'une instruction d'addition sur un octet : ADD : addition sans retenue d'entrée (cin = 0) et produisant une retenue de sortie dans le bit C (C = cout). ADC : addition prenant la retenue C en retenue d'entrée (cin = C) et produisant une retenue de sortie dans le bit C (C = cout) : ADD xL,yL,zL ADC xl,yl,zl ADC xh,yh,zh ADC xH,yH,zH ## Types de données flottants Les types flottants sont très souvent disponibles - Sauf sur les processeurs bas de gamme, où les nombres flottants sont émulés logiciellement Disponibles en plusieurs tailles - 32, 64, 80, ou 128 bits Souvent gérés par des registres séparés - Cas des 8 registres flottants de l'architecture x86, organisés sous forme de pile ## Types de données caractères La plupart des ordinateurs sont utilisés pour des tâches de bureautique ou de gestion de bases de données manipulant des données textuelles. - Quelques jeux d'instructions proposent des instructions de manipulation de suites de caractères - Caractères émulés par des octets (ASCII), des mots de 16 bits (Unicode), voire de 32 bits - Cas de l'architecture x86 avec les instructions micro-codées CMPS, SCAS, STOS, etc. utilisables avec les préfixes REP, REPZ, REPNZ ## Type de données booléen Il n'existe pas de type booléen natif sur les processeurs - Pas de possibilité d'adressage en mémoire Le type booléen est généralement émulé par un type entier (octet ou mot) - Valeur fausse si la valeur entière est zéro - Valeur vraie sinon - Cas de l'instruction beq r1,r0,addr et bne du jeu d'instructions MIPS, compatibles avec cette convention de codage ## Type de données référence Une référence est un pointeur sur une adresse Elle est émulée par un type de données entier : - Soit registres entiers généralistes - Soit registres entiers spécifiques d'adresses - Cas du CRAY-1 : 8 registres d'adresses sur 24 bits et 8 registres entiers sur 64 bits Utilisation de ces registres pour accéder aux données en mémoire, en fonction des modes d'adressage disponibles - Cas des registres SP et BP de gestion de la pile ## Format des instructions (1) Chaque instruction est composée d'un ou plusieurs champs Le premier, appelé "opcode", code le type d'opération réalisée par l'instruction : - Opération arithmétique, branchement, etc. Les autres champs, optionnels, qui spécifient où rechercher les opérandes de l'instruction, sont appelés "adresses" - Les instructions ont toujours de zéro à trois adresses ## Format des instructions (2) Différentes façons de concevoir l'adressage : - Architecture à trois adresses : on a deux adresses source et une adresse destination, qui peut être équivalente à l'une des adresses source - Cas de l'architecture MIPS : instruction add s1,s2,dst pouvant être utilisée en add r1,r2,r1 - Architecture à deux adresses : on a toujours une adresse source, non modifiée, et une adresse destination, modifiée ou mise à jour selon que l'opération fait ou non intervenir son ancienne valeur - Cas de l'architecture x86 : instructions MOV dst,src ou ADD dst,src ## Format des instructions (3) - Architecture à une adresse : toutes les instructions de calcul opèrent entre une adresse et un registre unique, appelé "accumulateur" - Anciennes architectures de type 8008 - Trop de transferts entre l'accumulateur et la mémoire - Architecture à zéro adresses : les adresses des opérandes sont implicites, situées au sommet d'une pile d'opérandes, où seront placés les résultats - Cas de l'architecture JVM ou du langage Forth ## Format des instructions (4) Les instructions peuvent soit toutes être de la même taille, soit être de tailles différentes. Avoir toutes les instructions de même taille facilite le décodage mais consomme plus de mémoire. La taille des instructions peut être plus petite, plus grande, ou de longueur équivalente à celle du mot mémoire. Exemple du formatage d'instructions 1, 2, 3, 4, etc. : Cas du MIPS (32 bits) : instructions toutes de 32 bits chacune, alignées sur les mots de la mémoire : 1111 2222 3333 4444 ... Cas du IA-64 (64 bits) : "groupage" ("bundle") de trois instructions à la fois, avec des informations supplémentaires ("B") indiquant si ces instructions sont indépendantes ou pas : B111222333 B444555666 B777888999 ... Cas du x86 (32 bits) : instructions de nombre d'octets variable : 1112 2345 5566 ... ## Format des instructions (5) Un jeu d'instructions est dit "orthogonal" si, quand une instruction opère sur un registre, elle peut opérer sur l'ensemble des registres de même type (registres entiers, registres flottants) - Facilite le décodage des instructions - Implémenté naturellement au sein des architectures de type RISC ## Modes d'adressage Les modes d'adressage sont les différentes manières dont on peut accéder aux opérandes des instructions : - Adressage immédiat - Adressage direct - Adressage registre - Adressage indirect par registre - Adressage indexé - Adressage basé indexé ## Adressage immédiat Le plus simple pour une instruction est que sa partie d'adresse contienne directement la valeur de l'opérande - Réservé aux constantes - Aucun accès mémoire supplémentaire nécessaire Exemples : - Branchements : l'adresse (déplacement relatif ou absolu) est spécifiée dans le corps de l'instruction : b 0C2F4 - Chargement de registres : li r1,100 ## Adressage direct Une méthode pour accéder à une valeur en mémoire consiste à donner son adresse pour qu'on puisse y accéder directement - On accédera toujours à la même zone mémoire - Réservé aux variables globales dont les adresses sont connues à la compilation ## Adressage registre Conceptuellement équivalent à l'adressage direct, mais on spécifie un numéro de registre plutôt qu'un numéro de mot mémoire. - Mode le plus couramment utilisé - Les accès aux registres sont très rapides - Les numéros de registres se codent sur peu de bits (compacité des instructions à plusieurs adresses) - Une grande partie du travail des compilateurs consiste à déterminer quelles variables seront placées dans quels registres à chaque instant, afin de diminuer les temps d'accès et donc d'exécution ## Adressage indirect par registre L'opérande spécifié provient de la mémoire ou y sera stockée, mais son adresse est contenue dans un registre de numéro donné plutôt que codée explicitement dans le corps de l'instruction - Le registre est un pointeur sur l'opérande - On peut référencer une zone mémoire sans avoir à coder son adresse dans l'instruction - On peut modifier dynamiquement l'adresse de la zone mémoire référencée en modifiant la valeur du registre ## Adressage indexé Ce mode combine les caractéristiques de l'adressage direct et de l'adressage registre. L'opérande considéré est localisé à une distance fixe de l'adresse fournie par un registre. Les champs de l'instruction sont le numéro du registre ainsi que le déplacement relatif ("offset") à ajouter à son contenu. Exemple : accès aux variables locales et paramètres placés dans la pile, par rapport au registre BP : MOV AX,(BP+4) ## Adressage basé indexé L'adresse mémoire de l'opérande est calculée à partir de la somme des valeurs de deux registres (un registre de base et un registre d'index) ainsi que d'une valeur de déplacement optionnelle. Exemple : accès aux champs des structures contenues dans un tableau : - Le registre de base est l'adresse de début du tableau - Le registre d'index référence l'adresse de début de la bonne structure par rapport à l'adresse du tableau - Le déplacement référence la position du début du champ par rapport au début de la structure ## Types d'instructions Les instructions de la couche ISA peuvent être groupées en une demi-douzaine de classes, que l'on retrouve sur toutes les architectures : - Copie de données - Calcul - Branchements, branchements conditionnels et comparaisons - Entrées/sorties et interruptions - Gestion de la mémoire ## Instructions de copie de données Les instructions de copie de données ont deux usages principaux : - Réaliser l'affectation de valeurs à des variables - Recopie de valeurs dans des variables temporaires devant servir à des calculs ultérieurs - Placer une copie de valeurs utiles là où elles pourront être accédées le plus efficacement - Utilisation des registres plutôt que de la mémoire On a toujours des instruction de copie entre registres, ou entre registre et mémoire, mais moins souvent de mémoire à mémoire ## Instructions de calcul (1) Ces instructions représentent les opérations réalisables par l'unité arithmétique et logique, mais sur des opérandes qui ne sont pas nécessairement tous des registres. - Calculs entre mémoire et registres (cas du x86) Les instructions de calcul les plus couramment utilisées peuvent faire l'objet d'un format abrégé. - Instruction INC R1 remplaçant la séquence MOV R2,1 et ADD R1,R2, par exemple ## Instructions de calcul (2) Dans une architecture de type "load/store", les seules instructions pouvant accéder à la mémoire sont les instructions load et store de copie entre mémoire et registre. Les instructions de calcul ne prennent dans ce cas que des opérandes registres. - Simplifie le format et le décodage des instructions - Permet d'optimiser l'utilisation de l'unité arithmétique et logique (pas de cycles d'attente des opérandes mémoire) ## Instructions de branchement (1) L'instruction de branchement inconditionnel déroute le flot d'exécution du programme vers une adresse donnée. L'instruction d'appel de sous-programme déroute aussi le flot d'exécution mais en plus sauvegarde l'adresse située après l'instruction afin de permettre le retour à la fonction appelante. - Sauvegarde dans un registre ou dans la pile ## Instructions de branchement (2) Les instructions de comparaison et de branchement conditionnel servent à orienter le flot d'exécution en fonction du résultat de l'évaluation d'expressions booléennes : - Implémentation des tests - Implémentation des boucles ## Instructions de branchement (3) Deux implémentations possibles : - Instructions de comparaison et de branchement distinctes utilisant un registre d'état du processeur - Cas de l'architecture x86 : instruction CMP mettant à jour les bits Z, S, O du mot d'état programme PSW, et instructions de branchement JEQ, JNE, JGE, etc. les utilisant comme conditions de branchement - Instructions de branchement conditionnel prenant en paramètres les noms de deux registres comparés à la volée pour décider du branchement - Cas des architecture MIPS et Power : avoir une seule instruction facilite la réorganisation dynamique de code ## Instructions d'entrée/sortie Diffèrent considérablement selon l'architecture. Mettent en œuvre un ou plusieurs parmi trois schémas d'E/S différents : - E/S programmées avec attente de disponibilité : - Très coûteux car le processeur ne fait rien en attendant - Cas des instructions IN et OUT de l'architecture x86 - E/S par interruptions : - Le périphérique avertit le processeur, au moyen d'une interruption, chaque fois que son état change (coûteux) - E/S par DMA ("Direct Memory Access") : - Un circuit spécialisé se charge des échanges de données ## Instructions de gestion de priorité (1) Les micro-architectures modernes implémentent nativement des mécanismes matériels permettant de distinguer entre deux modes d'exécution : - Mode non privilégié : accès restreint à la mémoire, interdiction d'exécuter les instructions d'entrées-sorties - Mode privilégié : accès à tout l'espace d'adressage et à toutes les instructions Instructions spécifiques de passage entre les deux modes ## Instructions de gestion de priorité (2) Servent à isoler le système d'exploitation des programmes d'application. - Les appels système s'exécutent en mode privilégié, pour pouvoir accéder à l'ensemble des ressources de la machine - Les programmes d'application s'exécutent en mode non privilégié, et ne peuvent donc accéder directement au matériel sans passer par les routines de contrôle d'accès du système - Le passage du mode non privilégié au mode privilégié ne peut se faire que de façon strictement contrôlée (traps et interruptions) ## Instructions d'interruption (1) Les interruptions sont des événements qui, une fois reçus par le processeur, conduisent à l'exécution d'une routine de traitement adaptée - L'exécution du programme en cours est suspendue pour exécuter la routine de traitement - Analogue à un appel de sous-programme, mais de façon asynchrone Il existe plusieurs types d'interruptions, identifiées par leur numéro - Exemple : numéro d'IRQ ("Interrupt ReQuest") ## Instructions d'interruption (2) Les interruptions peuvent être : - Asynchrones : interruptions "matérielles" reçues par le processeur par activation de certaines de ses lignes de contrôle . Gestion des périphériques - Synchrones : interruptions générées par le processeur lui-même : - Par exécution d'une instruction spécifique ("trap") - Exemple : l'instruction INT de l'architecture x86 - Sert à mettre en œuvre les appels système - Sur erreur logicielle (erreur d'accès mémoire, de calcul ...) - Sert à mettre en œuvre les exceptions ## Instructions d'interruption (3) Lorsque le processeur accepte d'exécuter une interruption : - Il sauvegarde dans la pile l'adresse de la prochaine instruction à exécuter dans le cadre du déroulement normal - Il se sert du numéro de l'interruption pour indexer une table contenant les adresses des différentes routines de traitement ("vecteur d'interruptions") - Il se déroute à cette adresse . Passage en mode privilégié si le processeur en dispose ## Instructions d'interruption (4) Au niveau du jeu d'instructions, on trouve donc des instructions : - Pour générer des interruptions logicielles - Pour autoriser ou non l'acceptation des interruptions . Ces instructions ne doivent pas être exécutables par les programmes d'application . Exécutables seulement en mode privilégié La modification du vecteur d'interruptions ne peut se faire qu'en mode privilégié - Protection par segmentation de la mémoire ## Espace d'adressage La plupart des couches ISA considèrent la mémoire comme un espace linéaire et continu commençant de l'adresse 0 à l'adresse 2^(32)-1 ou 2^(64)-1 - En pratique, on n'utilise pas plus de 44 fils d'adresses (adressage de 16 TéraMots) ## Architecture ISA du Pentium II (1) Architecture appelée IA-32 (ou x86). Est le résultat d'une évolution continue depuis le processeur 8 bits 8080. Maintien d'une compatibilité ascendente permettant encore l'exécution de programmes écrits pour le processeur 16 bits 8086 : - Mode réel : le processeur se comporte comme un 8086 - Mode virtuel : le processeur simule un 8086 - Mode protégé : utilise l'ensemble du processeur ## Architecture ISA du Pentium II (2) Enrichissement continu du jeu d'instructions : - Passage à une architecture 32 bits avec le 80386 - Ajout des instructions MMX ("MultiMedia eXtension") par Intel - Ajout des instructions "3D Now!" (par AMD) et SSE ("Streaming SIMD Extension", par Intel) - Passage à une architecture 64 bits avec l'Opteron d'AMD (architecture appelée x86-64 par AMD ou EM64T par Intel) ## Architecture ISA du Pentium II (3) Architecture à deux adresses, non orthogonale, avec registres généraux spécialisés : Registres de segment : CS DS ES FS GS SS Registres d'état : EIP EFLAGS Registres de travail : EAX = [. AX = [AH AL]] (EAX : 32 bits ; AX : 16 bits ; AH, AL : 8 bits) EBX = [. BX = [BH BL]] ECX = [. CX = [CH CL]] EDX = [. DX = [DH DL]] ESI (32 bits pur, car n'existaient pas avant le 80286) EDI EBP ESP Mémoire organisée en 16384 segments de 2^(32) octets ## Architecture ISA du Pentium II (4) La structure des instructions est complexe et irrégulière. Le code opération est "expansif" : le premier octet dit s'il y en a d'autres, et ainsi de suite. Structure d'une instruction : - Préfixe : 0 à 5 octets - Opcode : 1 à 2 octets - Mode : 0 à 1 octet - SIB : 0 à 1 octet ("Scale/Index/Base") - Déplacement : 0 à 4 octets - Immédiat : 0 à 4 octets ## Circuits synchrones (1) En l'absence de synchronisation, les résultats des calculs des circuits avec boucle de rétroaction seraient inexploitables, car faux. Comme les longueurs des chemins de calcul de toutes les valeurs de sortie ne sont généralement pas identiques, les résultats des données introduites au temps (t) commenceraient à être réinjectées dans le circuit de calcul pour obtenir les résultats de (t+1), alors que les autres ne sont pas encore sorties du circuit. Au final, on a une "bouillie" de valeurs qui "tournent" sans cohérence. Il faut mettre en place des "barrières" pour empêcher les résultats du tour courant de "déborder" sur le tour suivant. ## Circuits synchrones (2) On peut réaliser ces barrières au moyen de bascules D faisant "verrou" ("latch"). On intercale une rangée de bascules D dans la boucle, entre la fin du calcul et la réinjection des données dans l'entrée du circuit. Ainsi, tant que leur fil CK n'est pas activé, les bascules fournissent toujours la même valeur, celle de leur sortie Q, au circuit combinatoire, et bloquent la boucle en empêchant la nouvelle valeur calculée, qui est sur leur entrée D, de repartir dans le circuit. Ces bascules doivent être pilotées par une horloge, pour créer un front montant sur CK à intervales réguliers, pour faire avancer le calcul d'une étape à la suivante. ## Circuits synchrones (3) La fréquence de l'horloge doit être choisie de telle sorte que : - Le temps de cycle permette au circuit de se stabiliser . Dépend de la longueur du chemin critique du circuit - Le temps d'impulsion soit suffisamment court pour éviter toute interférence entre phases de calcul . Dépend de la longueur du plus court chemin . Impulsion la plus courte possible pour éviter tout problème de "rebouclage" des valeurs entre la sortie et l'entrée ## Pipe-line (1) Lorsqu'un même traitement se répète dans le temps, et peut être découpé en sous-tâches élémentaires, on peut mettre en place une chaîne de traitement appelée pipe-line. Le nombre de sous-unités fonctionnelles est appelé "nombre d'étages" du pipe-line. Exemple d'un circuit combinatoire non pipe-liné : on a un unique circuit pour câbler une unité fonctionnelle, avec un chemin critique très long : -11111111111111111111111111111111- Exemple du circuit combinatoire de la même unité fonctionnelle, découpé en quatre étapes : -11111111-22222222-33333333-44444444- ## Pipe-line (2) Exemple : le lavomatique. - Lavage "L" : 30 minutes - Séchage "S" : 40 minutes - Pliage "P" : 20 minutes Chaque lettre vaut dix minutes. Si on ne pipe-line pas quatre cycles de lessive : LLLSSSSPP LLLSSSSPP LLLSSSSPP LLLSSSSPP Si on pipe-line les quatre cycles : LLLSSSSPP LLL.SSSSPP LLL.SSSSPP LLL.SSSSPP On termine beaucoup plus tôt. ## Pipe-line (3) Trois conditions sont nécessaires à la bonne mise en œuvre d'un pile-line : - Une même opération doit être répétée dans le temps - Cette opération doit pouvoir être décomposée en étapes ("stages", improprement traduit en "étages") indépendantes - La durée de ces étages doit être à peu près la même ## Pipe-line (4) Un pipe-line à p étages sort son premier résultat après p cycles élémentaires, puis un résultat par cycle élémentaire. - N'est utile que si l'opération se répète ! Soit un circuit de latence 16 : -################- Sans pipe-line, le calcul se propage à partir des valeurs d'entrée, jusqu'à la stabilisation du résultat à la sortie : -1111############- -11111111########- -111111111111####- -1111111111111111- (sortie de la valeur 1) -2222111111111111- -2222222211111111- -2222222222221111- -2222222222222222- (sortie de la valeur 2) etc. Avec un pipe-line à quatre étages : -1111-####-####-####- -2222-1111-####-####- -3333-2222-1111-####- -4444-3333-2222-1111- (sortie de la valeur 1) -5555-4444-3333-2222- (sortie de la valeur 2) -6666-5555-4444-3333- (sortie de la valeur 3) -7777-6666-5555-4444- (sortie de la valeur 4) -8888-7777-6666-5555- (sortie de la valeur 5) etc. ## Pipe-line (5) Pour isoler les différents étages du pipe-line, on utilise des "latches". La fréquence de cadencement est limitée par la durée de l'étage le plus long. Il faut ajouter à cette durée le temps de traversée du latch associé. ## Pipe-line (6) Soient : - T le temps de traversée du circuit non pipe-liné - p la profondeur du pipe-line (nombre d'étages) - L Le temps de traversée d'un latch Si le pipe-line est idéalement équilibré, le circuit pipe-liné exécute n instructions en (p + n – 1) étapes de temps unitaire (T / p + L). On a (p + n – 1) étapes car le premier résultat sort au bout de p étapes (latence de traversée du pipe-line) et il reste encore (n - 1) résultats à sortir du pipe-line, à raison d'un résultat par étape. Le circuit non pipe-liné exécute pour sa part n instructions en n.T étapes élémentaires. ## Pipe-line (7) L'efficacité du pipe-line est donc égale à : n.T / ((n + p - 1)(T / p + L)). L'efficacité maximale théorique d'un pipe-line équilibré de profondeur p : - Est strictement inférieure à p . Intérêt d'augmenter p pour augmenter l'efficacité du pipe-line . Revient à augmenter le degré de parallélisme du circuit - Tend vers p quand n tend vers +infini . En supposant L petit devant T / p ## Pipe-line d'instruction (1) La tâche la plus répétitive qu'un processeur ait à effectuer est la boucle de traitement des instructions. Il faut pouvoir décomposer le traitement d'une instruction en sous-étapes de durée à peu près équivalente. ## Pipe-line d'instruction (2) Étapes classiques du traitement des instructions : - "Fetch" : Récupération de la prochaine instruction à exécuter - "Decode" : Décodage de l'instruction - "Read" : Lecture des opérandes (registre ou mémoire) - "Execute" : Calcul, branchement, etc... - "Write" : Écriture du résultat (registre ou mémoire) Elles-mêmes découpables en sous-étapes ## Pipe-line d'instruction (3) La création des pipe-lines d'instructions et l'augmentation de leur profondeur a été un facteur déterminant de l'amélioration de la performance des processeurs : - 5 étages pour le Pentium - 12 étages pour les Pentium II et III - 20 étages pour le Pentium IV ## Pipe-line d'instruction (4) Pourquoi ne pas continuer à augmenter la profondeur des pipe-lines d'instructions ? - Problème de taille des niveaux . Le surcoût des latches augmente en proportion - Problème d'équilibrage des niveaux . Plus la granularité souhaitée est fine, plus il est difficile de séparer les fonctions logiques en blocs équilibrés - Problème de dépendances entre instructions ## Dépendances d'instructions (1) Les instructions exécutées en séquence sont rarement indépendantes. On identifie classiquement quatre types de dépendances : - Certaines sont réelles, et reflètent le schéma d'exécution ; - D'autres sont de fausses dépendances : . Accidents dans la génération du code . Manque d'informations sur le schéma d'exécution ## Dépendances d'instructions (2) Dépendance réelle : mov [A],r1 ... add r1,r2,r3 On lit la valeur r1 après qu'elle a été calculée et écrite. Anti-dépendance add r1,r2,r4 ... mov [A],r1 On écrit la valeur r1 après qu'elle a été lue. Deux raisons possibles à cela : - on modifie la même valeur (p. ex. : incrémentation de compteur de boucle) ; - on réutilise le même registre pour stocker une deuxième valeur, lorsqu'on n'a plus besoin de la première. Si on avait eu plus de registres, on aurait pu utiliser un registre différent, et les instructions auraient pu être interverties sans modifier la sémantique du code : "fausse dépendance". Dépendance de contrôle bz r4,etiq # Pour ne pas diviser par zéro... div r1,r4,r1 etiq: ... L'exécution de la deuxième instruction est dépendante de la première. Dépendance de résultat add r2,r3,r1 … mov [A],r1 On écrase l'ancienne valeur de r1. Comme pour les anti-dépendances, il peut y avoir deux raisons : - mise à jour d'une valeur en fonction d'un sous-calcul ; - réutilisation d'un registre. ## Dépendances d'instructions (3) Lorsque le processeur n'est pas pipe-liné, des instructions dépendantes peuvent être exécutées l'une après l'autre sans problème. Cinq étapes du y68 : - "F" : Fetch - "D" : Decode - "E" : Execute - "M" : Memory - "W" : Write-back. Le résultat de l'instruction précédente est connu au moment où on en a besoin pour la suivante : add r2,r3,r1 F D E M W add r4,r1,r4 F D E M W ## Dépendances d'instructions (4) Lorsque le processeur est pipe-liné, séquencer deux instructions dépendantes peut conduire à des incohérences. Par exemple, la valeur d'un registre est lue dans la banque de registres avant que l'instruction précédente l'y ait placée : add r2,r3,r1 F D E M W add r4,r1,r4 F D E M W (On fait le "D" avant le "W" d'avant) De multiples techniques existent pour amoindrir les effets de ce problème. ## Superscalarité Afin d'augmenter le nombre d'instructions traitées par unité de temps, on fait en sorte que le processeur puisse lire et exécuter plusieurs instructions en même temps. - Problèmes de dépendances entre instructions - Entrelacement de code effectué par le compilateur - Réordonnancement dynamique des instructions par le processeur (exécution "out of order")