Fonctionnement d'un ordinateur/Le chemin de données

Un livre de Wikilivres.
Sauter à la navigation Sauter à la recherche

Comme vu précédemment, le chemin de donnée est l'ensemble des composants dans lesquels circulent les données dans le processeur. Il comprend l'unité de calcul, les registres, l'unité d communication avec la mémoire, et le ou les bus qui permettent à tout ce petit monde de communiquer.

Les unités de calcul[modifier | modifier le wikicode]

Le processeur contient des circuits capables de faire des calculs arithmétiques, des opérations logiques, et des comparaisons. Ceux-ci sont regroupés dans une unité de calcul, souvent appelée unité arithmétique et logique. Certains (dont moi) préfèrent l’appellation anglaise arithmetic and logic unit, ou ALU. Les instructions de comparaisons ou de calcul peuvent mettre à jour le registre d'état : le registre d'état est obligatoirement relié à certaines sorties de l’unité de calcul. De plus, il faudra bien spécifier l'instruction à effectuer à notre unité de calcul : il faut bien prévenir notre unité de calcul qu'on veut faire une addition et pas une multiplication. Pour cela, notre unité de calcul possède une entrée permettant de la configurer convenablement : l'entrée de sélection de l'instruction. Sur cette entrée, on va mettre un numéro qui précise l'instruction à effectuer. Pour chaque unité de calcul, il existe donc une sorte de correspondance entre ce numéro, et l'instruction à exécuter. Généralement, l'opcod de l'instruction est envoyé sur cette entrée, du moins sur les processeurs où l'encodage des instructions est "simple".

Unité de calcul usuelle.

L'intérieur d'une unité de calcul[modifier | modifier le wikicode]

Ces unités de calcul contiennent souvent un circuit différent pour chaque opération possible. L’entrée de sélection sert uniquement à sélectionner le bon circuit. Pour effectuer cette sélection, certaines unités de calcul utilisent des multiplexeurs commandés par l'entrée de sélection. D'autres envoient les opérandes à tous les circuits en même temps, et activent ou désactivent chaque sous-circuit suivant les besoins. Chaque circuit possède ainsi une entrée de commande, dont la valeur est déduite par un circuit combinatoire à partir de l'entrée de sélection d'instruction de l'ALU (généralement un décodeur).

