Fonctionnement d'un ordinateur/Les processeurs 8 bits et moins
Nous avons déjà abordé la notion de processeur 8, 16, 32, 64 bits. Elle désigne la taille des registres généraux, ou de l'accumulateur. De nos jours, les processeurs 32 et 64 bits sont la norme, mais les premiers micro-processeurs étaient des processeurs 4 et 8, avant de passer rapidement au 16 bits. La raison est que leur budget en transistor limité faisait qu'on ne pouvait pas utiliser une ALU trop grande : une ALU 8 bit prend deux fois moins de place et de transistors qu'une ALU 16 bits. Cependant, les processeurs 8 bits avaient de sérieuses limitations qui étaient compensées par diverses solutions. Dans ce chapitre, nous allons faire une revue rapide des processeurs 8 bits et de leurs fonctionnalités.
La principale limitation des CPU 8 bits était l'adressage : des registres de 8 bits limitaient l'espace d'adressage à seulement 256 adresses, soit à peine 256 octets ! Intuitivement, on se dit que 256 octets est purement impraticable, on ne peut rien faire d'intéressant avec seulement 256 de RAM + ROM. Et c'est partiellement vrai. L'adage veut que 16 bits est la taille minimale intéressante, la limite à partir de laquelle on a assez de mémoire RAM/ROM pour coder des applications intéressantes. Mais 256 octets de RAM est largement suffisant sur de nombreux systèmes embarqués simples s'en accommodent : des caisses enregistreuses, des systèmes de régulation de feu de circulation, etc.
Pour les applications demandant plus de mémoire RAM, les CPU de l'époque contournaient la limitation de 256 octets de RAM avec de nombreux stratagèmes. Dans ce qui va suivre, nous allons séparer les CPU 8 bits selon qu'ils utilisent une architecture Harvard ou une architecture Von Neumann. La raison est qu'une architecture Harvard permet en soi de contourner partiellement ce problème d'adressage. Les choses sont plus compliquées avec une architecture Von Neumann.
Les processeurs 8/16 bits à architecture Harvard
[modifier | modifier le wikicode]La première solution est d'utiliser une architecture Harvard. Le processeur 8 bit a alors deux espaces d'adressage : un pour la RAM, un autre pour la ROM. Et les deux utilisent des adresses de taille différente. La ROM est reliée à un bus d'adresse de 16 bits, alors que la RAM est elle connectée à un bus d'adresse de 8 bits. La RAM est donc limitée à 256 octets, alors que la ROM peut monter jusqu'à 64 kibioctets. Mine de rien, faire ainsi est largement suffisant.
Les processeurs 8 bits intègrent parfois les 256 octets de RAM directement dans le processeur. Vous noterez que 256 octets de RAM est très peu, comparable à ce qu'on trouve dans un banc de registres. Aussi, fusionner banc de registre et 256 octets de SRAM est une optimisation possible. Concrètement, cela veut dire que le processeur n'intégre pas une RAM séparée, mais a un banc de 256 registres de 8 bits. Beaucoup de processeurs en implémentent moins, avec par exemple seulement 64 octets de RAM, ou 32 octets, ou 128 octets.
L'avantage est que cela élimine le bus mémoire de la RAM, ce qui économise beaucoup de broches sur le circuit intégré du CPU. Et c'est ce qui explique que cette solution est beaucoup utilisée sur les microcontrôleurs peu puissants. Les exemples qui vont suivre devraient vous convaincre. Et pour les applications demandant plus de RAM, des optimisations permettent d'adresser plus de 256 octets de RAM. La plus connue est la commutation de banques, mais nous n'avons pas encore abordé celle-ci, et il faudra attendre le chapitre sur l'espace d'adressage pour cela.
Cependant, se limiter à 256 octets de RAM demande des compromis assez forts, pour économiser un maximum d'adresses mémoire. L'usage d'une architecture Harvard est une solution très intéressante, mais on peut aller plus loin en utilisant une architecture Harvard modifiée. Pour rappel, elles permettent de lire des constantes depuis la mémoire ROM. Les constantes manipulées par le programme sont mémorisées en ROM, lues depuis la ROM, au lieu d'être chargées en RAM. Une autre optimisation est d'utiliser un espace d'adressage séparé pour les entrées-sorties, à la place d'entrées-sorties mappées en mémoire qui détournent des adresses. Cependant, de nombreux processeurs 8 bits utilisent les entrées-sorties mappées en mémoire, comme on va le voir plus bas.
Avec ce genre de stratagèmes, 256 octets de RAM est largement suffisant sur de nombreux systèmes, à condition qu'on ait assez de ROM pour coder un programme digne de ce nom. Par exemple, de nombreux systèmes embarqués simples s'en accommodent : des caisses enregistreuses, des systèmes de régulation de feu de circulation, etc. Dans ce qui suit, nous allons voir quelques processeurs 8 bits de ce type. Deux exemples seront des microcontrôleurs très utilisés de nos jours.
Le Fairchild F8 et la console de jeu Fairchild Channel F
[modifier | modifier le wikicode]Un premier exemple est la Fairchild F8 de la défunte entreprise Fairchild Semiconductor. Il incorpore un processeur 8 bits, c’est-à-dire que le processeur fait des calculs sur des entiers de 8 bits maximum, une mémoire RAM de 64 octets, ainsi qu'une mémoire ROM de 1 kibioctet. Les 64 octets de RAM sont certes très peu, mais c'est suffisant pour certaines applications simples, comme les caisses automatiques de l'époque, ou des applications embarquées. Et de toute façon, il est possible d'ajouter de la mémoire RAM supplémentaire si les 64 octets de RAM intégrée ne suffisent pas. Le système était donc modulaire, adaptable selon les besoins, peu cher pour son implémentation minimale sans RAM en plus.
Vu qu'il s'agit d'un des tout premiers ordinateurs, il est sorti à une époque où la technologie était rudimentaire. À cette époque, le processeur était composé de pièces détachées qu'il fallait souder ensemble pour obtenir un processeur fonctionnel. Ce n'est que plus tard qu'est apparu le micro-processeur, qui regroupe un processeur complet dans un seul circuit intégré. Le résultat est que le processeur du Fairchild F8 est coupé en deux, l'unité de calcul et l'unité de contrôle sont placés dans des pièces détachées séparés.
Le microcontrôleur est composé de trois à quatre pièces détachées, certaines étant optionnelles. La première est le 3850 CPU, qui regroupe l'unité de calcul et la mémoire RAM intégrée. La seconde est le PSU (3851 Program Storage Unit), qui regroupe la mémoire ROM et l'unité de contrôle, program counter inclut. S'il y a besoin d'utiliser plus de RAM, il faut ajouter un circuit d’interface mémoire, qui est au choix le 3852 Dynamic Memory Interface (DMI) ou le 3853 Static Memory Interface, le premier étant connecté à une mémoire de type DRAM, l'autre à une SRAM (nous verrons ce que sont les DRAM et les SRAM dans le prochain chapitre).

