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 !
Les processeurs avec un seul espace d'adressage.
Pour le moment, considérons le cas intuitif où on ne dispose que d'un seul espace d'adressage. Même si on omet les portions inoccupées de l'espace d'adressage, la RAM n'est pas la seule occupante de l'espace d'adressage. On y trouve aussi la mémoire ROM, les périphériques et d'autres choses encore.
Les architectures Von Neumann.
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.
Sur ces architectures, mémoire ROM et mémoire RAM sont reliées au processeur par un bus unique. Toute la difficulté est de faire en sorte que la mémoire RAM et la mémoire ROM ne répondent pas en même temps quand on envoie une adresse sur le bus. Nous verrons quelle solution est retenue dans le chapitre sur l'adressage des périphériques.
Les entrées-sorties mappées en mémoire.
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.
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. Et mine de rien, quand on a une carte graphique avec 512 mégaoctets de mémoire intégrée, une carte son, une carte réseau PCI, des ports USB, un port parallèle, un port série, des bus PCI Express ou AGP, et un BIOS à stocker dans une EEPROM/Flash, ça part assez vite.
Un autre défaut de cette méthode apparait sur les processeurs disposant de mémoire cache. Pour rappel, le cache est une petite mémoire censée accélérer les accès à la mémoire RAM, dans laquelle on stocke des copies des données en RAM. Or, pour le cache, une adresse mémoire est une adresse mémoire: le cache ne sait pas si l'adresse correspond à un périphérique ou non, et il mettra en cache son contenu automatiquement. Mais le problème est que les adresses liées aux périphériques, qui correspondent à des registres ou à la mémoire des périphériques, peuvent être modifiés sans que le cache soit mis au courant. Le périphérique peut à tout instant modifier son état ou sa mémoire interne, ce qui se répercute automatiquement dans l'espace d'adressage, mais rien de tout cela n'est transmis au cache. La solution est que les accès aux périphériques ne doivent pas passer par l’intermédiaire du cache, si on veut qu'ils marchent comme ils le doivent. Cela demande d'adapter le cache et le matériel pour que accès aux périphériques mappés en mémoire contournent le cache. Des adresses, voire des zones entières de la mémoire, sont marquées comme étant non-cachables. Toute lecture ou écriture dans ces zones de mémoire ira donc directement dans la mémoire RAM, sans passer par la ou les mémoires caches.
Nous reparlerons plus en détail des entrées-sorties mappées en mémoire dans un chapitre dédié à l'adressage des périphériques. Aussi, je ne détaillerais pas plus que ça cette technique dans ce chapitre.
La "memory map" d'un ordinateur avec un seul espace d'adressage.
Voici quelques exemples tirés de vrais ordinateurs existants. On voit qu'outre la mémoire RAM principale, des mémoires vidéo et même plusieurs mémoires ROM sont mappées en mémoire. Il y a un unique espace d'adressage qui contient tout ce qui est adressable : toutes les mémoires et tous les périphériques de l'ordinateur. Généralement, 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.
Un bon exemple est celui des ordinateurs PC un peu anciens, avec des processeurs x86. A l'époque, les processeurs x86 avaient des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. 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, alors que les octets restants forment la mémoire haute. La mémoire au-delà du premier mébioctet, la mémoire étendue, est apparue quand les processeurs x86 32 bits sont apparus.
Les vielles machines, notamment les premiers ordinateurs comme les Commodores et les Amiga et les vielles consoles de jeux, utilisaient cette méthode pour sa simplicité. Ces machines n'étaient pas comme les ordinateurs personnels, pour lesquels on a une variété de cartes graphiques ou de cartes sons différentes. Tous avaient la même configuration matérielle, le matériel était fourni tel quel, ne pouvait pas être changé ni upgradé. Toutes les commodores 64 avaient exactement le même matériel, par exemple : la même carte son, la même carte graphique, les mêmes périphériques. Cette standardisation faisait que cela ne servait à rien de limiter l'accès au matériel. 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 processeurs avec plusieurs espaces d'adressages.
Il existe des processeurs qui sont capables de gérer plusieurs espaces d'adressage. Cela peut paraitre surprenant, mais nous avons déjà abordé un exemple dans les chapitres précédents (essayez de deviner lequel). Toujours est-il que l'on peut se demander quelle est l'utilité d'avoir plusieurs espaces d'adressage. La raison est pourtant simple, et même intuitive. Avec un seul espace d'adressage, les périphériques et la ROM sont mappés dans l'espace d'adressage. Des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques. Si très peu de RAM est installé, alors ce n'est pas un problème : des adresses inutilisées sont détournées pour des choses utiles. Mais si on veut utiliser plus de RAM, les choses se compliquent. L'idée est d'utiliser plusieurs espaces d'adressage dans lesquels on ne met pas la même chose, d'utiliser des espaces séparés pour des utilisations distinctes. On peut par exemple utiliser un espace d'adressage séparé pour la RAM, un autre pour la ROM, un autre pour les périphériques, etc. En dire plus demande de détailler plusieurs techniques qui utilisent chacune un espace d'adressage séparé.
Les architectures Harvard.
Le premier cas d'espace d'adressage séparé est celui des architectures Harvard. Pour rappel, 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.
La mémoire ROM et la mémoire RAM sont reliées au processeur par deux bus séparés. L'avantage de cette architecture est qu'elle permet de charger une instruction et une donnée simultanément : une instruction chargée sur le bus relié à la mémoire programme, et une donnée chargée sur le bus relié à la mémoire de données. On évite aussi les complication liées au fait que la RAM et la ROM se partagent le même bus et que seule l'une des deux doit répondre en fonction de l'adresse envoyée.
L'espace d'adressage séparé pour les entrées-sorties.
Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour la mémoire. 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.
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 gérer les périphériques et adresser 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.
Le "bank switching".
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. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. L'espace d'adressage du processeur, l'ensemble des adresses possibles, est présent en plusieurs exemplaires de même taille appelés des banques. En répartissant les données utiles dans différentes banques, le processeur peut donc adresser beaucoup plus de mémoire. 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 à 4kibioctets. 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.
Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle est plus grande que l'adresse utilisée par le processeur, les bits manquants étant fournit par un registre configurable du processeur : le registre de banque. La banque adéquate est sélectionnée par la valeur du 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.
La mémoire virtuelle.
Enfin, nous devons parler d'une dernière technique qui utilise plusieurs espaces d'adressage. Elle est implémentée sur les processeurs modernes, afin de faciliter le travail des systèmes d'exploitation modernes. Les systèmes d'exploitation modernes sont capables de lancer plusieurs logiciels en même temps. Autant les anciens systèmes d'exploitation ne pouvaient exécuter qu'un programme à la fois, autant les systèmes d’exploitation modernes implémentent la "multiprogrammation", le fait de pouvoir lancer et exécuter plusieurs logiciels. Et cela marche même si un seul processeur est présent dans l'ordinateur, comme vous avez pu le constater. La raison à cela est que chaque programme est exécuté à tour de rôle très rapidement, au point où cela ne se voit pas. Les programmes s’exécutent à tour de rôle sur un même processeur, ils partagent la mémoire RAM, etc.
Un point important est le partage de la RAM entre les différents programmes. Le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer une ou plusieurs partitions mémoire, des blocs de mémoire réservés à un programme précis. Toutefois, cela amène un paquet de problèmes qu'il faut résoudre au mieux. Notamment, chaque programme peut être placé n'importe où en RAM, sans pouvoir prévoir à l'avance où. A vrai dire, sa position en RAM change à chaque exécution. Et cela pose un paquet de problèmes pratiques, comme on le verra plus bas. Notamment, les adresses des branchements et des données statiques ne sont jamais les mêmes d'une exécution à l'autre.
Pour faciliter la répartition de la RAM entre plusieurs programmes, certaines techniques de mémoire virtuelle utilisent un espace d'adressage par logiciel/programme. L'avantage est que chaque programme est seul au monde. Il peut lire et écrire à toutes les adresses disponibles à partir de l'adresse zéro. Chaque programme a accès à autant d'adresses que ce que le processeur peut adresser : on se moque du fait que des adresses soient réservées aux périphériques, de la quantité de mémoire réellement installée sur l'ordinateur, ou de la mémoire prise par d'autres programmes en cours d’exécution. Par contre, il s'agit là d'une illusion maintenue par le système d'exploitation, qui s'occupe de répartir les différents programmes et leurs données en RAM. On a toujours des partitions mémoires en RAM, mais les programmes eux-mêmes n'en voient rien. Mais la mémoire virtuelle amène son lot de problèmes, qui sont gérés par le processeur, avec une assistance de la part du système d'exploitation. Mais nous détaillerons cela plus en détail dans le chapitre sur la mémoire virtuelle.