Architecture des processeurs modernes


20 Avril 1999 - mercredi matin, 10:36


J'ai lu beaucoup de choses palpitantes sur le net... tout ces "E-zines" sont pas mal cool, j'en ais tellement lus et appris d'eux. Aujourd'hui j'ai l'intention de revenir au sens propre du mot "E-Zine" (Electronic Magasine) et au sens propre du 'hacking'. Si vous aimer l'info comme moi, il est important de connaitre l'ordinateur et le fonctionnement interne de celui ci, apprenez comment il tourne. Sachez reconnaître les qualités de vos périphériques. Rappeler vous ce bon vieux temps que sûrement personne d'entre nous ait connu, où le 'hacking' impliquait de connaître parfaitement le fonctionnement interne d'une machine, et non de savoir comment manipuler un langage informatique évolué ou tout simplement de savoir comment nuker juste en bougeant une souris à l'aide de programmes.. La semaine passer j'ai eu un cours d'info très intéressant sur les microprocesseurs et l'architecture des processeurs, et, tant qu'à réviser mes notes de cours, j'ai décidé de vous faire pars de ce que j'ai appris...


1.Note pour les newbies


J'avoue que c'est un texte un peu lourd ; par contre, je vous conseil fortement de lire cet article spécialement si vous êtes newbie. Oui, c'est normal que vous aller trouver ennuyeuse la section sur la mémoire virtuelle et pagination mais lisez ce que vous pouvez, vous vous sentirez sûrement moins newbie :) Liser au moins les caractéristiques des Pentium, sachez reconnaître les bebelles cool sur un ordinateur ou un processeur, ce qui les caractérisent.


2.Note


Avant de commencer, un petit mot pour encourager votre lecture...

remember ! -> Knowledge is power, it's so fun to learn, to know.


3.Les caractéristiques fondamentales d'une architecture


Un ordinateur est essentiellement formé des composants suivants :


Les caractéristiques fondamentales d'une architecture seront donc déterminées par :


Le processeur, c'est un peu le cerveau de l'ordinateur.. Il est, dans sa version la plus dépouillée, constitué de registre internes, de l'UAL (Unité Arithmétique et Logique) et de l'UCC (Unité de Commande et de Contrôle).





La fréquence d'horloge du processeur, donnée en nombre de cycles par secondes, détermine la vitesse à laquelle les instructions élémentaires seront exécutées. Toutefois, comme nous le verrons plus loin, la fréquence de l'horloge n'est qu'un des facteurs qui influence la puissance de calcul : elle peut être utile pour comparer des processeurs d'une même famille, sans plus. Par exemple, un Pentium 66 MHz est beaucoup plus rapide qu'un i486 cadencé à la même fréquence.


Le processeur échange l'information avec la mémoire en utilisant des lignes de communication divisées en 3 bus :


