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

Un livre de Wikilivres.

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, qui sont regroupés dans une unité de calcul appelée unité arithmétique et logique. Certains préfèrent l’appellation anglaise arithmetic and logic unit, ou ALU.

L'interface de l'ALU est assez simple : on a des entrées pour les opérandes, et une sortie pour le résultat du calcul. De plus, les instructions de comparaisons ou de calcul peuvent mettre à jour le registre d'état, qui est relié à une autre sortie de l’unité de calcul. Une autre entrée, l'entrée de sélection de l'instruction, spécifie l'instruction à effectuer. Il faut bien prévenir notre unité de calcul qu'on veut faire une addition et pas une multiplication. Sur cette entrée, on envoie un numéro qui précise l'instruction à effectuer. La correspondance entre ce numéro et l'instruction à exécuter dépend de l'unité de calcul. Généralement, l'opcode 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.

La taille des opérandes de l'ALU[modifier | modifier le wikicode]

L'ALU manipule des opérandes codées sur un certain nombre de bits. Par exemple, une ALU peut manipuler des entiers codés sur 8 bits, sur 16 bits, etc. En général, la taille des opérandes de l'ALU est la même que la taille des registres. Un processeur 32 bits, avec des registres de 32 bit, a une ALU de 32 bits. C'est intuitif, et cela rend l'implémentation du processeur bien plus facile.

Mais il arrive rarement que l'ALU manipule des opérandes plus petits que la taille des registres. Un exemple serait une ALU de 8 bits alors que les registres font 16 bits, ou une ALU 4 bits avec des registres de 8 bits. Autant cette solution est faisable sans trop de soucis avec l'addition ou la soustraction, autant la multiplication et la division s'implémentent difficilement quand registres et ALU n'ont pas la même taille.

Par exemple, sur le Z80, les registres entiers étaient des registres de 8 bits, alors que l'ALU était de 4 bits. En conséquence, les calculs devaient être faits en deux phases : une qui traite les 4 bits de poids faible, et une autre qui traite les 4 bits de poids fort. L'unité de contrôle gérait tout cela, avec l'aide de registres placés en entrée/sortie de l'ALU, et de multiplexeurs/demultiplexeur. L'ensemble du circuit de l'ALU donnait ceci :

ALU du Z80

Les ALU sérielles[modifier | modifier le wikicode]

Un exemple extrême est celui des des processeurs sériels (sous-entendu bit-sériels), qui utilisent une ALU sérielle, qui fait leurs calculs bit par bit, un bit à la fois. N'allez pas croire que les processeurs sériels sont tous des processeurs de 1 bit. Certes, de tels processeurs ont existé, le plus connu d'entre eux étant le Motorola MC14500B. Mais beaucoup de processeurs 4, 8 et 16 bits étaient des processeurs sériels. Ils ont généralement un jeu d'instruction assez limité : des opérations logiques, l'addition, la soustraction, guère plus. Les ALU sérielles ne peuvent pas faire de multiplications ou de divisions.

Naturellement, effectuer les opérations bit par bit est plus lent comparé aux processeurs non-sériels. L'avantage de ces ALU est qu'elles peuvent gérer des opérandes de grande taille, avec plus d'une trentaine de bits, sans trop de problèmes. Autant utiliser des registres longs est facile, autant une ALU non-sérielle avec des opérandes aussi grands aurait été impraticable à l'époque.

Un autre avantage est que ces ALu utilisent peu de circuits. Et c'est pour cette raison que beaucoup de processeurs parallèles utilisaient des ALU sérielles. Le but de ces processeurs était d'exécuter pleins de calculs en parallèles, d'exécuter plusieurs calculs simultanément. Et cela demande d'utiliser pleins d'unités de calcul distinctes : exécuter N opérations en parallèle demande N unités de calcul. Mais un grand nombre d'unités de calcul signifie que celles-ci doivent être très simples, les ALU sérielles étant tout indiquées avec cette contrainte.

Le bit-slicing[modifier | modifier le wikicode]

Avant l'époque des premiers microprocesseurs 8 et 16 bits, le processeur n'était pas un circuit intégré unique, mais était formé de plusieurs puces électroniques soudées à la même carte. L'ALU était souvent une puce séparée, le séquenceur aussi, les registres étaient dans leur propre puce, etc. Les puces en question étaient des puces TTL assez simples, comparé à ce qu'on a aujourd'hui. Les ALU étaient vendues séparément, et elles manipulaient souvent des opérandes de 4/8 bits, les ALU 4 bit étant très fréquentes.

Si on voulait créer une ALU pour des opérandes plus grandes, il n'y avait pas le choix : il fallait construire l'ALU à partir de plusieurs ALU 4/8 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.

Cette technique est utilisée pour des ALU capables de gérer les opérations bit à bit, l'addition, la soustraction, mais guère plus. Il n'y a pas, à ma connaissance, d'ALU en bit-slicing capable d'effectuer une multiplication ou une division. La raison est qu'il n'est pas facile d'implémenter une multiplication entre deux nombres de 16 bits avec deux multiplieurs de 4 bits (idem pour la division). Alors que c'est plus simple pour l'addition et la soustraction : il suffit de transmettre la retenue d'une ALU à la suivante. Bien sûr, les performances seront alors nettement moindres qu'avec des additionneurs modernes, à anticipation de retenue, mais ce n'était pas un problème pour l'époque.

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

Un processeur peut disposer d’unités de calcul séparées de l'unité de calcul principale, spécialisées dans les décalages, les divisions, etc. Cette séparation est très utile pour certaines opérations compliquées à insérer dans une unité de calcul normale, la multiplication et la division étant clairement dans ce cas. Les ALU des processeurs modernes sont souvent couplées à un circuit multiplieur séparé, avec éventuellement un circuit diviseur lui aussi séparé des autres. Tous les processeurs récents incorporent un multiplieur, dans ou en-dehors de l'ALU, mais le support de la division est déjà moins fréquent. Les divisions sont des opérations assez rares, leur donner un circuit dédié n'en vaut pas toujours la peine.

