Cours II2

, par Cayrel

Cours au format pdf ici inspiré par celui de Florent Bernard.


No Title
[II2] Architecture des systèmes à processeurs



Chapitre 1
Présentation du module

1.1  Objectifs

  1. Comprendre quel type d'information est traitée dans un système à processeur;
  2. Comprendre l'architecture d'un système à processeur;
  3. Comprendre les mécanismes d'interruption (part logicielle/part matérielle);
  4. Être capable d'écrire un programme en langage de haut niveau pour une cible à microprocesseur ou à microcontrôleur.

1.2  Pré-requis

  • II1: Algorithmie et syntaxe du langage C;
  • ENSL1: Conception de systèmes logiques.

1.3  Intervenants

Pierre-Louis Cayrel : Cours/TD
Christian Pauze : TP (microcontrôleur C167 Infineon/Siemens)

1.4  Volume horaire

Partie cours/TD: 2 séance hebdomadaires (durée 1h30/séance) pendant 9 semaines.
Lundi 11:00 - 12:30 et Jeudi 9:30 - 11:00

Chapitre 2
Introduction

2.1  (Micro) processeurs

2.1.1  Définition et rôle

Le processeur (CPU: Central Processing Unit ou unité centrale de traitement) est le composant principal d'un ordinateur, ou plus généralement d'un système informatique.
Son rôle est d'interprèter les instructions et de traiter les données d'un programme informatique afin d'effectuer des calculs: opérations arithmétiques et logiques.
Un processeur construit sur un seul circuit intégré est appelé microprocesseur. C'est le processeur qui apporte aux ordinateurs et plus généralement aux systèmes à microprocesseurs leur capacité à être programmés.

