Fonctionnement d'un ordinateur/L'espace d'adressage du processeur

L'espace d'adressage du processeur correspond à l'ensemble des adresses utilisables par le processeur. Par exemple, si je prends un processeur 16 bits, il peut adresser en tout 2^16 = 65536 adresses et l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas la mémoire réellement installée : s'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi dans ce chapitre qu'il est possible qu'un processeur ait plusieurs espaces d'adressages séparés. Et même si cela peut sembler contre-intuitif, nous allons voir que les architectures avec plusieurs espaces d'adressage sont plus simples à comprendre !

L'adressage de la RAM et de la ROM

modifier

Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels succincts sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage de la mémoire RAM et de la mémoire ROM : est-ce que les deux sont dans un seul espace d'adressage, ou est-ce qu'elles ont chacun leur propre espace d'adressage séparé.

Les architectures Von Neumann

modifier

Si on n'a qu'un seul espace d'adressage unique, il est utilisé pour adresser non seulement la mémoire RAM, mais aussi la mémoire ROM. On est alors face à une architecture Von Neumann, où un seul espace d'adressage est découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise.

 
Vision de la mémoire par un processeur sur une architecture Von Neumann.

Les architectures Harvard

modifier

Avec l'architecture Harvard, on a un espace d'adressage séparé pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM : le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille : l'un peut contenir plus de mémoire/adresses que l'autre. Il est par exemple possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Mais cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille.

 
Vision de la mémoire par un processeur sur une architecture Harvard.

L'adressage des périphériques

modifier

Si l'adressage des mémoires RAM/ROM est important, il faut aussi penser à l'adressage des périphériques. En effet, la communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et là encore, ces registres peuvent avoir un espace d'adressage séparé, ou être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications.

L'espace d'adressage séparé pour les entrées-sorties

modifier

Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires. Sur ce genre d'architectures, on trouve un espace d'adressage pour la mémoire RAM et la mémoire ROM, et un espace d'adressage spécialisé pour les périphériques et les entrées-sorties.

 
Bit IO.

Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement.

Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques.

Les entrées-sorties mappées en mémoire

modifier

Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d'entrées-sorties mappées en mémoire.

 
IO mappées en mémoire

On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste.

Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement.

Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie Resizable Bar, qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.

La memory map d'un ordinateur

modifier

Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes.

IO mappées en mémoire IO séparées
ROM mappée en mémoire (architecture Von Neumann) Un seul espace d'adressage Deux espaces d'adressage :
  • un pour les mémoires RAM/ROM ;
  • un pour les IO.
ROM séparée (architecture Harvard) Deux espaces d'adressage :
  • un pour la RAM et les IO ;
  • un pour les IO.
Trois espaces d'adressages :
  • un pour la RAM ;
  • un pour la ROM ;
  • un pour les IO.

Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. Avec un seul espace d'adressage, des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques. Et cela limite la quantité de RAM qui peut être réellement adressée en pratique.

Mais c'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, pour lesquels on ne peut pas adresser beaucoup de RAM. Par exemple, avec des adresses codées sur 16 bits, utiliser un espace d'adressage unique serait compliqué. On est déjà limité à 64 kibioctets de RAM, si on rajoute une ROM et beaucoup d'entrées-sorties, la quantité de RAM chute rapidement. Par contre, pour des adresses de 32 à 64 bits, l'usage d'un espace d'adressage unique est clairement la solution la plus simple. Une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. Détourner ces adresses inutilisées pour mapper une ROM ou des entrées-sorties permet de mieux utiliser l'espace d'adressage.

Avec un espace d'adressage unique, les adresses hautes sont réservées aux périphériques et aux mémoires ROM, alors que les adresses basses sont pour la RAM. La ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage.

 
Espace d'adressage classique avec entrées-sorties mappées en mémoire

Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire !

Les premiers micro-ordinateurs et consoles de jeux

modifier