Presque tous les processeurs utilisent une unité de calcul spécialisée pour les nombres flottants : la floating-point unit, aussi appelée FPU. Néanmoins, ce regroupement des circuits pour nombres flottants n'est pas aussi strict qu'on pourrait le croire. Dans certains cas, les circuits capables d'effectuer les divisions flottantes sont séparés des autres circuits (c'est le cas dans la majorité des PC modernes) : tout dépend de l'architecture interne du processeur utilisé. 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, appelé le coprocesseur arithmétique. Un emplacement dans la carte mère était réservé au coprocesseur. Ils étaient très chers et relativement peu utilisés, ce qui fait que seules certaines applications assez rares étaient capables d'en tirer profit : des logiciels de conception assistée par ordinateur, par exemple.

De nombreux processeurs modernes disposent d'une unité de calcul spécialisée dans le calcul des conditions, des instructions de test et des branchements. C’est notamment le cas sur les processeurs sans registre d'état, qui disposent de registres à prédicats. En général, les registres à prédicats sont placés à part des autres registres, dans un banc de registre séparé. L'unité de calcul normale n'est pas reliée aux registres à prédicats, alors que l'unité de calcul pour les branchements/test/conditions l'est. les registres à prédicats sont situés juste en sortie de cette unité de calcul.

Il existe des unités de calcul spécialisées pour les calculs d'adresse. Elles gèrent moins d'opérations que les ALU normales, vu que peu d'opérations sont utiles pour les adresses. Elles ne supportent guère plus que des incrémentations/décrémentations, des additions/soustractions, et des décalages simples. L'usage d'ALU spécialisées pour les adresses est un avantage sur les processeurs où les adresses ont une taille différente des données, ce qui est fréquent sur les anciennes architectures.

Les anciens processeurs avaient un circuit incrémenteur séparé de l'unité de calcul. C'est le cas sur l'Intel 8085, le Z-80, et bien d'autres processeurs 8 bits. Il était utilisé pour incrémenter des adresses, ce qui est une opération très fréquente. Elle est utilisée pour manipuler des tableaux, le pointeur de pile, voire le program counter. Mais beaucoup d'architectures augmentaient ses capacités en lui permettant d'incrémenter des données. Pourtant, ce circuit incrémentait des nombres plus grands que l'ALU. Par exemple, c'est le cas sur le Z-80, où l'incrémenteur peut manipuler des nombres de 16 bits, alors que l'ALU ne peut gérer que des nombres de 8 bits.

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, afin d'augmenter les performances du processeur. 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.

Les registres du processeur[modifier | modifier le wikicode]

Après avoir vu l'unité de calcul, il est temps de passer aux registres d'un processeur. Et là, les choses deviennent bien plus complexes. Les registres d'un processeur peuvent se classer en deux camps : soit ce sont des registres isolés, soit ils sont regroupés en paquets appelés banc de registres. Un banc de registres (register file) est une RAM, dont chaque byte est un registre. Il regroupe un paquet de registres différents dans un seul composant, dans une seule mémoire.

Banc de registres simplifié.

Un processeur contient presque toujours un banc de registre, couplé à des registres isolés. Il y a cependant quelques exceptions, comme certaines architectures à accumulateur sans registres généraux, qui se passent de banc de registres. Mais dans une architecture normale, on trouve un ou plusieurs bancs de registres, et un ou plusieurs registres isolés. La répartition des registres, à savoir quels registres sont dans le banc de registre et quels sont ceux isolés, est très variable suivant les processeurs.

L'adressage du banc de registres[modifier | modifier le wikicode]

Le banc de registre est une mémoire comme une autre, avec une entrée d'adresse qui permet de sélectionner le registre voulu. Plutot que d'adresse, nous allons parler d'identifiant de registre. Le séquenceur forge l'identifiant de registre en fonction des registres sélectionnés. Dans les chapitres précédents, nous avions vu qu'il existe plusieurs méthodes pour sélectionner un registre, qui portent les noms de modes d'adressage. Et bien les modes d'adressage jouent un grand rôle dans la forge de l'identifiant de registre.

Pour rappel, sur la quasi-totalité des processeurs actuels, les registres généraux sont identifiés par un nom de registre, terme trompeur vu que ce nom est en réalité un numéro. En clair, les processeurs numérotent les registres, le numéro/nom du registre permettant de l'identifier. Par exemple, si je veux faire une addition, je dois préciser les deux registres pour les opérandes, et éventuellement le registre pour le résultat : et bien ces registres seront identifiés par un numéro. Mais tous les registres ne sont pas numérotés et ceux qui ne le sont pas sont adressés implicitement. Par exemple, le pointeur de pile sera modifié par les instructions qui manipulent la pile, sans que cela aie besoin d'être précisé par un nom de registre dans l'instruction.

Dans le cas le plus simple, les registres nommés vont dans le banc de registres, les registres adressés implicitement sont en-dehors, dans des registres isolés. L'idéntifiant de registre est alors simplement le nom de registre, le numéro. Le séquenceur extrait ce nom de registre de l'insutrction, avant de l'envoyer sur l'entrée d'adresse du banc de registre.

Adressage du banc de registres généraux

Dans un cas plus complexe, des registres non-nommés sont placés dans le banc de registres. Par exemple, les pointeurs de pile peuvent être placés dans le banc de registre, même s'ils sont adressés implicitement. Même des registres aussi importants que le program counter peuvent se mettre dans le banc de registre ! Nous verrons le cas du program counter dans le chapitre suivant, qui porte sur l'unité de chargement. Dans ce cas, le séquenceur forge l'identifiant de registre de lui-même. Dans le cas des registres nommés, il ajoute quelques bits aux noms de registres. Pour les registres adressés implicitement, il forge l'identifiant à partir de rien.

Adressage du banc de registre - cas général

Nous verrons plus bas que dans certains cas, le nom de registre ne suffit pas à adresser un registre dans un banc de registre. Dans ce cas, le séquenceur rajoute des bits, comme dans l'exemple précédent. Tout ce qu'il faut retenir est que l'identifiant de registre est forgé par le séquenceur, qui se base entre autres sur le nom de registre s'il est présent, sur l'instruction exécutée dans le cas d'un registre adressé implicitement.

Les registres généraux[modifier | modifier le wikicode]

Pour rappel, les registres généraux sont des registres qui ne sont pas spécialisés et peuvent mémoriser n'importe quoi : des entiers, des flottants, des adresses, etc. Ils sont opposés aux registres spécialisés, où chaque registre est spécialisé dans un type de données.