Le système minimal est composé d'un PSU et d'un 3850, sans les circuits d'interface mémoire. Le tout donne un système avec un processeur 8 bit, 64 octets de RAM et 1 kibioctet de ROM, les deux mémoires étant intégrées dans le "processeur". Le processeur gérait 4 entrées-sorties, chacune étant reliée avec un bus de 8 bit au processeur. Au passage, en 1977, Mostek a créé un microcontrôleur qui regroupe un PSU et un 3850 : le Mostek 3870. Le système minimal n'avait pas de bus d'adresse, vu que la RAM était intégrée au processeur. C'est les 3852 et 3853 qui ajoutaient un bus d'adresse. Ils ajoutent un bus d'adresse de 16 bits, ce qui permet d'adresser 64 kibioctets de RAM.

Le Fairchild F8 a été utilisé dans la Fairchild Channel F, une des toute première console de seconde génération, à savoir des consoles qui utilisaient des cartouches de jeu. Avant les consoles de seconde génération, les consoles étaient non-programmables. Elles étaient conçues pour faire tourner un jeu précis, aucun autre, le jeu étant implémenté dans leurs circuits.
La Fairchild Channel F est composé du système minimal, auquel on a ajouté une carte son rudimentaire et 2 kibioctets de mémoire vidéo. La mémoire vidéo sert uniquement pour afficher des images à l'écran : on écrit l'image à afficher dedans, un circuit vidéo se charge de l'afficher à l'écran automatiquement. Le processeur ne fait qu'écrire dans cette RAM vidéo, il ne peut pas en lire le contenu. De la RAM supplémentaire peut cependant être ajoutée dans les cartouches de jeu.
Les microcontrôleurs PIC
[modifier | modifier le wikicode]Les microcontrôleurs PIC regroupent des microcontrôleurs allant de microcontrôleurs 8 bits aux performances très faibles, à des microcontrôleurs 32 bits bien plus puissants. Les microcontrôleurs PIC sont très utilisés dans l'embarqué ou d'informatique industrielle. Ils sont vendus à des volumes assez impressionnants, plusieurs millions par an. Et si vous avez ou êtes en train de faire des études d'électronique, je parie que vous les avez déjà vus en classe, ou que ce ne saurait tarder. Dans ce qui suit, nous allons nous concentrer sur les microcontrôleurs PIC 8 bits, à savoir les PIC 16, 16 et 18. Les PIC 24, qui sont des microcontrôleurs 16 bits, ne seront pas abordés.
La mémoire ROM des PIC va de 1024 instructions à 4096 instructions. En comparaison, la mémoire RAM des microcontrôleurs PIC ne peut pas dépasser 256 adresses, et elle est encore plus petite en pratique sur certains modèles. Pour donner un exemple, le PIC16F84 a une RAM composée de 68 "registres" de 8 bits. Le terme "registres" devrait vous faire comprendre que les 256 octets de RAM sont en réalité un banc de registre de 256 registres d'un octet chacun. Vu que la RAM et le banc de registre sont fusionnés, il n'y a pas de différences entre nom/numéro de registre et adresse mémoire. Les registres sont donc adressables, on y accède en lisant/écrivant à une adresse mémoire bien précise.
Ce banc de registre contient des registres généraux, mais aussi des registres pour communiquer avec les ports d'entrée-sortie, des registres de contrôle, le program counter, le registre d'état, et le pointeur de pile. Pour donner un exemple, sur les modèles 16F, l'espace d'adressage fait 128 octets, dont 32 sont réservés aux registres du processeur, ce qui fait que seuls 96 octets sont adressables comme de la RAM. Cela gaspille quelques adresses mémoire, mais permet une implémentation plus simple, avec une petite économie de registres et donc de transistors.
- Les PIC 18 peuvent adresser plus de 256 octets de RAM en utilisant la commutation de banques, une technique qui sera abordée dans plusieurs chapitres.

