Aller au contenu

Fonctionnement d'un ordinateur/Les coprocesseurs : FPU et IO

Un livre de Wikilivres.

Les coprocesseurs sont des processeurs secondaires qui complémentent un processeur principal. Ils permettent de déléguer certains calculs à un processeur secondaire, afin de décharger le processeur principal. Un détail important est que le processeur principal et le coprocesseur sont très différents : ils n'ont pas le même jeu d'instruction, n'ont pas les mêmes performances, et bien d'autres différences. Nous en avions déjà vu dans le chapitre sur l'architecture de base, où nous avions analysé d'anciennes consoles de jeu. Mais il est temps de voir ces coprocesseurs en détail

Les différents types de coprocesseurs

[modifier | modifier le wikicode]

Les coprocesseurs peuvent se classer en plusieurs catégories : les coprocesseurs sonores, arithmétiques, et d'entrées-sorties.

Les coprocesseurs sonores sont une sorte d'ancêtre des cartes son, utilisés sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles antérieures. Ils s'occupaient respectivement de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un coprocesseur sonore. Le processeur principal était un Motorola 68000, alors que le coprocesseur sonore était un processeur Z80.

Les coprocesseur d'IO sont dédiés à l'accès aux entrées-sorties. Pour simplifier, ce sont des contrôleurs DMA programmables, capables d'effectuer quelques opérations de branchements et de calcul. Nous en avions parlé dans le chapitre sur les entrée-sorties, je ne reviendrais pas dessus ici.

Co-processeur pour l'accès aux entrées-sorties.

Les coprocesseurs arithmétiques sont dédiés aux calculs en virgule flottante. Ils étaient utilisés à une époque où les CPU ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants.

Les coprocesseurs faiblement et fortement couplés

[modifier | modifier le wikicode]

À ce stade du cours, nous allons distinguer les coprocesseurs faiblement couplés et fortement couplés. La distinction est que les premiers sont assez détachés du processeur, alors que les seconds ne le sont pas. La distinction n'est pas très claire, aussi j'ai décidé de prendre cette définition : les premiers sont traités comme des entrées-sorties, alors que les seconds sont intégrés sur le bus mémoire.

Avec les coprocesseurs fortement couplés, le CPU et le coprocesseur sont connectés sur le même bus mémoire. Le programme exécuté contient des instructions à destination du CPU, d'autres à destination du coprocesseur, dont l'encodage est différent. Les deux surveillent le bus mémoire et décident à qui est destinée l'instruction. Les instructions sont exécutées soit par le CPU, soit par le coprocesseur. En clair, le CPU et le coprocesseur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle.

Pour cela, le CPU envoie son program counter sur le bus d'adresse, ce qui entraine l'apparition d'une instruction sur le bus de données. Là, le processeur et le coprocesseur reçoivent l'instruction et la décodent. Si l'instruction est destinée au processeur, le CPU l'exécutera, alors que le coprocesseur la traitera comme un NOP. Et inversement si l'instruction est destinée au coprocesseur.

Les coprocesseurs faiblement couplés sont des entrées-sorties mappées en mémoire, avec des registres d'interfaçage. Le processeur écrit une instruction et ses opérandes dans ces registres d'interfaçage, et le coprocesseur fait les calculs dans son coin. Le processeur récupère le résultat quelques cycles plus tard, en le lisant dans un autre registre d’interfaçage. La récupération du résultat peut se faire avec du pooling ou des interruptions inter-processeurs. Le coprocesseur peut envoyer une interruption au processeur principal pour dire qu'il a terminé son travail.

Parfois, les interruptions peuvent aller dans l'autre sens. Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un IO arbiter chip, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire un numéro d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80.

Les coprocesseurs sonores sont tous dans ce cas. Le coprocesseur sonore exécute un programme pour gérer le son, qui est séparé au programme principal. Le programme principal communique avec le coprocesseur, mais c'est assez rare. Dans un jeu vidéo, cela arrive seulement quand il faut changer de musique ou déclencher un effet sonore. Et ce n'est possible que si ces coprocesseurs sont faiblement couplés. Mais quelques coprocesseurs arithmétiques sont dans ce cas aussi, comme on le verra plus bas.