Dans l'exemple pris dans cette section, le processeur n'a que des registres généraux, couplés à un program counter et un registre d'état. Le program counter et le registre d'état sont des registres isolés, les registres généraux sont rassemblés dans un banc de registre. Le banc de registre est appelé le banc de registres généraux, vu qu'il ne contient que ça et qu'ils sont tous dedans.

L'interface du banc de registres généraux[modifier | modifier le wikicode]

Le banc de registres généraux est une mémoire multiport, avec au moins un port d'écriture et deux ports de lecture. La raison est que les instructions lisent deux opérandes dans les registres et enregistrent leur résultat dans des registres. Le tout se marie bien avec un banc de registre à deux de lecture (pour les opérandes) et un d'écriture (pour le résultat).

Banc de registre multiports.

L'interface exacte dépend de si l'architecture est une architecture 2 ou 3 adresses. Pour rappel, la différence entre les deux tient dans la manière dont on précise le registre où enregistrer le résultat d'une opération. Avec les architectures 2-adresses, on précise deux registres : le premier sert à la fois comme opérande et pour mémoriser le résultat, l'autre sert uniquement d'opérande. Un des registres est donc écrasé pour enregistrer le résultat. Sur les architecture 3-adresses, on précise trois registres : deux pour les opérandes, un pour le résultat.

Les architectures 2-adresses ont un banc de registre où on doit préciser deux "adresses", deux noms de registre. L'interface du banc de registre est donc la suivante :

Register File d'une architecture à 2-adresses

Les architectures 3-adresses doivent rajouter une troisième entrée pour préciser un troisième nom de registre. L'interface du banc de registre est donc la suivante :

Register File d'une architecture à 3-adresses

Les bancs de registres scindés[modifier | modifier le wikicode]

Rien n'empêche d'utiliser plusieurs bancs de registres sur un processeur qui utilise des registres généraux. La raison est une question d'optimisation. 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.

Le problème est qu'il faut 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.

Les architectures à registres spécialisés[modifier | modifier le wikicode]

Passons maintenant aux architectures dont les registres sont spécialisés. L'utilisation de plusieurs bancs de registres est plus simple et intuitive. Mais ce n'est pas systèmatiquement le cas et il est possible de regrouper des registres de type différents dans un seul banc de registres. Les deux méthodes, que nous allons détailler ci-dessous, portent respectivement les noms de banc de registre dédié et de banc de registre unifié.

L'exemple type est celui où on a des registres qui ne peuvent contenir qu'un type bien défini de donnée, par exemple des registres séparés pour les entiers et les flottants. Il est alors plus simple d'utiliser un banc de registres séparé pour les nombres flottants, à côté d'un banc de registre entiers. L'avantage est que les nombres flottants et entiers n'ont pas forcément la même taille, ce qui se marie bien avec deux bancs de registres, où la taille des registres est différente dans les deux bancs.

Mais d'autres processeurs utilisent un seul banc de registres unifié, qui regroupe tous les registres de données, qu'ils soient entier ou flottants. 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 la même : le séquenceur ajoute des bits au nom de registre pour former l'adresse finale.

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

Un autre exemple est celui des vieux processeurs où les adresses sont séparées des nombres entiers, dans deux ensembles de registres distincts. Les adresses et les données n'ont pas la même taille, ce qui fait que la meilleure solution est d'utiliser deux bancs de registres, un pour les adresses, l'autre pour les entiers. Le processeur Z80 faisait cela, en partie parce qu'il gérait des adresses de 16 bits, mais des données de 8 bits.

Le registre d'état[modifier | modifier le wikicode]

Le registre d'état fait souvent bande à part et n'est pas placé dans un banc de registres. En effet, le registre d'état est très lié à l'unité de calcul. Il reçoit des indicateurs/flags provenant de la sortie de l'unité de calcul, et met ceux-ci à disposition du reste du processeur. Son entrée est connectée à l'unité de calcul, sa sortie est reliée au séquenceur et/ou au bus interne au processeur.

Sa sortie est reliée au séquenceur afin que celui-ci puisse gérer les instructions de branchement, qui ont parfois besoin de connaitre certains bits du registre d'état pour savoir si une condition a été remplie ou non, et donc s'il faut faire ou non le branchement. D'autres processeurs relient aussi le registre d'état au bus interne, ce qui permet d'implémenter certaines instructions, notamment celles qui permettent de mémoriser le registre d'état dans un registre général.

Place du registre d'état dans le chemin de données

Il est techniquement possible de mettre le registre d'état dans le banc de registre, mais cela complexifie l’implémentation du processeur. La principale difficulté est que les instructions arithmétiques doivent faire deux écritures dans le banc de registre : une pour le registre de destination, et une autre pour le registre d'état. Les deux écritures simultanées demandent d'utiliser un banc de registre à deux ports d'écriture, ce qui est très gourmand en transistors. Si le second port n'a pas l'occasion de servir pour d'autres instructions, c'est du gâchis. On pourrait aussi penser faire les deux écritures l'une après l'autre, mais cela demanderait de rajouter un registre en plus, ce qu'on cherche à éviter. Dans les faits, je ne connais aucun processeur qui utilise cette technique.

Les registres à prédicats[modifier | modifier le wikicode]

Les registres à prédicats sont, pour rappel, des registres de 1 bit qui mémorisent les résultats des comparaisons et instructions de test. Ils sont nommés/numérotés, mais les numéros en question sont distincts de ceux utilisés pour les registres généraux. Ils sont généralement placés à part, dans un banc de registres séparé. Ils sont placés au même endroit que le registre d'état, et sont connectés de la même manière. Le banc de registres à prédicat a une entrée de 1 bit connectée à l'ALU, et une sortie de un bit connectée au séquenceur. L'unité de calcul écrit dans ce banc de registres à volonté, le séquenceur lit ces registres si besoin.

Et non seulement ils ont leur propre banc de registres, mais celui-ci est relié à une unité de calcul spécialisée dans les conditions/instructions de test. Parfois, l'unité de calcul dédiée aux conditions peut lire les registres à prédicats, pour combiner le contenu de plusieurs d'entre eux, par exemple. Pour rappel, certaines instructions permettent de faire un ET, un OU, un XOR entre deux registres à prédicats, ce qui n'est possible qu'avec cette voie en lecture.

Banc de registre pour les registres à prédicats

Le pointeur de pile[modifier | modifier le wikicode]