Les PIC sont une architecture à accumulateur, ce qui permet d'utiliser un banc de registre à un seul port et simplifie grandement la conception du processeur. Il faut préciser que l'accumulateur n'est pas mappé en mémoire, sauf sur les modèles PIC 17, qui mappé l'accumulateur en mémoire RAM ! Leur implémentation de l'accumulateur ajoute une petite subtilité : le résultat d'une opération arithmétique peut être enregistré ailleurs que dans l'accumulateur. Le résultat peut aussi remplacer la seconde opérande, celle lue en mémoire RAM. Dit autrement, le résultat est enregistré dans l'adresse mémoire source. Les instructions de calcul ont le choix : un bit dans leur encodage précise où enregistrer le résultat.
La petite taille de la ROM d'un PIC n'est pas une surprise, car les architectures 8 bits sont conçues pour ne pas utiliser beaucoup de transistors et être très simples. Mais il faut adapter le jeu d'instruction, pour qu'il ait une bonne densité de code. Par exemple, il faut faire en sorte d'avoir des instructions assez courtes. Si les instructions font 16 bits au lieu de 32, on divise par deux le nombre de transistors nécessaire pour la mémoire ROM. Et le jeu d’instruction des PIC est adapté à cette contrainte.
Les microcontrôleurs PIC utilisent un jeu d'instruction propriétaire, dont les instructions sont très petites. Les premières versions du jeu d'instruction utilisaient des instructions codées sur 12 bits, dont 5 pour l'adresse mémoire de la seconde opérande. Pour les branchements, 3 bits étaient utilisés pour l'opcode, 9 pour l'adresse de destination. Par la suite, les instructions ont été étendues à 13 bits sur les PIC1670, puis à 14, 15 et 16 bits. Les adresses ont alors été élargies, ce qui permet d'adresser en peu plus de mémoire RAM.
Il est intéressant de regarder comment les PIC font pour obtenir des instructions aussi courtes. Premièrement, le fait d'utiliser une architecture à accumulateur, couplé à une RAM très petite aident grandement. Les architectures à accumulateur ont besoin de préciser un opcode et une adresse mémoire pour chaque instruction, pas besoin de préciser de noms de registres ni le mode d'adressage. La taille des adresses mémoire est censé être un problème pour la densité de code, mais les adresses d'un processeur 8 bits font 8 bits. On peut alors avoir un jeu d'instruction étoffé, en utilisant des instructions 16 bits, qui concatènent un opcode et une adresse mémoire, d'un octet chacun.
Mais d'autres optimisations permettent d'avoir une bonne densité de code. Les PIC ont des branchements pour les appels et retour de fonction. Les PIC incorporent une pile d'appel de retour, qui fait 2 à 8 adresses suivant les modèles. Elle permet d'économiser quelques registres, car la pile d'appel n'est pas mappée dans l'espace d'adressage, elle n'est pas adressable. On économise ainsi 2 à 8 registres, sur 32-256 registres. De plus, cela simplifie l'encodage des instructions d'appel/retour de fonction, qui n'ont pas à préciser où enregistre l'adresse de retour.
Les PIC n'ont pas d'instructions de branchement conditionnels, ils font avec une SKIP instruction et des branchements inconditionnels. Du moins, c'est le principe, car si vous regardez la liste des instructions ci-dessous, vous verrez qu'il n'y a pas d'instruction SKIP. À la place, les instructions SKIP sont fusionnées avec d'autres instructions. Elles sont notamment fusionnées avec les instructions d'incrémentation et de décrémentation. Les PIC supportent une incrémentation normale, une décrémentation normale, une décrémentation fusionnée avec une instruction SKIP, et une incrémentation fusionnée avec une instruction SKIP. Les deux dernières incrémentent/décrémentent l'accumulateur et SKIP l'instruction suivante si le résultat est zéro. Elles permettent de fortement simplifier l'implémentation des boucles qui s'exécutent tant que leur compteur atteint zéro.
Les PIC disposent d'instructions de manipulation de bit, pour tester/masquer un bit dans une opérande. Il y a en a 4 en tout : mettre à 0, mettre à 1, tester si bit = 1, tester si bit = 0. Les instructions qui testent un bit font le test et décident s'il faut skipper l'instruction suivante ou non. Il y a aussi deux instructions de rotation à gauche et droite, qui tiennent compte de la retenue mémorisée dans le registre d'état. Les instructions de décalages sont émulées avec une rotation suivie d'un ET bit à bit. Mentionnons aussi une instruction pour inverser deux nibbles dans un octet. De telles instructions sont très utiles pour manipuler les ports I/O.
Les modèles haute performance ajoutent des instructions d'addition avec retenue entrante, idem pour la soustraction. De telles instructions sont très utiles pour faire des additions/soustractions sur des opérandes de 16 bits. Le processeur a beau être un CPU 8 bits, il peut être utilisé pour faire des calculs 16 bits, la performance est alors amoindrie, mais ce n'est pas un problème pour les applications d'un tel microcontrôleur.
Les modèles initiaux, comme les PIC 10 et 12, ne supportaient pas les interruptions, mais c'est chose réglé sur les modèles plus récents. Ils ont même des registres séparés pour les interruptions et les programmes normaux. De plus, ils intègrent nativement un watchdog timer.
| Instructions diverses | ||||
|---|---|---|---|---|
| 00 0000 0000 0000 | NOP | Ne fait rien | ||
| 00 0000 0000 1000 | RETURN | Instruction de retour pour une fonction | ||
| 00 0000 0000 1001 | NOP | Instruction de retour pour une interruption | ||
| 00 0000 0110 0011 | SLEEP | Mise en veille | ||
| 00 0000 0110 0100 | CLRWDT | RESET le watchdog timer, intégré dans le PIC | ||
| Instructions de manipulation de bit | ||||
| 0100 | Position du bit dans l'opérande | Adresse mémoire | BCF | Met le bit à 1 |
| 0101 | Position du bit dans l'opérande | Adresse mémoire | BSF | Met le bit à 0 |
| 0110 | Position du bit dans l'opérande | Adresse mémoire | BTFSC | Teste si le bit adressé est à 1 |
| 0111 | Position du bit dans l'opérande | Adresse mémoire | BTFSS | Teste si le bit adressé est à 0 |
| Branchements | ||||
| 100 | Adresse mémoire | CALL | Appel de fonction | |
| 100 | Adresse mémoire | GOTO | Branchement inconditionnel | |
| Instructions logiques/arithmétiques avec opérande lue en mémoire RAM | ||||
| Le bit d dans l'opcode indique la destination : accumulateur ou adresse mémoire. | ||||
| 0000001 | Adresse mémoire | MOVWF | Store (accumulateur vers destination) | |
| 000001d | Adresse mémoire | MOVWF | Met à zéro la destination (adresse ou registre) | |
| 000001d | Adresse mémoire | SUBWF | Soustraction | |
| 000010d | Adresse mémoire | DECF | Décrémentation | |
| 000100d | Adresse mémoire | IORWF | OU bit à bit | |
| 000101d | Adresse mémoire | ANDWF | ET bit à bit | |
| 000110d | Adresse mémoire | XORWF | XOR bit à bit | |
| 000111d | Adresse mémoire | ADDWF VWF | Addition | |
| 001000d | Adresse mémoire | MOVF | LOAD | |
| 001001d | Adresse mémoire | COMF | NOT bit à bit | |
| 001010d | Adresse mémoire | INCF | Incrémentation | |
| 001011d | Adresse mémoire | DECFSZ | Décrémentation, puis instruction SKIP si résultat = 0 | |
| 001100d | Adresse mémoire | RRF | Rotation à droite avec retenue entrante | |
| 001101d | Adresse mémoire | RLF | Rotation à gauche avec retenue entrante | |
| 001110d | Adresse mémoire | SWAPF | Echange deux niblle dans un octet | |
| 001111d | Adresse mémoire | INCFSZ | Incrémentation, puis instruction SKIP si résultat = 0 | |
| Instructions logiques/arithmétiques avec constante immédiate | ||||
| 1100xx | Constante immédiate (8bits) | MOVLW | Copie constante dans accumulateur | |
| 1101xx | Constante immédiate (8bits) | RETLW | MOV fusionné avec un retour de fonction | |
| 111000 | Constante immédiate (8bits) | IORLW | OU | |
| 111001 | Constante immédiate (8bits) | ANDLW | ET | |
| 111010 | Constante immédiate (8bits) | XORLW | XOR | |
| 11110x | Constante immédiate (8bits) | SUBLW | Soustraction | |
| 11111x | Constante immédiate (8bits) | ADDLW | Addition | |
Les PIC gèrent l'adressage absolu et immédiat, mais pas plus. L'adressage absolu, celui où l'adresse est intégrée dans l'instruction, est très simple à implémenter. Les adresses prennent moins d'un octet, vu que la taille de la RAM est très petite, les adresses sont très courtes, les intégrer dans des instructions elles-mêmes courtes n'est pas un problème. L'adresse est encodée sur 5 à 7 bits, selon le modèle, ce qui permet d'adresser entre 32 et 128 octets.
Les PIC ne supportent pas l'adressage indirect, mais peuvent l'émuler grâce à un registre d'adresse dédié. Il s'agit du registre FSR, qui mémorise l'adresse à lire/écrire. Il est possible de copier un pointeur dedans, qui provient soit de l'accumulateur, soit de la mémoire SRAM. Dans les deux cas, la donnée adressée est lue ou écrite dans un registre INDR, est un registre d’interfaçage rendu adressable. Le registre FSR est directement connecté sur le bus d'adresse.
Les modèles de base se débrouillent avec un registre FSR et un registre INDR, mais les modèles plus performants ont deux ou trois copies de chaque registre. Les modèles PIC18 ajoutent aussi des instructions LOAD/STORE qui émulent les modes d'adressage à post- ou pré-incrément/décrément, et le mode d'adressage "Base + Indice + Décalage", encore une fois en utilisant les registres FSR et INDR.
Avec tout ce qu'on vient de dire, vous devriez pouvoir comprendre la microarchitecture d'un PIC, qui est résumée dans ce schéma.