Architecture de la Megadrive et de la Néogeo

Il existe cependant des cas assez difficiles à classer. Nous verrons le cas des Motorola 68881 dans ce qui suit, qui sont un mélange des deux solutions.

Les coprocesseurs arithmétiques : quelques généralités

[modifier | modifier le wikicode]

Dans le reste de ce chapitre, nous allons surtout voir les coprocesseurs arithmétiques. Et ce pour une raison très simple : nous avons déjà vu les coprocesseurs I/O et les coprocesseurs sonores dans un chapitre antérieur. Pour rappel, les coprocesseurs sonores sont des cartes sons, ou du moins une partie de carte son. De plus, on a plus de documentation sur les coprocesseurs arithmétiques. Il faut dire que c'étaient des processeurs commerciaux, vendus autrefois en magasin, avec de la documentation destinée aux utilisateurs.

Les coprocesseurs arithmétiques étaient spécialisés dans les calculs flottants Ils étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans coprocesseur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Une autre solution était d'émuler les calculs flottants en logiciel.

Le problème ne se pose pas sur les consoles de jeu, mais il est assez rare que les consoles de jeu incorporent des coprocesseurs arithmétiques. Il existe cependant des exceptions. Un exemple récent est de la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, deux processeurs RISC qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques, et était traité comme une entrée-sortie comme une autre.

Les coprocesseurs pour les CPU x86

[modifier | modifier le wikicode]

Dans le chapitre précédent, nous avons parlé du jeu d'instruction x87, qui ajoutait le support des nombres flottants aux PC x86. Formellement, le x87 est apparu sur le coprocesseur 8087, un coprocesseur Intel prévu pour être utilisé avec un 8086. Mais ce n'a pas été le premier coprocesseur flottant pour PC. Il a existé quelques coprocesseurs avant lui, qui ne respectaient pas le standard x87. Et ne parlons pas des coprocesseurs Motorola et autres, qui suivaient un autre standard et ne fonctionnaient pas avec un CPU x86. Dans cette section, nous allons voir les coprocesseurs conçus pour les PC, qui fonctionnaient en tandem avec un CPU x86.

Les premiers coprocesseurs de ce type étaient l'Intel 8231/8232, destinés à être utilisés avec le 8088, un processeur 8 bits. Par la suite, Intel a récidivé avec le 8087, qui était destiné pour servir en tandem avec le 8086. Il a été suivi par les Intel 187, le 287, le 387, le 487 et le 587, qui étaient censés servir avec les CPU 186, 286, 386, 486, etc. Mais d'autres compagnies ont crée des coprocesseurs x87, comme Weitek, Cyrix, AMD, Texas Instrument, et bien d'autres.

Les précurseurs : l'Intel 8231 et le 8232

[modifier | modifier le wikicode]

L'Am9511 et Am9512 sont une des toutes premières FPU pour PC, si ce n'est les premières si on en croit AMD. Ils ont été licenciés par Intel sous le nom d'Intel 8231 et le 8232. Ils étaient conçus pour complémenter le CPU Intel 8080, mais on pouvait parfaitement les utiliser avec d'autres CPU, comme le Z80. La documentation AMD donnait même des exemples assez variés. La raison est qu'on y accédait comme n'importe quelle entrée-sortie connectée au bus système. Ils étaient accessibles via pooling, interruptions ou même via DMA. Nous expliquerons comment c'est possible plus bas.

L'Intel 8231 ne supportait pas le jeu d'extension x87, qui est apparu après. Il gérait des nombres flottants de 32 bits, mais aussi des nombres en virgule fixe de 16 et 32 bits. Les flottants 32 bits suivaient globalement la norme IEEE 754, mais les nombres en virgule fixe utilisaient un format propriétaire. Il gérait les quatre opérations de base, mais aussi des calculs trigonométriques. Il utilisait pour cela du microcode, avec une approximation basée sur des polynômes de Tchebychev. L'intel 8232 supportait lui des flottants 32 et 64 bits, mais ne supportait que les quatre opérations de base (addition, soustraction, multiplication et division).