Il n'est pas rare que certains processeurs aient des registres spécialisés pour le pointeur de pile et le frame pointer. Il est possible de mettre ces registres à part, en dehors du banc de registres, ou de les mettre dans le banc de registre. Sur les architectures où adresses et entiers sont dans des bancs de registre différents, le pointeur de pile est placé avec les adresses. Il faut dire que ces registres contiennent une adresse, pas un entier, et qu'il vaut mieux éviter de mélanger les deux.

Certains processeurs placent le pointeur de pile dans le banc de registre. C'est le cas sur le Z-80 ou sur l'Intel 8085, par exemple, où le pointeur de pile est dans le même banc de registre que les registres entiers (qui contient aussi les adresses). Le désavantage est qu'on perd un registre adressable : Avec un banc de registres de 16 registres, on ne peut plus en adresser que 15, le dernier étant pour un pointeur de pile non-adressable (en théorie). Mais l'avantage est que l'implémentation du processeur est plus simple. Les opérations réalisées sur le pointeur de pile sont de simples additions et soustractions, réalisées par l'ALU. Or, le banc de registre est déjà connecté à l'ALU, ce qui facilite l'implémentation des instructions de gestion de la pile, comme PUSH et POP.

Intel 8085.

L'autre solution est d'utiliser un registre isolé pour le pointeur de pile. L'avantage est que le pointeur de pile est censé être adressé implicitement. Pour le dire autrement, si on a un banc de registre de 16 registres, on a bien 16 registres adressables, alors que la solution précédente donne 15 registres adressables et un pointeur de pile qui ne l'est pas. L'inconvénient est dans la mise à jour du pointeur de pile, qui demande de l'incrémenter ou de le décrémenter. Soit on trouve un moyen pour le relier à l'ALU, soit on lui dédie un incrémenteur/décrémenteur spécialisé. Les deux solutions ajoutent des circuits et complexifient le chemin de données et le séquenceur.

Sur les vielles architectures, la solution est d'utiliser un incrémenteur spécialisé partagé avec le program counter. Le pointeur de pile est alors regroupé avec le program counter, les deux sont incrémentés par le même incrémenter. Un exemple est celui de l'Intel 4004, qui place les pointeurs de pile et le program counter dans un banc de registres séparé du reste du processeur. LA raison de cet arrangement est une économie de circuit : pas besoin d'utiliser deux incrémenteurs, on n'en utilise qu'un seul.

Microarchitecture de l'Intel 4040, une version améliorée de l'Intel 4004.

Le registre accumulateur[modifier | modifier le wikicode]

Pour rappel, les architectures à accumulateurs disposent d'un registre accumulateur, où on lit une opérande et enregistre le résultat. Les autres opérandes sont soit lues depuis la mémoire, soit lues depuis des registres nommés.

Le registre accumulateur, présent sur les architectures à accumulateur, n'est jamais mis dans le banc de registres, et est toujours un registre isolé, à part. Il est relié à l'unité de calcul comme indiqué dans le schéma ci-dessous. Il est souvent associé à d'autres registres temporaires qui servent à mémoriser les opérandes temporairement, le temps du calcul.

Accumulateur.

Si le processeur dispose de registres nommés, ils sont placés dans un banc de registres à part. Le banc de registre n'a qu'un seul port, qui sert à la fois pour la lecture et l'écriture. La raison est qu'on ne lit qu'une seule opérande depuis les registres, l'autre opérande étant lue depuis l'accumulateur. De plus, le résultat est enregistré dans l'accumulateur.

Les registres d'interruption et le fenêtrage de registres[modifier | modifier le wikicode]

Dans le chapitre sur les registres, nous avions vu que certains processeurs dupliquaient leurs registres architecturaux, pour accélérer les interruptions ou les appels de fonction. Par exemple, les interruptions avaient accès à leurs propres registres, séparés des registres architecturaux. Ou encore, chaque appel de fonction avait accès à son propre ensemble de registres architecturaux séparé du reste, grâce à la fonctionnalité du fenêtrage de registres. Reste à voir comment ces fonctionnalités sont implémentées dans le processeur.

Les bancs de registres dédiés/unifiés pour les interruptions[modifier | modifier le wikicode]

Certains processeurs ont deux ensembles de registres identiques : un dédié aux interruptions, un autre pour les programmes normaux. Les registres dans les deux ensembles ont les mêmes noms, mais le processeur choisit le bon ensemble suivant s'il est dans une interruption ou non. Si on peut utiliser deux bancs de registres séparés, il est aussi possible d'utiliser un banc de registre unifié pour les deux.

Sur certains processeurs, le banc de registre est dupliqué en plusieurs exemplaires. La technique est utilisée pour les interruptions. Certains processeurs ont deux ensembles de registres identiques : un dédié aux interruptions, un autre pour les programmes normaux. Les registres dans les deux ensembles ont les mêmes noms, mais le processeur choisit le bon ensemble suivant s'il est dans une interruption ou non. On peut utiliser deux bancs de registres séparés, un pour les interruptions, et un pour les programmes.

Sur d'autres processeurs, on utilise un banc de registre unifié pour les deux ensembles de registres. Les registres pour les interruptions sont dans les adresses hautes, les registres pour les programmes dans les adresses basses. Le choix entre les deux est réalisé par un bit qui indique si on est dans une interruption ou non, disponible dans une bascule du processeur. Appelons là la bascule I.

Le processeur Z80 utilisait cette technique. Avec cependant une petite différence : il avait un accumulateur séparé du banc de registre. Les six registres B, C, D, E, H et L étaient dans le banc de registre, de même que leurs copies pour interruptions nommées B', C', D', E', H' et L'. Par contre, l'accumulateur A et le registre d'état étaient aussi dupliqués : un pour les interruptions, l'autre pour les programmes. Le choix entre les deux se faisait par une bascule séparée pour les registres A,F,A',F', appelée la bascule A. Bascule qui parait redondante avec celle pour le banc de registres, mais qui ne l'est pas quand on sait ce qui va suivre.

Le Z80 incorporait des instructions pour échanger le contenu des deux ensembles de registres, accumulateur inclus. L'instruction EXX échangeait le contenu des registres B, C, D, E, H et L et B', C', D', E', H' et L'. L'instruction EX échangeait les registres A,F avec les registres A',F'. Ces instructions permettaient d'utiliser les registres d'interruption pour les programmes et réciproquement. Cela permettait de doubler le nombre de registres pour les programmes, si les interruptions n'étaient pas utilisées. Mais cela demandait de séparer l'accumulateur du reste.