La mémoire garde l'information nécessaire au travail du processeur, exemple : les programmes et les données. L'emplacement de chaque octet est identifié à l'aide d'une adresse. Pour adresser un octet dans un espace mémoire de 2^n octets, on a besoin d'une adresse codée sur '^n' bits. Pour échanger une information, le processeur doit transmettre l'adresse de cette information. La voie de communication des adresses est nommée bus d'adresse. Sur les PC à base de 8088, le bus d'adresse était composé de 20 lignes (d'où l'espace d'adressage maximal de 1 Mo = 2^20) alors que le Pentium, le bus d'adresse est composé de 32 lignes : l'espace d'adressage maximal est donc de 4 Go.


Les informations quant à elles, transitent sur le bus de données. Sur les PC 8088, le bus de données avait 8 bits (i. e les informations voyageaient un octet à la fois), alors que sur un PC a base de Pentium, le bus de données est formé de 64 lignes.


Le tableau suivant résume les principales caractéristiques pour un ensemble de processeurs :


/---------------------------------------------------------------------------------------------------------\

|Processeur | Taille des *registres* | Taille du bus de données | Taille du bus | Fréquences de |

| | (bits) | (lignes) | (lignes) | l'horloge (MHz) |

|---------------------------------------------------------------------------------------------------------|

|8088 | 16 | 8 | 20 | 2,77 - 12 |

|80286 | 16 | 16 | 24 | 6 - 16 |

|80386 | 32 | 32 | 32 | 16 - 33 |

|i486 | 32 | 32 | 32 | 25 - 100 |

|Pentium | 32 | 64 | 32 | 60 - 233 |

|Pentium Pro | 32 | 64 | 32 | 150 - 200 |

|Pentium II | 32 | 64 | 32 | 233 - 450 |

|Pentium III | 32 | 64 | 32 | 450 -... ? |

|UltraSPARC II | 64 | 128 | 64 | 250 - 450 |

|Alpha 21164 | 54 | 128 | 64 | 366 - 600 |

\---------------------------------------------------------------------------------------------------------/

*registre* : on ne tient compte ici que des registres de l'unité entière.


4.La famille x86 de processeurs Intel


Le processeur 8088 a été introduit par Intel en 1979 et a équipé au début des années 80 les premiers ordinateurs personnels d'IBM. Bien que le Pentium II avec 7,5 millions de transistors soit des centaines de fois plus puissantes qu'un 8688 (29 000 transistors), il reste néanmoins compatible vers le bas avec ce dernier : c'est donc dire qu'une application écrite pour le 8088 pourrait être exécutée sur un Pentium II. Évidemment, un Pentium II peut faire bien des choses qu'un 8088 ne peut pas faire. Toutefois, cette volonté chez Intel de vouloir demeurer compatible avec une technologie qui date de 20 ans a donné lieu à une architecture qui n'est pas peut-être aussi élégante que si on avait choisit de repartir à neuf avec ces 7,5 millions de transistors.


En plus des fréquences d'horloges de plus en plus rapide et de bus élargis, chaque nouvelle génération de processeur a introduit de nouveaux composants, alors que d'autre ont été améliorés.


Le 286 :


Le 386 : (mon premier pc ! Avec lequel je faisais du BBS et du warez-trading a longueur de journée, ah l'bon vieux temps !)


Le 486 :


Le Pentium :


Le Pentium Pro :







Le Pentium MMX :



Quant aux Pentium II et III, même s'ils se présentent sous un nouveau format, il s'agit essentiellement d'un Pentium Pro auquel on aurait ajouté les instructions MMX (et MMX-2 pour le Pentium III). Intel n'a que rajouté que 70 instructions MMX boboche dans le Pentium III qu'on connaît, qu'on ne se sert même pas, ils ont décidé d'appeler ca "MMX2".. (Dans le bus de compétitionner avec la nouvelle technologie de 3D-now de je me rappelle pus trop qui !)


Du point de vue du programmeur, il y a très peu de différences entre un 80386 et un Pentium II. En effet, si on fait exception des instructions MMX, le mode d'adressage et l'ensemble des instructions des machines n'ont pas changé depuis l'introduction en 1985 du 80386 et son mode protégé. C'est donc à partir de ce mode de fonctionnement que débutera notre étude.


5.Le mode protégé du processeur Pentium :


Les processeurs Intel de la première génération n'avaient qu'un seul mode de fonctionnement qu'on a par la suite appelée le mode réel. Sous un tel mode, le processus en cours d'exécution pouvait exécuter n'importe quelle instruction machine et adresser n'importe quelle partie de la mémoire. Une telle architecture n'est pas adéquate dans un environnement multitâche : d'une part, il ne devrait pas être permis à un processus d'accéder à des zones mémoires réservées pour un autre processus ; d'autre part, tous les programmes devraient s'exécuter sous le contrôle du système d'exploitation qui doit donc être le seul à pouvoir exécuter certaines opérations, comme par exemple, effectuer un changement de contexte (c'est-à-dire basculer d'un processus à un autre).


Donc si on veut mettre en place un système multitâche, on aura besoin d'un mode de fonctionnement où certaines instructions privilégiées ne seront accessibles que pour le noyau du système d'exploitation et d'un mode d'adressage qui interdit à un processus X d'accéder à des zones mémoires qui ne lui ont pas été allouées explicitement. C'est justement ce que permet le mode protégé du Pentium (en fait, du 80386).


Sous ce mode, 4 niveaux de privilèges sont définis : le niveau 0 (qu'on appelle aussi le mode superviseur ou mode noyau), qui est le plus privilégié, n'est utilisable que par le noyau du système d'exploitation. À ce niveau, toutes les instructions machine sont permises. Le niveau 3, aussi appelé mode utilisateur, est le plus surveillé puisque aucune instruction serait immédiatement interrompue, le processeur rendant alors le contrôle au système d'exploitation qui habituellement, punira le processus fautif en mettant tout simplement fin à ses jours. Notons au passage que les niveaux de privilèges 1 et 2 ne sont pas utilisé par les principaux systèmes d'exploitation.


6.Mémoire virtuelle et pagination


Avantages:


Une des caractéristiques qui intéresse beaucoup les programmeurs concerne la quantité de mémoire qu'une architecture peut adresser ainsi que la façon de l'adresser. L'idée fondamentale de la mémoire virtuelle, c'est de présenter à chaque processus un espace d'adressage distinct qu'on appelle espace d'adressage virtuel. Le Pentium par exemple, présente à chaque processus un espace d'adressage virtuel de 4 Go. Cet espace d'adressage n'a rien à voir avec la mémoire physique installée sur un ordinateur donné (qu'on appelle aussi l'espace d'adressage physique). Cette technique permet d'une par de fournir au programmeur un modèle de programmation très simple puisque les adresses (virtuelles) vont de 0 jusqu'à un certain maximum et qu'il n'y a aucun danger d'empiéter sur l'espace d'adressage d'un processus voisin (les espaces d'adressage virtuels étant distincts pour chaque processus). D'autre part, il est possible avec cette technique d'exécuter un programme même s'il n'est pas chargé au complet en mémoire. On pourra donc exécuter des programmes qui sont plus gros que la quantité de mémoire physique installée.


Voyons le fonctionnement de cette technique à l'aide d'un exemple très simple. Supposons qu'un système possède un espace d'adressage virtuel de 64 ko et qu'il dispose également de 64 ko de mémoire physique. Supposons de plus que deux processus A et B utilisent entièrement leurs espaces d'adressage virtuel respectifs. L'espace d'adressage virtuel est divisé en blocs de tailles égales qu'on appelle des pages. Nous supposerons dans notre exemple que les pages ont une taille de 4 ko ce qui signifie que caque processus verra son espace d'adressage virtuel divisé en 16 pages numérotées de 0 à F. L'espace d'adressage physique sera aussi divisé en blocs de 4 ko que nous appellerons ici des cadres de page. La technique de la

Pagination consiste ainsi qu'à ne charger dans les cadres de page en mémoire physique que les pages qui sont nécessaires à un moment donné de l'exécution d'un processus.


Pour chacun des 2 processus on construit une table de pages qui en fait indique dans quel cadre de page se situe une page donnée. L'espace d'adressage virtuel ayant 64 ko, les adresses selon donc codée sur 16 bits. Chaque adressage virtuel sera décomposé en 2 parties : les 4 bits de poids fort qui représentent le numéro de page à laquelle l'adresse appartient, et les 12 bits de poids faible qui représentent un déplacement dans cette page.


Par exemple, l'adresse 01100000110010000 = 61C8 h est située dans la page no.6 à 276 octets du début de cette page.


Voici par exemple, ce que pourraient contenir les tables de pages des processus A et B.



Processus A Processus B

/-------------------------------------------------------------------------------------------\

|No. page | No. cadre | Prés/abs (en mémoire)| No. page | No. cadre | Prés/abs (en mémoire) |

\-------------------------------------------------------------------------------------------/

|0 | 3 | 1 | 0 | 2 | 1 |

|1 | 4 | 1 | 1 | 6 | 1 |

|2 | B | 1 | 2 | 7 | 1 |

|3 | -- | 0 | 3 | 8 | 1 |

|4 | -- | 0 | 4 | -- | 0 |

|5 | -- | 0 | 5 | -- | 0 |

|6 | 9 | 1 | 6 | -- | 0 |

|7 | -- | 0 | 7 | -- | 0 |

|8 | -- | 0 | 8 | -- | 0 |

|9 | -- | 0 | 9 | -- | 0 |

|A | -- | 0 | A | -- | 0 |

|B | 1 | 1 | B | D | 1 |

|C | 5 | 1 | C | A | 1 |

|D | E | 1 | D | -- | 0 |

|E | F | 1 | E | -- | 0 |

|F | C | 1 | F | -- | 0 |

\-------------------------------------------------------------------------------------------/

Note: les cadres de page n'apparaissent pas 2 fois.


Ce tableau établi une correspondance entre les adresses virtuelles et les adresses physiques. Ainsi pour le processus A, les adresses virtuelles allant de 0000h à 0FFFh seront réellement aux adresses physiques allant de 3000h à 3FFFh. Ce qu'il est important de noter ici, c'est que cette correspondance d'effectue à l'insu des processus A et B : elle est entièrement réalisée par le système d'exploitation avec l'aide d'un des modules du processeur, l'unité de gestion mémoire (ou MMU en anglais pour Memory Managment Unit). De plus, même si les processus A et B utilisent apparemment les mêmes adresses, on voit bien, en fait, qu'il n'en est rien puisque les pages ne sont jamais en correspondance dans les mêmes cadres de page.


Ainsi dans notre exemple, nous pouvons facilement calculer l'adresse physique qui correspond à une adresse virtuelle donnée en remplaçant le numéro de page par le numéro de cadre de page qui apparaît dans la table des pages.


Processus A Processus B

/--------------------------------------------------------------------------\

| Adresse virtuelle Adresse réelle | Adresse virtuelle Adresse réelle |

| 0112h 3112h | 0112h 2112h |

| 2726h B726h | 2726h 7726h |

| C305h 5305h | D305h A305h |

| D72h EC72h | DC72h absente |

\--------------------------------------------------------------------------/


Que se passe-t-il lorsqu'une adresse est située dans une page qui n'est pas en mémoire physique ? Le système déclenche alors une demande de page. L'instruction qui fait référence à cette adresse est alors temporairement suspendue. Le système d'exploitation localise sur le disque la page demandée, la charge dans un cadre de page libre, si aucun cadre de page n'est libre, on doit choisir (selon un certain algorithme) la page qui sera évincée de son cadre ; si cette page a été modifiée, son contenu sera réécrit sur le disque... Finalement, l'exécution de l'instruction ayant causée la demande de page pourra reprendre.


1.Le système de pagination des processeurs Intel


Comme plusieurs processeurs modernes, le Pentium a un espace d'adressage virtuel de 4 Go. Si on divisait cet espace en pages de 4 Ko chacune, on obtiendrait, pour chaque processus, une table des pages de 1 048 576 entrées, ce qui est beaucoup trop. On a plutôt choisit une autre méthode, la pagination à 2 niveaux. Une adresse virtuelle (sur 32 bits) sera divisée en 3 parties : 10 bits pour le répertoire de page, 10 bits pour la table des pages et 12 bits pour le déplacement dans une page. Les pages auront donc toujours 4 Ko, mais plutôt que d'utiliser une table des pages unique, on aura plutôt jusqu'à 1204 entrées d'un répertoire de pages.


Supposons que les 10 bits de poids fort contiennent la valeur i. L'entrée no.i du répertoire de pages fournira l'adresse de la table de page no.i ; ensuite les 10 bits suivants nous indiquerons l'entrée de la table no.i qui doit être consulter ; à cette entrée, on trouvera un nombre de 20 bits qui correspond au numéro de poids fort de l'adresse virtuelle donnée pour ainsi former l'adresse physique qui lui correspond.


Puisque le répertoire de pages et les tables de pages sont conservées en mémoire vive, la mécanique de traduction d'une adresse virtuelle en adresse physique manque d'efficacité puisque cette traduction demande deux accès supplémentaires à la mémoire. C'est pourquoi l'unité de gestion mémoire dispose d'un ensemble de registres internes appelé TLB ( Translation Lookaside Buffer) qui contient les entrées de la table des pages les plus récemment utilisées. En cas de coïncidence, l'adresse virtuelle est immédiatement transformée en adresse physique sans que la table des pages n'ait été consultée. Des simulations réalistes ont démontré qu'avec un TLB de 64 entrées, on obtient un taux de coïncidence d'environ 90%.


Remarque: Les processeurs Intel supportent également un mode d'adressage segmenté. Bien qu'ayant été complètement revu avec le 80386, ce mode d'adressage n'en est pas moins qu'un héritage des premiers PC. Il n'est pas plus utilisé dans les systèmes d'exploitation 32 bits que nous retrouvons de nos jours (Windows, Windows NT, Linux,...) sauf pour la compatibilité avec les applications 16 bits.


7.La mémoire cache

Les processeurs, on le sait, sont très rapides et peuvent effectuer plusieurs millions d'opérations par secondes. Encore faut-il que les instructions à effectuer et les données à traiter lui parvienne. Or les bus de données sont beaucoup plus lents que les processeurs eux-mêmes : par exemple, un Pentium III peut être cadencé à 500 MHz alors que son bus de données "tourne" à 100 MHz. Dans un monde idéal, le bus de données aurait la même fréquence que le processeur, mais un tel bus serait très difficile à construire. Et même si on le pouvait, on devrait alors concevoir un autre type de mémoire vive car celle qu'on utilise aujourd'hui serait trop lente.


Pour profiter au maximum de la rapidité de traitement du processeur, nous y avons incorporé une petite zone de mémoire d'accès très rapide que nous appelons la mémoire cache ou encore le Cache, Sur les processeurs Pentium, le cache L1(mémoire cache du niveau 1) est séparé en 2 zones : 8 ko pour les instructions et 8 ko pour les données. Notons que le cache L1 des Pentium MMX Pentium Pro et Pentium II est de 17 ko pour les instructions de 16 ko également pour le code. L'idée de base, c'est de conserver dans la mémoire cache les instructions et les données les plus fréquemment utilisées : on pourra alors obtenir ces instructions et données en un seul cycle d'horloge. Mais si la mémoire cache est d'une telle efficacité, alors pourquoi ne construit-on pas des processeurs n'utilisant que ce type de mémoire ? Et bien, si on considère qu'il faut 6 transistors pour chaque bit de mémoire cache en plus de circuits de contrôle, 16 ko de mémoire cache représente environ un millions de transistors, sur un total de 3,1 millions pour le Pentium.


Les deux caches du processeur Pentium ont essentiellement la même structure, et peuvent être accédés de façon indépendante et simultanée. Le cache utilise les principes de localité spatiale et de localité temporelle. Le principe de localité spatiale veut que des informations contiguës à une information qui vient d'être accédée soient à leur tour accédées dans un avenir rapproché. Pour cette raison, tous les cycles de lecture sur le Pentium s'effectuent en mode rafale : plutôt que d'effectuer la lecture d'une seule quantité de 64 bits qui demande toujours au moins 2 cycles (dépôt de l'adresse sur le bus d'adresse, lecture sur le bus de données). Le principe de localité temporelle veut quant à lui que des informations qui viennent d'être accédées le soient encore et encore dans un avenir rapproché.


Chaque cache sera divisé en lignes de 32 octets chacune : on notera que ces 32 octets correspondent à une rafale (4 x 8 octets). Chaque ligne contient des informations stockées à des adresses consécutives en mémoire. Un repère d'adresses est également associé à chaque ligne. Lorsque l'adresse de la donnée demandée se trouve dans un des repères (succès), la donnée est alors transférée directement a l'unité d'exécution. Si l'adresse ne peut être trouvée dans les repères (échec), la lecture sera effectuée normalement en mémoire vive (ou plus précisément, d'abord en cache L2). Cette nouvelle lecture sera immédiatement placée dans une des lignes du cache.


Bien entendu, si le cache est déjà plein, une des lignes doit d'abord être évincée : on utilise alors un algorithme LRU (Least Recently Used) pour choisir la ligne qui sera évincée du cache. C'est ici qu'interviennent les bits MESI (Modified, Exclusive, Shared, Invalid) : Si la ligne à évincer n'a pas été modifiée (bit M à 0) alors elle est tout simplement écrasée ; Si au contraire, des modifications lui ont été apportées (bit M à 1), son contenu est alors transféré en mémoire vive. Les bits E, S et I sont utilisés sur les systèmes multiprocesseurs.


8.L'architecture super scalaire


On dit d'un processeur qu'il possède une architecture super scalaire lorsqu'il possède au moins deux unités d'exécution tournant en parallèle. Une telle architecture permet d'augmenter le nombre d'instructions exécutées par cycle d'horloge. Le processeur Pentium possède 2 unités d'exécution, chacune capable d'exécuter des opérations entières indépendantes. Chaque unité d'exécution dispose de son propre UAL (Unité arithmétique et logique) et de ses propres circuits de génération d'adresses et de communication avec le cache de données. Donc en théorie, il est possible d'exécuter 2 fois plus d'instructions que si on ne disposait que d'une seule unité d'exécution : en pratique toutefois, les séquences de 2 instructions indépendantes l'une de l'autre ne sont pas si fréquentes.


Une autre des caractéristiques des processeurs modernes est que chaque unité d'exécution fonctionne en pipeline. Un pipeline est divisé en étages ; chaque étage correspond à une phase dans l'exécution d'une instruction. Par exemple, 2 pipelines entiers du processeur Pentium sont divisés en 5 étages ou unités :



Cache instruction

/ \

/ \

/ \

/-----------\ /-----------\

| PF | (Pré Extraction) | PF |

|-----------| |-----------|

| D1 | (Décodage 1) | D1 |

|-----------| |-----------|

| D2 | (Décodage 2) | D2 |

|-----------| |-----------|

| EX Pipe U | (UAL) | EX Pipe U |

|-----------| |-----------|

| WB |(Écriture différée) | WB |

\-----------/ \-----------/


Pipeline U Pipeline V



Bien qu'une unité d'exécution en pipeline ne réduise pas la durée d'exécution d'une opération, elle permet d'en exécuter plusieurs "simultanément" à condition que chacune de ces opérations soit à des étages différents. En effet, on peut comparer le fonctionnement d'un pipeline à celui d'une chaîne de montage : Par exemple, dès qu'une instruction passe de D1 à D2 dans un des pipelines, l'instruction suivante peut aussitôt entrer en D1. Pour une instruction assez simple, elle passera un seul cycle d'horloge à un étage donnée. Ainsi lorsque les deux pipelines sont pleins, le processeur traite 2 instructions par cycle.


/---------------------------------------------------------------\

| U V | U V | U V | U V | U V | U V | U V | U V |

|---------------------------------------------------------------|

PF | i1 i2 | i3 i4 | i5 i6 | i7 i8 | | | | |

D1 | | i1 i2 | i3 i4 | i5 i6 | i7 i8 | | | |

D2 | | | i1 i2 | i3 i4 | i5 i6 | i7 i8 | | |

EX | | | | i1 i2 | i3 i4 | i5 i6 | i7 i8 | |

WB | | | | | i1 i2 | i3 i4 | i5 i6 | i7 i8 |

\---------------------------------------------------------------/

Cycle 1 Cycle 2 Cycle 3 Cycle 4 Cycle 5 Cycle 6 Cycle 7 Cycle 8


Le tableau qui précède montre toutefois une situation idéale : les instructions chargées simultanément dans les pipelines U et V sont supposées indépendantes. Or, il se pourrait bien que l'instruction i2 par exemple, dépende du résultat de l'instruction i1. C'est à l'étape de décidage D1 que cette dépendance sera détectée ; le pipeline V est alors ralenti jusqu'au prochain cycle. Si on veut profiter pleinement des 2 pipelines du Pentium, on aura intérêt à utiliser un compilateur qui génère du code optimisé pour le processeur Pentium. Ce compilateur pourrait par exemple, créer des séquences d'instructions qui peuvent être appariées pour le double traitement. Or le code exécutable de la très grande majorité des logiciels disponibles n'a pas été optimisée de cette façon.


Le processeur Pentium contient également une unité de calcul en virgule flottante (FPU) qui est aussi organisée en pipeline. Toutefois ce pipeline n'est pas entièrement indépendant des pipelines entiers U et V : en fait le pipeline U est utilisé pour former les 5 premiers étages du FPU. Plusieurs autres processeurs ont des unités de calcul en virgule flottante complètement indépendantes des pipelines entiers. C'est notamment le cas du processeur UltraSPARC II qui possède 2 pipelines FPU : en théorie, on peut donc y lancer 4 instructions par cycle.


Notons finalement qu'à partir du Pentium Pro, les pipelines U et V ont été complètement redessinés et comportent maintenant 12 étages chacun.


9.L'unité d'anticipation des branchements


Comme nous l'avons vu, l'exécution en pipeline permet de sauver beaucoup de temps lorsqu'il s'agit de traiter une séquence d'instructions puisque nous pouvons commencer à décoder immédiatement les instructions qui suivent une instruction dont l'exécution n'est pas terminée. Malheureusement, les programmes ne sont pas que formé d'instructions séquentielles : les branchements sont suffisamment fréquents pour empêcher les pipelines de fonctionner à plein régime. Considérons par exemple, le fragment de programme suivant ainsi que sa traduction dans un langage d'assemblage hypothétique :



/--------------\ /----------------\

| if (i == 0) | | CMP i, 0 |

| k = 1; | | BNE Else |

| else | | Then: MOV k, 1 |

| k = 2; | | BR Next |

\--------------/ | Else: MOV k, 2 |

| Next: |

\----------------/



On remarque dans ce petit exemple apparemment anodin que 2 des 5 instructions sont des branchements. De plus, un de ces branchements (else) est conditionnel et dépend donc du résultat de la comparaison de i avec 0. La plus longue séquence linéaire du code se limite ici à 2 instructions : il est donc difficile avec de telles instructions de "nourrir", de façons soutenues, un pipeline (et encore moins deux). Même les branchements inconditionnels, comme ici BR Next, posent un problème car ce n'est qu'à l'étage de décodage qu'on se rend compte qu'il s'agit d'un saut inconditionnel, alors que l'instruction qui suit ce branchement a déjà été chargé dans l'unité de préextraxtion. Finalement, l'exécution en pipeline serait-elle une mauvaise idée ?


C'est ici qu'intervient l'unité d'anticipation des branchements (BPU - Branche Prediction Unit). Il s'agit d'un mécanisme formé d'un petit cache nommée BTB (Branch Target Buffer) qui contient les cibles des branchements déjà effectués en plus d'un certain nombre de ces données de prévoir où et quand le programme se branchera : Si le BPU prédit qu'il aura branchement, les pipelines seront alors chargés avec les instructions cibles des branchements ; Sinon, on continue le chargement séquentiel des instructions.


Bien entendu, le BPU peut se tromper à l'occasion : les pipelines seront alors purgés et chargés avec, cette fois-ci, les bonnes instructions. Plus le pipeline comporte d'étage, plus la pénalité (en temps) associée à une mauvaise prédiction sera grande. C'est pourquoi l'unité d'anticipation des branchements du Pentium Pro et de ses successeurs a été complètement redessinée : on ose à peine imaginer la perte de temps associée à la purge des 12 étages d'un pipeline... Fort heureusement, le principe de la localité évoque plus haut fait en sorte qu'il est relativement aisé d'anticiper les branchements. Les simulations effectuées chez Intel montrent que le taux de prédictions réussies frise les 95%.


10. Exécution "dans le désordre"


But: l'idée ici est de détecter une dépendance entre les instructions et de sauter par-dessus ces instructions pour exécuter celle qui suive et qui sont indépendante. Cool, eh ? :)


La plupart des processeurs modernes possèdent des architectures super scalaires avec unités d'exécution en pipeline. Comme nous avons déjà remarqué plus haut, on ne peut tirer le plein potentiel d'une telle architecture que lorsque les instructions qui se suivent sont indépendantes l'une de l'autre, ce qui n'est pas toujours le cas. Pour contourner ce problème et ainsi améliorer davantage les performances, plusieurs processeurs permettent l'exécution d'instructions dans le désordre ou exécution OOO (Out Of Order) : Les instructions qui dépendent du résultat d'une instruction en cours seront momentanément escamotées permettant ainsi d'introduire dans les pipelines des instructions indépendantes... Évidemment, à la fin, les résultats devront être les mêmes que si les instructions avaient été exécuter dans l'ordre.


Cela peut paraître simple lorsque ainsi énoncé, mais la technique est très complexe. Un des nombreux trucs utilisés consiste à ne jamais effectuer les opérations sur les vrais registres dans l'ordre que les vrais registres seront affectés. Cette technique se nomme la ré attribution des noms de registres (register renaming). Elle permet également d'éliminer les "fausses dépendances" entre les instructions. Par exemple, le Pentium Pro et le Pentium II possèdent 40 registres internes (r0-R39) qui sont utilisés de cette façon. Quant au processeur Pentium, il ne supporte tout simplement pas l'exécution OOO (pas plus que l'exécution spéculative d'ailleurs).


11. L'exécution spéculative


La méthode décrite précédemment fonctionne assez bien dans un bloc d'instructions sans branchements conditionnels. Toutefois, de tels blocs sont généralement petits et ne contiennent souvent pas suffisamment d'instruction parralélisables. Logiquement, la prochaine étape serait donc de réordonner des instructions qui sont situées dans des blocs différents afin de garder les pipelines aussi occupés que possible. La technique d'exécution spéculative tire son nom du fait qu'on pourrait, par exemple, se retrouver à débuter l'exécution des 2 possibilités d'un Si... Alors... Sinon avant même de connaître le résultat du test : Dès que ce résultat sera connu, on abandonnera les instructions de la branche qui n'aurait pas dû être exécutées. Bien entendu, il est essentiel qu'aucune des instructions exécutées de façon spéculative n'ait de conséquences irrévocables puisqu'on doit pouvoir défaire ce qui a été fait : on utilisera encore ici la technique de ré attributions des noms de registres.


En cas du reste toutefois ennuyeux : qu'arrive-t-il lorsqu'une instruction spéculative génère une exception ? On écrit souvent du code comme :


if (y != 0)

z = y / y;


Justement pour éviter une division par zéro. Si la division est effectuée avant que le résultat du test ne soit connu, le processeur pourrait déclencher une exception justement dans le cas où y vaudrait zéro : le programmeur ne la trouvera pas drôle... Une des solutions consiste à ne pas générer d'erreur immédiatement, mais plutôt à associer un "bit poison" à chaque registre. Ainsi, lorsqu'une instruction spéculative échoue, elle met simplement le bit poison à 1 : Si plus tard, on se rend compte que cette instruction devait être exécutée, une exception sera générée ; si elle ne devait pas l'être (comme dans notre exemple de division par zéro), le résultat sera simplement ignoré et le bit poison sera éventuellement remis à zéro.


3 Types de dépendances :


1- RAW: Read after Write

2- WAR: Write after Read ("fausse dépendance")

3- WAW: Write after Write ("fausse dépendance")



/-------\

| IFU0 |

\-------/

/-------\

| IFU1 |

\-------/

/-------\

| IFU2 |

\-------/

/-------\

| ID0 |

\-------/

/-------\ /-------\

| ID1 | /--> | ALU |---- (FPU, MMX)

\-------/ | \-------/

/-------\ | /-------\

| RAT | |--> | ALU |---- (FPU, MMX)

\-------/ | \-------/

/-------\ | /-------\

| ROB | -----------------|--> | LOAD |

\-------/ | \-------/

| | /-------\

| |--> | STORE |

/------------------\ | \-------/

| Unité d'écriture | | /-------\

| différée | \--> | STORE |

\------------------/ \-------/



Ce pipeline se divise en 3 unités :


1- Pré-extraction et décodage :


Cette section est formée des 7 premiers étages. Les instructions sont d'abord amenées vers IFU0 par groupe de 32 octets (une ligne du cache). Puisque les instructions x86 sont relativement complexes et de longueur variable, l'étage IFU1 se charge de décoder la longueur des instructions ; ensuite celles-ci sont alignées en IFU2. Le décodage effectif commence en ID0. Ici les instructions complexes sont décomposées en micro opérations : le résultat de cette décomposition est une suite d'instructions où toutes les opérandes sont des registres. Il est à noter que ID0 possède 3 décodeurs qui fonctionnent en parallèle. Ces micro opérations sont placées en file à l'étage ID1. C'est aussi ici qu'intervient l'unité d'anticipation des branchements : un historique à 4 bits est utilisé, ce qui donne une prédiction relativement fiable. Pour une cible de branchement qui n'apparaît pas dans le BTB, on utilise un algorithme statique pour la prédiction : les branchements conditionnels vers le haut seront effectués (on suppose qu'il s'agit d'une boucle), alors que ceux vers le bas ne le seront pas.


L'étage suivant, RAT (register allocator) alloue des registres internes pour chaque micro opération. Ces opérations sont ensuite placées dans le ROB (ReOrder Buffer) au rythme de trois par cycle. Il est à noter que jusqu'ici, toutes les instructions avaient été traitées dans l'ordre. La phase d'exécution OOO commence à l'étage suivant.


2- Ordonnancement et exécution :


C'est à ce niveau-ci que les micro opérations seront ordonnancées en tenant compte des dépendances, puis exécutées : L’exécution peut se faire dans le désordre. Cette unité est divisée en 5 ports tel qu'indiqué par la figure précédente. Bien que l'unité de préextraction/décodage puisse fournir 3 instructions par cycle, jusqu'à 5 micro opérations par cycle peuvent être exécutées simultanément : Ce rythme ne peut toutefois pas être soutenu puisqu'il dépasse les capacités de l'unité d'écriture différée. Une fois exécutée, les micro opérations retournent au ROB.


3- L'unité d'écriture différée :


Cette unité a la tâche ingrate de retirer dans le bon ordre les micro instructions qui ont été exécutées. On copie ensuite les résultats dans les vrais registres, mais aussi dans les registres pour les instructions en attente d'une valeur à l'un des ports de l'unité d'exécution. Puisque le processeur supporte l'exécution spéculative, certaines instructions auront été exécutées en vain : c'est aussi à l'unité d'écriture différée de s'assurer que les instructions qui n'auraient pas du être effectuée n'auront aucun effet.


12.2.13. RISC vs CISC


Il y a quelques années, tout un débat faisait rage : "L'architecture RISC est meilleure que l'architecture CISC" entendais-on souvent... ou encore : "L'architecture RISC est trop limitée"... Mais qu'en est-il ?


RISC signifie Reduced Instruction Set Computing : une telle architecture propose un nombre limité d'instructions simples. Par exemple, toutes les opérations arithmétiques se feront de registre à registre. Les processeurs PowerPC, Alpha, MIPS et SPARC sont des exemples d'architecture de type RISC. Par opposition, CISC signifie Complex Instruction Set Computing : les processeurs Intel de la famille x86 ont ce type d'architecture.


On a souvent dit que les techniques d'exécution dynamique (anticipation des branchements, exécution OOO et exécution spéculative) ne pouvaient pas être adaptées aux architectures de type CISC. Pourtant, Intel (et aussi AMD avec ses processeurs K6 et K7) y est parvenu. Il y simplement suffi de décomposer les instructions complexes en micro opérations plus simples (de type RISC) dans les premiers étages des pipelines. C'est d'ailleurs la principale raison pour laquelle les pipelines des Pentium Pro, Pentium II et Pentium III ont tant d'étages.


D'un autre coté, les processeurs construits selon une architecture RISC tout à fait pure sont de plus en plus rare. On emprunte donc allègrement de part et d'autre les meilleures techniques de chacune des 2 architectures. Par exemple, le processeur UltraSPARC II a aussi ses propres instructions qui permettent d'accélérer les traitements multimédias : est-il nécessaire de souligner que ces instructions ne sont pas si simple ? Ainsi, la distinction qu'on faisait autrefois (et qu'on fait encore) entre RISC et CISC tend à s'estomper. Mais les amoureux de polémiques n'ont rien à craindre puisqu'un nouveau débat fait déjà rage : Intel (encore) et sa nouvelle architecture, IA-64, vient tout remettre en question...


13. Les personnalités multiples du Pentium II


Nous terminerons ce chapitre sur une note beaucoup plus légère. Depuis qu'Intel a introduit ses processeurs Pentium II en 1997, une certaine confusion s'est installée chez le consommateur puisqu'on se retrouve avec 6 types de Pentium II. D'abord le Pentium II original qui se présente dans un emballage particulier, la cartouche SEC (Single Edge Contact) : Il s'agit en fait d'un circuit imprimé recouvert d'un coté d'un plastique et de l'autre d'une tablette métallique agissant comme dissipateur de chaleur. Sur ce circuit imprimé, on retrouve le processeur lui-même ainsi que les modules SRAM du cache L2. La cartouche SEC se connecte à la carte mère à l'aide d'un connecteur ayant 242 contacts appelés "Slots 1". La première génération de Pentium II comportait 512 Ko de cache L2 connecté au processeur via un bus spécial, le BSB (Back Side Bus) qui tourne à la moitié de la fréquence du processeur. Le bus de données standard (qu'on a rebaptisé FSP pour Front Side Bus) est quant à lui cadencé à 66 MHz. Cette version du Pentium II était disponible à des fréquences de 266, 230 et 333 MHz. Plus tard, Intel a introduit trois nouveaux Pentium II (350, 400, 450 MHz) dont la particularité est de permettre une communication à 100 MHz sur le bus de données. Une version dite "mobile" du Pentium II a également vu le jour pour équiper les portables.


Entre temps, une version haut de gamme, le Pentium II Xeon, et une version bas de gamme, le Celeron ont été lancée. Le Pentium II Xeon a comme principale caractéristique de faire tourner son BSB à ka même fréquence que le processeur. De plus, il est disponible avec 512 Ko, 1 Mo ou 2 Mo de cache L2. Le Xeon n'utilise toutefois pas le même type de connecteur que le Pentium II standard, mais plutôt un connecteur appelé "Slot 2". De plus, le Xeon peut être installé dans des systèmes multi-processeurs supportant jusqu'à 8 processeurs (on est limité à 2 avec le Pentium II standard).


Le celeron est une version dépouillée du Pentium II : on y a tout simplement enlevé le cache L2 et la cartouche ; On a donc un circuit imprimé sur lequel on retrouve le processeur et quelques composants discrets (régulateurs de voltage,...) Les premières versions du Celeron étaient disponibles à des fréquences de 266 et 300 MHz. À la sortie, le Celeron a été sévèrement critiqué en raison de ses performances plutôt décevantes : il s'agit pourtant bel et bien d'un processeur Pentium II, mais l'absence de cache L2 se fait cruellement sentir. En réponse, Intel a développé une nouvelle version, le Celeron A (ou Celeron Pro ?) sur lequel on retrouve un cache L2 de 128 Ko. Contrairement au Pentium II, le cache L2 de ce nouveau Celeron est intégré au processeur et son bus (le BSB) tourne à la même fréquence que le CPU. Et comme si ce n'était pas assez, Intel se propose d'introduire de nouveau Celeron en format traditionnel PPGA (Plastic Pin Grid Array). Un nouveau connecteur, appelé Socket 370, sera utilisé. Le tableau suivant résume les principales caractéristiques des divers types de Pentium II disponibles :


/--------------------------------------------------------------------------------------------------------\

| | Fréquence du | Fréquence du | Fréquence du | Quantité de | Support multi- |

| | CPU (MHz) | BSB | FSB (MHz) | cache L2 | processeurs |

\--------------------------------------------------------------------------------------------------------/

| Pentium II standard | 233,266,300 | 1/2 | 66 | 512 Ko | Oui(2) |

| Pentium II avec FSB 100 | 350,400,450 | 1/2 | 100 | 512 Ko | Oui(2) |

| Pentium II Xeon | 400, 450 | 1 | 100 | 512 Ko,1 Mo, | Oui(8) |

| | | | | 2 Mo | |

| Pentium II mobile | 233,266,300 | 1/2 | 66 | 512 Ko | Non |

| | 333, 366 | | | | |

| Celeron | 266, 300 | - | 66 | 0 | Non |

| Celeron A | 300,333,366 | 1 | 66 | 128 Ko | Non |

| | 400 | | | | |

\--------------------------------------------------------------------------------------------------------/



14. Les bus :


Définition: dispositif qui permet l'échange d'information entre divers composants, ex : entre le processeur et une carte vidéo..


En général, une machine donnée possèdera plusieurs type de bus. Par exemple, sur un PC moderne, on retrouve les bus suivants :



De plus, le processeur lui-même a des bus internes. Ex: le bus qui relit le cache d'instruction a l'unité de pré extraction ou le bus de cache de donné au unité d'exécution.


Si on prend comme cas typique un Pentium II :

Chacun de ses bus possède des propriétés particulières.


1.Bus mémoire (FSB et BSB)


DIB (Dual Independant Bus)


L'idée de ce dual independant bus est de séparer bus mémoire


L'avantage de cette technique la c'est que les 2 bus peuvent être accédé simultanément (en même temps).

Par exemple : tu peux faire une opération dans le cache L2 en même temps dans faire une en mémoire vive.


Autre avantage : ces 2 bus ne sont pas nécessairement cadencés à la même fréquence (même qu'en générale ils ne le sont pas).


en parlant de mémoire :


RAM= Random Access Memory

SRAM = Static RAM

DRAM = Dynamic RAM (plus lent que du SRAM)


exemple... prenons un Pentium II de 400 MHz ou on a un BSB à 200 MHz et un FSB à 100 MHz (les 2 à 64 bits)


Calculons la bande passante (bandwith):

FSB: 100 000 000 de cycles par secondes et 64 bits transférer à chaque bits DONC 8 octets, on a alors un taux de transfert ou un débit maximum ou une 'bandwith' de 800 Mo/secondes ! (En théorie).


FSB: 200 000 000 * 8 octets par cycles = environ 1,6 Go/secondes ! Énorme.


Un autre exemple :

- Celeron 300 A

BSB: 300 Mhz (rappeler vous, ici on a la même fréquence que le cpu)

FSB: 66 MHz


Bandwith:

FSB: 66 MHz * 8 octets = 528 Mo/secondes

BSB: 300 MHz * 8 octets = 2,4 Go/secondes !


Remarque: bien entendu, la 'bandwith' n'est qu'une donnée théorique ; dans les faits, on ne s'approche même pas de cette valeur (sauf pour de très courtes périodes de temps, quelques microsecondes).


Pourquoi ?





2.2.15.2. Le bus PCI :


Peripheral Component Interconnect (une façon de connecter ensemble des périphériques).


L'idée ici c'est d'abord, le bus PCI est indépendant de l'architecture Intel, ix86. Intel encourage tout le monde à l'utiliser. On le retrouve en effet sur d'autres plates-formes, pas juste sur des PC, sur des iMac, PowerMac, Alpha, certains modèles SUN,...


En 1995, c'est la publication de la norme PCI 2.1 (la dernière norme PCI jusqu'à nos jours, du moins je crois ?)


Caractéristiques:

PCI standard (ordinaire) c'est un bus de 32 bits avec une fréquence de 33 MHz.

sa 'bandwith': 33 000 000 * 4 octets (32 bits) = 133 Mo/sec


La norme PCI 2.1 prévoit aussi une version 64 bits à 33 ou 66 MHz.


Un super PCI:

bus: 64 bits

fréquence: 64 MHz

Bandwith: 528 Mo/sec

-> petite différence, sortez votre cochon rose et un marteau :p


Fini le radotage, passons au fonctionnement !


3.Fonctionnement


Le bus PCI fonctionne selon un modèle "maitre/esclave" avec "arbitrage" des condntions" ; Ce qui veux tout simplement dire que plusieurs périphériques pourraient accéder au PCI en même temps, l'arbitre s'en charge.



15. Conclusion


5 Mai 1999 - mercredi midi, 15:40


J'espère que vous avez apprécié la première de mes publications, si vous avez des questions et/ou commentaires n'hésiter pas à me le faire savoir. Si j'ai fait des erreurs en ce qui concerne contenu S.V.P. veuillez me les dire ! (Contenue... entendons-nous, e-mailer moi pas pour me dire que j'ai fait des fautes d'orthographes : p on se comprend)


16. Remerciements




****************************

* Connected *

* montrealcafe@hotmail.com *

* Undernet: #Montreal-Cafe *

****************************