Un microprocesseur exécute, l'une après l'autre, des instructions très simples rangées en mémoire : charger un nombre, l'additionner, le ranger, sauter ailleurs. Un microcontrôleur réunit sur une seule puce ce processeur, sa mémoire et ses périphériques : minuteries, convertisseurs, ports d'entrée-sortie. Il pilote une machine à laver, un capteur ou un frein ABS. Cinq planches pour voir comment ces puces calculent, pourquoi elles vont si vite, et comment on les programme pour qu'elles dialoguent avec le monde réel.
Presque tous les processeurs suivent le modèle décrit en 1945 dans le rapport de John von Neumann sur l'EDVAC. Le programme et les données sont rangés dans la même mémoire. Une unité de commande va y chercher les instructions une à une, grâce au compteur de programme (PC). Une unité arithmétique et logique (UAL) fait les calculs dans des registres. Un bus relie le tout aux périphériques. Le microprocesseur d'un ordinateur cherche la vitesse : pipeline, caches, plusieurs cœurs. Le microcontrôleur cherche la sobriété, le prix et la réaction rapide aux événements : il consomme quelques milliampères, parfois quelques microampères, et coûte souvent moins d'un euro.
| Microprocesseur (ordinateur, téléphone) | Microcontrôleur (système embarqué) | |
|---|---|---|
| Mémoire | externe : plusieurs gigaoctets de mémoire vive | intégrée : quelques kilooctets de mémoire vive, de la mémoire flash pour le programme |
| Fréquence | plusieurs gigahertz | de quelques mégahertz à quelques centaines |
| Consommation | de quelques watts à plus de cent watts | de quelques milliwatts, et quelques microwatts en veille |
| Périphériques | sur des puces séparées | sur la puce : minuteries, convertisseurs, liaisons série, entrées-sorties |
| Logiciel | système d'exploitation complet | programme unique, souvent sans système d'exploitation, ou avec un noyau temps réel |
Voici un processeur minimal, à accumulateur : un seul registre de calcul, l'ACC, et seize cases de mémoire de 8 bits. Chaque instruction tient dans une case : les 4 bits de poids fort donnent le code opération, les 4 bits de poids faible une adresse. Le processeur répète sans fin le même cycle. Il cherche l'instruction à l'adresse indiquée par le PC et la copie dans le registre d'instruction (IR). Il la décode. Il l'exécute, en allant si besoin lire ou écrire une donnée en mémoire. Le programme chargé calcule la somme 1 + 2 + … + n. Chaque instruction, comme chaque donnée, passe par le même bus : c'est le « goulot de von Neumann ».
Dans une laverie, on n'attend pas que le linge soit sec pour lancer la machine suivante. Le processeur fait de même : il découpe chaque instruction en cinq étapes. Il la cherche (IF), la décode et lit ses registres (ID), calcule (EX), accède à la mémoire (MEM) et écrit le résultat (WB). Pendant qu'une instruction calcule, la suivante est décodée et la troisième déjà cherchée. Idéalement, une instruction se termine à chaque cycle : le nombre moyen de cycles par instruction (CPI) vaut 1, au lieu de 5. Mais la chaîne s'enraye. Une instruction qui a besoin du résultat de la précédente doit l'attendre : c'est un aléa de données. Le contournement (forwarding) transmet le résultat dès qu'il sort de l'UAL, sans attendre qu'il soit écrit ; seule une lecture en mémoire suivie de son utilisation coûte encore un cycle. Un branchement pris oblige à jeter les instructions cherchées trop tôt.
Le processeur calcule en une fraction de nanoseconde, mais la mémoire principale met une centaine de cycles à répondre. Entre les deux, une petite mémoire très rapide, le cache, garde une copie des données récemment utilisées. Il ne travaille pas octet par octet mais par lignes de 16 à 128 octets : lire un élément d'un tableau fait venir ses voisins, qui seront probablement lus juste après (localité spatiale). Et ce qui a servi resservira sans doute bientôt (localité temporelle). Un accès trouvé dans le cache est un succès ; sinon, c'est un défaut. On en distingue trois sortes : obligatoires (première lecture d'une ligne), de capacité (le tableau ne tient pas dans le cache), de conflit (dans un cache à correspondance directe, deux lignes qui tombent au même emplacement se chassent mutuellement).
L'UAL additionne deux mots de 8 bits colonne par colonne, comme à l'école, en propageant les retenues. Elle ne sait pas si ces bits représentent des nombres non signés (0 à 255) ou signés en complément à deux (−128 à 127, le bit de poids fort valant −128) : c'est le programmeur qui décide. Elle fournit donc à chaque opération quatre indicateurs dans le registre d'état. Z : le résultat est nul. N : son bit de poids fort est à 1, il est négatif s'il est signé. C : une retenue sort du mot, le résultat non signé est faux. V : débordement, le résultat signé est faux. Les instructions de saut conditionnel testent ces indicateurs. Pour soustraire, l'UAL ajoute le complément à deux : a − b = a + (non b) + 1.
Un microcontrôleur passe sa vie à surveiller des capteurs, à produire des signaux et à dormir. Trois questions d'ingénieur, simulées ici. Faut-il vérifier régulièrement si un événement est arrivé, ou se faire prévenir par une interruption ? Comment une minuterie matérielle produit-elle une fréquence précise sans occuper le processeur ? Combien de temps une pile bouton fait-elle vivre un capteur qui se réveille de temps en temps ?
Par scrutation (polling), le programme lit l'état du périphérique toutes les T microsecondes. Chaque lecture coûte du temps, même quand rien n'est arrivé. Un événement attend en moyenne environ T/2 avant d'être vu. Si deux événements arrivent entre deux lectures, le second est perdu, car l'indicateur du périphérique ne compte pas. Avec une interruption, le périphérique déclenche lui-même l'exécution d'une routine de service : la réaction est quasi immédiate, et le processeur ne travaille que lorsqu'il le faut. Les événements arrivent ici au hasard (loi de Poisson), au rythme moyen λ.
Une minuterie est un compteur matériel. Un prédiviseur ne laisse passer qu'une impulsion d'horloge sur P ; le compteur avance d'une unité à chacune, de 0 jusqu'à une valeur maximale, le TOP, puis repart de zéro. Un comparateur met la sortie à 1 tant que le compteur est inférieur à une valeur de consigne, le CMP. On obtient un signal carré de fréquence et de rapport cyclique réglables : une MLI (modulation de largeur d'impulsion), produite sans aucune instruction du processeur. Avec une horloge de 16 MHz, comme celle d'un Arduino Uno, on cherche les réglages qui approchent le mieux une fréquence voulue.
Un capteur sans fil alimenté par une pile bouton CR2032 (environ 225 mAh) dort presque tout le temps, avec un courant de quelques microampères. Toutes les P secondes, il se réveille, mesure, transmet par radio, et se rendort. Ce réveil ne dure que quelques millisecondes, mais il consomme plusieurs milliampères. Tout l'art du concepteur est d'équilibrer ces deux régimes : la consommation moyenne décide de la durée de vie.
Planche 1. Machine à accumulateur 8 bits, 16 mots. Recherche : 1 cycle, décodage : 1 cycle, exécution : 1 cycle, plus 1 cycle d'accès mémoire pour LOAD, ADD, SUB et STORE. La boucle compte 8 instructions, dont 6 accèdent à la mémoire : 6 × 4 + 2 × 3 = 30 cycles par tour.
Planche 2. Pipeline à cinq étages. Dépendance seulement envers l'instruction précédente, si celle-ci produit un résultat (calcul ou lecture). Sans contournement, le résultat est lu au décodage pendant l'écriture : 2 cycles d'attente. Avec contournement, seule une lecture en mémoire suivie de son utilisation coûte 1 cycle. Un branchement retarde de k cycles la recherche de l'instruction suivante. 20 000 instructions, graine fixe.
Planche 3. Cache à correspondance directe, adresses d'octets, entiers de 4 octets, quatre parcours successifs du tableau. La prévision suppose un cache de même taille sans conflits, avec remplacement de la ligne la plus anciennement utilisée. Un parcours cyclique plus grand que le cache y produit un défaut à chaque nouvelle ligne.
Planche 4. Arithmétique modulo 256. Les règles de C et de V sont comparées aux résultats exacts (a + b > 255 ; somme signée hors de [−128, 127]) pour les 65 536 couples.
Planche 5. Scrutation : 200 000 événements poissonniens, indicateur de périphérique à un seul bit. Minuterie : simulation impulsion par impulsion sur trois périodes ; choix du plus petit prédiviseur qui permet d'atteindre la fréquence, pour garder la meilleure résolution. Énergie : courant intégré sur 24 h par paliers.