Les instructions d'échange de registres du Z80 ne faisaient que modifier les bascules I et A. Cela évitait de faire des copies d'un ensemble de registre à l'autre. L'instruction EX inverse la bascule A, l'instruction EXX inverse le contenu de la bascule I. Il est aussi possible d'échanger le contenu des registres DE et HL, ce qui est là encore fait par deux bascules : une par ensemble de registres.

Le fenêtrage de registres[modifier | modifier le wikicode]

Fenêtre de registres.

Le fenêtrage de registres fait que chaque fonction a accès à son propre ensemble de registres, sa propre fenêtre de registres. Là encore, cette technique duplique chaque registre architectural en plusieurs exemplaires qui portent le même nom. Chaque ensemble de registres architecturaux forme une fenêtre de registre, qui contient autant de registres qu'il y a de registres architecturaux. Lorsqu'une fonction s’exécute, elle se réserve une fenêtre inutilisée, et peut utiliser les registres de la fenêtre comme bon lui semble : une fonction manipule le registre architectural de la fenêtre réservée, mais pas les registres avec le même nom dans les autres fenêtres.

Il peut s'implémenter soit avec un banc de registres unifié, soit avec un banc de registre par fenêtre de registres.

Il est possible d'utiliser des bancs de registres dupliqués pour le fenêtrage de registres. Chaque fenêtre de registre a son propre banc de registres. Le choix entre le banc de registre à utiliser est fait par un registre qui mémorise le numéro de la fenêtre en cours. Ce registre commande un multiplexeur qui permet de choisir le banc de registre adéquat.

Fenêtrage de registres au niveau du banc de registres.

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.

Désambiguïsation des fenêtres de registres.

L'interface de communication avec la mémoire[modifier | modifier le wikicode]

L'interface avec la mémoire est, comme son nom 'l'indique, des circuits qui servent d'intermédiaire entre le bus mémoire et le processeur. Elle est parfois appelée l'unité mémoire, l'unité d'accès mémoire, la load-store unit, et j'en oublie.

Unité de communication avec la mémoire, de type simple port.

Sur certains processeurs, elle gère les mémoires multiport.

Unité de communication avec la mémoire, de type multiport.

Les registres d'interfaçage mémoire[modifier | modifier le wikicode]

L'interface mémoire se résume le plus souvent à des registres d’interfaçage mémoire, intercalés entre le bus mémoire et le chemin de données. Généralement, il y a au moins 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.

Au lieu de lire ou écrire directement sur le bus, le processeur lit ou écrit dans ces registres, alors que l'unité d'accès mémoire s'occupe des échanges entre registres et bus mémoire. Lors d'une écriture, le processeur place l'adresse dans le registre d'interfaçage d'adresse, met la donnée à écrire dans le registre d'interfaçage de donnée, puis laisse l'unité d'accès mémoire faire son travail. Lors d'une lecture, il place l'adresse à lire sur le registre d'interfaçage d'adresse, il attend que la donnée soit lue, puis récupère la donnée dans le registre d'interfaçage de données.

L'avantage est que le processeur n'a pas à maintenir une donnée/adresse sur le bus durant tout un accès mémoire. Par exemple, prenons le cas où la mémoire met 15 cycles processeurs pour faire une lecture ou une écriture. Sans registres d'interfaçage mémoire, le processeur doit maintenir l'adresse durant 15 cycles, et aussi la donnée dans le cas d'une écriture. Avec ces registres, le processeur écrit dans les registres d'interfaçage mémoire au premier cycle, et passe les 14 cycles suivants à faire quelque chose d'autre. Par exemple, il faut faire un calcul en parallèle, envoyer des signaux de commande au banc de registre pour qu'il soit prêt une fois la donnée lue arrivée, etc.

Cet avantage simplifie l'implémentation du mode d'adressage indirect, où l'adresse à lire/écrire est lue depuis un registre, surtout si le banc de registre a peu de ports, voire un seul. La lecture demande de récupérer l'adresse de lecture dans le banc de registre, mais aussi d'écrire la donnée lue dedans. L'écriture demande de lire la donnée à écrire et son adresse dans le banc de registre. Dans les deux cas, cela demande deux accès au banc de registre, d'accéder à deux registres.

Les deux accès peuvent être faits en même temps si le banc de registre est multiport. Par exemple, lors d'une lecture, il faut connecter le bus de donnée sur le port d'écriture (on écrit la donnée lue dans le banc de registres), et connecter le port de lecture sur le bus d'adresse (pour envoyer l'adresse). L'écriture connecte quant à elle le second port de lecture sur le bus de données.

Mais si le banc de registre n'a qu'un seul port, on doit utiliser des registres d'interfaçage, pas le choix. La lecture envoie l'adresse et récupère la donnée à des instants séparés, grâce aux registres d'interfaçage. L'écriture récupère donnée et adresse dans deux cycles consécutifs, mais les mémorise dans les registres d'interfaçage.

L'unité de calcul d'adresse[modifier | modifier le wikicode]

Les registres d'interfaçage sont presque toujours présents, mais le circuit que nous allons voir est complétement facultatif. Il s'agit d'une unité de calcul spécialisée dans les calculs d'adresse, dont nous avons parlé rapidement dans la section sur les ALU. Elle s'appelle l'Address generation unit, ou AGU. Elle est parfois séparée de l'interface mémoire proprement dit, et est alors considérée comme une unité de calcul à part, mais elle est généralement intimement liée à l'interface mémoire.

Elle sert pour certains modes d'adressage, qui demandent de combiner une adresse avec soit un indice, soit un décalage, plus rarement les deux. Les calculs d'adresse demandent de simplement incrémenter/décrémenter une adresse, de lui ajouter un indice (et de décaler les indices dans certains cas), mais guère plus. Pas besoin d'effectuer de multiplications, de divisions, ou d'autre opération plus complexe. Des décalages et des additions/soustractions suffisent. L'AGU est donc beaucoup plus simple qu'une ALU normale et se résume souvent à un vulgaire additionneur-soustracteur, éventuellement couplée à un décaleur pour multiplier les indices.

Unité d'accès mémoire avec unité de calcul dédiée

Les registres d'adresse[modifier | modifier le wikicode]