La documentation décrit ces flottants 32 bits comme étant de la double précision, mais c'est parce que la terminologie de l'époque n'était pas encore bien stabilisée.
Intel 8231 FPU PIN CONFIGURATION.

Les broches de l'intel 8231 sont illustrées ci-contre. La plupart des broches nous sont familières : 8 broches pour le bus de données (qui fait 8 bits), une entrée d'horloge, une entrée de RESET, une entrée chip select pour le décodage d'adresse. Les broches restantes sont très intéressantes, mais on les verra dans ce qui suit.

Toujours est-il que le coprocesseur est relié à un bus de 8 bits, alors que ses registres font 32 à 64 bits. Pour cela, le 8231/8232 lisait les opérandes octet par octet depuis le bus de données. Idem mais pour les écritures. Par contre, les instructions sont prévues pour faire 8 bits, pas plus. Pour avoir des instructions aussi courtes, la seule solution est d'utiliser une machine à pile et c'est ce que le 8231/8232 a fait. Précisons cependant que ce n'est pas la même pile de registre que la pile x87, mais c'était une sorte de pile similaire, qui a évolué pour donner la pile x87. Il s'agissait pour le coup d'une vraie pile, les opérations utilisaient systématiquement le sommet de la pile et l'opérande en dessous. Il n'y avait pas de possibilité d'adresser un opérande dans la pile.

Le processeur intégrait 8 registres de 16 bits, organisés comme une pile. Les registres pouvaient être utilisés : soit comme une pile de 8 opérandes 16 bits, soit une pile de 4 opérandes 32 bits, soit une pile de 2 opérandes 64 bits. Les opérandes étaient empilées octet par octet dans le processeur. Ils étaient dépilés là aussi octet par octet. Pour cela, le 8231/8232 dispose de trois entrées nommées A0, RD et WR. Les trois bits décident s'il faut faire une lecture, une écriture, exécuter une instruction, ou lire le registre d'état. Les quatre opérations sont appelées des commandes dans la documentation Intel et AMD. Les trois entrées font donc office de bus de commande simplifié.

A0, RD, WR Action
000 Lecture de l'octet depuis les registres de données.
010 Écriture de l'octet dans les registres de données.
111 L'octet est l'opcode d'une instruction, qui est exécutée immédiatement.
101 Lecture du registre d'état.

Le processeur principal envoyait des commandes à l'Intel 8231/8232, qui les exécutait dans son coin. Le 8231/8232 envoyait un signal END OF EXECUTION pour prévenir qu'il avait fini son travail, que la commande précédente était terminée. Il avait une broche dédiée, appelée END, dédiée à ça. Le coprocesseur avait donc une interface de communication asynchrone, qui se voit quand on étudie ses broches. Les broches suivantes servent à la communication asynchrone avec le 8231/8232.

  • READY est à 1 quand le 8231/8232 est libre, capable d'accepter une nouvelle instruction/commande. Il passe à 0 quand une instruction démarre, avec la commande 111 vue plus haut.
  • END indique que la commande précédente a terminé son exécution. Lorsque END passe à 1, BUSY passe automatiquement à 0.
  • EACK est une entrée sur laquelle le processeur dit qu'il a bien reçu le signal END, et que ce dernier peut être remis à 0.

Ce système pouvait être utilisé avec du pooling, avec des interruptions, voire du DMA. Avec des interruptions, la sortie END était utilisée comme sortie d'interruption, reliée au CPU ou au contrôleur d'interruption. Pour le pooling, le registre d'état du 8231/8232 contenait un bit BUSY, qui indiquait si le coprocesseur était utilisé ou non.