2.1.2  Bref historique


    1941: Z3 allemand (machine programmable électromécanique: utilisation de relais), Colossus britanique (Alan Turing, mécanisation de la cryptographie) utilisation de tubes à vide;
    1945: ENIAC (Electronic Numerical Integrator Analyser and Computer) premier ordinateur entièrement électronique. Inconvénient: recâblage physique avant de traiter un nouveau programme ordinateurs à un programme
    John Von Neumann: ordinateur à programme enregistré EDVAC (Electronic Discrete Variable Automatic Computer).
    Caractéristiques de l'EDVAC:
    • Possède un certain nombre d'instructions de base (programme enregistré) pouvant être combinées pour réaliser des programmes plus élaborés (ainsi le changement de programme pouvait être réalisé par simple modification de la mémoire);
    • Capacité mémoire: 1000 mots de 44 bits → capacité de 5,5 kB (en termes actuels);
    • 6000 tubes à vide et 12 000 diodes;
    • Consommation: 56 kW;
    • Surface: 45,5 m2;
    • Poids: 7850 kg;
    • Fonctionnement: 3 équipes de 30 personnes se succédant en continu;
    • Coût: ≈ 500 000 $

    1947: Invention du transistor (John Bardeen, William Shockley et Walter Brattain -Bell Téléphone)
    Énorme progrès scientifique sans lequel l'électronique et l'informatique n'aurait pas leur forme actuelle aujourd'hui.
    Avantages principaux:
    • beaucoup plus petit et léger (comparé au tube électronique): permet la miniaturisation des composants électroniques;
    • robuste, fiable et efficace: comparé aux tubes à vide (fragile, préchauffage), relais mécanique (gestion de l'anti rebonds) ⇒ limitation de la fréquence de commutation;
    • fonctionne avec des tensions faibles: faible consommation et permet le fonctionnement sur pile (fonctionnement autonome).

    années 1950: Intégration de plusieurs transistors sur une même surface de matériau semi-conducteur: naissance du circuit intégré.
    Plusieurs niveaux d'intégration:
    • SSI (Small Scale Integration) petite: inférieur à 12;
    • MSI: (Medium Scale Integration) moyenne: 12 à 99;
    • LSI: (Large Scale Integration) grande: 100 à 9999;
    • VLSI: (Very Large Scale Integration) très grande: 10000 à 99999;
    • ULSI: (Ultra Large Scale Integration) ultra grande: 100000 et plus.
    Rappel: Dès qu'il a été possible d'intégrer suffisament de transistors sur un même circuit intégré afin de réaliser un processeur, le terme microprocesseur a supplanté celui de processeur.
    Avantages: le fait de pouvoir intégrer les transistors le composant sur un même circuit semi-conducteur, permet des temps de commutation plus court (du fait de la dimension réduite et de la réduction du nombre de capacités parasites des portes logiques) augmentant ainsi la fréquence de fonctionnement des microprocesseurs synchrones de quelques dizaines de MHz à plusieurs GHz.
    1971: Apparition du premier microprocesseur: Intel 4004;
    1974: Apparition du premier microprocesseur employé couramment: Intel 8080;
    1975 - Loi de Moore:  
    1. La Loi de Moore a été exprimée en 1965 dans Electronics Magazine par Gordon Moore, ingénieur de Fairchild Semiconductor, un des trois fondateurs d'Intel. Constatant que la complexité des semiconducteurs proposés en entrée de gamme doublait tous les ans à coût constant depuis 1959, date de leur invention, il postulait la poursuite de cette croissance (en 1965, le circuit le plus performant comportait 64 transistors). Cette augmentation exponentielle fut rapidement nommée Loi de Moore ou, compte tenu de l'ajustement ultérieur, Première loi de Moore.
    2. En 1975, Moore réévalua sa prédiction en posant que le nombre de transistors des microprocesseurs (et non plus de simples circuits intégrés moins complexes car formés de composants indépendants) sur une puce de silicium double tous les deux ans. Bien qu'il ne s'agisse pas d'une loi physique mais juste d'une extrapolation empirique, cette prédiction s'est révélée étonnamment exacte. Entre 1971 et 2001, la densité des transistors a doublé chaque 1,96 année. En conséquence, les machines électroniques sont devenues de moins en moins coûteuses et de plus en plus puissantes.
    3. Une version commune, variable et sans lien avec les énoncés réels de Moore est : quelque chose double tous les dix-huit mois, cette chose étant la puissance, la capacité, la vitesse et bien d'autres variantes mais très rarement la densité des transistors sur une puce. Ces pseudo lois de Moore sont celles le plus souvent diffusées, car elles fleurissent dans des publications grand public et sur de nombreux sites Internet. Leur seul point commun est donc ce nombre de dix-huit mois, qu'on ne trouve pourtant dans aucun des deux énoncés de Moore.
    Figure 2.1: Loi de Moore

    2005: Apparition des multi-coeurs (Intel et AMD les deux leaders sur le marché). Plusieurs coeurs sur un même circuit intégré. L'efficacité de tels systèmes dépend de la façon dont sont connectés les coeurs (topologie de connexion).
    Intérêt principal, avoir du vrai parallélisme. Problème de gestion des tâches pour répartir les calculs sur chaque coeur.
    2008: Super calculateur, Records
    • Mips: Million d'instructions par seconde;
    • Flops: Opération à virgule flottante par seconde;
    • Barre du Petaflop (1015 opérations en virgule flottante par seconde) franchie par un supercaclulateur militaire d'IBM (Roadrunner). Puis franchie par le supercalculateur Jaguar du Cray. (En avril 2009 ce sont les deux seuls à avoir franchi cette barre symbolique).

Même si on peut avoir l'impression (réelle) qu'il y a eu de grandes évolutions tant au niveau de la complexité, la taille, la construction et la forme générale des processeurs au cours des soixantes dernières années, il faut noter que la conception et les fonctions de base n'ont pas beaucoup changé. La plupart des architectures actuelles peuvent être décrites comme de machines à programme enregistré de Von Neumann.

2.2  Microcontrôleurs

Un microcontrôleur est un circuit intégré qui rassemble les éléments essentiels d'un ordinateur:
  • (micro)processeur;
  • mémoires (morte, vive);
  • unités périphériques;
  • interfaces d'entrées/sorties.
Les caractéristiques principales d'un microcontrôleur sont:
  • haut degré d'intégration;
  • faible consommation électrique: quelques mW en fonctionnement, quelques nW en veille;
  • vitesse de fonctionnement plus faible (dizaine à centaine de MHz) que les microprocesseurs plus polyvalent qu'on trouve dans nos ordinateurs personnels;
  • coût réduit.
Les microcontrôleurs, ont permis de démocratiser l'usage de l'informatique dans un grand nombre de produits et de procédés. Ils sont de fait fréquemment utilisés dans les systèmes embarqués comme les contrôleurs des moteurs automobiles, les télécommandes, les appareils de bureau, l'électroménager, les jouets, la téléphonie mobile, etc ....

Certains constructeurs se sont spécialisés dans des secteurs d'activités précis, par exemple:
  • Infinéon: secteur automobile;
  • Philips: électroménager;
  • Texas Instruments: basse consommation, portabilité.

Au préalable à l'étude de l'organisation matérielle d'un système à microprocesseur (par exemple un microcontrôleur), il est important de comprendre quel type d'information peut traiter un processeur.

Chapitre 3
Codage de l'information

3.1  Information discrète

Avant de comprendre comment un système à microprocesseur traite l'information (notamment pour faire des calculs), il est nécessaire de comprendre comment est représentée l'information.

Les éléments de commutation dans les processeurs (historiquement: relais électromécaniques, tubes à vide, puis transistors depuis 1947) leurs permettent de traiter des états discrets. C'est à dire un nombre fini d'états.
Le transistor permet la commutation entre deux états, on parlera d'état haut, de niveau logique haut codé par un `1' ou d'état bas, de niveau logique bas codé par un `0'.
Cette information à deux états (0 ou 1) est qualifiée d'information binaire. On parlera de chiffre binaire (Binary digIT en anglais) qui a donné l'appellation courante de bit d'information.

Un processeur ne peut donc traiter que de l'information binaire, c'est à dire des bits ou encore des chiffres 0 ou 1.
Comment lui permettre de réaliser l'opération 27×34?
De représenter la lettre `a'?
De faire des calculs de surface de disque (πR2)?
etc, ...

Plus généralement, un processeur doit pouvoir traiter plusieurs types d'information différents (texte, nombres, vidéo, audio, ...). Cette information, intelligible pour nous, doit être transformée en information intelligible par le processeur afin qu'il puisse la traiter.
Cette étape est appelée codage de l'information et transforme notre information en une suite de 0 ou 1, qui est la seule information intelligible par le processeur.
Outre le fait que l'information binaire représente les états possibles d'un transistor, elle présente l'avantage de rendre particulièrement simple les opérations arithmétiques de base (addition, multiplication binaire).
Exemple 1 Donner les tables d'addition et de multiplication opérant sur des bits.

3.2  Représentation des nombres

En base 10, on écrit par exemple 2011 pour représenter le nombre:
2×1000+0×100+1×10+1×1
ou encore
2×103+0×102+1×101+1×100
Les chiffres utilisés en base 10 sont: {0,1,2,3,4,5,6,7,8,9}

Cette écriture se généralise dans une base quelconque b ≥ 2:
anan−1... a1a0
=
n

i=0 
aibi
=
anbn+an−1bn−1+...+a1b+a0
Les chiffres utilisés en base b sont: {0,1,...,b−1}.
Exemple 2 Donner la liste des chiffres utilisés en base 2, en base 16.
Pouvez-vous répondre à la question suivante: Que représente 101?
Pourquoi? Que faut-il préciser?
Quelle est la base de représentation utilisée dans les processeurs?

3.3  Conversions d'entiers naturels

3.3.1  Passage d'une base quelconque à la base 10

C'est le cas le plus facile (mais le moins utilisé, sauf pour vérifier des calculs en phase de développement).
On écrit le nombre en base b:
anbn+...+a1b+a0
et on effectue les calculs en base 10.
Afin d'éviter toute ambigüité, il est utile de préciser en quelle base sont représentés les nombres: (·)b.
Exemple 3
(12101121)3
=
1×37+2×36+1×35+1×33+1×32+2×31+1×30
=
2187+2×729+243+27+2×3+1
=
(3931)10

3.3.2  Passage de la base 10 à une base quelconque

On procède par divisions successives. On divise le nombre par la base, on obtient un quotient et un reste. Le reste correspond au premier chiffre, le quotient obtenu est redivisé par la base pour obtenir un nouveau quotient et un nouveau reste. On recommence jusqu'à l'obtention d'un quotient nul.
La suite des restes obtenus a0,a1,..., an dans cet ordre, correspond à l'écriture du nombre de départ dans la nouvelle base.
Figure 3.1: base 10 - base 2
Figure 3.2: base 10 - base 16
Exemple 4 Convertir (93)10 en base 3.
Remarque 1 Nous n'abordons pas ici la représentation en général des nombres fractionnaires. Nous étudierons plutôt la représentation des nombres réels dans les processeurs.
Figure 3.3: Changement de bases

3.4  Représentation des nombres dans les processeurs

3.4.1  Limitation de la taille

Pourquoi distingue-t-on la représentation des nombres en général de la représentation des nombres dans les processeurs?
En général, on peut travailler avec des entiers aussi grand que l'on souhaite dans la base que l'on souhaite.
En revanche, dans les processeurs, on ne peut travailler que sur des nombres de taille limitée, c'est à dire sur un nombre de bits prédéfini.
Exemple 5 Si 1 bit est disponible, on peut représenter les états 0 et 1, soit 2 états.
Si 2 bits sont disponibles, on peut représenter les états:
0
0
0
1
1
0
1
1
soit 4 états.
Si 3 bits sont disponibles, 2 bits représentent les 4 états précédents et on peut rajouter un troisième bit à 0 ou un troisième bits à 1 pour donner 2×4=8 états:
0
0
0
0
0
1
0
1
0
0
1
1
1
0
0
1
0
1
1
1
0
1
1
1
Conséquence: à chaque ajout d'un bit, on multiplie par deux le nombre d'états que l'on peut représenter.
Avec n bits, on va donc pouvoir représenter 2n états.

3.4.2  Les entiers logiques

Les entiers logiques sont les entiers naturels, c'est à dire les entiers positifs ou nul. Comme on sait que ces entiers sont toujours positifs, il n'est pas nécessaire de représenter l'information signe. On parle donc d'entiers non signés, pour lesquels tous les bits sont significatifs.
Exemple 6 Quels sont les entiers logiques que l'on peut représenter avec 4 bits? avec 8 bits? avec n bits?

Représentation des entiers logiques

Si n bits sont disponibles, on peut représenter un entiers logique x par:
x=an−12n−1+...+a12+a0
(an−1... a1a0)2 est appelé la représentation binaire naturelle de x.
Exemple 7 On donne un entier logique codé sur 8 bits par (10100110)2.
  1. Quel est la valeur maximale que peut avoir cet entier?
  2. Donner sa valeur décimale.
  3. Changer le bit a7 de 1 à 0, donner la nouvelle valeur décimale.
  4. Changer le bit a0 de 0 à 1, donner la nouvelle valeur décimale.
  5. Que constatez-vous?
Les bits a0, a1, ..., an−1 composant l'entier logique n'ont pas tous la même importance. Il est clair que si a0 change de 0 à 1 (ou l'inverse), l'ordre de grandeur de l'entier ne va pas être modifié. En revanche si an−1 change de 0 à 1 (ou l'inverse), l'ordre de grandeur de l'entier logique va être grandement modifié.
On parle de poids associés aux bits composant l'écriture des entiers logiques. Ces poids sont les puissances de 2 associées à chaque bit.
Ainsi, a7 est de poids 27=128. C'est à dire que si a7 change de valeur, l'entier logique associé change de valeur de ±27=±128, alors que si a0 change de valeur, l'entier logique associé change de valeur de ±20=±1.
Les poids forts sont donc les plus grandes puissances de 2 et les poids faible les plus petites puissance de 2.
Remarque 2 On utilisera souvent le terme MSB (Most Significant Bit), pour parler du bit significatif de poids le plus élevé d'un entier logique et de LSB (Least Significant Bit), pour parler du bit de poids le moins élevé d'un entier logique.
Figure 3.4: Bit de poids fort et bit de poids faible
Remarque 3 La plupart du temps, les entiers logiques sont représentés avec le bit de poids le plus fort à gauche et le bit de poids le plus faible à droite.
On parle de représentation Little Endian , le plus petit à la fin.
Dans le cas contraire, on parle de représentation Big Endian, le plus fort à la fin.

Groupement de bits, cas particulier des bases 2, 8, 16

Avec les progrès technologiques et l'augmentation de la densité de transistors intégrés dans un même circuit, il est possible de travailler sur des nombres de plus en plus grands (typiquement 32 ou 64 bits sur les processeurs actuels).
Il est donc intéressant de regrouper l'information en groupes de bits afin d'avoir des opérations de plus haut niveau et d'améliorer la lisibilité de l'information (1 234 567 890 est par exemple plus lisible que 1234567890).

Ainsi, un groupe de 8 bits forme un octet (Byte en anglais) et permet de représenter 28=256 états (par exemple: les 256 caractères possibles de la table ASCII).
Les capacités des mémoires sont souvent données sous la forme de groupements d'octets.
Remarque 4 Contrairement à la croyance populaire, le terme Ko qui désigne 1 Kilo-octet correspond à 1000 octets (officiellement depuis 1998 afin que le kilo utilisé par ailleurs correspondent toujours à 103 quelque soit le domaine où on le rencontre).
Les informaticiens utilisent (ou devraient utiliser) le Kilo binaire (Kibi) qui est égal à 210=1024 et se note Kio.
Remarque 5 [Terminologie]

    Un Kilo-octet (Ko)= 103=1 000 octets
    Un Méga-octet (Mo)= 106=1 000 000 octets
    Un Giga-octet (Go)= 109=1 000 000 000 octets
    Un Tera-octet (To)= 1012= 1 000 000 000 000 octets
    Un Kibi-octet (Kio)= 210=1 024 octets
    Un Mébi-octet (Mio)= 220=1 048 576 octets
    Un Gibi-octet (Gio)= 230=1 073 741 824 octets
    Un Tébi-octet (Tio)= 240=1 099 511 627 776 octets
Remarque 6 L'appellation octet est typiquement francophone, l'appellation Byte (anglophone) est beaucoup plus courante. On rencontrera donc souvent les capacités mémoires sous la forme: KiB, MiB, GiB, TiB
Ces bases sont des puissances de 2. Les passages d'une base à l'autre sont donc très simples, puisque pour passer de l'une à l'autre il suffit de faire/défaire des groupes de bits.
  • base binaire (2 chiffres {0,1}): 21→ groupes de 1 bit
  • base octale (8 chiffres {0,1,2,3,4,5,6,7}): 23→ groupes de 3 bits
  • base hexadécimale (16 chiffres {0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F}): 24→ groupes de 4 bits
Exemple 8 Conversions:
binaire → octale:
(110101)2
=
((110)2(101)2)8
=
(65)8    (=6×

23
8 
+5)
binaire → hexadécimale:
(10110101)2
=
((1011)2(0101)2)16
=
(B5)16    (=11×

24
16 
+5)
hexadécimale → octale:
(B5)16
=
((1011)2(0101)2)16
=
((010)2(110)2(101)2)8
=
(265)8    (=2×82+6×8+5)

Opérations arithmétiques

Elles s'effectuent de la même façon qu'en base 10, dès qu'on atteint ou on dépasse la valeur de la base b, une retenue se reporte sur le chiffre de poids directement supérieur.
Exemple 9 Additionner les entiers logiques suivants sur 8 bits:
  1. (10100101)2 et (00111011)2
  2. (11111111)2 et (00000001)2, que se passe-t-il dans ce cas?
Multiplier les entiers logiques suivants sur 4 bits (résultat sur au plus 8 bits):
  1. (1011)2 et (1001)2
  2. (1010)2 et (0111)2
(Pour les plus courageux) Diviser l'entier logique (11010110)2 par (1011)2 et donner le quotient et le reste.
N'hésitez pas à vérifier vos calculs en faisant les conversions en base 10.
Remarque 7 Opérations triviales.
En base 10, il est facile de diviser ou de multiplier un nombre par 10, 100, 10n. Rappeler comment on procède.
De manière similaire en base 2, il sera donc facile de diviser ou multiplier un entier logique par une puissance de 2.
Exemple 10 On donne l'entier logique (1011010101011010)2.
Donner, en base 2 et sans conversion, son quotient et son reste dans la division par 2048.

3.4.3  Les entiers arithmétiques

Les entiers arithmétiques correspondent aux entiers relatifs qui peuvent être positifs ou négatifs, on parle d'entiers signés.
Contrairement aux entiers logiques, il faut ici coder l'information signe. Si on a n bits disponibles pour coder notre entier arithmétique, on va utiliser 1 bit de signe (soit 2 états: + ou −) et n−1 bits significatifs. On distingue deux cas selon le signe de l'entier:
  1. Entiers positifs ou nuls:
    0signe
    an−2... a1a0bits significatifs

    Les n−1 bits significatifs sont codés en binaire naturel (càd: de la même manière que pour les entiers logiques, bit de poids fort à gauche).
    Le plus grand entier positif qu'il est possible de représenter est:
    +2n−1−1=(0|

    1...11
    n−1 
    )2
  2. Entiers négatifs:
    L'idée la plus naturelle serait de coder le signe − par un bit de signe à 1 et de coder la valeur absolue de l'entier négatif en représentation binaire naturelle en utilisant les n−1 bits restants. Cette méthode de codage, dîte par signe/amplitude n'est pas du tout adaptée au calcul arithmétique.
    Exemple 11 Coder sur 4 bits l'entier arithmétique +7.
    Coder sur 4 bits l'entier arithmétique −7 en représentation signe/amplitude.
    Effectuer l'addition binaire de ces deux représentations, que constatez vous?

    Pour avoir une représentation des entiers négatifs utilisable pour les opérations arithmétiques, on utilise la méthode du complément à 2 (vue en ENSL1):
    1. On prend le complément à 1 de l'entier arithmétique dont on veut l'opposé (on inverse tous les bits de sa représentation, y compris le bit de signe puisque lorsqu'on prend l'opposé, le signe change).
    2. On ajoute 1
    Sur n bits, on peut donc représenter 2n entiers, de −2n−1 à 2n−1−1.
    Exemple 12 Donner la représentation en complément à 2 des entiers négatifs suivant:
    1. Sur 4 bits, (−7)10
    2. Sur 8 bits, (−7)10
    3. Sur 8 bits, (−128)10
    4. (−1027)10, de combien de bits avez vous besoin pour stocker ce nombre?
    Remarque 8 Lorsque le nombre de bits sur lequel effectuer le codage n'est pas précisé, il convient de prendre le nombre de bits minimal nécessaire au codage.
    Exemple 13 Opérations, cohérence:
    1. En utilisant la méthode complément à 2, prenez l'opposé de (−7)10. Que constatez-vous?
    2. En utilisant les représentations des entiers arithmétiques sur 8 bits, effectuez les opérations :
      1. (115)10−(37)10
      2. (7)10−(98)10

3.4.4  Codage des nombres réels (norme IEEE 754)

Problématique: comment coder le nombre 3,25?

3,25
=


3
partie entière 
+

1

4

partie fractionnaire
 
=
1×21+1×20 + 0×2−1+1×2−2
=
(11,01)2
qui peut aussi se représenter par +1,101×21.

Comme il y a de nombreuses façons de représenter un même nombre réel, il a été proposé plusieurs normalisation, dont la plus utilisée est la norme IEEE 754.
Dans cette norme, un nombre réel non nul s'écrit
s  1,2n
et peut être représenté par 32 bits selon:
  • 1 bit de signe: s {
    + si s=0
    − si s=1
    ;
  • 8 bits d'exposant: eeeeeeee représentant 127+n;
  • 23 bits de mantisse: mmmmmmmmmmmmmmmmmmmmmmm.
Ainsi 3,25 (+1,101×21)de l'exemple se code:


0
signe positif 


10000000
127+1 


10100000000000000000000
mantisse 
Remarque 9 Le réel 0 se note avec 32 bits à 0.
Remarque 10 Attention, pour l'exposant, on code bien 127+n sur 8 bits, où n est l'exposant.
Les exposants 00000000 correspondant à n=−127 et 11111111 correspondant à n=128 sont interdits:
  • 00000000 indique que le nombre est dénormalisé;
  • 11111111 indique que l'on n'a pas affaire à un nombre. On note cette configration NaN (Not a Number). Cela permet de signaler des erreurs de calcul comme une division par zéro.
Conséquence, le plus petit exposant est −126 et le plus grand est 127. Exemple 14 En utilisant la norme IEEE 754
  • Représenter le nombre réel −32.125 selon la norme IEEE 754
  • On donne les 32 bits représentant un nombre réel selon la norme IEEE 754:
    11000000111000100000000000000000
    Trouver de quel nombre il s'agit.

En vous rendant sur ce lien, si vous essayez de coder 7,34, vous vous rendrez compte que vous ne pouvez pas coder exactement cette valeur, mais seulement des valeurs approchées:
01000000111010101110000101000111 correspondant à 7.3399997 ou
01000000111010101110000101001000 correspondant à 7.3400002 qui est une meilleure valeur approchée.

Important: même si 7,34 est un nombre décimal exact, il n'est pas possible d'obtenir une représentation exacte de ce nombre selon la norme IEEE 754 avec 32 bits de précision.
On peut augmenter la précision en utilisant une représentation sur 64 bits, mais il faut avoir conscience que le calcul utilisant les nombres réel est un calcul approché dont la précision dépend du nombre de bits disponible pour représenter votre information.
Ainsi, si vous voulez travailler sur des nombres réels ayant une écriture décimale finie, nous vous encourageons à travailler en précision fixée en utilisant des entiers.
Par exemple, 7,34 peut très bien être vu comme l'entier 734 divisé par 100. Si vous voulez faire l'opération 7,342, vous obtiendrez une valeur approchée du résultat: 53,875602 alors que si vous faites l'opération 7342 vous obtiendrez le résultat exact: 538756. Il vous suffit alors de lire correctement les chiffres du résultat sachant que 7,342=(734/100)2=7342/10000 c'est à dire de décaler la virgule de 4 positions vers la gauche, pour obtenir le résultat exact: 53,8756.

Conclusion: arrêtez d'utiliser des float en C, à tort et à travers !
Au contraire, prenez le temps de reflechir à quel type d'information vous avez affaire. Si c'est un indice de tableau, celui-ci est un entier positif ou nul, donc un entier logique. Il conviendra la plupart du temps de le déclarer dans votre code C comme un unsigned int ou unsigned char.
Figure 3.5: Codage IEEE754

3.4.5  Codage des caractères

Les caractères que vous tapez au clavier d'un ordinateur ne sont pas des nombres. Pourtant, comme un processeur ne comprend que de l'information binaire, chaque caractère de votre clavier admet un codage binaire sur un octet (ou un Byte en anglais) c'est à dire sur 8 bits.
Il est donc possible de représenter 256 caractères, chacun pouvant être vu comme un entier compris entre 0 et 255. Cette table de correspondance communément utilisée est la table ASCII (American Standard Code for Information Interchange).
Les caractères ne sont pas rangés n'importe comment, ainsi pour faire du tri par ordre alphabétique, on peut comparer les valeurs des entiers codant les lettres de l'alphabet. La comparaison 'a'<'b' a un sens, puisque 'a' est codé par 01100001 c'est à dire l'entier 97 et 'b' est codé par 01100010 c'est à dire l'entier 98.
Remarque 11 Plusieurs points importants concernant le code ASCII:
  • Les codes compris entre 0 et 31 ne représentent pas des caractères, ils ne sont pas affichables. Ces codes souvent nommés caractères de contrôle sont utilisés pour indiquer des actions comme passer à la ligne (CR, LF), émettre un bip sonore (BEL), etc...
  • Les lettres se suivent dans l'ordre alphabétique (codes 65 à 90 pour les majuscules, 97 à 122 pour les minuscules), ce qui simplifie les comparaisons.
  • On passe des majuscules aux minuscules en modifiant le 5ième bit, c'est à dire en ajoutant 32 au code ASCII décimal.
  • Les chiffres sont rangés dans l'ordre croissant (codes 48 à 57), et les 4 bits de poids faibles définissent la valeur en binaire du chiffre.

3.4.6  Code BCD

Ce code (décimal codé binaire), vu au premier semestre en ENSL notamment, consiste à coder chaque chiffre de l'écriture décimale d'un nombre (0 à 9) en binaire naturel:
Base 10
0
1
2
3
4
5
6
7
8
9
Binaire
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
Exemple 15 En BCD, 345 est représenté par 0011 0100 0101
Ce codage est pratique dans les systèmes ne comportant pas d'unité de calcul est destiné seulement à afficher des nombres pour des utilisateurs humains, par le biais d'afficheur 7 segments par exemple.

3.4.7  Lien avec les types du langage C

Dans le tableau suivant vous trouverez une correspondance entre les différents types d'information vus dans ce chapitre et les types utilisés en C.
Type d'informationType en C
Entier logiqueunsigned int
Entier arithmétiqueint
Nombre réelfloat
Caractèrechar
Attention, la taille d'un int dépend du système à processeur utilisé (notamment de la taille du mot machine que peut traiter le processeur) et donc de l'interprétation que donne le compilateur associé au système lorsqu'il rencontre un int.

A titre d'exemple, on peut donner une table plus précise pour un processeur 32 bits.
Type d'informationType en C
Entier logique (8 bits)unsigned char
Entier logique (16 bits)unsigned short int
Entier logique (32 bits)unsigned long int ou unsigned int
Entier logique (64 bits)unsigned long long int
Entier arithmétique (8 bits)char
Entier arithmétique (16 bits)short int
Entier arithmétique (32 bits)long int ou int
Entier arithmétique (64 bits)long long int
Nombre réel (32 bits)float
Nombre réel (64 bits)double
Caractèrechar

Chapitre 4
Description générale - Principes de fonctionnement

Dans cette partie, nous décrivons l'architecture générale d'un système à microprocesseur et les principes généraux de fonctionnement.
Précisons ici qu'un (micro)processeur ne peut pas fonctionner en étant isolé ce qui justifie l'appellation de systèmes à microprocesseur. Il est donc en interaction avec d'autres composants. L'objectif de ce chapitre est de définir quelles sont ces unités, de préciser leur rôle ainsi que les interactions entre ces unités et le processeur.

4.1  Modèle général de Von Neumann

En 1945, John Von Neumann propose un concept général d'ordinateur à programme enregistré. Même si depuis 1945 la technologie a connu d'énormes progrès, ce modèle reste la base de l'architecture des systèmes informatiques actuels.
Figure 4.1: Architecture de Von Neumann
Les deux composants principaux d'un système à microprocesseur sont la mémoire principale et le processeur avec chacun des rôles bien définis:
  • La mémoire principale (MP) stocke l'information (programmes et données).
    Une des caractéristiques de l'architecture de Von Neumann est sa structure de stockage unique pour conserver les instructions et les données.
    Remarque 12 Lorsque les accès aux instructions et aux données sont séparés, on parle d'architecture de Harvard.
  • Le processeur (CPU) représenté ici par son UAL (Unité Arithmétique et Logique) et son unité de Contrôle, exécute pas à pas les instructions d'un programme.
La communication avec le monde extérieur (humains ou autres systèmes matériels) se fait par l'intermédiaire d'unité d'entrées/sorties.
Par exemple, dans le cas d'une interaction homme-machine, un ordinateur aura l'architecture générale présentée sur la figure .
Figure 4.2: Principe général de fonctionnement d'un ordinateur

Un processeur est entouré (ne voit que) de cases mémoires. Chaque case est accessible grace à une adresse donnée. Pour communiquer avec une case mémoire, le processeur doit donc indiquer son adresse. Puis, le processeur peut soit lire le contenu de la case, soit écrire dans une case. Dans les deux cas, une donnée doit transiter entre le processeur et la mémoire.
Il y a trois types différents d'information que s'échangent le processeur et la mémoire:
  • Une adresse;
  • Un signal de contrôle (lire/ecrire dans l'exemple);
  • Une donnée.
Ces types d'information transitent par l'intermédiaire de fils d'interconnexion, chaque fil permet de transporter un bit d'information. Pour transporter plusieurs bits d'information simultanément, il est nécessaire de grouper les fils, on parle alors de bus de communication qui permettent aux différents composants d'un système à microprocesseur de communiquer.
On distinguera donc:
  • Un bus d'adresse;
  • Un bus de contrôle (ou bus de commande);
  • Un bus de données.
Les largeurs et les significations précises de ces bus seront vues ultérieurement.

4.2  Technologies de fabrication des circuits intégrés

Les premiers ordinateurs ont été construits durant la deuxième guerre mondiale avec des tubes cathodiques (des 'lampes') comme dispositif de commutation. En 1947, les laboratoires Bell inventent le premier transistor, qui va très vite supplanter la lampe et apporter un facteur de réduction de taille important. Mais la véritable révolution n'interviendra qu'avec l'invention des circuits intégrés VLSI dans les années 1970, qui amèneront à la réalisation des premiers microprocesseurs. Un grand nombre de technologies de fabrication de circuits intégrés existent, qui se distinguent par des qualités différentes en termes de vitesse de propagation, densité d'intégration, consommation et dissipation thermique. On va ébaucher rapidement dans cette section les trois familles les plus connues, en terminant par celle qui est actuellement la plus répandue et la plus significative, la famille CMOS.

4.2.1  Technologie TTL

La technologie TTL était la technologie standard utilisée à partir des années 1960 pour la réalisation de tous les types d'ordinateurs. Elle s'alimente typiquement en +5V, a une consommation modérée, est robuste et a des règles d'interface simples. Une très grande famille de circuits spécialisés, la famille 7400, est disponible sous forme de circuits en boîtier DIL, et fournit des modules tout prêts : portes, bascules, compteurs, etc. Les capacités d'intégration de circuits TTL sont faibles, et on ne peut guère mettre plus de quelques milliers de portes TTL sur une puce de circuit intégré. On l'utilise encore aujourd'hui pour des petites réalisations, notamment dans l'interfaçage avec d'autres circuits.

4.2.2  Technologie ECL

La technologie ECL est caractérisée par sa très grande rapidité, mais aussi par une consommation de courant élevée. Elle est utilisée dans des petites réalisations où la vitesse est critique, mais certains experts prévoient un développement de la technologie ECL avec l'utilisation de nouveaux types de semi-conducteurs.

4.2.3  Technologie CMOS

C'est la technologie reine actuelle. La technologie CMOS a d'abord été développée et vendue comme une alternative au TTL, plus lente mais avec une consommation réduite. Comme par ailleurs elle peut utiliser des tensions d'alimentation faibles (jusqu'à 1V), elle a immédiatement été très utilisée par les fabricants de montres digitales, pour qui la vitesse de traitement importait peu par rapport à la consommation. Mais on a progressivement réalisé que sa grande qualité était son taux d'intégration très élevé ; de plus, des tailles de transistors de plus en plus petites ont amenées avec elles des temps de commutation de plus en plus faibles. C'est la technologie CMOS qui est utilisée actuellement pour la fabrication de tous les processeurs et microcontrôleurs du marché, ainsi que pour toutes les mémoires statiques et les mémoires flash.


Par ailleurs, un circuit CMOS ne consomme significativement du courant que lors des commutations des transistors. Cette propriété a été à l'origine de circuits qui consomment moins de courant que leur équivalent TTL, qui eux en consomment au repos et lors des commutations. Néanmoins, au fur et à mesure qu'on diminue leur tension d'alimentation, les courants de fuite des transistors CMOS deviennent de plus en plus proches des courants de commutation, et par conséquent la consommation de ces circuits au repos n'est plus aussi négligeable qu'auparavant.

4.3  Le processeur (CPU)

Le processeur (CPU, pour Central Processing Unit soit Unité Centrale de Traitement) est le cerveau du système. Il s'agit d'un circuit électronique complexe qui exécute chaque instruction très rapidement, en quelques cycles d'horloge. En effet, toute l'activité de l'ordinateur est cadencée par une horloge interne unique de façon à ce que tous les circuits travaillent ensemble de façon synchronisée sur cette horloge. On parle alors de conception synchrone. L'horloge interne est le plus souvent un quartz, qui alimenté par un courant électrique, produit des impulsions appelées tops. Le rythme de production de ces tops définit la fréquence d'horloge, exprimée en MHz (millions de battements par seconde).

Le rôle du processeur est d'exécuter un ou des programmes. Un programme est une séquence d'instructions placées en mémoire.
En général (selon le modèle de Von Neumann), pour réaliser une instruction un processeur suit 4 étapes:
  1. recherche de l'instruction à réaliser: fetch;
  2. décodage de l'instruction (opération et opérandes): decode;
  3. exécution de l'opération: execute;
  4. écriture du résultat: writeback.
Pour effectuer ces opérations, on distingue deux unités composant un processeur:
  • l'unité de commande: responsable de la lecture en mémoire et du décodage des instructions;
  • l'unité de traitement, aussi appelée Unité Arithmétique et Logique (UAL) ou Arithmetic and Logic Unit (ALU): exécute les instructions qui manipulent les données.

4.4  Jeu d'instructions

4.4.1  Le programme exécuté est dans la mémoire centrale

Le programme qu'exécute un ordinateur n'est pas sur le disque dur, comme le pensent beaucoup de gens. Une copie du programme est effectivement souvent stockée sur un CDROM ou un disque dur, mais ce n'est pas avant d'avoir été copié en mémoire centrale, comme le serait n'importe quelle autre ensemble de données, que son exécution peut commencer. Un programme qu'exécute un ordinateur est composé d'instructions sous forme de mots mémoire. Le langage utilisé pour coder les instructions en mots binaires est appelé langage machine, c'est le seul que comprenne le processeur. La taille des instructions varie beaucoup selon le type des processeurs. Parfois toutes les instructions ont la même taille, égale à un ou plusieurs mots mémoire ; parfois elles ont des tailles variables selon la complexité de l'instruction.

4.4.2  Le processeur est le coordonnateur de l'exécution

Le processeur contrôle l'exécution des programmes, en organisant le chargement des instructions et le détail de leur exécution ; c'est aussi lui qui effectue tous les calculs, notamment sur les nombres entiers. Ces deux fonctions sont associées dans la même unité car le contrôle de l'exécution nécessite souvent des calculs entiers, lorsque par exemple il faut faire une somme pour déterminer l'adresse de l'instruction suivante.
Le processeur a besoin de garder la trace d'un certain nombre d'informations pendant l'exécution des instructions : l'adresse de l'instruction en cours par exemple, ou le résultat de la dernière opération arithmétique. Il utilise pour cela des registres, qui sont des mots mémoire fixes, à accès très rapide.

4.4.3  En résumé

Un programme informatique (tel que vous pouvez l'écrire en C) est une suite d'instructions de haut niveau, qui peuvent être très variées. Il est illusoire de penser qu'un processeur peut éxecuter tous ces types d'instructions.
Un processeur possède un certain nombre d'instructions de base qui constituent son jeu d'instructions. Ces instructions de base sont par exemple:
  • addition de deux mots machine;
  • lecture/écriture d'une case mémoire;
  • ou logique entre deux mots machine;
  • etc, ... le jeu d'instruction d'un processeur est en général décrit précisément dans sa documentation.
A partir de ce jeu d'instructions, le processeur est capable (en séquençant les instructions de base) de traiter des instructions plus complexes qui peuvent composer un programme.
Les instructions du jeu d'instructions du processeur sont codifiées selon un langage très précis appelé le langage machine. Le langage machine dépend du processeur.
Pour écrire un programme en langage machine, il faut donc connaître les détails du fonctionnement du processeur qui va être utilisé.

4.5  Langage machine

Le langage machine (et sa forme équivalente symbolique dite assembleur) est exécuté directement par le processeur,et il est à ce titre l'intermédiaire obligé de tous les composants logiciels d'un ordinateur. Même s'il est vrai que peu de gens programment directement en assembleur, son étude est éclairante pour la compréhension de la relation entre logiciel et matériel

4.5.1  Langage machine et ISA

Les instructions qu'exécute un processeur sont écrites dans un langage appelé langage machine, et sont formées de codes écrits dans des mots mémoires consécutifs ; on appelle ISA (Instruction Set Architecture) ce langage et son organisation. L'informatique utilise un grand nombre de langages différents : des langages de programmation tels que C ou Java pour les programmeurs, des langages interprétés tels que les langages de macro des suites bureautiques, des langages de gestion de bases de données, etc. Mais la particularité du langage machine,c'est d'être le langage intermédiaire en lequel tous les autres langages vont être traduits, en une ou plusieurs étapes. Il est situé à la frontière entre le processeur et les logiciels,entre hardware et software, et le processeur est l'interpréteur de ce langage (figure V.1), celui qui met en action ses instructions.

4.5.2  ISA générale ou spécialisée ?

Bien sûr, il serait plus efficace d'avoir un processeur qui exécute directement un langage évolué, tout au moins pour les programmes écrits dans ce langage. De tels processeurs existent ou ont existé, notamment pour le langage Lisp, et plus récemment pour le langage intermédiaire (bytecode) utilisé en Java. Ces deux langages sont généralistes et peuvent être utilisés dans toutes les situations concrètes de l'informatique, et l'espoir des concepteurs de ces processeurs était d'élever au niveau de ces langages la frontière entre matériel et logiciel. Mais le bytecode Java exploite une machine à base de pile, peu adaptée à la compilation d'autres langages de programmation tels que C. Finalement, le compromis généralement adopté lors de la conception d'un nouveau processeur est le développement d'une ISA adaptée à la compilation de la plupart des langages de programmation courants.

4.5.3  évolution des ISA

Comme toute structure située à une frontière, l'ISA est un équilibre entre des forces opposées.Les concepteurs de compilateurs souhaitent une ISA régulière et possédant le plus de possibilités en termes d'adressage mémoire, d'opérations arithmétiques, etc., alors que les architectes des processeurs veulent une ISA simple à implémenter efficacement. Une force importante qui limite l'apparition de nouvelles ISAs (de nouveaux langages machine) est la demande des clients d'une compatibilité ascendante d'un processeur d'une génération à l'autre, de façon à pouvoir faire fonctionner sans changement leurs anciens programmes. Cette force se manifeste de façon très puissante avec les processeurs de PC (dont l'ISA est souvent désignée par x86, parce que les noms des processeurs se terminent par 86 : 8086, 80286, etc.), pour laquelle tout changement conduisant à une non compatibilité est exclu. Ainsi, les processeurs x86 les plus récents continuent à manifester la bizarrerie d'écriture en mémoire inversée de type little-endian, parce qu'elle était présente à l'origine sur le processeur 8 bits Intel 8080 pour des motifs de simplicité de conception, et qu'elle s'est transmise pour raison de compatibilité tout le long de la chaîne 8080, 8085, 8086, 80286, 80386, Pentium, Pentium 2, etc. jusqu'aux processeurs des PCs actuels. D'autres processeurs moins connus ont également une lignée très longue et respectable. On peut citer le processeur ARM, un des premiers processeurs 32 bits commercialisés, qui fait évoluer son ISA de façon ascendante depuis les années 80 jusqu'à maintenant, et qui équipe un grand nombre de téléphones portables, les consoles de jeux portables Nintendo et un grand nombre d'appareils mobiles.

4.5.4  Processeurs CISC et RISC

Une nouvelle génération de processeurs est apparue dans les années 80, caractérisée par un jeu d'instruction limité à l'essentiel, d'où leur nom de processeurs RISC (Reduced Instruction Set Computer). Par ailleurs, leur ISA est généralement très régulière, contient beaucoup de registres utilisateurs, et les instructions sont codées sous forme d'un mot mémoire unique. Tout cela rend heureux les architectes du processeur, qui peuvent concevoir des architectures très efficaces pour cette ISA, et ne rend pas trop malheureux les concepteurs de compilateurs, qui ont un peu plus de difficulté à traduire les programmes, mais qui apprécient le gain en vitesse d'exécution. Par opposition, les anciens processeurs tels que les x86 ont été rebaptisés processeurs CISC (Complex Instruction Set Computer), et si ce n'était l'énorme investissement dû au succès du PC avec l'ISA x86, les processeurs RISC auraient connus un développement plus immédiat et plus large. On les trouve néanmoins dans la plupart des stations de travail, dans les processeurs de Macintosh, et dans les consoles de jeux Sony PS[123], avec les performances que l'on sait
Remarque 13 Le langage machine est un langage de très bas niveau qui ne travaille qu'avec des adresses mémoires. Par exemple, le programme écrit en langage machine pour une architecture 80x86 par la séquence
A1op. lecture 01 10adr 03 06op. add 01 12adr A3op. écriture 01 14adr implantée en mémoire additionne le contenu de deux cases mémoires et range le résultat dans une troisième.
Il apparait donc difficile d'écrire un programme complexe uniquement en langage machine. L'intérêt d'un langage de haut niveau (comme le C), permet d'avoir un langage normalisé indépendant du processeur ciblé. Le problème est qu'un processeur donné ne peut pas interpréter les instructions écrites en C. Il faut transformer ce langage de haut niveau en langage machine, c'est le rôle du compilateur. Le compilateur connaît le jeu d'instructions du processeur ciblé et peut donc transformer les instructions de haut niveau en instructions de base du processeur.
Remarque 14 Pour avoir une représentation plus parlante du langage machine, on utilise souvent un langage appelé langage symbolique (utilisé dans le langage assembleur) pour indiquer à quoi correspondent les instructions codées en hexadécimal.
Ainsi, la séquence précédente: A1op. lecture 01 10adr 03 06op. add 01 12adr A3op. écriture 01 14adr
s'écrit en langage symbolique (pour une architecture 80x86):
MOV AX, [0110] Charge le registre AX avec le contenu de la case d'@ 0110
ADD AX, [0112] Ajoute à AX le contenu de la case d'@ 0112 (res. dans AX)
MOV [0114], AX Envoie le contenu de AX à la case d'@ 0114

4.6  Espace adressable, mémoire

Cette section est consacrée aux circuits utilisés dans la mémoire centrale des ordinateurs, qui contient les programmes et les données manipulés par les programmes. L'information y est stockée sous forme de mots de taille fixe, dont la largeur dépend de l'architecture utilisée. Ces mots mémoire sont ordonnés et possèdent chacun une adresse, sous forme d'un nombre.
Le processeur ne voit que des cases mémoires, à la fois pour "parler" à la mémoire proprement dîte, c'est à dire l'espace de stockage (RAM, ROM) mais aussi pour parler aux périphériques du système (chacun étant accessible à une adresse donnée).
L'ensemble des "cases" auxquelles peut "parler/écouter" un processeur est appelé l'espace adressable du processeur. Dans cette partie, on entend par mémoire, la partie adressable qui contient les données et les programmes. On distinguera dans la suite du cours (chapitre sur les memoires), la mémoire vive (données) de la mémoire morte (programmes).
Il est important de noter que seul le processeur peut modifier l'état de la mémoire. Chaque emplacement mémoire conserve les informations que le processeur écrit jusqu'à ce que le courant électrique soit coupé (cas des mémoires volatiles par opposition aux mémoires mortes, ou autre périphériques de stockage externe: disquettes, clés USB, disques durs,...).

Les seules opérations possibles sur la mémoire sont:
  • écriture d'un emplacement: le processeur émet le signal qu'il veut écrire (bus de contrôle) une valeur (sur le bus de données) à une adresse (sur le bus d'adresse). La valeur transite du processeur vers la mémoire qui enregistre la valeur à l'emplacement indiqué par l'adresse;
  • lecture d'un emplacement: le processeur émet le signal qu'il veut lire une valeur à une adresse donnée. Le contenu de l'emplacement lu n'est pas modifié et la valeur transite sur le bus de données de la mémoire vers le processeur.
De telles mémoires se classent en deux grands types, selon qu'elles sont en lecture seule ou en lecture-écriture : les RAMs (Random Access Memory) peuvent être lues et écrites, et l'attribut random indique que ces lectures-écritures peuvent se faire à des suites d'adresses totalement quelconques, par opposition à des mémoires de type séquentiel (comme les disques durs) qui font des séries d'accès à des adresses consécutives. Les ROMs (Read Only Memory) sont fonctionnellement identiques aux RAMs, mais ne permettent que la lecture et pas l'écriture. Utilisées dans la mémoire centrale d'un ordinateur, les RAMs pourront stocker les programmes et les données, alors que les ROMs vont seulement stocker des programmes invariables, comme par exemple le programme exécuté au démarrage de la machine, et stocké dans la ROM dite de BIOS. Dans certains ordinateurs plus anciens, tout le système d'exploitation était stocké en ROM, ce qui permettait d'avoir un système incorruptible et au démarrage rapide.


Une RAM peut être statique ou dynamique. Chaque bit mémoire d'une RAM statique (SRAM) est constitué d'une bascule, et conserve son état tant qu'elle est alimentée. A l'inverse, chaque bit d'une RAM dynamique (DRAM) est composé d'une capacité, qui doit être rafraîchie périodiquement par une électronique séparée. Les RAMs statiques ont un taux d'intégration plus faible que les RAM dynamiques, puisqu'un bit mémoire nécessite 6 transistors dans un cas, et une capacité plus un transistor dans l'autre. Une RAM peut être synchrone ou asynchrone, une RAM synchrone étant en fait une RAM asynchrone à laquelle on a ajouté une machine à états finis synchrone qui place les commandes de lecture et d'écriture dans un pipeline, afin de permettre d'accepter une nouvelle commande avant que la précédente n'ait été complétée.


Les barrettes de RAM de nos ordinateurs personnels sont des SDRAM, c'est à dire des RAM dynamiques synchrones, fonctionnant à des fréquences de 200MHz et plus. Elles sont souvent de type DDR (double data rate), quand les fronts montants et descendants de l'horloge sont exploités pour les changements d'état. Dans beaucoup d'autres appareils (assistants personnels, consoles de jeux, etc.), la RAM est de type statique asynchrone (SRAM), sous la forme de circuits intégrés. Les ROMs existent également dans un grand nombre de types différents, principalement selon la façon dont on peut programmer leur contenu (invariable, par définition). Il y a d'abord les ROMs programmées par masque à l'usine ; elles sont produites en grand nombre avec un faible coût à l'unité, mais leur contenu ne peut jamais être mis à jour ultérieurement.


4.7  Implantation d'un programme (simplifié)

Lorsque vous écrivez un programme informatique en vue d'être exécuté sur un système à microprocesseur, les principales étapes suivantes sont effectuées:
  1. Ecriture du code de votre programme dans le langage de votre choix;
  2. Compilation de votre code pour le rendre intelligible par le processeur ciblé. Le code est ainsi transformé en une suite d'instructions élémentaires faisant partie du jeu d'instruction du processeur ciblé;
  3. Le processeur:
    1. récupère une instruction composant votre programme en mémoire;
    2. décode l'instruction;
    3. exécute l'opération;
    4. écrit le résultat puis passe à l'instruction suivante.
  4. le résultat de votre programme en mémoire peut alors être utilisé comme entrée d'un autre programme, ou comme sortie utilisable par un périphérique de sortie (afficheur 7 segments, actionneur, écran, etc...).

Chapitre 5
Espace adressable - Mémoires

Un processeur ne voit que des cases mémoires qui lui permettent de stocker de l'information binaire et de communiquer avec les périphériques composant le système. Cet ensemble de cases mémoires constitue l'espace adressable du processeur. Cependant, il faut distinguer dans l'espace adressable du processeur, la partie dédiée au stockage de l'information (données et programmes) et la partie correspondant aux périphériques du système. Enfin, il convient de préciser quelles sont les différents types de mémoires, leurs capacités ainsi que les différentes technologies permettant de stocker l'information.

5.1  Types de mémoire

Par espace adressable, on entend ici tout élément de mémorisation externe au processeur (c'est à dire qu'on ne considère pas les registres processeurs) auquel le processeur peut s'adresser.
Cela concerne la mémoire en tant qu'espace de stockage mais également l'accès aux périphériques du système.
Remarque 15 Pour s'adresser aux périphériques du système, le processeur doit connaître leur adresse. On peut donc voir les périphériques d'un système comme des parties de l'espace adressable que voit le processeur.
Pour éviter toute confusion par la suite, on emploiera donc le terme d'espace adressable pour indiquer le nombre de cases que voit le processeur et de mémoire la partie de l'espace adressable réservée au stockage de l'information.

5.1.1  Espace de stockage

Le rôle d'un espace de stockage est de conserver des données. Le stockage peut être de longue durée ou temporaire, on distingue donc deux types de mémoires pour stocker l'information binaire à l'intérieur d'un système informatique.

Mémoire centrale

Cette partie de la mémoire est la mémoire interne ou encore appelée mémoire vive ou encore mémoire RAM (Random Access Memory). Elle permet de stocker temporairement des données, notamment lors de l'exécution de programmes. C'est une mémoire volatile au sens où lorsqu'elle n'est plus alimentée par un courant électrique, elle perd sa capacité de mémorisation.
L'accès à cette mémoire est rapide (quelques dizaines de nanosecondes) ce qui est important pour ne pas ralentir la cadence à laquelle le processeur peut travailler.
Remarque 16 La mémoire accessible dans le processeur, (registres voire mémoire cache), a un temps d'accès encore plus court (quelques nanosecondes).

Mémoire de masse

Cette partie de la mémoire, appelée mémoire physique ou encore ROM (Read Only Memory) permet de stocker des données sur le plus long terme, même lorsque le courant électrique est coupé. C'est le cas des CD-ROM, DVD-ROM, mais aussi les disques durs et autres clés USB.
Les temps d'accès à ce type de mémoire sont beaucoup plus lents (quelques millisecondes), aussi pour travailler sur des données présentes en mémoire de masse, le processeur va d'abord les récupérer dans la mémoire centrale qui offre des temps d'accès beaucoup plus rapide.
Remarque 17 L'appellation mémoire à lecture seule (ROM) est conservée pour des raisons historiques, cependant la plupart des périphériques de stockage de masse actuels (tels que les disques durs, les clés USB utilisant la technologie FLASH) sont accessibles en lecture ET écriture. Cependant les temps d'accès à ces mémoires sont beaucoup plus lent que pour des accès à la RAM.

5.1.2  Les périphériques

Ils ne représentent pas un espace de stockage à proprement parler. Cependant pour envoyer un signal à un actionneur, récupérer une donnée d'un capteur, allumer une led, le processeur doit envoyer des données aux différents périphériques du système. Pour savoir à quel périphérique s'adresser, le processeur indique son adresse. L'ensemble des adresses des périphériques du système occupe une partie de l'espace adressable.

5.2  Capacité adressable

Dans cette partie, nous répondons à la question suivantes: A combien d'éléments mémoires le processeur peut-il s'adresser? en d'autres termes, on cherche à définir la capacité adressable du processeur.

5.2.1  Espace adressable

Les éléments mémoires que voit le processeur sont accessibles grâce à leurs adresses. Un processeur ne peut s'adresser qu'à un nombre limité de cases mémoires (en lecture/écriture). Ce nombre correspond au nombre d'adresses pouvant circuler sur le bus d'adresses.
Si le bus d'adresses comporte n fils de communication, on parlera alors d'un bus de largeur n bits, le processeur pourra s'adresser à 2n cases (de 0 à 2n−1).
On rappelle que l'espace adressable est le nombre de cases mémoires auxquelles le processeur peut s'adresser. Ce nombre est une des deux dimensions de la mémoire et est forcément une puissance de 2 (Pourquoi?).

5.2.2  Largeur mémoire

La seconde dimension de la mémoire est donnée par le nombre de bits d'information que peut contenir une case. Il n'y a pas de règles spéciales concernant le nombre de bits que peut contenir une case. Toutefois, la largeur de la mémoire est souvent calée sur la taille du mot machine que peut traiter le processeur. Comme en général, les mémoires sont organisées en nombre d'octets, une case mémoire pourra contenir un multiple de 8 bits (=1 octet) d'information.
Si une case mémoire peut stocker m bits d'information, on dit que la mémoire a une largeur de m bits.
Adresse Emplacement
2n−1 0 1 0 1 1 0 1 0
2n−2
......
2
1
0
7 6 5 4 3 2 1 0
numéros des bits
Figure 5.1: Exemple d'espace adressable de capacité 2n octets
La définition de la capacité mémoire ou capacité adressable est donnée par le produit des deux dimensions:
  • nombre de cases (2n);
  • taille d'une case (m bits, en général m est un multiple d'octets).
Dans le cas le plus courant où la taille d'une case est d'un octet, la capacité mémoire est donnée sous la forme d'un multiple informatique d'octets (Ki, Mi, Gi) d'où les tailles mémoires associées Kio, Mio, Gio.
Exemple 16 Un processeur a un bus d'adresse d'une largeur de 16 bits et un bus de donnée d'une largeur de 8 bits. Calculez la capacité adressable du processeur. Schématiser les connections entre le processeur et son espace adressable dans ce cas.

5.3  Mapping mémoire

Comme le processeur ne voit que des cases mémoires, il peut communiquer non seulement avec l'espace de stockage (RAM, ROM) mais aussi avec les autres périphériques du système en donnant leur adresse.
Pour savoir avec quelle partie de son espace adressable le processeur communique, il faut effectuer une cartographie de la mémoire (en anglais: mapping) pour savoir dans quelle plage d'adresses se trouve la RAM, la ROM mais aussi les autres périphériques auxquels le processeur peut s'adresser.

5.3.1  Sélection de boitier (CS)

Tous les périphériques du système (mémoires, E/S, CAN, ...) sont raccordés physiquement au processeur via le bus de données. Lorsque le processeur veut communiquer avec un périphérique en particulier, la donnée échangée doit l'être entre le processeur et le périphérique sélectionné et pas un autre périphérique. Si deux périphériques étaient amenés à envoyer en même temps sur le bus de donnée, une donnée au processeur, il y aurait un conflit de bus.
Pour éviter ces conflits, le processeur envoie un signal de contrôle (CS: Chip Select, actif niveau bas) pour indiquer à quel périphérique il souhaite s'adresser.
Chaque périphérique doit donc avoir un port permettant de recevoir ce signal. Si le signal reçu par le périphérique est 1 indiquant que le périphérique n'est pas sélectionné, le bus de donnée du périphérique doit être à haute impédance (état logique ′Z′ pour chaque fil du bus de donnée).
Le signal CS agit sur les buffers tristates de sortie du bus de donnée (cf: ENSL1 et [TD2] Chip select et mapping mémoire).

5.4  Hiérarchie mémoire

L'espace de stockage disponible pour le processeur est hierarchisé, c'est à dire qu'il est constitué de plusieurs composants de stockage de technologie différentes qui n'ont pas le même rôle et pas le même ordre ni les même temps d'accès.

Il convient de retenir que plus on s'éloigne du processeur, plus l'espace de stockage est important mais plus les temps d'accès sont longs.
A l'inverse, plus on s'approche du processeur plus les capacités de mémorisation sont réduites mais plus leur temps d'accès est rapide. Ceci est schématisé dans la figure 2.
Figure 5.2: Hiérarchie mémoire
Cette notion de hiérarchie est importante pour ne pas ralentir le système. En effet, on distingue:
  • L'information sur laquelle est en train de travailler le processeur (stockée dans des registres processeurs, stockage et durée de vie limités mais temps d'accès très rapide (quelques ns));
  • L'information sur laquelle le processeur va travailler (stockée dans la mémoire centrale, stockage et durée de vie plus importants mais temps d'accès plus lent (quelques dizaines de ns));
  • L'information que le processeur est amenée à consulter de temps en temps (stockée dans de la mémoire de masse (mémoire flash principalement en système embarqué), stockage et durée de vie beaucoup plus important mais temps d'accès très lents (quelques ms)).
Lorsque le processeur veut exécuter un programme présent dans la mémoire de masse, il le charge d'abord en mémoire centrale, puis transfert les données à traiter dans ses registres processeurs afin d'exécuter les instructions plus rapidement en limitant au maximum les accès mémoire qui prennent du temps et ralentissent l'exécution du programme.
Le temps passé à consulter la mémoire est appelé temps de latence. Ce temps est forcément présent (la capacité de stockage des registres processeurs n'est pas illimitée et des transferts entre les registres et les mémoires centrales et de masse sont indispensables), mais doit être le plus petit possible.

5.5  Technologies

L'objectif de cette partie est de comprendre comment peut être mémorisée l'information binaire et d'étudier les technologies employées couramment pour mémoriser l'information.

5.5.1  Mémoire centrale

Mémoires dynamiques

Le principe de mémorisation de la mémoire centrale (selon la technologie DRAM, D comme Dynamique) est présenté figure 3
Figure 5.3: Principe de fonctionnement de la technologie DRAM (RAM Dynamique)
La mémoire est organisée sous forme d'un tableau dont chaque case est accessible par une ligne et une colonne. Chaque intersection est composée d'un transistor et d'un condensateur. Lorsque le condensateur est chargé son état logique est assimilé à 1, lorsqu'il est déchargé son état logique représente un 0. Chaque case de ce tableau représente donc un bit de la mémoire.

Un des inconvénients majeurs de ce système est dû au fait que les condensateurs se déchargent naturellement, il faut donc régulièrement (de l'ordre de quelques ms) rafraichir leurs états (c'est à dire venir lire leur valeur et les recharger). Ce temps s'ajoute au temps d'accès à la mémoire (temps de latence).

Cette technologie est simple à mettre en place ce qui offre l'avantage d'être peu onéreuse et d'occuper une surface matérielle 4 fois moins importante que les RAM statiques (SRAM).

RAM (Random-Access Memory)

Contrairement aux ROMs, une RAM est une mémoire volatile, c'est à dire qui ne garde son contenu que tant qu'elle est sous tension (ce que son nom anglo-saxon n'indique pas, contrairement à la dénomination française). Le vocable de RAM (Random Access Memory), assez sommairement traduit en français par mémoire à accès aléatoire, est historiquement apparu pour signifier que contrairement aux bandes magnétiques qui étaient alors les seuls supports d'information existants, il n'est pas nécessaire de lire toutes les données situées devant celle que l'on cherche avant de pouvoir accéder à l'information désirée.
Principe   La cellule de base d'une RAM est typiquement constituée par une bascule 4 :
Figure 5.4: Principe de fonctionnement d'une RAM
Cette cellule prend place dans deux matrices qui se superposent, l'une destinée à l'écriture, l'autre à la lecture :
Figure 5.5: Écriture/lecture RAM
Exemple   La matrice de cellules est intégrée dans un ensemble de circuits servant à sélectionner correctement les lignes et les colonnes :
Figure 5.6: Exemple de fonctionnement d'une RAM
Fonctionnement   Le bus d'adresse A1A0 pilote un décodeur qui fournit les lignes de sélection des cellules. La même ligne sert pour l'écriture et la lecture.
L'utilisation d'un même fil pour sélectionner les cellules pour l'écriture d'une part, et pour la lecture d'autre part, nécessite une ligne pour indiquer quelle est l'opération désirée : R/WR- est à 0 lors d'une écriture, et à 1 lors d'une lecture. Ceci impose de modifier le schéma de la cellule de la façon suivante :
Figure 5.7: Fonctionnement d'une RAM
Les sorties peuvent être mises en haute impédance. Ceci permet d'éviter un conflit entre la sortie des bascules et la donnée présentée sur le bus bidirectionnel lors de la phase d'écriture.
La mise en haute impédance peut ètre contrôlée depuis l'extérieur grâce au signal d'entrée OE (Output Enable). Cela permet de connecter plusieurs circuits de ce type sur un même bus de données : seul celui qui sera sélectionné par l'intermédiaire de la ligne OE fixera les niveaux du bus.
Le signal CS (Chip Select) doit être à 1 pour que la mémoire soit opérationnelle. S'il est à 0, la matrice est sélectionnée en lecture (pas de perte de données par écriture intempestive) et les sorties sont en haute impédance, quel que soit l'état de OE.

Mémoires statiques

La réalisation de ces mémoires est effectuée à l'aide d'un tableau de bascules (typiquement les bascules RS vues en ENSL1, voir figure ).
Figure 5.8: Bascule RS
R S Q Q
0 0 1 1
01 0 1
1 0 1 0
1 1 Q0 Q0
Fonctionnement   L'état de repos des entrées est R = S = 1. Dans cet état, la valeur des sorties Q Q dépend de l'histoire du système :
  • Si R passe à 0 (Reset), la sortie Q passe à 0 et Q passe à 1. Les sorties n'évoluent pas lorsque R repasse à 1.
  • Si S passe à 0 (Set), la sortie Q passe à 1 et Q passe à 0. Les sorties n'évoluent pas lorsque S repasse à 1.
  • Si R et S sont à 0, les deux sorties Q et Q valent 1. L'appellation Q et Q n'est plus appropriée. Dans la majorité des cas d'utilisation de ce type de bascule on préfère éviter ce cas de fonctionnement.
La bascule RS possède la fonctionnalité d'une case mémoire. La sortie mémorise un état 0 ou 1, déterminé par une impulsion négative sur R ou S. Cette bascule est le montage le plus simple (à base de portes logiques) réalisant cette fonction.
Chaque bascule peut mémoriser un bit, il faut donc:
  • m bascules, où m est la taille du mot machine;
  • 2n étages de m bascules où n est la largeur du bus d'adresse de la RAM.
Cette réalisation est simple mais nécessite un décodeur interne à 2n sorties, donc une surface de boitier importante. Son accès est cependant rapide.

5.5.2  Les mémoires de masse

Ces mémoires sont destinées à stocker de l'information sur le long terme. Elles ne nécessitent pas d'énergie pour stocker les données et sont donc non volatiles (conservation des données sans alimentation électrique).

Les ROMs

Principe   Les mémoires ROMs (Read Only Memory) sont par définition des mémoires en lecture seule. Leur contenu a été défini une fois pour toute et elles ne sont plus inscriptibles (voir par exemple figure ).
Figure 5.9: Exemple de fonctionnement d'une ROM
Fonctionnement   Grâce à la résistance de tirage une ligne de sortie vaut 1 en l'absence de diode entre elle et le fil d'adresse sélectionné.
Si par contre une diode est présente, elle ramène le potentiel de la ligne sortie à 0. Le contenu de cette mémoire est alors :
adresse S4S3S2S1S0
11 01101
10 10111
01 10111
00 11110
Remarque   Les liaisons entre les lignes de sélection d'adresse (lignes horizontales de la matrice), et les lignes de données (lignes verticales de la matrice) ne peuvent être de simples connexions, mais doivent être réalisées à l'aide de diodes. Les diodes servent à éviter un retour de courant depuis la ligne sélectionnée vers une autre qui ne l'est pas (court-circuit) :
Figure 5.10: Les diodes
Réalisation pratique   Les connexions entre les lignes et les colonnes peuvent par exemple être réalisées à l'aide de transistors MOS dont on intègre ou non la grille au moment de la fabrication de la puce :
Figure 5.11: En pratique
Cependant il existe des types de ROM effaçable ou reprogrammable. La terminologie suivante est adoptée:
  • ROM (Read Only Memory): programmées en usine, ne peuvent pas être reprogrammées;
  • PROM (Programmable Read Only Memory): programmable une seule fois par l'utilisateur, ne peuvent pas être reprogrammées par la suite. Les PROMs sont des mémoires non volatiles, dont le contenu comme dans le cas des ROMs, est défini une fois pour toutes. Toutefois, contrairement aux ROMs, elles sont programmables (1 seule fois), par l'utilisateur. Il existe différents types de PROMs. Les noeuds de la matrice peuvent comprendre soit des fusibles soit des jonctions ayant une faible tension de claquage. Dans les deux cas, la programmation s'effectue en sélectionnant l'adresse désirée, en présentatnt la donnée sur les lignes de sortie, et en alimentant pendant un bref instant (quelques centaines de ms) le circuit avec une tension élevée (10 à 15 V suivant les cas), ce qui a pour effet de faire fondre le fusible (ouverture du circuit) ou de claquer la jonction (fermeture du circuit). Ce processus est évidemment irréversible.
  • EPROM (Erasable Programmable Read Only Memory): également appelées UV-PROM, elles sont programmables par l'utilisateur et effaçables par effet photo-électrique à l'aide d'UV captés par une fenêtre située au dessus du boîtier mémoire. Elles sont programmables par octets, mais effaçable en globalité.
    Le processus est réversible en irradiant la puce aux rayons ultraviolets pendant plusieurs minutes, ce qui décharge la grille par effet photoélectrique.
    Les EPROMs sont des mémoires non volatiles programmables puis effaçables par l'utilisateur. Dans ce cas les noeuds de la matrice sont constitués de transistors MOS à grille isolée. Cette grille peut être chargée par influence en appliquant une tension importante (10 à 15 V) entre le drain et le substrat. Une fois chargée, elle peut conserver sa charge de manière quasiment indéfinie, ce qui rend le transistor passant.
  • EEPROM (Electricaly Erasable Programmable Read Only Memory): elle peut être effacée électriquement (plus pratique que les EPROM) et reprogrammée (octet par octet). Le nombre de reprogrammations est limité (de 100000 à 1000000 de fois) mais la plupart du temps suffisant.
  • Flash EPROM (Flash Erasable Programmable Read Only Memory): La mémoire flash offre les avantages d'une RAM et d'une ROM, c'est à dire qu'elle est non volatile et qu'elle est accessible en lecture mais aussi en écriture et offre un temps d'accès plus rapide qu'une ROM, mais plus lent qu'une RAM tout en ayant une consommation faible.
    Elle est idéale pour de nombreuses applications embarquées (appareil photos numériques, téléphonie, mini PC, lecteurs MP3, clés USB,...). Plus de 50% des mémoires volatiles sont des mémoires flash.
    Elle est programmable et effaçable électriquement comme les EEPROM (mais par secteur entier et pas seulement par octets), elle offre cependant un meilleur compromis vitesse d'accès/capacité.

5.6  Associations mémoires (TD)

Avec l'augmentation de la taille de mots machine que peut traiter un processeur et des vitesses de traitement, il faut utiliser des mémoires avec une capacité de plus en plus importante et une largeur compatible avec la taille de mot machine du processeur.
Pour éviter de recréer à chaque fois de nouvelles mémoires, on peut recourrir à associer des mémoires de plus faibles largeur et espace adressable.
Les associations mémoires (série pour augmenter la largeur mémoire et parallèle pour augmenter la capacité mémoire) seront étudiées au cours d'un TD sur les associations mémoires.

5.7  Cas particuliers

Nous avons vu les principes généraux de fonctionnement des mémoires. Il convient cependant d'étudier certains cas particuliers que l'on retrouve dans la plupart des architectures à microprocesseurs.

5.7.1  La pile système (Stack)

La pile est une partie de la mémoire centrale (la RAM), elle est utilisée pour stocker temporairement des valeurs. Plus précisément, il s'agit d'une zone en mémoire principale et d'un pointeur qui conserve l'adresse du sommet de la pile.
En effet, comme son nom l'indique, on obtient une pile en empilant des données les unes sur les autres (imaginez une pile d'assiettes). Seule la donnée au sommet de la pile est accessible, c'est donc celle-ci qui est repérée par son adresse à l'aide du pointeur de pile (SP: Stack Pointer). Pour accèder à une donnée (une assiette) dans la pile, il faut donc avoir dépiler toutes les données qui lui sont au dessus en mémoire.

L'ordre d'empilement est l'inverse de l'ordre de dépilement. Ainsi le dernier entré est le premier sorti, on parle de pile LIFO (Last In First Out).
En langage symbolique, on trouve deux instructions permettant de gérer la pile système:
  • PUSH registre: met le contenu du registre au sommet de la pile;
  • POP registre: retire la valeur en haut de la pile pour la mettre dans un registre.
La pile est souvent utilisée pour sauvegarder temporairement le contenu des registres.

5.7.2  Pagination - Segmentation

L'espace mémoire adressable d'un microcontrôleur peut être relativement important.
Cet espace mémoire est découpé de deux façons (le plus souvent complémentaires):
  • la segmentation: les programmes sont découpés en blocs. Chaque bloc comporte un nombre d'instructions variable. Chaque bloc est appelé segment. (Le découpage peut être fait de manière à ce que tous les segments aient la même taille);
  • la pagination: la mémoire est divisée en blocs appelés pages. Les programmes sont alors découpés en pages de longueur fixe.
Exercice 1 Le microcontrôleur C167 d'Infinéon possède un espace adressable de 16Mio qui est segmenté et paginé.
  1. Donner la taille de son bus d'adresse;
  2. La mémoire est organisée en 256 segments de taille identique. Trouver la taille de ces segments;
  3. Sachant que les pages ont une taille fixe de 16 Kio, trouver le nombre de pages par segment.
Remarque 18 La pagination et la segmentation de la mémoire sont intéressantes pour travailler avec des modes d'adressage logiques (Segment 1, page 6, offset 37) plus simples à gérer que les adresses physiques (représentées en hexadécimal: 0x18025).

5.8  En pratique

Les barrettes mémoires

Il existe de nombreux types de mémoires vives. Celles-ci se présentent toutes sous la forme de barrettes de mémoire enfichables sur la carte mère. Les premières mémoires se présentaient sous la forme de puces appelées DIP (Dual Inline Package). Désormais les mémoires se trouvent généralement sous la forme de barrettes, c'est-à-dire des cartes enfichables dans des connecteurs prévus à cet effet. On distingue deux types de barrettes de RAM :
  • les barrettes au format SIMM (Single Inline Memory Module) : il s'agit de circuits imprimés dont une des faces possède des puces de mémoire. Il existe deux types de barrettes SIMM, selon le nombre de connecteurs :
  • Les barrettes SIMM à 30 connecteurs (dont les dimensions sont 89x13mm) sont des mémoires 8 bits qui équipaient les premières générations de PC (286, 386).
    Figure 5.12: Premier exemple de carte mémoire SIMM.
  • Les barrettes SIMM à 72 connecteurs (dont les dimensions sont 108x25mm) sont des mémoires capables de gérer 32 bits de données simultanés. Ces mémoires équipent des PC allant du 386DX aux premiers pentiums. Sur ces derniers le processeur travaille avec un bus de données d'une largeur de 64 bits, c'est la raison pour laquelle il faut absolument équiper ces ordinateurs de deux barrettes SIMM. Il n'est pas possible d'installer des barrettes 30 broches sur des emplacements à 72 connecteurs dans la mesure où un détrompeur (encoche au centre des connecteurs) en empêche l'enfichage.
    Figure 5.13: Deuxième exemple de carte mémoire SIMM.
  • Les barrettes au format DIMM (Dual Inline Memory Module) sont des mémoires 64 bits, ce qui explique pourquoi il n'est pas nécessaire de les apparier. Les barrettes DIMM possèdent des puces de mémoire de part et d'autre du circuit imprimé et ont également 84 connecteurs de chaque côté, ce qui les dote d'un total de 168 broches. En plus de leurs dimensions plus grandes que les barrettes SIMM (130x25mm) ces barrettes possèdent un second détrompeur pour éviter la confusion.
A noter que les connecteurs DIMM ont été améliorés afin de permettre une insertion facile des barrettes grâce à des leviers situés de part et d'autre du connecteur.
Figure 5.14: Exemple de carte mémoire DIMM
Pour reconnaître une barrette de mémoire, vous devez tout d'abord identifier le type de barrette que vous pouvez mettre dans votre ordinateur. Pour cela, allez jeter un oeil du côté des emplacements mémoire (les bancs). Si vous avez deux détrompeurs, il s'agit soit de SDRAM soit de RAMBUS. La RAMBUS à ses deux détrompeurs très rapprochés (voir ici), alors que la SDRAM (voir ici) en à deux mais éloignés. La SDRAM-DDR (voir ici) n'a qu'un détrompeur situé presque au milieu des bancs.


Identifiez aussi la barrette : des informations peuvent être inscrites sur une étiquette collée dessus. Dans le cas contraire, On peut aussi se fier à la hauteur des chips : les chips sont très peu épais, environ 1 mm (c'est de la mémoire « récente » : SDRAM, SDRAM-DDR, SDRAM-DDR2, DR-RAM, etc.), si les chips sont assez épais, plus de 2 mm (c'est de la SIMM, SIMM-EDO, etc.). On peut aussi compter le nombre de broches : 30 (c'est de la SIMM-30), 72 (c'est de la SIMM-72 ou SIMM-EDO), 168 (c'est de la SDRAM), 184 (c'est de la SDRAM-DDR, SDRAM-DR2, DR-SDRAM), 240 (c'est de la SDRAM-DDR2). Enfin, pour savoir si votre barrette est une DIMM, SO-DIMM et Micro DIMM, il suffit de mesurer la longueur de la barrette. Avec 133 mm, votre barrette sera une DIMM. 67 mm pour une SO-DIMM 144 broches, 39 mm pour la Micro DIMM 144 broches et 44 mm pour la Micro DIMM 172 broches (note : les mesures sont arrondies au mm le plus proche).


Choisir de l'ECC ou de la NON-ECC ?
La mémoire enregistrée ECC (Error Correction Code) est la mémoire à correction d'erreur, elle est à réserver en général aux serveurs ou aux stations de calcul. La mémoire ECC à parité ajoute certaines fonctionnalités pour vérifier si les données en mémoire ont été corrompues. La mémoire ECC étend ces fonctionnalités et tente de corriger certaines erreurs de corruption de bits à la volée. Cette option s'applique surtout aux ordinateurs. La plupart des autres architectures requièrent simplement de la mémoire ECC ou à parité. Par exemple, le Pegasos I a son chipset VIA qui ne gère que l'ECC. Plusieurs machines ne démarreront même pas avec de la mémoire sans parité. Si vous n'utilisez pas de la mémoire ECC ou à parité, vous aurez peut-être de la corruption de données et d'autres anomalies. Plusieurs fabricants de « mémoire à faible coût pour PC » ne font même pas une variété ECC ! Ce qui vous aiderez à les éviter.


Les fabricants d'ordinateurs vendent souvent plusieurs gammes de produits, organisées en serveurs et en stations de travail. Les serveurs vont contenir de la mémoire ECC dans leur architecture. Les fabricants de stations de travail Unix utilisent de la mémoire à parité et maintenant ECC depuis plusieurs années dans toutes leurs gammes de produits. Cette dernière offre aussi théoriquement un moyen de détecter des modifications semi-aléatoires causées par certains rayonnements (rayonnements alpha). Utiliser de la mémoire ECC sur un ordinateur dont le contrôleur ne le gère pas, est une aberration. Prenez donc une mémoire NON-ECC qui sera moins chère et tout aussi efficace dans votre cas.

Chapitre 6
Architecture des (micro)processeurs

Dans ce chapitre, nous décrivons précisément l'architecture des processeurs et précisons le rôle de leurs principaux constituants.
Cette description étant faite en toute généralité, elle ne décrit pas l'architecture d'un processeur particulier mais les constituants de base de tout processeur. Pour connaître l'architecture précise d'un processeur particulier, il n'y a pas d'autre solution que de se référer à son manuel.

On rappelle que le rôle d'un processeur est d'exécuter des programmes. Un programme est une suite d'instructions élémentaires stockées en mémoire. Le processeur exécute donc séquentiellement ces instructions.

6.1  Schématique générale

La schématique générale d'un microprocesseur est donnée dans la figure .
Figure 6.1: Exemple de structure interne d'un processeur
On distingue les composants suivants:

6.1.1  L'horloge

Son rôle est de cadencer le microprocesseur. C'est le métronome du système permettant de synchroniser les communications avec les autres éléments du système telle que la mémoire.
L'horloge peut être générée à l'aide d'un oscillateur à quartz (cristal à quartz souvent repéré par son nom anglais XTAL) ou par l'utilisation d'une PLL (Phased Lock Loop) qui produit des multiples de la fréquence d'oscillation du quartz.
Exemple 17 C'est le cas dans le C167 d'Infineon (Siemens). Un multiplexeur permet de choisir si on veut utiliser la fréquence du quartz ou un de ses multiples.
Le signal produit est un signal créneau dont chaque front montant correspond à des tops d'horloge.
Plus la vitesse de l'horloge est élevée, plus le μP peut exécuter les instructions de base à un rythme élevé. La contre-partie est que cela génère des transitions plus fréquemment au niveau des transitors et donc une consommation de courant plus importante ce qui implique un échauffement du circuit d'où la nécessité d'avoir une solution de refroidissement du processeur adaptée.

6.1.2  Les bus de communication

Les instructions composant le programme que doit exécuter le processeur se trouvent à l'extérieur de celui-ci, dans la mémoire. Le processeur doit donc communiquer avec la mémoire. Cette communication se fait par l'intermédiaire de routes appelées bus de communication.
Pour savoir à quel endroit de la mémoire regarder, le processeur utilise des adresses mémoires pour repérer les cases contenant les instructions et les données du programme.
Les adresses circulent sur un bus de communication appelé bus d'adresse. Une adresse ne circule que du processeur vers la mémoire. Le bus d'adresse est donc un bus unidirectionnel.

Une fois l'adresse choisie, une donnée (instruction ou donnée du programme) transite entre le processeur et la mémoire.
Ces données transitent sur un bus de communication appelé bus de donnée. Une donnée peut aller du processeur vers la mémoire (écriture) ou de la mémoire vers le processeur (lecture). Le bus de donnée est donc bidirectionnel.

6.1.3  Les registres

Le processeur contient un certain nombre d'éléments mémoires directement accessibles (de l'ordre de la dizaine à plusieurs dizaines de milliers) en fonction de la complexité/généricité/performance du processeur. Ces éléments mémoires sont appelés des registres. Le principal avantage, dû au fait qu'ils sont accessibles à l'intérieur du processeur, fait que l'UAL et l'unité de commande peuvent manipuler ces données à grande vitesse.
Parmis les registres les plus importants (qu'on retrouve dans la plupart des architectures à microprocesseurs), on distingue:
  1. les registres tampons: ils sont à l'interface entre le processeur et la mémoire et permettent de stocker temporairement les communications mémoire/processeur. Comme la mémoire et le processeur s'échangent deux principaux types d'information (adresses et données), on distinguera:
    • les registres tampons d'adresse;
    • les registres tampons de données;
  2. les registres de travail: stockent les différentes données en cours de traitement par le processeur. Leur nombre est variable et ils possèdent chacun leur nom propre qui permet au programmeur de les manipuler en langage machine. On distingue:
    1. Les registres de données (contiennent une donnée):
      • le registre accumulateur (ACC) ou registre de travail situé dans l'UAL: stocke les données faisant l'objet de calculs arithmétiques et contient les résultats de ces calculs;
      • le registre d'état (PSW: Processor Status Word) situé dans l'UAL: lorsqu'une division par zéro est détectée, un dépassement de capacité (overflow) survient, ou tout autre évènement indiquant un résultat a priori inattendu, un bit particulier de ce registre (par exemple les bits indicateurs d'état: Z,N,C ou V) est positionné à 1. On parle de drapeau (flag) hissé pour représenter le fait qu'un tel évènement se produit;
      • le registre d'instruction (RI): contient le code en langage machine de l'instruction qui doit être exécutée;
    2. Les registres d'adresse (contiennent une adresse):
      • Le Compteur Ordinal (CO) ou Program Counter (PC) ou Instruction Pointer (IP): contient l'adresse mémoire de la prochaine donnée à importer dans le processeur;
      • Le pointeur de pile (SP: Stack Pointer): les registres généraux du processeur ne sont pas en nombre suffisant pour assurer la bonne marche du processeur (appel à des sous programmes, interruptions notament). Pour sauvegarder toutes les données utiles, le processeur utilise la mémoire centrale de l'ordinateur. Plus particulièrement, il utilise une partie précise de la mémoire centrale appelée pile mémoire (stockée sur une plage d'adresse précise). Pour accéder à cette mémoire, le processeur utilise un registre d'adresse appelé pointeur de pile (SP).
Mais il existe de nombreux autres registres dont le nombre varie selon le processeur. On peut citer par exemple (pour le C167 d'infineon):
  • un registre de configuration du système: SYSCON;
  • des registres de contrôle (contrôle de l'accès à la mémoire externe): BUSCONx et ADDRSELx;
  • un registre de pointeur de contexte: CP;
  • ... (se référer à la documentation ad hoc).

6.1.4  L'unité de contrôle et le décodeur

Le décodeur a pour rôle de récupérer l'instruction codée en langage machine dans le registre d'instruction (RI) et de la décoder pour la traduire en une instruction élémentaire compréhensible par le processeur (c'est à dire une instruction de son jeu d'instructions).
L'unité de contrôle est l'unité qui a la vision globale de la bonne marche du processeur, c'est le chef d'orchestre du système. Elle est en charge de traduire l'instruction à exécuter en une séquence synchronisée d'ordres simples à destination des différentes unités actives.
Exemple 18 Supposons que l'instruction présente dans le registre (RI) soit décodée en une instruction d'addition, le rôle de l'unité de contrôle est d'acheminer les deux opérandes dans les registres adéquats de l'unité arithmétique et logique, puis d'ordonner à celle-ci de réaliser l'opération d'addition, puis de récupérer le résultat du calcul pour le placer dans un registre approprié. Et enfin d'indiquer qu'il faut traiter l'instruction suivante (mise à jour du registre IP).
L'unité de contrôle génère également tous les signaux de synchronisation internes ou externes (bus de commandes ou bus de contrôle) au microprocesseur.

6.1.5  Unité Arithmétique et Logique (UAL)

L'UAL (ALU en anglais) a pour rôle le traitement des calculs de type:
  • opérations logiques: AND, OR, NAND, décalages, ...
  • opérations arithmétiques: additions, soustractions, multiplications, divisions.
    Les opérations flottantes (faites sur des nombres rééls) sont souvent réalisées par une unité dédiée appelée coprocesseur arithmétique ou FPU (Floating Point Unit).
On rappelle que deux registres sont associés à l'UAL:
  • Le registre d'état: contenant en particulier les bits d'états Z,N,C,V
  • Le registre acccumulateur: stocke les résultats intermédiaires des calculs faits par l'UAL.
Même si c'est l'unité responsable des calculs, elle n'est qu'un élément exécutant les ordres que lui donne le séquenceur.

6.2  Jeu d'instructions

Comme nous l'avons mentionné, un processeur ne peut exécuter qu'un nombre limité d'instructions élémentaires. Ces instructions définissent son jeu d'instructions.

6.2.1  Types d'instructions

Instructions d'affectation

Ce sont des instructions indiquant un transfert entre la mémoire et un des registres principaux du processeur.
Lorsque le transfert est effectué de la mémoire vers le CPU, on parle de lecture en mémoire. Lorsque le transfert est effectué du CPU vers la mémoire, on parle d'écriture en mémoire.

Instructions arithmétiques et logiques

Ce sont des instructions mettant en jeu une donnée et le registre accumulateur de l'UAL. Le résultat de cette opération est placée dans le registre accumulateur.
Exemple 19 Quelques exemples d'instructions arithmétiques et logiques:
  • addition (ACC ← ACC + donnee);
  • soustraction (ACC ← ACC − donnee);
  • incrémentation de l'accumulateur (ACC ← ACC + 1);
  • decrémentation de l'accumulateur (ACC ← ACC −1);
  • décalages à droite ou à gauche.

Instructions de comparaison

Comparaison du registre ACC à une donnée et positionnement des indicateurs d'état.
Exemple 20 L'opération ACC − donnee est calculée, le résultat est perdu mais le bit indicateur d'état N est mis à 1 dans le registre d'état si ACC < donnee.

Instructions de branchement

Ces instructions apparaissent lorsqu'il y a des boucles
(do{...}while(condition), while(condition){...}, ...)
ou des tests
(if(condition){...} else {...}, ...).
Dans ce cas, le processeur ne doit plus suivre l'ordre séquentiel des instructions placées en mémoire mais doit effectuer des sauts (en avant ou en arrière). La prochaine instruction à exécuter étant repérée en mémoire par le registre IP, c'est ce registre qui est modifié lorsqu'un saut doit être effectué.
On distingue deux types de branchements:
  • branchements inconditionnels: IP← @instruction;
  • branchements conditionnels: Si une condition est satisfaite, alors branchement, sinon passage simple à l'instruction suivante.

6.2.2  Codage des instructions

Les instructions et leurs opérandes sont stockées en mémoire. Cet ensemble de données composent le programme qui doit être exécuté.
Comme toute information, une instruction admet un codage binaire. Dû à la structure de la mémoire (organisée en ensemble d'octets), une instruction sera représentée par un nombre entier d'octets.
On distingue deux champs qui permettent de coder une instruction:
  • le code opération indiquant au processeur quelle instruction réaliser;
  • le code opérande qui contient la donnée ou la référence à une donnée en mémoire (son adresse).

6.2.3  Temps d'exécution

Chaque instruction nécessite un certain nombre de cycles d'horloges pour s'effectuer. Le nombre de cycles dépend de la complexité de l'instruction. Il dépend également de l'endroit où le processeur doit récupérer la donnée. Il est plus long d'accéder à la mémoire qu'aux registres du processeur.
La durée d'un cycle d'horloge dépend de la fréquence d'horloge utilisée. Plus l'horloge bat rapidement, plus un cycle est court et plus on exécute un grand nombre d'instructions par seconde.

6.2.4  Architectures CISC et RISC

Plus un processeur dispose d'instructions cablées (donc un jeu d'instruction important contenant à la fois des instructions de base et des instructions évoluées) plus celles-ci sont effectuées rapidement.
Cette efficacité se paie par un coût important en terme de silicium. De plus, ces instructions nécessitent souvent plus d'un cycle d'horloge pour être réalisées, ce qui ralentit le temps d'exécution de l'instruction.
Un processeur comportant ce type d'instructions complexes est appelé processeur à architecture CISC (Complex Instruction Set Computer, c'est à dire calculateur à jeu d'instructions complexe).
Ce type d'architecture est réservé aux processeurs spécialisés dans un type particulier de traitement qui rend intéressant le fait de câbler des instructions évoluées.

Pour les processeurs possédant un jeu d'instructions réduit aux instructions les plus basiques, on parlera de processeurs à architecture RISC (Reduced Instruction Set Computer, c'est à dire calculateur à jeu d'instructions réduit). La plupart des processeurs génériques aujourd'hui sont conçus selon cette architecture, notament pour son coût réduit et le fait que les instructions basiques peuvent être exécutées en un cycle d'horloge.

6.3  Pipeline

Plus un processeur peut exécuter d'instructions rapidement plus il est performant. Nous avons vu que la fréquence d'horloge utilisée était un des facteurs de performance du processeur. Ce n'est pas le seul.
On rappelle que pour exécuter une instruction, le processeur suit 4 étapes principales:
  1. fetch;
  2. decode;
  3. execute;
  4. writeback
Deux politiques sont alors possibles:
  • Une instruction démarre uniquement lorsque l'instruction précédente est terminée.

    Fetch I1 I2
    Decode I1 I2
    Execute I1 I2
    Writeback I1 I2

    → time
  • Dès qu'une étape d'une instruction est terminée, on effectue la même étape pour l'instruction suivante:

    Fetch I1 I2 I3 I4 I5
    Decode I1 I2 I3 I4 I5
    Execute I1 I2 I3 I4 I5
    Writeback I1 I2 I3 I4 I5

    → time
Cette seconde approche, appelée technique du pipeline, permet d'effectuer beaucoup plus d'instructions dans le même temps.
Attention, les instructions sont toujours exécutées séquentiellement mais à un instant donné, plusieurs instructions sont traitées en même temps.
Exemple 21 Par exemple au temps 4, I1 est en phase writeback, I2 en phase execute, I3 en phase decode et I4 en phase fetch. Aucune instruction n'est terminée, elles sont toutes en cours de traitement (à des stades différents) par le processeur.
Remarque 19 Notez que le pipeline n'est pas toujours possible, en particulier lorsqu'il y a une dépendance entre deux instructions. Par exemple lorsque une instruction doit attendre le résultat d'un long calcul (plusieurs cycles d'horloge) pour être exécutée, cela provoque des trous dans le pipeline.
Ce n'est pas le cas le plus grave, lorsqu'il y a une interruption (logicielle ou matérielle, cf TP avec Christian PAUZE) il faut purger le pipeline, c'est à dire sauvegarder le contexte des instructions en cours d'exécution. Cette sauvegarde se fait en utilisant la pile et un pointeur de contexte pour se situer dans la pile.

Chapitre 7
Les pointeurs

Cette partie est tirée de 5.

7.1  Présentation

Lorsque l'on déclare une variable, on réserve (on dit que l'on älloue") une place en mémoire (par exemple sur le disque dur) pour elle (plus ou moins grande suivant le type). Par exemple, si on écrit int x;, on peut représenter la mémoire comme ci-dessous :
Le numéro 1041 en face de la case x indique où est située la variable x dans la mémoire, en un mot son ädresse". A chaque instant de l'exécution d'un programme, l'ordinateur sait où sont situées les variables qui sont utilisées, il y a quelque part une structure (mettons, un tableau), qui récapitule tout ça. Par exemple supposons que l'on a déclaré un caractère char y, on se retrouve avec le schéma suivant :
Maintenant l'initialisation ne pose pas de problème, si on veut donner à y la valeur 'a', il suffit d'écrire y = 'a'; et l'ordinateur mettra le charactère 'a' dans l'adresse (la case) 1045. Idem pour l'instruction x = 3; :
On peut bien entendu récupérer l'adresse de x, avec &x. Cette adresse est notamment utilisée pour stocker une valeur en la demandant à l'utilisateur : l'instruction scanf("%d",&x);, (x a préalablement été déclarée, donc son adresse existe) va demander une valeur entière à l'utilisateur, et la stocker à l'adresse de x, c'est-à-dire à l'adresse 1041. L'ancienne valeur stockée à cette adresse, si il y en avait une, est écrasée.

7.2  Utilisation des pointeurs

Petit rappel sur les appels de fonctions   Supposons que l'on ait écrit une fonction f (pour une procédure, c'est pareil) qui prend en paramètre deux arguments, ainsi que le programme principal suivant :
int f (int a, char c)
{
	int tmp
	if (c == 'z')
		{
		 a = 2+a;
		 tmp = a;
		}
	else
		{
		  a = 4+a;
		  tmp = a;
		}
	return tmp;
}


int main(void)
{
	int x, char y;
	x = 4;
	y = 'z';
	printf("%c",f(x,y));
	return 0;
}

Lorsque l'on exécute la fonction main, on appelle la fonction f sur l'entier x, qui vaut 2 à ce moment là, et le caractère y, qui vaut 'z' à ce moment là. La fonction f est donc appelée sur les paramètres (2,'z'), et tout se passe comme si on avait directement fait appel à la fonction de la façon suivante : f(2,'z').
En particulier, lorsque la fonction f est exécutée, on ïnvente" un paramètre a, qui est un entier et qui vaut 2, et un paramètre c qui est un caractère et qui vaut 'z', on leur fait subir les calculs à l'intérieur de la fonction f, ils sont utiles pour calculer la valeur de la fonction, mais sont oubliés ensuite. En aucun cas la valeur 6 ne sera stockée quelque part lorsque la fonction f aura fini de s'exécuter. NI DANS a, NI DANS x, CETTE VALEUR EST OUBLIéE.
Passage par adresse   Quelquefois on aimerait bien pouvoir modifier les paramètres d'une fonction. Par exemple, comme on ne peut pas retourner deux entiers à la fois comme résultat d'une fonction, il peut être commode de pouvoir modifier les paramètres entiers d'une fonction, tout en enregistrant ces modifications. Pour celà, on va utiliser les pointeurs :
void g(int *pa, int *pb)
{
	*pa = *pa + 1;
	*pb = *pb + 1;
}

int main(void)
{
	int a,b;
	a = 0;
	b = 3;
	g(&a,&b);
	printf("%d %d",a,b);
	return 0;
}

La fonction g ne prend plus en argument deux entiers, mais deux pointeurs sur les entiers, autrement dit elle prend en argument deux ädresses de variables entières". int *pa désigne la valeur entière pointée. Remarquez l'utilisation des étoiles.
Comment se déroule l'appel ? La fonction g ayant besoin de deux adresses, on lui donne les adresses de a et b. La fonction g agit sur les contenus des cases d'adresses &a et &b, autrement dit la fonction g a enregistré les modifications faites sur les variables a et b. Le programme imprime donc 1 4.
Remarque :   Quand on utilise des tableaux comme paramètres de fonctions/procédures, on a vu que les modifications étaient enregistrées. Heureusement car une fonction ne peut pas retourner un tableau. Mais pourquoi donc ? Parce que, en fait, lorsque l'on écrit int t[N], on écrit en fait int *t[0] (autrement dit c'est un pointeur sur la première case du tableau).

7.3  échange de deux variables

Soit le programme suivant :
void echange(int *x, int *y)
{
	int tmp;
	tmp = *x;
	*x = *y;
	*y = tmp;
}

int main (void)
{
	int a,b;
	a = 5;
	b = 10;
	exchange(&a, &b);
	printf("a = %d, b = %d\n",a,b);
	return 0;
}

La procédure prend en paramètre deux adresses (pointeurs sur des entiers), et échange les valeurs pointées par ces adresses. Le programme principal commence par déclarer et initialiser deux variables entières a et b, autrement dit on a en mémoire quelque chose comme ça :
Ensuite, le programme principal appelle la procédure echange sur les adresses des variables a et b, et donc celà échange les contenus des cases :
Le programme principal fait ensuite une impression à l'écran du contenu des variables a et b, qu'il va chercher aux adresses qu'il connaît, donc il imprime : a = 10, b = 5.

Footnotes:

1Voir la fin du chapitre.
2source: http://www.commentcamarche.net/contents/pc/memoire.php3
3http://www.commentcamarche.net/contents/pc/ram.php3
4source: http://tic01.tic.ec-lyon.fr/ muller/trotek/cours/logique/
5source: http://www.lamsade.dauphine.fr/~manouvri/C/CoursTDC_MM.html


File translated from TEX by TTHgold, version 4.00.
On 25 Oct 2011, 17:13.
SPIP | | Plan du site | Suivre la vie du site RSS 2.0

Habillage visuel © Kozlika sous Licence GPL