Il y a quelques chapitres, nous avons vu que certains processeurs ont des registres séparés pour les entiers et les adresses. Typiquement, le processeur incorpore un banc de registre séparé pour les adresses. D'anciens processeurs utilisaient des registres d'index, utilisés pour manipuler des tableaux, séparés des registres entiers. Les indices sont plus petits que les entiers normaux, ce qui fait qu'il vaut mieux utiliser un banc de registre séparé. Dans les deux cas, ces registres sont placés dans l'interface mémoire, juste avant l'unité de calcul d'adresse, seule à manipuler leur contenu.

Unité d'accès mémoire avec registres d'adresse ou d'indice

Sur certains processeurs, il arrive que le program counter soit placé dans le banc de registre pour les adresses et soit mis à jour par l'AGU. L'avantage est une économie de circuit : pas besoin de rajouter un troisième additionneur/incrémenteur. Après tout, le program counter est une adresse, et sa mise à jour est un calcul d'adresse comme un autre.

La gestion de l'alignement et du boutisme[modifier | modifier le wikicode]

L'interface mémoire est aussi celle qui gère les accès mémoire non-alignés, à cheval sur deux mots mémoire (rappelez-vous le chapitre sur l'alignement mémoire). Elle détecte les accès mémoire non-alignés, et réagit en conséquence. Dans le cas où les accès non-alignés sont interdits, elle lève une exception matérielle. Dans le cas où ils sont autorisés, elle les gére automatiquement, à savoir qu'elle charge deux mots mémoire et les combine entre eux pour donner le résultat final. Dans les deux cas, cela demande d'ajouter des circuits de détection des accès non-alignés, et éventuellement des circuits pour le double lecture/écriture.

Les circuits de détection des accès non-alignés sont très simples. Dans le cas où les adresses sont alignées sur une puissance de deux (cas le plus courant), il suffit de vérifier les bits de poids faible de l'adresse à lire. Prenons l'exemple d'un processeur avec des adresses codées sur 64 bits, avec des mots mémoire de 32 bits, alignés sur 32 bits (4 octets). Un mot mémoire contient 4 octets, et les contraintes d'alignement font que les adresses autorisées sont des multiples de 4. En conséquence, les 2 bits de poids faible d'une adresse valide sont censés être à 0. En vérifiant la valeur de ces deux bits, on détecte facilement les accès non-alignés. Une vulgaire porte OU fait l'affaire, que ce soit dans l'exemple ou dans le cas général. Cette porte génère un signal qui indique si l'accès testé est aligné ou non : 1 si l'accès est non-aligné, 0 sinon. Le signal peut être transmis au séquenceur pour générer une exception matérielle, ou utilisé dans l'unité d'accès mémoire pour la double lecture/écriture.

La gestion automatique des accès non-alignés est plus complexe. Dans ce cas, l'unité mémoire charge deux mots mémoire et les combine entre eux pour donner le résultat final. Charger deux mots mémoires consécutifs est assez simple, si le registre d'interfaçage est un compteur. L'accès initial charge le premier mot mémoire, puis l'adresse stockée dans le registre d'interfaçage est incrémentée pour démarrer un second accès. Le circuit pour combiner deux mots mémoire contient des registres, des circuits de décalage, des multiplexeurs.

La memory management unit[modifier | modifier le wikicode]

Dans le chapitre sur l'abstraction mémoire, nous avions parlé de la MMU. Pour rappel, l'abstraction mémoire fait que les adresses de l'espace d'adressage sont des adresses fictives, qui doivent être traduites en adresses mémoires réelles pour être utilisées. Les adresses de l'espace d'adressage portent le nom d'adresses logiques, alors que les adresses de la mémoire RAM sont appelées adresses physiques. Ce niveau d'indirection facilite l'implémentation de certaines fonctionnalités, comme la mémoire virtuelle, la relocation matérielle, la protection mémoire, et quelques autres.

La traduction des adresses logiques en adresses physiques se fait par un circuit spécialisé appelé la Memory Management Unit (MMU), qui est souvent intégré directement dans l'interface mémoire. La MMU est souvent associée à une ou plusieurs mémoires caches, qui visent à accélérer la traduction d'adresses logiques en adresses physiques. En effet, nous verrons plus bas que la traduction d'adresse demande d'accéder à des tableaux, gérés par le système d'exploitation, qui sont en mémoire RAM. Aussi, les processeurs modernes incorporent des mémoires caches appelées des Translation Lookaside Buffers, ou encore TLB. Nous nous pouvons pas parler des TLB pour le moment, car nous n'avons pas encore abordé le chapitre sur les mémoires caches, mais un chapitre entier sera dédié aux TLB d'ici peu.

MMU.

La MMU est aujourd'hui intégrée directement dans le processeur. Mais il a existé des processeurs avec une MMU externe, soudée sur la carte mère.

  • Par exemple, les processeurs Motorola 68000 et 68010 pouvaient être combinés avec une MMU de type Motorola 68451. Elle supportait des versions simplifiées de la segmentation et de la pagination. Au minimum, elle ajoutait un support de la protection mémoire contre certains accès non-autorisés. La gestion de la mémoire virtuelle proprement dit n'était possible que si le processeur utilisé était un Motorola 68010, en raison de la manière dont le 68000 gérait ses accès mémoire. La MMU 68451 gérait un espace d'adressage de 16 mébioctets, découpé en maximum 32 pages/segments. On pouvait dépasser cette limite de 32 segments/pages en combinant plusieurs 68451.
  • Le Motorola 68851 était une MMU qui était prévue pour fonctionner de paire avec le Motorola 68020. Elle gérait la pagination pour un espace d'adressage de 32 bits.

Les processeurs suivants, les 68030, 68040, et 68060, avaient une MMU interne au processeur.

La MMU avec la segmentation[modifier | modifier le wikicode]

L'implémentation de la MMU dépend fortement du processeur. Mais sur les processeurs qui font seulement de la segmentation simple, la MMU se résume à quelques registres et des additionneurs/soustracteurs. Pour rappel, la segmentation découpe la mémoire en segments qui contiennent chacun un programme en cours d'exécution (pour simplifier). Le programme suppose qu'il commence à l'adresse 0, mais son segment est placé en mémoire RAM à une adresse différente à chaque exécution. Il faut donc corriger les adresses logiques du programme pour les transformer en adresses physiques destinées à la RAM. Pour cela, le processeur contient un registre de relocation, qui contient l'adresse du début du segment en RAM. A chaque accès mémoire, le processeur ajoute cette adresse à l'adresse logique. La MMU d'un processeur avec segmentation contient donc un ou plusieurs registres de relocation, et un additionneur. Les registres de relocation disposent de leur propre banc de registre et ne sont pas mélangés avec les autres.