Un tel fonctionnement peut sembler étrange, et vous aurez l'impression que communiquer avec le coprocesseur est très lent. Mais cela prend tout son sens quand on connait le temps mis pour exécuter une instruction sur le coprocesseur. Une opération simple sur des flottants 32 bits prenait facilement une cinquantaine de cycles d'horloge, et c'était parmi les meilleurs temps de calcul. Il n'était pas rare d'avoir des opérations prenant plusieurs centaines, voire milliers de cycles d'horloge. Pas loin de 5000 cycles d'horloge pour une division de deux flottants 64 bits sur le 8232, plusieurs dizaines de milliers de cycles pour certaines opérations trigonométriques. Et le pire, c'était que c'était plus rapide que l'émulation logicielle !

Pas étonnant donc que le 8231/8232 aient été traités comme des entrées-sorties, à une époque ou tout était connecté sur un bus système assez rapide. Un autre avantage est que le 8231/8232 pouvaient fonctionner à une fréquence sans rapport avec celle du processeur. Par exemple, on pouvait utiliser un processeur à 1 MHz alors que le 8231/8232 allait à 4 MHz. Le coprocesseur faisait juste des calculs rapidement, comparé au CPU. Et ça a été utilisé sur certains systèmes Apple II. Ou encore, on pouvait utiliser un processeur légèrement plus rapide que le coprocesseur, avec quelques MHz de différence, comme un CPU à 5 MHz avec un coprocesseur de 2 MHz.

L'intel 8087 et ses successeurs

[modifier | modifier le wikicode]
Intel 8087

Le 8087 été fabriqué avec 65 000 transistors. Le 8087 avait pour particularité qu'il était connecté directement sur le bus mémoire, au même titre que le 8086. Mais le 8087 n'avait pas de bus d'adresse et de données séparé. Le processeur utilisait un bus multiplexé. Il avait 20 broches pour se connecter au bus : 16 d'entre elles servaient alternativement de bus d'adresse et de données. L'interface avec le bus était donc un peu compliquée.

L'intel 387 était le coprocesseur associé au 386 d'Intel. Il était le premier coprocesseur à s'intégrer sur un bus de 32 bits. Il a été décliné en plusieurs versions, dont certaines sont spécifiques à un modèle de 386. Par exemple, le i386SX était une version simplifiée du 386 initial, qui avait notamment un bus de seulement 16 bits. Et de ce fait, il avait son propre coprocesseur i387SX, qui était adapté à un bus de 16 bits. De même, le i386SL était adapté aux ordinateurs portables et avait son propre coprocesseur i387SL.

Tout ce qui va suivre est valide pour tous les coprocesseurs x87 de marque Intel.

Le processeur central lisait des instructions, en envoyant le program counter sur le bus d'adresse, les instructions étaient récupérées sur le bus de données. Là, les deux processeurs déterminaient si l'instruction chargée était destinée au coprocesseur ou au CPU. Pour cela, les instructions x87 commencent toutes par la suite de bit 11011, qui permet de savoir facilement si une instruction est destinée au coprocesseur. Le 11011 était suivi par un opcode et un mode d'adressage.

Si le mode d'adressage demandait de lire un opérande mémoire, le 8086 envoyait l'adresse de l'opérande sur le bus, et le coprocesseur récupérait celle-ci sur le bus de données. Si l'opérande devait être lu en plusieurs fois, le coprocesseur lisait le reste de lui-même, en prenant le contrôle du bus d'adresse. Il récupérait l'adresse envoyée initialement par le CPU, puis l'incrémentait et relançait un nouvel accès mémoire. Il l'incrémentait autant de fois que nécessaire pour charger l'opérande.

Un problème est que le CPU ne sait pas combien de temps dure une instruction x87. Et cette durée dépendait de l'implémentation du processeur, elle n'était pas la même selon la marque du coprocesseur. Un 186 n'avait pas les mêmes timings que le 286, par exemple. Pour le CPU, une instruction x87 met juste deux cycles pour s'exécuter (plus si des opérandes doivent être lus en mémoire). Pour cela, le CPU disposait d'un mécanisme de synchronisation.