Les microcontrôleurs Intel 8051/8031
[modifier | modifier le wikicode]
Les microcontrôleurs Intel 8051/8031 sont des microcontrôleurs 8 bits, commercialisés par Intel. Ils contiennent un processeur 8/16 bits, 4 kibioctets de mémoire ROM et une RAM de 128 octets. Niveau entrées-sorties, il contient un port série, ainsi que 4 ports d'un octet chacun sur lesquels il peut lire ou écrire. Il supporte des interruptions, avec un contrôleur d'interruption intégré. Il contient aussi deux timers de 16 bits chacun, qui déclenchent des interruptions quand ils tombent à zéro ou à une valeur configurable.
Ils ont de nombreuses ressemblances avec les microcontrôleurs PIC, mais aussi des différences notables. Par exemple, le 8051 n'a pas de pile d'adresse de retour, contrairement aux processeurs PIC. Il fait autrement, à savoir qu'il gère une pile d'appel placée en mémoire RAM,ce qui est très différent. De même, il utilise deux accumulateurs nommés A et B, le second étant surtout utilisé pour les multiplications. Car oui, le 8051 supporte les multiplications et divisions, ce qui est une autre différence avec les processeurs PIC.
Une autre différence est que les registres sont placés dans un banc de registre à part des 128 octets de SRAM. Il y a une SRAM séparée du banc de registre, contrairement à ce qu'on a sur les microcontrôleurs PIC. Par contre, les registres du processeur sont mappés en mémoire RAM, y compris les deux accumulateurs, le registre d'état, le program counter, et bien d'autres. Mais le fait qu'ils soient séparés de la SRAM permet de les mapper dans l'espace d'adressage sans marcher sur les pieds de la SRAM. Vu que le processeur peut adresser 256 octets, mais n'a que 128 octets de SRAM, les registres sont mappés dans les 128 octets restants. Les entrées-sorties aussi sont mappées dans ces 128 octets restants.
L'ensemble utilise une architecture Harvard, contrairement à ce que pourrait faire croire le schéma ci-dessous.

Un détail original est que 16 octets de la SRAM sont bit-adressables, à savoir que chaque bit a sa propre adresse. En tout, cela permet d'adresser 128 bits (16 adresses * 8 bits par octet adressé). L'intervalle bit-adressable va de l'adresse 32 (inclue) à l'adresse 47 (inclue).
Les 32 premières adresses sont réservées à la mémoire SRAM, c'est 32 octets de RAM utilisable à volonté par le programmeur. Les 16 adresses suivantes sont les 16 adresses bit-adressables mentionnées dans le paragraphe précédent. Les 96 octets suivants sont de la mémoire RAM adressable, affectée par défaut à la pile d'appel mais adressable via adressage direct/indirect si besoin. Les 128 octets de poids fort sont réservés aux registres du processeur et aux entrées-sorties mappées en mémoire.

Les 128 octets au sommet de l'espace d'adressage sont décrits dans le schéma ci-dessous. On voit que les deux accumulateurs sont mappés dedans, de même que le registre d'état PSW, le registre de configuration des interruptions IE (Interrupt Enable), et quelques autres. Les ports I/O sont aussi mappés dedans, que ce soit les 4 ports parallèles ou le port série. Les deux timers sont aussi mappés dedans, ainsi que leurs registres de configuration.
Une partie des octets sont bit-adressables, et cette partie inclus les registres du processeur et les ports I/O. Pour le dire autrement, chaque bit d'un port I/O a sa propre adresse mémoire, qui permet de le sélectionner. Par contre, les registres pour les timers ne sont pas dedans, sauf pour le troisième ajouté dans des extensions du 8051.