Relocation assistée par matériel

Un exemple est l'Intel 8086, un des tout premier processeur Intel. Le processeur était découpé en deux portions : l'interface mémoire et le reste du processeur. L'interface mémoire est appelée la Bus Interface Unit, et le reste du processeur est appelé l'Execution Unit. L'interface mémoire contenait les registres de segment, au nombre de 4, ainsi qu'un additionneur utilisé pour traduire les adresses logiques en adresses physiques. Elle contenait aussi une file d'attente où étaient préchargées les instructions, dont nous parlerons dans le chapitre suivant, sur l'unité de chargement.

Sur le 8086, la MMU est fusionnée avec les circuits de gestion du program counter. Les registres de segment sont regroupés avec le program counter dans un même banc de registres. Au lieu d'utiliser un additionneur séparé pour le program counter et un autre pour le calcul de l'adresse physique, un seul additionneur est utilisé pour les deux. L'idée était de partager l'additionneur, qui servait à la fois à incrémenter le program counter et pour gérer la segmentation. En somme, il n'y a pas vraiment de MMU dédiée, mais un super-circuit en charge du Fetch et de la mémoire virtuelle, ainsi que du préchargement des instructions. Nous en reparlerons au chapitre suivant.

Architecture du 8086, du 80186 et de ses variantes.

La MMU du 286 était fusionnée avec l'unité de calcul d'adresse. Elle contient les registres de segments, un comparateur pour détecter les accès hors-segment, et plusieurs additionneurs. Il y a un additionneur pour les calculs d'adresse proprement dit, suivi d'un additionneur pour la relocation.

Intel i80286 arch