Le mécanisme de synchronisation était une instruction WAIT, qui forçait le CPU à attendre que le coprocesseur ait terminé l'instruction précédente. L'implémentation matérielle était assez simple. Le coprocesseur disposait d'une sortie BUSY, qui indiquait qu'il était en train d'exécuter une instruction et ne pouvait pas en accepter une nouvelle. Le CPU, quant à lui, avait une entrée TEST qui vérifiait si le, coprocesseur était occupé ou non. La sortie BUSY était reliée à l'entrée TEST. L'instruction test vérifiait juste ce qu'il y avait sur l'entrée TEST. Tant qu'elle était à 1, le processeur attendait et ne chargeait pas de nouvelle instruction. Dès qu'elle passe à 0, l'exécution reprend.

Il faut noter que l'instruction WAIT n'est nécessaire qu'entre deux instructions flottantes assez proches. Mais il est possible d'intercaler des instructions entières entre deux instructions flottantes. Le programme pouvait ainsi mixer instructions entières et flottantes, les instructions entières étant exécutées sur le 8086, les instructions flottantes sur le coprocesseur. Il y avait donc une possibilité de parallélisme, à savoir que les deux processeurs pouvaient exécuter des instructions différentes en même temps. Mais cela demandait que les calculs soient coopératifs et mélangent bien entiers et flottants.

Le 8087 et ses successeurs avaient une microarchitecture assez simple. L'unité de contrôle contenait un décodeur d'instruction microcodé, le registre de contrôle, le registre d'état. La plupart des instructions sont microcodées, l'unité de calcul est assez limitée. Elle permet d'additionner deux mantisses flottantes, de faire des décalages, d'additionner deux exposants, mais pas plus. Les multiplications et divisions sont donc microcodées et émulées en enchainant des additions flottantes. Les instructions trigonométriques sont implémentées en utilisant l'algorithme CORDIC, qu'on a vu dans le chapitre sur les circuits de calcul flottant.

Microarchitecture de l'Intel 8087.

Le chemin de données est composé d'un banc de registre flottant pour la pseudo-pile, et de plusieurs circuits de calcul. Le banc de registre était mono-port, ce qui fait que les ALUs étaient précédés par deux registres temporaires pour les opérandes. Les circuits pour l'exposant et la mantisse sont séparés, et sont même reliés au banc de registre par deux bus séparés. Il y a un additionneur pour les exposants, un additionneur pour les mantisses et un décaleur pour les mantisses (pour les normaliser).

De plus, on trouve une mémoire ROM dédiée aux constantes les plus utilisées. Elle sert pour les constantes de base, gérées par le jeu d'extension x87. Mais elle contient aussi des constantes utilisées pour l'algorithme CORDIC. Elle n'est pas illustrée sur le schéma ci-dessous, mais elle existe.

L'interface avec le bus est un simple registre d’interfaçage avec le bus. Pour rappel, le bus de données fait 16 bits sur le 8087, 32 bits sur le 387. Entre le bus et le chemin de données, on trouve une file servant à simplifier la gestion des lectures. L'idée est que les opérandes lus/écrits font 32, 64 ou 80 bits, alors que le bus de données n'en fait que 16/32. Les opérandes sont donc lus/écrits en plusieurs passes. Sur le 8087, il doit réaliser deux passes pour des opérandes de 32 bits, quatre passes pour celles de 64 bits, 5 pour des opérandes de 80 bits (80 = 5 × 16). Le 387 doit faire deux fois moins.

Microarchitecture de l'Intel 387. Les circuits pour les exposants sont à gauche dans le chemin de données, les circuits pour les mantisses sont à droite.

L'implémentation du banc de registre est assez simple : une RAM avec un registre qui indique la position du sommet de la pile dedans. Le registre fait 3 bits, pour 8 registres. En plus de cela, il y a un petit soustracteur et un multiplexeur, pour adresser les opérandes dans la pile. Pour rappel, il est possible d'adresser la seconde opérande dans la pile. Mais on précise pas le numéro du registre dans la pile pour cela, on précise sa position sous le sommet de la pile, à savoir si elle est deux, trois, quatre opérandes sous le sommet de la pile. Pour déterminer quel registre lire, il faut soustraire ce "décalage" au numéro de registre du sommet de la pile. Pour cela, il y a un petit soustracteur pour faire le calcul.