Une particularité est que le processeur incorpore une unité de calcul de 1 bit. Elle permet de faire des opérations sur des bits, avec 17 instructions possibles au total. L'ALU de 1 bit est reliée à un accumulateur de 1 bit et 32 registres de 1 bit. Elle peut lire des opérandes de 1 bit en mémoire RAM ou sur les ports I/O, pour les portions qui sont bit-adressables.
Le processeur supporte aussi l'adressage indirect dans la SRAM, mais celui-ci fonctionne différemment comparé aux microcontrôleurs PIC. Il n'y a pas deux registres d'adresse dédiés, séparés des autres. À la place, l'adressage indirect est implémenté normalement, en utilisant les registres généraux. Par contre, il y a une contrainte : seuls les deux premiers registres R0 et R1 sont utilisables. En clair, seuls ces deux registres peuvent contenir une adresse, un pointeur. C'est là une différence liée au fait que le 8051 a un banc de registre séparé.
Le 8051 peut communiquer avec une mémoire RAM externe, en plus de la SRAM interne. Pour cela, le processeur du 8051 avait un espace d'adressage séparé pour la RAM externe, capable d'adresser 64 kibioctets. Communiquer avec cet espace d'adressage se faisait avec des instructions d'accès mémoire séparées des autres, pas les LOAD/STORE normaux. Par exemple, l'instruction MOVX lisaient une donnée dans cet espace d'adressage, et la copiait dans l'accumulateur ou la SRAM. Pour envoyer l'adresse à lire/écrire à cette RAM externe, le processeur incorporait un registre d'adresse dédié. Il s'agit du registre DPTR, qui contient une adresse de 16 bits. Le processeur devait copier l'adresse à lire/écrire dedans, puis lancer l'instruction MOVX.
Le HP Nanoprocessor : un processeur sans ALU
[modifier | modifier le wikicode]Un dernier exemple est celui du processeur HP Nanoprocessor, de l'entreprise HP. Il a été commercialisé en 1974, en même temps que les premiers micro-processeurs d'Intel. Tous les processeurs de l'époque étaient des processeurs 8 bits et le HP Nanoprocessor ne faisait pas exception. Ce n'était pas un processeur hautes performances, il était destiné à des applications d'informatique industrielle ou embarquée. D'ailleurs, il utilisait des transistors de type metal-gate, peu performants, là où ses contemporains utilisait des transistors en silicium, plus performants.
Mais une caractéristique qui rend cela encore plus claire est qu'il n'y a pas d'ALU ! En effet, ce processeur est incapable de faire des additions, des soustractions ou autres opérations simples ! Il est cependant capable d'effectuer des incrémentations et décrémentations, des décalages, des opérations logiques comme des ET/OU/NON/XOR et bien d'autres. Il supporte en tout 48 instructions, avec un support limité des modes d'adressage indirects. Les instructions de branchement étaient des instructions SKIP, il n'y avait pas d'instructions de branchement généralistes.
Pour ce qui est des registres, il disposait de 16 registres de 8 bits et d'un accumulateur. Niveau mémoire, il était relié à une mémoire ROM, rien de plus. Il n'avait de RAM supplémentaire qu'on pouvait lui adjoindre, il devait se débrouiller avec seulement ses registres. Et cela ne posait pas de problèmes pour son usage. Seul le mode d'adressage indirect à registre est supporté, il n'y a pas de registres d'indices. Ce mode d'adressage permet de lire des données en mémoire ROM, la ROM pouvant contenir des tableaux de valeurs constantes.
- Il est possible de lui adjoindre de la RAM via les ports d'entrée-sortie, ainsi qu'une ALU, mais RAM et ALU sont traités comme des périphériques avec lesquels on communique, ils ne sont pas intégrés au processeur.
Niveau micro-architecture, il incorporait une unité de calcul logique et un circuit comparateur. Le comparateur était un circuit comparateur série des plus basiques, qui était utilisé pour les instructions de branchement et associées. L'unité de calcul logique gérait des incrémentations/décrémentation, des décalages, les opérations logiques et quelques autres. L'unité de calcul logique était bit-slicée, à savoir conçue en assemblant 8 ALU de 1 bit chacune, enchainées en série. Le tout était combiné à des circuits de carry skip.

Son unité de décodage d'instruction était très simple, essentiellement basée sur des portes NOR, grâce à un encodage efficace des instructions machine. Le program counter fait 11 bits, ce qui permet d'adresser une ROM de 2 kibioctets. Le program counter était relié à un circuit incrémenteur qui gérait les isntructions SKIP (il pouvait incrémenter le program counter par pas de 1 ou de 2 en cas de SKIP). Le processeur contient aussi deux registres, qui servent en quelque sorte de pile d'adresse de retour. Un premier registre mémorise l'adresse de retour d'une fonction, l'autre mémorise le program counter lors d'une interruption, pour savoir où brancher à la fin de l'interruption.

Fait intéressant, le concepteur du processeur a gracieusement donné les masques utilisés pour graver le silicium du processeur au site CPU Shack. Les masques en question sont disponibles ici :
Les masques ont été analysés par Ken Shirrif dans cet article de blog :
Les processeurs 8/16 bits à architecture Von Neumann
[modifier | modifier le wikicode]L'usage d'une architecture Harvard est une solution pour adresser plus de mémoire avec des CPU 8 bits. Mais plusieurs microprocesseurs commerciaux n'utilisaient pas cette solution. À la place, c'était des architectures Von Neumann, mais qui étaient capables de gérer des adresses de 16 bits. Les données faisaient 8 bits, les registres faisaient 8 bits, mais quelques techniques permettaient de gérer des adresses de 16 bits. Nous parlerons de processeurs 8/16 bits pour les désigner, et nous allons les étudier à part.
Un point important est que la gestion d'adresses de 16 bits permet d'adresser 64 kibioctets, qui peuvent être remplis avec de la RAM et de la ROM. On a donc le meilleur des mondes : 64 kibioctets de mémoire, mais avec des données de 8 bits et les économies de circuit qui vont avec. L'avantage est que le processeur peut maintenant gérer une vraie pile d'appel, pas seulement une pile d'adresse de retour. Il lui suffit pour cela d'intégrer un pointeur de pile codé sur 16 bits.
Les modes d'adressage par page des CPU Motorola
[modifier | modifier le wikicode]Les processeurs Motorola 8 bits étaient des processeurs 8 bits avec un bus d'adresse de 16 bits. C'était des processeurs à accumulateur incorporant deux registres d'indice, un registre d'état, un pointeur de pile, et un program counter. Tous les registres sont reliés au bus d'adresse, sauf le registre d'état et l’accumulateur. Tous les registres font 8 bits, sauf le program counter. La pile est câblée pour être dans un bloc de 256 octets compris entre l'adresse 1 0000 0000 et l'adresse 1 1111 1111, ce qui fait que son adressage se fait avec un seul octet.