Le bus interne au processeur[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.

Les micro-architecture à un seul bus, à accumulateur interne[modifier | modifier le wikicode]

Chemin de données à un seul bus, principe général.

Dans le cas le plus simple, le processeur utilise un seul bus interne. Les architectures à accumulateur les plus simples étaient de ce type. Sur ces processeurs, l’exécution d'une instruction dyadique (deux opérandes) prend plusieurs étapes, car on ne peut transmettre deux opérandes en même temps sur un seul bus. Pour résoudre ce problème, on doit utiliser un registre 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. Sur les architectures à accumulateur, ce registre temporaire n'est autre que l'accumulateur lui-même.

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.

De tels processeurs sont souvent des architectures à accumulateur simples, les plus simples n'ayant pas de banc de registre. Une telle architecture est illustrée ci-dessous. Elles se résument alors à une ALU, l'accumulateur, des registres d'interfaçage avec la mémoire, et l'unité de contrôle qu'on détaillera dans le chapitre suivant. Les architectures à accumulateur plus évoluées disposent de registres architecturaux nommés, et donc d'un banc de registres. Le banc de registre n'à qu'un seul port, vu que la présence d'un seul bus fait de toute façon qu'on ne peut connecter qu'un seul port dessus.

Architecture à accumulateur sans banc de registres.

Les micro-architectures à 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. Le cas le plus simple est celui des architectures de type LOAD-STORE. Sur ces architectures, les accès mémoire se font avec une instruction de lecture, une instruction d'écriture, et éventuellement une instruction de copie entre registres.

Il faut aussi ajouter de quoi effectuer une lecture, ce qui demande de relier le bus mémoire sur l'entrée d'écriture du banc de registres. L'écriture demande de faire l'inverse : de connecter la sortie de lecture du banc de registre vers le bus mémoire. Enfin, les opérations arithmétiques demandent de lire deux opérandes depuis deux sorties de lecture, de les envoyer à l'unité de calcul, puis de connecter la sortie de l'ALU (donc le résultat de l'opération) sur l'entrée d'écriture du banc de registres. Avec quelques multiplexeurs, on arrive à s'en sortie. Voici ce que cela donne :

Chemin de données minimal d'une architecture LOAD-STORE (sans MOV inter-registres)

Mais on peut faire légèrement mieux sur un point : les instructions de copie entre registres. Elles existent sur la plupart des architectures LOAD-STORE, mais cela ne signifie pas que l'on doit modifier le chemin de données pour cela. Il est possible de faire passer les copies de données entre registres par l'ALU. Lors de ces copies, l'ALU ne fait rien et recopie simplement une de ses opérandes sur sa sortie. Ou alors elle effectue une instruction logique qui a pour résultat sa première opérande, comme un OU entre un registre et lui-même. Un autre solution modifie le chemin de données pour implémenter les copies entre registres. Pour cela, on doit pouvoir de relier les deux registres directement, ce qui demande de boucler l'entrée du banc de registres sur son entrée.

Chemin de données d'une architecture LOAD-STORE

N'oublions pas que l'ALU consomme deux opérandes par opération, du moins pour la plupart des opérations logiques et arithmétiques. Si on omet la voie pour les transferts entre registres, et qu'on considère qu'ils passent par l'ALU, cela donne ceci :

Bus interne au processeur sur archi LOAD STORE avec banc de registres multiport

Nous n'avons pas représenté les connexions avec le séquenceur, mais elles existent. Notamment, le séquenceur doit configurer le banc de registres, et l'unité de calcul. Pour les connexions avec le banc de registre, cela inclus le fait d'envoyer les noms de registres adéquats au banc de registre. Cela permet de gérer l'adressage inhérent, où les opérandes sont précisées par des noms de registre.

Le schéma précédent permet d'implémenter la plupart des modes d'adressage présents sur les architectures LOAD-STORE, mais pas tous. La raison principale est que ce bus ne contient aucune connexion avec le bus d'adresse. Impossible donc de préciser l'adresse à lire ou écrire, impossible de placer une adresse sur le bus d'adresse. Il faut donc rajouter des connexions avec le bus d'adresse, ce qui permet d'implémenter pas mal de modes d'adressage utiles. Ensuite, les modes d'adressage immédiat et directs ne sont pas supportés. Nous rappellerons ce que font ces modes d'adressage en temps voulu, mais nous pouvons expliquer pourquoi ils ne sont pas implémentés avec l'organisation précédente. Les modes d'adressage immédiat et directs incorporent une adresse ou une constante dans l'instruction elle-même. Il faut donc les extraire de l'instruction, pour placer le tout sur le bus interne du processeur. Cela demande que le séquenceur fasse l'extraction.

Les modes d'adressage qui impliquent des adresses[modifier | modifier le wikicode]

Divers modes d'adressages demandent de placer une adresse sur le bus d'adresse. Les implémenter demande donc d'ajouter le bus d'adresse dans le schéma précédent, et de le connecter aux bons composants. Pour simplifier les schémas, nous allons omettre le cas où les copies entre registres passent par l'ALU, afin d'enlever une voie de transfert possible. Mais nous allons supposer que cette voie existe, et qu'elle est implémentée en ajoutant quelques multiplexeurs et démultiplexeurs.

Le premier mode d'adressage de ce type est le mode d'adressage indirect à registre. Pour rappel, l'adressage indirect à registre correspond au cas où un registre contient une adresse à lire/écrire. L'implémenter demande donc de connecter la sortie des registres au bus d'adresse. Notons que le bus de données est utilisables pour effectuer une lecture ou une écriture.

Bus avec adressage indirect

L'adressage direct est celui où une instruction contient une adresse. Pour cela, le séquenceur doit extraire l'adresse à lire/écrire et l'envoyer sur le bus d'adresse.

Chemin de données à trois bus.

Le mode d'adressage base+index est un mode d'adressage où l'adresse à lire/écrire est calculée à partir d'une adresse et d'un indice, tous deux présents dans un registre. Dans ce cas, on doit connecter la sortie de l'éunité de calcul au bus d'adresser.

Bus avec adressage base+index

L'adressage immédiat[modifier | modifier le wikicode]

Passons maintenant au mode d’adressage immédiat, qui permet de préciser une constante dans une instruction directement. il faut donc extraite cette constante de l'instruction et la placer au bon endroit dans le bus interne du processeur. La constante est extraite et fournie par le séquenceur. L'implémentation dépend de si on parle d'une instruction arithmétique ou d'une instruction MOV qui copie une constante dans un registre. Les deux situations ne sont en effet pas identiques, car il faut insérer la constante à un endroit différent dans les deux cas.

Pour les opérations arithmétiques ou une opérande est transmise par adressage immédiat, placer la constante extraite sur l'entrée de l'ALU. 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.

Passons maintenant à l'instruction MOV qui copie une constante dans un registre et/ou une adresse mémoire. Dans ce cas, la constante doit être envoyée sur l'entrée d'écriture du banc de registre.

Implémentation de l'adressage immédiat dans le chemin de données

Les simplifications possibles des chemins de données précédents[modifier | modifier le wikicode]

les chemins de données précédents sont assez complexes. Mais il existe un moyen de fortement les simplifier. Pour cela, il faut juste que l'unité de calcul soit capable d'effectuer une opération NOP, c'est à dire une opération qui ne fait rien et recopie la première opérande sur sa sortie. En faisant cela, le chemin de données est fortement simplifié, car certaines connexions deviennent redondantes. le tout donne le chemin de données illustré ci-dessous.

Par exemple, prenons la voie qui relie les registres au bus d'adresse, pour le mode d'adressage indirect à registre : elle devient redondante avec la voie pour le mode d'adressage base+index. Les deux demandent de connecter le bus d'adresse sur le chemin de données, mais l'une doit le faire avant l'ALU et l'autre après. Mais si l'ALU supporte l'opération NOP, les deux peuvent passer par l'ALU, puis être redirigées vers le bus d'adresse. La seule différence est que l'ALU fera une opération NOP pour le mode d'adressage indirect à registre, et un calcul d'adresse pour le mode d'adressage base + index.

Pareil pour le mode d'adressage immédiat, qui peut être simplifié. Le mode d'adressage immédiat demande d'insérer la constante soit avant l'ALU pour une instruction arithmétique, soit en entrée des registres (après l'ALU et après le bus d'adresse) pour une copie inter-registres. Avec l'organisation ci-dessous, il suffit d'insérer la constante en avant de l'ALU. Si on veut faire une opération dessus, l'ALU sera configurée pour faire une opération. Mais si on veut juste copier un registre dans un autre, alors l'ALU est configurée pour faire un NOP.

Le mode d'adressage direct peut être traité de la même manière. La logique veut l'adresse sorte du séquenceur et soit envoyée au bus d'adresse. Mais, on a connecté la sortie de l'ALU au bus d'adresse pour gérer le mode d'adressage base+index, et on a connecté le séquenceur à l'entrée de l'ALU pour le mode d'adressage immédiat. Sachant cela, on peut faire comme suit : l'adresse est insérée au même endroit que pour le mode d'adressage immédiat, parcours l'unité de calcul inchangée parce que NOP, et termine sur le bus d'adresse. La seule difficulté est de désactiver l'extension de signe pour les adresses.

Au final, le chemin de données devient le suivant avec ces simplifications. En faisant cela, de nombreux modes d'adressage sont supportés.

Chemin de données avec une ALU capable de faire des NOP

La liste des modes d'adressage supportés se détermine assez facilement. Premièrement, étudions le cas où l'ALU ne fait rien, elle effectue un NOP. Dans ce cas, on peut relier le banc de registre à lui-même pour faire un MOV entre registres. On eut aussi relier le banc de registre en lecture au bus d'adresse, ce qui permet de faire de l'adressage indirect. Le séquenceur peut envoyer une adresse à l'ALU, qui passe alors sur le bus d'adresse. Ou le séquencuer peut envoyer une constante, qui traverse l'ALU et termine dans les registres. Deuxièmement, étudions le cas où l'ALU fonctionne. Dans ce cas, si la sortie de l'ALU est connectée aux registres, on gère les opérations arithmétiques entre registres, les opérations arithmétiques avec adressage immédiat. Mais si on connecte la sortie de l'ALU au bus d'adresse, on gère le mode d'adressage base+index (les deux opérandes sont lues dans les registres), mais aussi les deux modes d'adressage base+décalage et indexed absolute (les deux demandent une opérande en mode immédiat, une autre lue depuis les registres). Si l'ALU gère une opération d'incrémentation, on peut me^me gérer le mode d'adressage indirect à registre avec auto-incrément : il suffit de lire l'adresse dans les registres, incrémenter le tout, et envoyer le résultat sur le bus d'adresse.