Le circuit décaleur est composé de deux sous-décaleurs. Le premier fait des décalages au niveau des octets, le second décale l'opérande de 0 à 7 rangs.

Pour finir, voici quelques liens sur la microarchitecture du 8087 :

Les coprocesseurs x87 de Weitek

[modifier | modifier le wikicode]

Intel n'a pas été le seul fabricant à commercialiser des coprocesseurs x87. Weitek et de nombreuses autres entreprises s'y sont mises. Weitek a commercialisé plusieurs coprocesseurs : un premier coprocesseur appelé le 1067, le 1167, le 2167, le 3167 et le 4167. Ils sont tous rétrocompatibles entre eux, à savoir que le 4167 ne fait qu'ajouter des fonctionnalités au 3167, qui lui-même ajoute des fonctionnalités au 2167, et ainsi de suite. Ils gèrent tous les quatre opérations de base, ainsi que le calcul de la racine carrée.

Le Weitek 1067 avait pour particularité d'être fourni en pièces détachées, avec trois circuits séparés : un circuit de contrôle, une ALU flottante, et un multiplieur/diviseur. Ces trois pièces détachées étaient censées être soudées sur la carte mère. Le Weitek 1167 regroupait ces trois pièces détachées sur une carte d'extension ISA. Par la suite, le Weitek 2167 regroupa les trois pièces détachées dans un seul circuit imprimé.

Il n'y avait pas de registres flottants adressables, ni de pile de registres. À la place, l'ALU et le multiplieur/diviseur intégraient deux registres pour les opérandes et un registre accumulateur. Les opérandes étaient présentés sur le bus de données et l'ALU les mémorisait dans deux registres internes de 64 bits chacun, nommés A et B. Le coprocesseur lisait les opérandes depuis ces deux registres et mémorisait le résultat dans un registre de résultat interne. Il envoyait alors le résultat sur le bus de données, prévenait le CPU avec une interruption, et le CPU récupérait le résultat sur le bus.

Il faut noter que toutes les communications avec l'ALU passent par le bus de données, appelé le bus X. La transmission d'un flottant 32 bits se faisait en un cycle d'horloge, vu que le bus était de 32 bits. Par contre, la transmission d'un flottant 64 bits se faisait en deux cycles. À part le bus de données, il y avait un bus de commande, relié à l'unité de contrôle 1163. Elle envoyait l'opcode sur une entrée dédiée, notée F. Les bits de commande L, CSL, CSUS, CUSX, U commandent la lecture des opérandes ou leur écriture. L'unité de contrôle recevait le registre d'état via une sortie dédiée nommée S ou STATUS.

Weitek WTL1167.

Pour charger les opérandes dans l'ALU, celle-ci intégrait diverses entrées de commande nommées L0, L1, L2, L3 et CSL.

  • Le signal CSL est le Chip Select Load, ce qui indique qu'il est mis à 1 lors d'une lecture.
  • Le bit L0 à 1 indique qu'un opcode est envoyé sur l'entrée F, l'entrée pour l'opcode est recopiée dans un registre interne, qui n'est pas un registre d'instruction vu que l'ALU n'a pas de décodeur.
  • Les bits L1 et L2 indiquent si ce qu'il y a sur le bus est : un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.
  • Le registre L3 indique quel est le registre de destination : A ou B.

Les signaux U, CSUS et CSUX servaient pour l'envoi du résultat sur le bus. Ils précisaient s'il fallait copier un flottant 32 bits, les 32 bits de poids fort d'un flottant 64 bits, les 32 bits de poids faible d'un flottant 64 bits.