X et Y : registres d'indice ;
S : le pointeur de pile ;
A : accumulateur ;
P : registre d'état.
Les processeurs Motorola 8 bits utilisaient une méthode simple pour adresser 64 kibioctets de RAM, avec des registres de 8 bits. Pour cela, ils utilisaient une variante de l'adressage base + décalage. Elle améliore l'adressage absolu, qui intègre l'adresse à lire/écrire dans l'instruction elle-même. L'adresse en question est de 8 bits et n'encode que les 8 bits de poids faible de l'adresse, les 8 bits de poids fort sont dans un registre de page adressé implicitement. L'adresse finale est obtenue en concaténant ce registre avec l'adresse mémoire intégrée dans l’instruction. Le résultat est que la mémoire était découpée en blocs de 256 consécutifs, chacun pouvant servir de fenêtre de 256 octets. Une page est réservée pour la pile, la seconde, comme dit plus haut.
Formellement, ce mode d'adressage a des ressemblances avec la commutation de banques, une technique qu'on verra dans les chapitres sur l'espace d'adressage et la mémoire virtuelle. Mais ce n'en est pas du tout : le registre de page est utilisé uniquement pour les accès mémoire avec le mode d'adressage base + décalage, mais pas pour les autres accès mémoire, qui gèrent des adresses complètes. Notons que l'usage d'un registre de page dédié fait que celui-ci est adressé implicitement.
Il faut noter que la technique a été utilisée sur des processeurs 16 bits, pour contourner la petite taille des instructions. Un exemple est celui du HP 2100, un processeur 16 bits. Il avait un registre de page de 5 bits et encodait 10 bits d'adresse dans ses instructions. Ses instructions d'accès mémoire disposaient d'un bit qui choisit quel mode d'adressage utiliser. S'il était à 0, l'adressage absolu était utilisé, le registre de page n'était pas utilisé. Mais s'il était à 1, le registre de page était utilisé pour calculer l'adresse.
Une évolution de l'adressage précédent est le mode page direct. Avec lui, le registre de page est étendu et contient une adresse mémoire complète. L'adresse finale n'est pas obtenue par concaténation, mais en additionnant le registre de page avec l'adresse fournie par adressage absolu + décalage. Un exemple est celui des premiers processeurs Motorola, qui géraient des adresses courtes de 8 bits. L'adresse courte de 8 bits correspondait non pas aux 256 premiers octets de la mémoire, mais à une fenêtre de 256 octets déplaçable en mémoire. La position de la fenêtre de 256 octets était spécifiée par le registre de page de 16 bits, qui précisait l'adresse du début de la fenêtre, celle de sa première donnée.
Il s'agit donc formellement d'adressage base + décalage, à un détail près : il n'y a qu'un seul registre de base. Le fait que ce registre de base soit unique fait qu'il est adressé implicitement, on n'a pas à encoder le numéro/noms de registre dans l'instruction. Le registre de base est utilisé uniquement pour l'adressage absolu, pas pour les autres accès mémoire. S'il y a des ressemblances avec la segmentation, une technique de mémoire virtuelle qu'on abordera dans quelques chapitres, ce n'en est pas vu que le registre de base est utilisé seulement pour un mode d'adressage bien précis.
Le pseudo-aliasing des registres des processeurs Intel x86
[modifier | modifier le wikicode]Voyons maintenant le cas des anciens processeurs 8 bits d'Intel à savoir l'Intel 4004, 4040, 8008 et 8080. Le Z80, un processeur inspiré des CPU Intel 8 bits, est aussi concerné. Avant de poursuivre, parlons des registres de ces processeurs. Les premiers CPU Intel incorporaient 7 registres de 8 bits nommés A, B, C, D, E, F, H, L, SP. Ils correspondent respectivement à : l'accumulateur A ; six registres nommés B, C, D, E, H et L ; le registre d'état F ; le pointeur de pile SP. Le Z80 ajoute deux registres d'indice X et Y. De plus, le Z80 dispose de registres d'interruption, qui sont en quelque sorte des copies des registres A, B, C, D, E, F, H, L. Le tout est illustré dans le tableau ci-dessous.
| Registres des CPU Intel | Registres supplémentaires du Z80 | |
|---|---|---|
| Accumulateur | A | A' |
| Registre d'état | F | F' |
| Registres généraux | B, C, D, E, H, L | B', C', D', E', H', L' |
| Pointeur de pile | SP (sauf pour le 8008) | |
| Registres d'indice | X et Y | |
Le 8008 incorpore une pile d'appel de 7 adresses, dans le processeur lui-même. Il en est de la même avec le 4004 et le 4040, comme on le verra dans la section sur les processeurs 4 bits. Mais le 8080 et le 8085 ont remplacé le tout par un simple pointeur de pile, afin d'avoir une véritable pile d'appel. Autant c'est impossible d'avoir une pile d'appel avec seulement 256 octets de mémoire, autant on peut se le permettre avec 65 536 adresses.
Pour gérer des adresses de 16 bits, ils auraient pu utiliser des registres d'adresse séparés, mais on préféré un système de pseudo-aliasing de registres. Formellement, c'est un système qui concatène deux registres 8 bits pour obtenir une adresse de 16 bits. L’appariement a lieu lors de certaines opérations bien précises, qui n'ont de sens que pour les adresses : la lecture/écriture en mémoire, et l'incrémentation.
Il y avait cependant des contraintes quant aux paires de registres possibles, toutes les paires de registres n'étaient pas autorisées. Les CPU concernés sont donc des processeurs hybrides accumulateur-registre généraux. L'accumulateur ne peut pas être appairé, seuls les registres généraux le peuvent. Le Z80 autorise plus de paires que les CPU Intel. Voici les paires de registres qui pouvaient servir d'adresse 16 bits :
- Les CPU 8008 d'Intel autorisait une seule paire formée des registres H et L.
- Les autres processeurs, à savoir le 8080, le 8085 et le Z80, autorisaient : les paires BC, DE et HL.
Le 8008 ne gérait que des opérations sur 8 bits. Mais le 8080 et le 8085 ont ajouté des instructions 16 bits qui agissaient sur des paires de registre, que le Z80 a repris. Une paire de registres est considérée comme un registre unique de 16 bits pour certaines opérations arithmétiques, dont l'incrémentation. Voici la liste des instructions concernées :
- INC et DEC pour l'incrémentation et la décrémentation ;
- DAD : additionne le registre HL avec une autre paire (Hl inclus) ;
- LXI : copie une constante immédiate dans une paire de registres ;
- LHLD : lit 16 depuis la mémoire et l'enregistre dans une paire de registres ;
- SHLD : copie 16 bits depuis une paire de registres et l'enregistre en mémoire ;
- PUSH et POP : empile ou dépilent une paire de registre sur la pile ;
- XTHL : échange la paire HL avec les 16 bits au sommet de la pile d'appel ;
- XCHG : échange le contenu de la paire HL et DE.
Pour résumer, le processeur avait des instructions LOAD, STORE, PUSH et POP adaptées pour gérer des paires de registre. De plus, l'instruction XCHG permettait d’échanger le contenu des paires DE et HL. L'instruction a été étendue sur les processeurs 16 bits pour échanger tous les registres. Mais l'implémentation était alors différente. L'incrémentation sur une paire de 16 bit était très utile, car les adresses sont souvent incrémentées quand on parcours un tableau. Ces instructions nous donnent quelques indices sur ce qu'il y a dans le processeur.
Le système de pseudo-aliasing a un impact sur le banc de registre. Le banc de registre regroupe les registres généraux et le pointeur de pile, mais ne contient pas l'accumulateur. Les contraintes sur les paires de registres possibles ne s'expliquent que d'une seule manière : le banc de registre contient des registres de 16 bits, une paire de registres 8 bits est mémorisée dans un registre unique. Il faut donc faire une différence entre les registres architecturaux de 8 bits, et les registres réels de 16 bits.
Le banc de registre a un port de 8 bits pour les données, qui est fabriqué à partir d'un port 16 bits auquel on a rajouté un multiplexeur pour choisir soit l'octet de poids faible, soit l'octet de poids fort. Le bus de données fait 8 bits, ce qui fait que les instructions LOAD, STORE, PUSH et POP pour paires 16 bits sont réalisées en deux fois : on lit/écrit l'octet de poids faible, puis celui de poids fort. Par contre, le bus d'adresse fait bien 16 bits. Cela suggère que le banc de registre dispose d'un port de 16 bits dédié aux adresses, en plus du port de données 8 bits.

L'incrémentation d'une paire 16 bits ne coute rien à implémenter, ou presque. En effet, le processeur incorpore un incrémenteur 16 bits pour le program counter, qui est relié au bus d'adresse. L'idée est de rentabiliser cet incrémenteur, en lui permettant d'incrémenter des données/adresses de 16 bits obtenues via pseudo-aliasing. De même, il peut être utilisé pour incrémenter le pointeur de pile, voire un compteur de rafraichissement mémoire (pour rappel, le rafraîchissement mémoire demande de balayer la mémoire d'adresse en adresse et été fait par le CPU à l'époque). Pour cela, il suffit de connecter le program counter, le banc de registre, et l'incrémenteur, via un bus dédié.

Notez qu'on peut encore simplifier le tout en placant le program counter dans le banc de registre, ce qui ne gênera pas le travail de l'incrémenteur mais simplifiera le câblage au bus d'adresse. Ce n'était pas systématique, certains processeurs mettaient le program counter dans le banc de registre, d'autres non.
- Inclure le program counter dans le banc de registre permet parfois de remplir un vide. Par exemple, sur un processeur avec 12 registres, le banc de registre devrait faire idéalement 16 registres, inclure le program counter dedans fait monter le tout à 13.

Les processeurs Intel 8 bits, à savoir le 8008, le 8080 et le 8085 ; mettaient le program counter dans le banc de registre. Vous avez donc toutes les armes pour comprendre leur micro-architecture. Par exemple, voici la micro-architecture du processeur 8080. Remarquez le banc de registre, qui contient tous les registres sauf l'accumulateur et le registre. Il a deux ports : un pour les adresses et un pour les données. Notez le dé-multiplexeur sur le port de donnée. Pour le reste, il s'agit d'une architecture à accumulateur hybride des plus classiques.

Le Z80 est un processeur inspiré du design des processeurs Intel 8 bits, mais avec pas mal d'améliorations. Un point particulier de ce processeur est son banc de registre. Il contient tous les registres du processeur, y compris l'accumulateur et le registre d'état ! Les seuls registres en-dehors du banc de registre sont le program counter et un compteur de rafraichissement mémoire IR.
Un autre point particulier est que les registres dans le banc de registres avaient une copie séparée accessible uniquement pour les interruptions, via fenêtrage de registres. Et l'accumulateur et le registre d'état étant dans le banc de registre, il y avait deux accumulateurs et deux registres d'état. Les registres A, B, C, D, E et F étaient dupliqués, par le pointeur de pile ni les registres d'indice. Les copies des registres étaient nommées A', B', C', D', E' et F'.
Le Z80 supportait des instructions d'échange de registre en plus des CPU Intel 8 bits. L'instruction EX peut échanger soit le contenu des paires D,E et H,F, soit la paire A,F avec la paire A',F'. L'instruction EXX échange les paires BC, DE, HL avec les paires BC', DE', HL'. En faisant cela, le programmeur pouvait utiliser deux fois plus de registres généraux s'il n'utilisait pas les interruptions. L'échange de paires se fait sans copies de registres, mais simplement en modifiant l'adresse envoyée sur le banc de registre. Pour cela, plusieurs bascules mémorisent des bits qui indiquent si les paires ont été échangées ou non. Il y a une bascule pour les paires DE , HL ; une autre pour les paires A,F et paire A',F', une dernière pour l'instruction EXX. En combinant ces bascules avec les noms de registres envoyés par le séquenceur, on peut échanger les paires de registres.
Le cas particulier des processeurs 4 bits : le 4004 d'Intel
[modifier | modifier le wikicode]L'Intel 4004 était le tout premier micro-processeur. Il s'agissait d'un microprocesseur de 4 bits, basé sur une architecture à accumulateur. Il disposait d'un accumulateur et de 16 registres d'indice, tous de 4 bits. Il faisait des calculs sur des données de 4 bits, appelées des nibbles. Cependant, sur ses 46 instructions machines supportées, 41 agissaient sur des opérandes de 8 bits, les 5 restantes sur des opérandes de 16 bits. Simplement, les calculs se faisaient en deux fois, en échangeant des données entre mémoire RAM et accumulateur.
Le 4004 est un circuit intégré avec peu de broches. Il dispose notamment d'un bus de données/adresse, qui est multiplexé, c’est-à-dire qu'il sert alternativement de bus de données et de bus d'adresse selon les besoins. Le bus de commande mémoire fait 5 bits, dont 4 pour les chips de RAM, un pour la ROM. Les adresses sont codées sur 12 bits, les données sur 4 et les instructions sur 8 bits. L'envoi d'une adresse sur le bus se fait en plusieurs fois, de même que la lecture d'une instruction. Il s'agit là d'une adaptation pour outrepasser les limitations d'un processeur 4 bit.