Pour vous donner un exemple, voici à quoi ressemblerait l'intérieur d'une unité de calcul simple, qui ne gérerait que les instructions logiques et l'addition. On voit que les circuits pour le ET, le OU, le XOR, la négation et l'addition sont séparés. Leurs sorties sont reliées un multiplexeur, qui permet de sélectionner le résultat voulu. Ce multiplexeur est naturellement commandé par l'entrée de sélection de l'instruction (l'opcode).

Simplified-ALU

Voyons maintenant un exemple d'unité de calcul configurable. Si vous vous souvenez du chapitre sur les circuits de calcul, vous devez vous souvenir du circuit additionneur-soustracteur, capable d'effectuer soit des additions, soit des soustractions. Et bien on peut l'utiliser comme unité de calcul. Cela donne une unité de calcul très simple, qui ne peut faire que deux opérations, mais c'est malgré tout une unité de calcul. Or, ce circuit est configurable :l'envoi d'un bit permet de choisir quelle opération faire, sans pour autant configurer un multiplexeur. C'est donc un exemple d'unité de calcul configurable.

Additionneur-soustracteur en complément à deux

Sur certains processeurs assez anciens, l'ALU est elle-même découpée en plusieurs ALU qui traitent chacune un petit nombre de bits. Par exemple, l'ALU des processeurs AMD Am2900 est une ALU de 16 bits composée de plusieurs sous-ALU de 4 bits. Cette technique qui consiste à créer des unités de calcul plus grosses à partir d’unités de calcul plus élémentaires s'appelle en jargon technique du bit slicing.

Les unités de calcul spécialisées[modifier | modifier le wikicode]

Il n'est pas rare qu'un processeur possède plusieurs unités de calcul. Un processeur peut aussi disposer d’unités de calcul spécialisées, séparées de l'unité de calcul principale, pour les décalages, les divisions, etc. Cette séparation est parfois nécessaire : certaines opérations sont assez compliquées à insérer dans une unité de calcul normale, et les garder à part peut simplifier la conception du processeur. Il faut signaler que les processeurs modernes possèdent plusieurs unités de calcul, toutes reliées aux registres. Cela permet d’exécuter plusieurs calculs en même temps dans des unités de calcul différentes. Cela permet d'augmenter les performances du processeur relativement facilement. Diverses technologies, abordées dans la suite du cours permettent de profiter au mieux de ces unités de calcul : pipeline, exécution dans le désordre, exécution superscalaire, jeux d'instructions VLIW, etc.

Généralement, les processeurs utilisent une unité de calcul spécialisée pour les nombres flottants : la floating-point unit, aussi appelée FPU. Autrefois, ces FPU n'étaient pas incorporés dans le processeur, mais étaient regroupés dans un processeur séparé du processeur principal de la machine. Un emplacement dans la carte mère était réservé à un de ces processeurs spécialisés. On appelait ces processeurs spécialisés dans les calculs flottants des coprocesseurs arithmétiques. Ces coprocesseurs étaient très chers et relativement peu utilisés. Aussi, seules certaines applications assez rares étaient capables d'en tirer profit : des logiciels de conception assistée par ordinateur, par exemple.

Registres[modifier | modifier le wikicode]

De nombreux registres n'ont pas de nom de registre ou d'adresse et sont sélectionnés implicitement par certaines instructions : program counter, registre d'état, etc. Ces registres sont reliés au chemin de données directement. On peut citer le cas du registre accumulateur, présent sur les architectures à accumulateur, qui est un simple registre relié à l'unité de calcul de cette façon.

Accumulateur.

Les registres qui sont numérotés avec un nom de registre sont rassemblés dans une RAM, dont chaque byte est un registre. Celle-ci porte le nom de banc de registres (register file). Le banc de registres est souvent une mémoire multiport, avec au moins un port d'écriture et deux ports de lecture. Cela sert pour les instructions dites dyadiques, qui ont besoin de lire deux données et d'enregistrer leur résultat. On peut avoir plus de ports, pour certaines instructions spéciales. Bien sûr, il y a des exceptions. Il ne faut pas oublier que certains registres n'ont pas de noms : le Program Counter, le registre d'état, etc. Ceux-ci ne sont pas forcément rassemblés avec les autres registres et sont souvent intégrés dans des circuits spécialisés ou mis à part des autres registres. Ce n'est toutefois pas systématique : on peut placer ces registres dans un banc de registre, mais c'est rarement utilisé. Dans ce cas, on doit jouer un peu sur les noms de registre avant de les envoyer sur les entrées d'adresse du banc de registre. Rien de bien méchant.

Banc de registre multiports.

Bancs de registres séparés[modifier | modifier le wikicode]

Plus un banc de registres a de ports, plus il utilisera de circuits, consommera de courant et chauffera. Les concepteurs de processeurs sont donc obligés de faire des compromis entre le nombre de ports du banc de registres (et donc la performance du processeur), et la chaleur dégagée par le processeur.

Au-delà d'un certain nombre de registres, il devient difficile d'utiliser un seul gros banc de registres. Il faut alors scinder le banc de registres en plusieurs bancs de registres séparés. C'est assez souvent utilisé sur les architectures dont les registres sont spécialisés, et ne peuvent contenir qu'un type bien défini de donnée (entiers, nombres flottants, adresses) : on trouve alors un banc de registre pour chaque type de registre. Mais rien n'empêche d'utiliser plusieurs bancs de registres sur un processeur qui utilise des registres généraux, : il faut juste prévoir de quoi échanger des données entre les bancs de registres. Dans la plupart des cas, cette séparation est invisible du point de vue du langage machine. Sur d'autres processeurs, les transferts de données entre bancs de registres se font via une instruction spéciale, souvent appelée COPY.

Bancs de registres unifiés[modifier | modifier le wikicode]

Certains processeurs utilisent un seul gros banc de registres dit unifié, même avec des registres spécialisés. Par exemple, c'est le cas des Pentium Pro, Pentium II, Pentium III, ou des Pentium M : ces processeurs ont des registres séparés pour les flottants et les entiers, mais ils sont regroupés dans un seul banc de registres. Avec cette organisation, un registre flottant et un registre entier peuvent avoir le même nom de registre en langage machine, mais l'adresse envoyée au banc de registres ne doit pas être le même : le séquenceur doit ajouter des bits au nom de registre pour former l'adresse finale.

Désambiguïsation de registres sur un banc de registres unifié.

L'utilisation d'un banc de registres unifié permet d'implémenter facilement le fenêtrage de registres. Il suffit pour cela de regrouper tous les registres des différentes fenêtres dans un seul banc de registres. Il suffit de faire comme vu au-dessus : rajouter des bits au nom de registre pour faire la différence entre les fenêtres. Cela implique de se souvenir dans quelle fenêtre de registre on est actuellement, cette information étant mémorisée dans un registre qui stocke le numéro de la fenêtre courante. Pour changer de fenêtre, il suffit de modifier le contenu de ce registre lors d'un appel ou retour de fonction avec un petit circuit combinatoire. Bien sûr, il faut aussi prendre en compte le cas où ce registre déborde, ce qui demande d'ajouter des circuits pour gérer la situation.

Communication avec la mémoire[modifier | modifier le wikicode]

L'unité de communication avec la mémoire possède une entrée pour spécifier l'adresse à lire ou écrire, une entrée pour indiquer si l'accès est une lecture ou une écriture, et au moins une entrée/sortie connectée au bus de données. Certaines unités de communication avec la mémoire peuvent gérer certains modes d'adressage elle-mêmes, et faire des calculs d'adresse à partir d'un indice ou d'un décalage. Pour cela, l'unité de communication avec la mémoire contient une unité de calcul interne, l'Address generation unit, ou AGU.

Pour simplifier la conception du processeur, le bus mémoire est parfois relié à des registres d’interfaçage mémoire, intercalés entre le bus mémoire et le chemin de données. Au lieu d'aller lire ou écrire directement sur le bus, on va lire ou écrire dans ces registres spécialisés. Généralement, on trouve deux registres d’interfaçage mémoire : un registre relié au bus d'adresse, et autre relié au bus de données.

Registres d’interfaçage mémoire.

Le bus interne[modifier | modifier le wikicode]

Pour échanger des informations entre les composants du chemin de données, on utilise un ou plusieurs bus internes. Toute micro-instruction configure ce bus, configuration qui est commandée par le séquenceur. Chaque composant du chemin de données est relié au bus via des transistors, qui servent d'interrupteurs. Pour connecter le banc de registres ou l'unité de calcul à ce bus, il suffit de fermer les bons interrupteurs et d'ouvrir les autres.

Micro-architecture à un seul bus[modifier | modifier le wikicode]

Dans le cas le plus simple, le processeur utilise un seul bus interne. Sur ces processeurs, l’exécution d'une instruction dyadique (deux opérandes) peut prendre plusieurs étapes. Si les deux opérandes sont dans les registres, il faut relier l'ALU à ces deux registres, ce qui est impossible avec un seul bus. Pour résoudre ce problème, on doit utiliser un registre spécial pour mettre en attente un opérande pendant qu'on charge l'autre. De même, il est préférable d'utiliser un registre temporaire pour le résultat, pour les mêmes raisons. Le déroulement d'une addition est donc simple : il faut recopier la première opérande dans le registre temporaire, connecter le registre contenant la deuxième opérande sur l’unité de calcul, lancer l’addition, et recopier le résultat du registre temporaire dans le banc de registres. Sur les architectures à accumulateur, ce registre temporaire n'est autre que l'accumulateur lui-même. Les exemples qui vont suivre montrent cependant que d'autres registres temporaires peuvent être ajoutés, pour les autres opérandes.

Micro-architecture à plusieurs bus[modifier | modifier le wikicode]

Certains processeurs s'arrangent pour relier les composants du chemin de données en utilisant plusieurs bus, histoire de simplifier la conception du processeur ou d'améliorer ses performances. Par exemple, on peut utiliser trois bus reliés comme indiqué dans le schéma qui suit. Avec cette organisation, le processeur peut gérer les modes d'adressage absolus, et à registre, pas plus.

Chemin de données à trois bus.

Pour gérer l'adressage immédiat, on doit placer la constante inscrite dans l'instruction sur l'entrée de notre ALU. Cette constante est fournie par le séquenceur. Sur certains processeurs, la constante peut être négative et doit alors être convertie en un nombre de même taille que l'entrée de l'ALU. Pour effectuer cette extension de signe, on peut implanter un circuit spécialisé.

Chemin de données - Adressage immédiat avec extension de signe.

La gestion du mode d'adressage indirect à registre demande de relier un bus interne du processeur sur le bus d'adresse, ou sur le registre d’interfaçage mémoire adéquat. Si on veut effectuer une écriture, il suffit d’envoyer la donnée à écrire sur le bus de données via un autre bus interne au processeur.

Bus avec adressage indirect

Bref, je suppose que vous voyez le principe : on peut toujours adapter l’organisation des bus internes de notre processeur pour gérer de nouveaux modes d'adressage, ou diminuer le nombre de micro-instructions nécessaires pour exécuter une instruction.