Le Weitek 3167 était conçu pour fonctionner en tandem avec un CPU Intel 386, alors que le Weitek 4167 était prévu pour aller avec un Intel 486, mais ils fonctionnaient de la même manière au-delà de quelques détails. Contrairement à leurs prédécesseurs, ils intégraient 32 registres flottants de 32 bits. Un registre pair et un registre impair pouvaient être concaténés pour mémoriser un opérande 64 bits. Les opérations se faisaient soit entre deux registres flottants, soit entre un registre flottant et l'opérande présentée sur le bus de données. Le résultat est stocké dans un registre flottant. Leur registre de status contenait les résultats des comparaisons flottantes, ainsi que 8 bits pour les exceptions flottantes. Le registre de contrôle avait un champ pour configurer les arrondis, mais aussi un masque d'exceptions de 8 bits disant quels bits d'exceptions ignorer dans le registre de statut.

Le coprocesseur est mappé en mémoire, ce qui fait qu'il a des adresses réservées dans lesquelles le processeur peut lire/écrire, pour lui envoyer une instruction, envoyer un opérande ou récupérer un résultat. Les adresses en question sont les adresses COOO OOOOh à COOO FFFFh, ce qui fait que le coprocesseur est adressé si les 16 bits de poids fort de l'adresse valent COOO. Par contre, les 65536 adresses réservées n'étaient pas associées à de la mémoire, pas même aux registres. A la place, les 16 bits de poids faible de l'adresse encodaient une instruction au coprocesseur, à savoir un opcode de 6 bits deux numéros de registres.

Le processeur communique avec le coprocesseur en envoyant l'instruction sur le bus d'adresse, et éventuellement un opérande sur le bus de données. L'opérande vient généralement des registres, par simplicité, car cela permet de tout envoyer en une seule écriture. Par exemple, une écriture du registre EAX à l'adresse COOO OOOOh va copier le registre EAX sur le bus de données, et envoyer l'opcode de l'addition (0000) sur le bus d'adresse avec deux numéros de registre. Le coprocesseur fait alors une addition entre le registre flottant sélectionné, et l'opérande sur le bus de données copiée depuis EAX.

Le Motorola 68881 et le 68882

[modifier | modifier le wikicode]

Le 68881 de Motorola était conçu pour fonctionner avec les CPU 68020 et 68030. Les programmes mixaient instructions entières et flottantes, le 68000 exécutant les instructions entières, le 68881 exécutant les instructions flottantes. Les instructions flottantes avaient un opcode qui commençait par F (en hexadécimal), ce qui permettait de les distinguer rapidement du reste. Le 68000 chargeait les instructions, et regardait si l'instruction était destinée soit au coprocesseur, soit pour lui. Pour une instruction coprocesseur, il lisait les opérandes en RAM, puis envoyait instruction et opérandes au 68881. Il continuait son travail dans son coin, et récupérait le résultat quelques cycles plus tard.

Malgré le fait qu'il y ait des instructions destinées au coprocesseur, le 68881 n'était pas un coprocesseur fortement couplé. A la place, le 68881 était géré comme une entrée-sortie, du point de vue du CPU. Il était mappé en mémoire, avait une entrée Chip Select commandé par décodage d'adresse. Il contenait aussi des registres d'interface, appelés des coprocessor interface registers (CIRs). Il y avait des registres pour l'opcode de l'instruction à exécuter, un autre pour chaque opérande, etc. Pour envoyer une instruction au 68881, le CPU avait juste à écrire dans les registres adéquats, idem pour charger les opérandes de l'instruction si besoin.

Les coprocesseurs Motorola utilisaient des flottants codés sur 80 bits : la mantisse était codée sur 64 bits, l'exposant sur 15 bits. Le 68881 incorporait 8 registres flottants, nommés, de 80 bits chacun. Fait étonnant, cela ressemble beaucoup à ce qui est fait avec les coprocesseurs x87 : usage de flottants codés sur 80 bits, 8 registres flottants. Mais les détails sont différents, le jeu d'instruction est complétement différent.

Les coprocesseurs Motorola avaient aussi un registre de statut et un registre de contrôle, guère plus. Le registre de statut mémorisait les conditions classiques, mais aussi des bits pour les exceptions qui ont été levées lors d'un calcul. Le registre de contrôle mémorisait de quoi configurer les arrondis, mais aussi un masque pour indiquer quelles exceptions ignorer dans le registre de statut. Pareil que pour les processeurs précédents, donc.