L'espace d'adressage de l'Intel 4004
[modifier | modifier le wikicode]Les adresses du processeur sont sur 12 bits, ce qui fait 4096 adresses différentes. Elles regroupent à la fois la ROM et la mémoire RAM, vu que l'architecture est une sorte de mélange bizarre entre une architecture Von Neumann et Harvard. Le processeur gère bien deux espaces d'adressage séparés, mais la RAM et la ROM sont reliés au même bus. La sortie CM-ROM du processeur indique si l'adresse envoyée correspond à la RAM ou la ROM. Un circuit de décodage d'adresse active ou désactive la RAM selon la valeur de ce bit, l'inverse pour la ROM, en agissant sur leurs bits Chip Select. Seule la RAM ou la ROM répondent alors à l'adresse.
Pour ce qui est de la RAM, le circuit de décodage d'adresse pouvait gérer au maximum 1024 adresses différentes, chacune contenant un nibble. Pour cela, le CPU 4004 était relié à une ou plusieurs RAM externes, des Intel 4002. Chaque Intel 4002 contenait 64 nibbles de données, plus 16 nibbles de statut qu'on ne détaillera pas ici. Les nibbles de statut étaient accessibles en utilisant des instructions d'entrée-sortie, donc pas avec des lectures/écritures mémoire. Les 64 nibbles de données sont découpés en 4 groupes de 16 nibbles. Le CPU pouvait être relié à maximum 16 Intel 4002, ce qui fait 1024 nibbles de données et 256 nibbles de statut. L'ensemble permet de remplir l'espace d'adressage de 1024 adresses.
Le registre de page et le pseudo-aliasing des registres
[modifier | modifier le wikicode]Sélectionner une adresse mémoire utilisait un mélange entre pseudo-aliasing et registre de page. Pour les données, les 1024 adresses étaient découpées en 4 pages de 256 adresses. Pour la ROM, les 4096 adresses étaient découpées en 16 pages de 256 adresses. Localiser une donnée dans une page ne requiert que 8 bits, 2/4 bits supplémentaires permettent de sélectionner la page voulue. Le registre de page était dans le processeur lui-même, et était relié aux sorties du processeur nommées CM-RAM 0, 1, 2 et 3. Les 4 sorties encodaient un nombre qui permet de sélectionner le numéro de page voulu.
Le registre de page fait 5 bits : un pour distinguer RAM et ROM, 4 bits pour sélectionner la page voulue. L'avantage est que la carte mère gère au maximum 16 chips de RAM, ce qui colle parfaitement avec les 4 utilisés pour sélectionner une page. Un chip de RAM correspond à une page, et chacun est activé ou désactivé suivant la page sélectionnée. Pour cela, il suffit d'ajouter un décodeur sur la carte mère, qui prend les 4 bits du registre de page et fournit les signaux Chip Select aux différents chips de RAM.
Reste alors à utiliser les 8 bits d'adresse à l'intérieur d'un chip de RAM. Le 4004 permettait de concaténer deux registres d'indice de 4 bits pour former une adresse de 8 bits. Les instructions d'accès mémoire pouvaient ainsi préciser une paire de registre d'indice pour préciser quelle adresse lire/écrire. Idem pour les branchements indirects : le processeur supportait une instruction de branchement indirect qui précisait l'adresse dans une paire de registres d'indice. Les registres d'indice sont donc assez mal nommés sur cette architecture, vu qu'ils peuvent servir soit de registre d'indice, soit de demi-registre d'adresse. Il s'agit là du système de pseudo-aliasing des registres, mais appliqué aux registres d'indice.
L'Intel 4004 gérait deux instructions pour gérer les adresses mémoires. La première, l'instruction DCL (Designate Command Line) configure le registre de page. Elle prend en entrée un numéro de page et le copie dans le registre de page. L'opérande peut provenir d'un registre d'indice ou de l'accumulateur. La seconde, l'instruction SRC (Send Register Control) concatène deux registres d'indice pour obtenir une adresse de 8 bits. Un accès mémoire se fait donc en maximum trois instructions : une qui configure le registre de page si ce n'est pas déjà fait, une instruction SRC pour envoyer l'adresse, et une troisième instruction qui dit s'il faut faire une lecture ou une écriture.
La microarchitecture de l'Intel 4004
[modifier | modifier le wikicode]Il incorporait une pile d'adresse de retour intégrée au processeur. La pile d'adresse acceptait exactement 3 adresses maximum. Elle était regroupée avec le program counter dans un banc de registre, de 4 registres. Le program counter et le pointeur de pile faisaient chacun 12 bits, soit trois nibble. Ils étaient incrémentés par un incrémenteur dédié.
SI on omet le registre de page et la pile d'adresse, le 4004 est une architecture à accumulateur des plus classiques, avec un bus interne unique, un banc de registre pour les registres d'indices, un accumulateur relié à une unité de calcul. Les registres d'indice étaient dans un banc de registre séparés, car ils faisaient 4 bits chacun, contrairement au pointeur de pile et le program counter. Un détail important est que les bancs de registres du 4004 étaient implémentés avec de la DRAM, et non des registres usuels fabriqués avec des bascules 1 bit.