Les vielles machines, notamment les premiers ordinateurs comme les Commodores et les Amiga et les vielles consoles de jeux, utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. De plus, de telles machines n'avaient pas de système d'exploitation, ou bien celui-ci était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple.

Les PC IBM x86

modifier
 
IBM PC Memory areas

Un autre exemple est celui des ordinateurs PC un peu anciens, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps, et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Au tout début, les processeurs x86 avaient des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. La mémoire adressable était alors peuplée par de la RAM, une ROM pour le BIOS, et des périphériques standardisés comme des cartes vidéos VGA/EGA. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux.

Mais par la suite, l'espace d'adressage s'est agrandit, pour passer de 20 bits à 24 bits, puis à 32 bits. L'espace d'adressage ayant grandit, les adresses au-delà du premier mébioctet ont été allouées à de la RAM. La ROM et les périphériques sont restés à leur place, dans le premier mébioctet. La mémoire au-delà du premier mébioctet est appelée la mémoire étendue. Elle est apparue quand les processeurs x86 32 bits sont apparus. La mémoire étendue n'est pas réservée pour une utilisation précise, les programmes et l'SO peuvent en faire ce qu'ils veulent.

Le premier mébioctet de mémoire est décomposé en deux portions de mémoire : les premiers 640 kibioctets sont ce qu'on appelle la mémoire conventionnelle, les octets restants forment la mémoire haute. La mémoire conventionnelle est réservée à la RAM. Elle est segmentée comme suit par le BIOS :

  • Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la BIOS Data Area, mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur.
  • Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution.

La mémoire haute est réservée aux ROM et aux péripériques, BIOS inclu :

  • Le bas de la mémoire haute est réservé pour communiquer avec les périphériques. On y trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe) , qui sont nécessaires pour les initialiser et parfois pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo, et éventuellement la mémoire d'autres périphériques comme la carte son.
  • Le sommet de la mémoire haute est réservé au BIOS.
 
Organisation Mémoire des vieux PC, à l'époque du DOS.

La memory map d'un processeur 64 bits

modifier

Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de tera-octets. Mais le bus d'adresse ne fait pas 64 bits, afin d'économiser des interconnexions, de simplifier le bus d'adresse. En réalité, il fait 48 bits dans le cas général, même si quelques processeurs utilisent 57 bits. Prenons le cas avec 48 bits, le plus courant actuellement. Il manque donc 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. Le fait de n'utiliser que des adresses réelles de 48 bits a des conséquences assez importantes.

Avec ce système, seules les 48 bits de poids faible codent une adresse, les autres bits sont gérés autrement. Sur les architectures 64 bits, la règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Impossible d'avoir un 0000 0100 1100 1100 dans les 16 bits de poids fort, seules les valeurs 1111 1111 1111 1111 et 0000 0000 0000 0000 sont autorisées. Les adresses qui respectent cette contrainte sont appelées des adresses canoniques. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous.

  • Les adresses canoniques basses ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses.
  • Les adresses canoniques hautes ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses.
  • Entre les deux, se trouvent des adresses non-canoniques, qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle.
Les futurs systèmes x86 devraient passer à des adresses de 57 bits, ce qui conservera la séparation en trois sections mais avec des frontières différentes.
 
Répartition des adresses entre noyau (jaune/orange) et programme (verte), sur les systèmes x86-64 bits, avec des adresses physiques de 48 bits.
 
Répartition des adresses entre noyau (jaune/orange) et programme (verte), sur les systèmes x86-64 bits, avec des adresses physiques de 57 bits.

Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des pointeurs tagués, à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses.

Un exemple est la technique du memory tagging, qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte.

Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : Top Byte Ignore. Chez Intel et AMD, il s'agit des fonctionnalités UAI (Upper Address Ignore) d'AMD et de LAM (Linear Address Masking) d'Intel.

La commutation de banques (bank switching)

modifier

Le bank switching, aussi appelé commutation de banque, permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des banques. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner.

Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée.

 
exemple de Bank switching.

Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le registre de banque. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables.