# Fonctionnement d'un ordinateur/Les liaisons point à point

On a vu dans le chapitre précédent qu'il faut distinguer les liaisons point à point des bus de communication. Dans ce chapitre, nous allons voir tout ce qui a trait aux liaisons point à point, à savoir comment les données sont transmises sur de telles liaisons, comment l'émetteur et le récepteur s'interfacent, etc. Gardez cependant à l'esprit que tout ce qui sera dit dans ce chapitre vaut aussi bien pour les liaisons point à point que pour les bus de communication. En effet, les liaisons point à point font face aux même problèmes que les bus de communication, si ce n'est la gestion de l'arbitrage.
Deux composants électroniques communiquent entre eux en s'envoyant des trames, des paquets de bits où chaque information nécessaire à la transmission est à une place précise. Le codage des trames indique comment interpréter les données transmises. Le but est que le récepteur puisse extraire des informations utiles du flux de bits transmis : quelle est l'adresse du récepteur, quand la transmission se termine-t-elle, et bien d'autres. Les transmissions sur un bus sont standardisées de manière à rendre l'interprétation du flux de bit claire et sans ambiguïté.
Le terme trame est parfois réservé au cas où le paquet est envoyé en plusieurs fois. Le cas le plus classique est celui des bus série où la trame est envoyée bit par bits. Sur un bus parallèle, il peut y avoir des trames, si les données à transmettre sont plus grandes que la largeur du bus. Tout ce qui sera dit dans ce chapitre vaut pour les trames au sens : paquet de données envoyé en plusieurs fois.

## La taille d'une trame

La taille d'une trame est soit fixe, soit variable. Par fixe, on veut dire que toutes les trames ont la même taille, le même nombre de bits. Une taille de trame fixe rend l'interprétation du contenu des trames très simple. On sait où sont les données, elles sont tout le temps au même endroit, le format est identique dans toutes les trames. Mais il arrive que le bus gère des trames de taille variable.
Par exemple, prenons l'exemple d'un bus mémoire série, à savoir que la mémoire est reliée à l'extérieur via un bus série (certaines mémoires FLASH sont comme ça). Un accès mémoire doit préciser deux choses : s'il s'agit d'une lecture ou d'une écriture, quelle est l'adresse à lire/écrire, et éventuellement la donnée à écrire. Une trame pour le bus mémoire contient donc : un bit R/W, une adresse, et éventuellement une donnée. La trame pour la lecture n'a pas besoin de préciser de données à écrire, ce qui fait qu'elle contient moins de données, elle est plus courte. Pour une mémoire à accès série, reliée à un bus série, cela fait que la transmission de la trame est plus rapide pour une lecture que pour une écriture.
Sur un bus parallèle, la taille de la trame pose quelques problèmes. Dans le cas idéal, la taille de la trame est un multiple de la taille d'un mot, un multiple de la largeur du bus. Une trame contient N mots, avec N entier. Mais si ce n'est pas le cas, alors on fait face à un problème. Par exemple, imaginons que l'on doive envoyer une trame 2,5 fois plus grande qu'un mot. Dans ce cas, on envoie la trame dans trois mot, le dernier sera juste à moitié remplit. Il faut alors rajouter des bits/octets de bourrage pour remplir un mot.

## Le codage des trames : début et de la fin de transmission

Le transfert d'une trame est soumis à de nombreuses contraintes, qui rendent le codage de la trame plus ou moins simple. Le cas le plus simple sont ceux où la trame a une taille inférieur ou égale à la largeur du bus, ce qui permet de l'envoyer en une seule fois, d'un seul coup. Cela simplifie fortement le codage de la trame, vu qu'il n'y a pas besoin de coder la longueur de la trame ou de préciser le début et la fin de la transmission. Mais ce cas est rare et n'apparait que sur certains bus parallèles conçus pour. Sur les autres bus parallèles, plus courants, une trame est envoyée morceau par morceau, chaque morceau ayant la même taille que le bus. Sur les bus série, les trames sont transmises bit par bit grâce à des circuits spécialisés. La trame est mémorisée dans un registre à décalage, qui envoie celle-ci bit par bit sur sa sortie (reliée au bus).
Il arrive qu'une liaison point à point soit inutilisée durant un certain temps, sans données transmises. Émetteur et récepteur doivent donc déterminer quand la liaison est inutilisée afin de ne pas confondre l'état de repos avec une transmission de données. Une transmission est un flux de bits qui a un début et une fin : le codage des trames doit indiquer quand commence une transmission et quand elle se termine. Le récepteur ne reçoit en effet qu'un flux de bits, et doit détecter le début et la fin des trames. Ce processus de segmentation d'un flux de bits en trames n’est cependant pas simple et l'émetteur doit fatalement ajouter des bits pour coder le début et la fin de la trame.

### Ajouter un bit sur le bus de commande

Pour cela, on peut ajouter un bit au bus de commande, qui indique si le bus est en train de transmettre une trame ou s'il est inactif. Cette méthode est très utilisée sur les bus mémoire, à savoir le bus qui relie le processeur à une mémoire. Il faut dire que de tels bus sont généralement assez simples et ne demandent pas un codage en trame digne de ce nom. Les commandes sont envoyées à la mémoire en une fois, parfois en deux fois, guère plus. Mais il y a moyen de se passer de ce genre d'artifice avec des méthodes plus ingénieuses, qui sont utilisées sur des bus plus complexes, destinés aux entrées-sorties.

### Inactiver la liaison à la fin de l'envoi d'une trame

Une première solution est de laisser la liaison complètement inactive durant un certain temps, entre l'envoi de deux trames. La liaison reste à 0 Volts durant un temps fixe à la fin de l'émission d'une trame. Les composants détectent alors ce temps mort et en déduisent que l'envoi de la trame est terminée. Malheureusement, cette méthode pose quelques problèmes.
* Premièrement, elle réduit les performances. Une bonne partie du débit binaire de la liaison passe dans les temps morts de fin de trame, lorsque la liaison est inactivée.
* Deuxièmement, certaines trames contiennent de longues suites de 0, qui peuvent être confondues avec une liaison inactive.
Dans ce cas, le protocole de couche liaison peut résoudre le problème en ajoutant des bits à 1, dans les données de la trame, pour couper le flux de 0. Ces bits sont identifiés comme tel par l'émetteur, qui reconnait les séquences de bits problématiques.

### Les octets START et STOP

De nos jours, la quasi-totalité des protocoles utilisent la même technique : ils placent un octet spécial (ou une suite d'octet) au début de la trame, et un autre octet spécial pour la fin de la trame. Ces octets de synchronisation, respectivement nommés START et STOP, sont standardisés par le protocole.
Problème : il se peut qu'un octet de la trame soit identique à un octet START ou STOP. Pour éviter tout problème, ces pseudo-octets START/STOP sont précédés par un octet d'échappement, lui aussi standardisé, qui indique qu'ils ne sont pas à prendre en compte. Les vrais octets START et STOP ne sont pas précédés de cet octet d'échappement et sont pris en compte, là où les pseudo-START/STOP sont ignorés car précédés de l'octet d'échappement. Cette méthode impose au récepteur d'analyser les trames, pour détecter les octets d'échappements et interpréter correctement le flux de bits reçu. Mais cette méthode a l'avantage de gérer des trames de longueur arbitrairement grandes, sans vraiment de limites.
Une autre solution consiste à remplacer l'octet/bit STOP par la longueur de la trame. Immédiatement à la suite de l'octet/bit START, l'émetteur va envoyer la longueur de la trame en octet ou en bits. Cette information permettra au récepteur de savoir quand la trame se termine. Cette technique permet de se passer totalement des octets d'échappement : on sait que les octets START dans une trame sont des données et il n'y a pas d'octet STOP à échapper. Le récepteur a juste à compter les octets qu'il reçoit et 'a pas à détecter d'octets d'échappements. Avec cette approche, la longueur des trames est bornée par le nombre de bits utilisés pour coder la longueur. Dit autrement, elle ne permet pas de trames aussi grandes que possibles.
Dans le cas où les trames ont une taille fixe, à savoir que leur nombre d'octet ne varie pas selon la trame, les deux techniques précédentes sont inutiles. Il suffit d'utiliser un octet/bit de START, les récepteurs ayant juste à compter les octets envoyés à sa suite. Pas besoin de STOP ou de coder la longueur de la trame.

### Les bits de START/STOP

Il arrive plus rarement que les octets de START/STOP soient remplacés par des bits spéciaux ou une séquence particulière de fronts montants/descendants.
Une possibilité est d'utiliser les propriétés certains codages, comme le codage de Manchester. Dans celui-ci, un bit valide est représenté par un front montant ou descendant, qui survient au beau milieu d'une période. L'absence de fronts durant une période est censé être une valeur invalide, mais les concepteurs de certains bus ont décidé de l'utiliser comme bit de START ou STOP. Cela donne du sens aux deux possibilités suivantes : la tension reste constante durant une période complète, soit à l'état haut, soit à l'état bas. Cela permet de coder deux valeurs supplémentaires : une où la tension reste à l'état haut, et une autre où la tension reste à l'état bas. La première valeur sert de bit de START, alors que l'autre sert de bit de STOP. Cette méthode est presque identique aux octets de START et de STOP, sauf qu'elle a un énorme avantage en comparaison : elle n'a pas besoin d'octet d'échappement dans la trame, pas plus que d'indiquer la longueur de la trame.
Un autre exemple est celui des bus RS-232, RS-485 et I²C, où les bits de START et STOP sont codés par des fronts sur les bus de données et de commande.

## Le codage des trames : les bits d'ECC

Lorsqu'une trame est envoyée, il se peut qu'elle n'arrive pas à destination correctement. Des parasites peuvent déformer la trame et/ou en modifier des bits au point de la rendre inexploitable. Dans ces conditions, il faut systématiquement que l'émetteur et le récepteur détectent l'erreur : ils doivent savoir que la trame n'a pas été transmise ou qu'elle est erronée.
Pour cela, il existe diverses méthodes de détection et de correction d'erreur, que nous avons abordées en partie dans les premiers chapitres du cours. On en distingue deux classes : celles qui ne font que détecter l'erreur, et celles qui permettent de la corriger. Tous les codes correcteurs et détecteurs d'erreur ajoutent tous des bits de correction/détection d'erreur aux données de base, aussi appelés des bits d'ECC. Ils servent à détecter et éventuellement corriger toute erreur de transmission/stockage. Plus le nombre de bits ajoutés est important, plus la fiabilité des données sera importante.
Les bits d'ECC sont générés lors de l'envoi de la donnée sur la liaison point à point. Dans ce qui suit, on part du principe que l'on utilise une liaison série, la donnée est envoyée sur le bus bit par bit. La conversion parallèle-série est faite en utilisant un registre à décalage. La sortie du registre à décalage donne le bit envoyé sur le bus.

### Le générateur/checker de parité

Dans le cas le plus simple, on se contente d'un simple bit de parité. C'est par exemple ce qui est fait sur les bus ATA qui relient le disque dur à la carte mère, mais aussi sur les premières mémoires RAM des PC. Lors de l'envoi d'une donnée, le bit de parité est généré par un circuit appelé le générateur de parité sériel. Comme son nom l'indique, il calcule le bit de parité bit par bit, avec une bascule et une porte XOR. Rappelons que le bit de parité se calcule en faisant un XOR entre tous les bits du nombre à envoyer.
Le registre à décalage est initialisé avec le nombre dont on veut calculer la parité. La bascule est initialisée à zéro et son but est de conserver le bit de parité calculé à chaque étape. À chaque cycle, un bit de ce nombre sort du registre à décalage et est envoyé en entrée de la porte XOR. La porte XOR fait un XOR entre ce bit et le bit de parité stocké dans la bascule, ce qui donne un bit de parité temporaire. Ce dernier est mémorisé dans la bascule pour être utilisé au prochain cycle. Le bit de parité final est disponible quand tous les bits ont été envoyés sur le bus, et la sortie du générateur de parité est alors connectée au bus pendant un cycle.

### Le générateur/checker de CRC

Dans d'autres cas, on peut ajouter une somme de contrôle ou un code de Hamming à la trame, ce qui permet de détecter les erreurs de transmission. Mais cet usage de l'ECC est beaucoup plus rare. On trouve quelques carte mères qui gèrent l'ECC pour la communication avec la RAM, mais elles sont surtout utilisées sur les serveurs.
Pour les transmissions réseaux, le code utilisé est un code de redondance cyclique, un CRC. Les circuits de calcul de CRC sont ainsi très simples à concevoir : ce sont souvent de simples registres à décalage à rétroaction linéaire améliorés. Le registre en question a la même taille que le mot dont on veut vérifier l'intégrité. Il suffit d'insérer le mot à contrôler bit par bit dans ce registre, et le CRC est calculé au fil de l'eau, le résultat étant obtenu une fois que le mot est totalement inséré dans le registre.
Le registre dépend du CRC à calculer, chaque CRC ayant son propre registre.

## Les méthodes de retransmission

Les codes de détection d'erreurs permettent parfois de corriger une erreur de transmission. Mais il arrive souvent que ce ne soit pas le cas : l'émetteur doit alors être prévenu et agir en conséquence. Pour cela, le récepteur peut envoyer une trame à l'émetteur qui signifie : la trame précédente envoyée est invalide. Cette trame est appelée un accusé de non-réception. La trame fautive est alors renvoyée au récepteur, en espérant que ce nouvel essai soit le bon. Mais cette méthode ne fonctionne pas si la trame est tellement endommagée que le récepteur ne la détecte pas.
Pour éviter ce problème, on utilise une autre solution, beaucoup plus utilisée dans le domaine du réseau. Celle-ci utilise des accusés de réception, à savoir l'inverse des accusés de non-réception. Ces accusés de réception sont envoyés à l'émetteur pour signifier que la trame est valide et a bien été reçue. Nous les noterons ACK dans ce qui suivra.
Après avoir envoyé une trame, l'émetteur va attendra un certain temps que l'ACK correspondant lui soit envoyé. Si l’émetteur ne reçoit pas d'ACK pour la trame envoyée, il considère que celle-ci n'a pas été reçue correctement et la renvoie. Pour résumer, on peut corriger et détecter les erreurs avec une technique qui mélange ACK et durée d'attente : après l'envoi d'une trame, on attend durant un temps nommé time-out que l'ACK arrive, et on renvoie la trame au bout de ce temps si non-réception. Cette technique porte un nom : on parle d'Automatic repeat request.

### Le protocole Stop-and-Wait

Dans le cas le plus simple, les trames sont envoyées unes par unes au rythme d'une trame après chaque ACK. En clair, l'émetteur attend d'avoir reçu l'ACK de la trame précédente avant d'en envoyer une nouvelle. Parmi les méthodes de ce genre, la plus connue est le protocole Stop-and-Wait.
Cette méthode a cependant un problème pour une raison simple : les trames mettent du temps avant d'atteindre le récepteur, de même que les ACK mettent du temps à faire le chemin inverse. Une autre conséquence des temps de transmission est que l'ACK peut arriver après que le time-out (temps d'attente avant retransmission de la trame) soit écoulé. La trame est alors renvoyée une seconde fois avant que son ACK arrive. Le récepteur va alors croire que ce second envoi est en fait l'envoi d'une nouvelle trame !
Pour éviter cela, la trame contient un bit qui est inversé à chaque nouvelle trame. Si ce bit est le même dans deux trames consécutives, c'est que l'émetteur l'a renvoyée car l'ACK était en retard. Mais les temps de transmission ont un autre défaut avec cette technique : durant le temps d'aller-retour, l'émetteur ne peut pas envoyer de nouvelle trame et doit juste attendre. Le support de transmission n'est donc pas utilisé de manière optimale et de la bande passante est gâchée lors de ces temps d'attente.

### Les protocoles à fenêtre glissante

Les deux problèmes précédents peuvent être résolus en utilisant ce qu'on appelle une fenêtre glissante. Avec cette méthode, les trames sont envoyées les unes après les autres, sans attendre la réception des ACKs. Chaque trame est numérotée de manière à ce que l'émetteur et le récepteur puisse l’identifier. Lorsque le récepteur envoie les ACK, il précise le numéro de la trame dont il accuse la réception. Ce faisant, l'émetteur sait quelles sont les trames qui ont été reçues et celles à renvoyer (modulo les time-out de chaque trame).
On peut remarquer qu'avec cette méthode, les trames sont parfois reçues dans le désordre, alors qu'elles ont été envoyées dans l'ordre. Ce mécanisme permet donc de conserver l'ordre des données envoyées, tout en garantissant le fait que les données sont effectivement transmises sans problèmes. Avec cette méthode, l'émetteur va accumuler les trames à envoyer/déjà envoyées dans une mémoire. L'émetteur devra gérer deux choses : où se situe la première trame pour laquelle il n'a pas d'ACK, et la dernière trame envoyée. La raison est simple : la prochaine trame à envoyer est l'une de ces deux trames. Tout dépend si la première trame pour laquelle il n'a pas d'ACK est validée ou non. Si son ACK n'est pas envoyé, elle doit être renvoyée, ce qui demande de savoir quelle est cette trame. Si elle est validée, l'émetteur pourra envoyer une nouvelle trame, ce qui demande de savoir quelle est la dernière trame envoyée (mais pas encore confirmée). Le récepteur doit juste mémoriser quelle est la dernière trame qu'il a reçue. Lui aussi va devoir accumuler les trames reçues dans une mémoire, pour les remettre dans l'ordre.

## La transmission des trames sur un bus série

L'envoi d'une trame sur une liaison série demande d'envoyer celle-ci bit par bit. Et il existe des composants spécialisés, qui traduisent une trame en un flux de bit, qui envoient les bits un par un à la fréquence adéquate. De tels composants sont appelés des Universal asynchronous receiver-transmitter (UART ) ou encore des Universal synchronous and asynchronous receiver-transmitter (USART). La différence entre les deux tient dans le fait que l'un gère des communications asynchrones, l'autre gére des communications synchrones et asynchrones. Pour rappel, la communication asynchrone se passe de signal d'horloge et utilise surtout des bits de START/STOP.
Les UART étaient beaucoup utilisés avec les anciens connecteurs RS-232 et RS-485, autrefois appelés ports séries sur les anciens PC. Le port série était utilisé pour brancher des imprimantes, des modems, et d'autres périphériques du genre. Le port série était opposé au port parallèle, qui lui était relié à un bus parallèle capable de transférer un octet à la fois. Les ports séries étaient tous précédés par un UART ou un USART. Ils sont aujourd'hui tombés en désuétude et l'UART est aujourd'hui intégré au chipset de la carte mère.
Notons que ce qui va suivre sera surtout valide pour les anciennes liaisons série, qui transmettaient des informations par petits paquets. Généralement, la transmission se faisait octet pat octet, parfois par trames de 5, 6, 7 bits, plus rarement 9. Les liaisons série modernes comme le PCI-Express ont des trames nettement différentes. Mais les principes de base restent les mêmes.

### L'interface d'un UART/USART

L'interface d'un USART est assez simple à comprendre quand on sait qu'il n'est qu'un intermédiaire. Il est un intermédiaire entre la liaison série, et un autre composant. Historiquement, les tout premiers USART étaient reliés directement sur le processeur. Par la suite, ils ont été connectés à d'autres circuits de la carte mère, en l’occurrence le chipset.
Toujours est-il que leur statut d'intermédiaire fait que certaines broches sont reliées à la liaison série, alors que d'autres sont reliées au processeur/chipset ou à un autre composant. Les broches en question sont donc séparées en deux ports : un port série pour la liaison série, un port externe pour la liaison avec le reste. D'autres broches ne font pas partie d'un port, comme la broche pour la tension d'alimentation, celle pour la masse, celle pour l'horloge, etc.
L'UART est un circuit séquentiel qui a une fréquence plus élevée que la liaison série, généralement 8 à 16 fois plus élevée. Et c'est là une des différences entre le port série et le port externe. Le port série a la même fréquence la liaison série, si celle-ci est une liaison synchrone. Dans le cas d'une liaison asynchrone, la fréquence de l'UART est conçue pour être compatible avec une transmission asynchrone sur la liaison. Par contre, le port externe a une fréquence bien plus élevée que le port série, identique à la fréquence de l'UART proprement dit. Cette différence de fréquence s'explique par le fait que la liaison série est plus lente que le processeur/chipset. Et cela aura quelques conséquences qu'on verra dans ce qui suit.
Le port externe a une taille qui généralement de l'ordre de l'octet, à savoir qu'il est connecté à un bus de un octet, ou une liaison point à point de un octet. Et historiquement, la majorité des bus série utilisait des trames d'un octet. Notons que les trames ne sont pas forcément d'un octet, il arrive que les trames fassent facilement 4 à 10 octets, selon le bus série utilisé. Les liaisons Ethernet utilisent par exemple des trames de 1500 octets. Dans ce cas, les trames sont envoyées octet par octet à l'UART, qui doit mémoriser ces octets pour qu'ils fassent une trame complète. Il contient pour cela des registres, comme nous le verrons plus haut.

### L'intérieur d'un UART/USART

Le fonctionnement interne d'un USART est assez simple, sur le principe. Pour envoyer ou recevoir les données sur une liaison série, il utilise des registres à décalage. Pour simplifier les explications, nous allons prendre une liaison série bidirectionnelle, et plus précisément de type full duplex, à savoir qu'il y a un fil pour l'envoi et un autre pour la réception. Sur le principe, elles restent valables pour une liaison unidirectionnelle, ou half-duplex, mais avec quelques changements mineurs. Même chose pour l'adaptation à un bus série.
Un UART contient un registre à décalage PISO dont la sortie est reliée à la liaison série, ce qui fait que les bits sont envoyés un par un. Il y a la même chose en réception, où un second registre à décalage SIPO voit le fil de réception connecté sur son entrée, ce qui fait que les bits reçus sont accumulés un par un dans ce registre à décalage. Le récepteur accumule les bits dans ce registre à décalage SIPO, jusqu'à avoir une trame complète.
Il faut noter que l'USART ne fait pas qu'envoyer les bits de données. En envoi, il ajoute aussi des bits de START/STOP pour délimiter les trames, ainsi que des bits de parité. En réception, il retire les bits START/STOP de la trame. Pour être précis, il utilise les bits START/STOP pour délimiter les trames. Il contient des circuits pour détecter les bits de START, ainsi que les bits de STOP. En clair, il ne fait pas de la conversion série-parallèle, il code et encode des trames complètes. Pour cela, les registres à décalage sont couplés à des circuits de contrôle internes à l'USART.
Parmi les circuits de contrôle, il faut mentionner ceux qui calculent les bits de parité ou d'ECC. Le premier génère les bits de parité à ajouter à la trame, le second vérifie les bits de parité reçu. Ils peuvent corriger les données reçues si l'ECC est utilisé et qu'une seule erreur de transmission a eu lieu. En cas d'échec, ils peuvent lancer la procédure adéquate pour gérer l'erreur, mais certains UART simples n'en sont pas capables.
Les deux registres à décalage sont cadencés par un signal d'horloge généré à l'intérieur de la puce. En effet, une liaison série peut fonctionner à plusieurs fréquences différentes. Par exemple, le RS-232 pouvait fonctionner à une vitesse allant de 50 à 19 200 bits par secondes. Les fréquences en question sont souvent multiples l'une de l'autre. En clair, elles peuvent s'obtenir à partir d'une fréquence de base, multipliée par un coefficient. Quelques UART implémentaient cependant ces fréquences à l'envers : elles prenaient la fréquence maximale, et la divisaient par un coefficient pour obtenir la fréquence voulue. Les UART contenaient pour cela des multiplieurs/diviseurs de fréquences.
L'UART contient aussi des registres de configuration, qui permettent de configurer la vitesse de transmission, la taille d'une trame, etc. Par exemple, il est possible de configurer la taille d'une trame pour choisir entre des tailles prédéfinies, comme des trames de 5, 7, 8, 9 bits. Il est aussi possible de désactiver les bits de parité ou d'ECC si le périphérique n'en a pas besoin. On a alors un gain en vitesse, vu que les bits d'ECC ne sont pas transmis. Enfin, il est aussi possible de configurer la vitesse de transmission, selon que le composant périphérique relié à la liaison série est un composant rapide ou non.

### Les registres d’interfaçage

Un UART contient aussi deux autres registres, appelés le registre d'émission et de réception. Ils servent d'interface avec le port externe, qui ne fonctionne pas à la même fréquence que la liaison série. Lors d'une émission, le processeur/chipset envoie une trame à l'UART. Elle est alors copiée dans le registre d'émission, où elle attend que la liaison soit libre. La trame peut être envoyée à l'UART octet par octet, les octets sont alors accumulés dans le registre d'émission jusqu'à obtenir une trame complète. En réception, le registre de réception sert à la même chose : lorsqu'une trame complète a été reçue, elle est copiée dans le registre de réception, puis envoyée octet par octet sur le port externe.
La présence de ces deux registres permet aussi de simplifier les échanges avec le processeur. Par exemple, lors d'un envoi, le processeur écrit dans le registre d'émission, mais la liaison série n'est pas forcément libre. Aussi, la trame envoyée doit attendre que la liaison soit libre, dans le registre d'émission. Par exemple, une trame peut être en cours d'envoi dans le registre PISO, pendant que le processeur écrit dans le registre d'émission. Idem avec le registre de réception : le processeur peut récupérer une donnée dans ce registre, pendant qu'une autre transmission est en cours.
Nous verrons dans le chapitre sur le contrôleur de périphérique que c'est là un principe général, qui s'applique à tous les périphériques, et que les registres équivalents pour les autres périphériques sont appelés des registres d’interfaçage. Pour le moment, nous allons les appeler registres d'interface processeur, car on suppose que l'UART est relié directement au processeur. S'il n'est pas connecté directement au processeur, il est connecté à un bus auquel le processeur a accès et où seul le processeur est censé commander l'UART. Notons que ce bus est souvent half-duplex, ce qui n'est pas forcément le cas de la liaison série, les deux registres d'émission/réception sont alors utiles pour faire l'interface entre les deux.
Une autre utilité est que l'ajout des bits de START et de STOP est plus simple avec ces registres. Lorsqu'un paquet de données est copié du registre d'émission vers le registre à décalage, les bits de START/STOP sont ajoutés. Ils sont physiquement présents dans le registre à décalage PISO. Même chose en réception, où les bits de START/STOP sont présents, mais sont retirés lors de la copie dans le registre de réception. Idem avec les bits de parité ou d'ECC, qui sont ajoutés ou retirés.

### Les UART/USART historiques : le 8250 et le 16 550

De nos jours, plus personne n'utilise de UART standalone, ils sont intégrés dans les composants eux-mêmes et ne sont que des portions minuscules de circuits intégrés plus grands. Par exemple, une carte mère a de nos jours un chipset, un circuit intégré qui fait tout, dont l'UART n'occupe que 1% du circuit intégré. Mais étudier les anciens circuits UART est intéressant pour comprendre comment fonctionnent les USART.
Dans cette section, nous allons voir le circuit intégré 8250 de la société National Semiconductor, et son successeur le 16 550. C'étaient des UART assez simples. Ils étaient présents dans les tout premiers PC 8 bits, mais ils étaient aussi très utilisés dans des modems, des imprimantes, et bien d'autres composants. Il est illustré ci-contre. Comme vous le voyez, son interface est assez complexe, avec beaucoup de broches, 40 au total. Nous n'allons pas détailler toutes les broches, seulement les principales.
En premier lieu, les broches D0 à D7 permettent de lire/écrire un octet sur la liaison série. Elles servent d'entrées et de sorties. En entrée, c'est là que le processeur écrit la donnée à envoyer. En sortie, c'est là qu'est récupéré l'octet réceptionné. Elles sont connectées à un bus parallèle, sur lequel le processeur ou un contrôleur de périphérique envoie/récupère l'octet voulu. Les broches RD, WR, /RD et /WR indiquent si l'USART doit être configuré en envoi ou réception, les signaux /CS2, CS1, CS0 ils activent ou désactivent l'USART (c'est des signaux de Chip Select).
La broche INTRPT est connectée directement au processeur. Pour simplifier, elle est impliquée dans la communication entre UART et processeur. Elle indique au CPU qu'un octet a été réceptionné et est disponible. Il s'agit formellement d'une broche d'interruption, dont nous ne pouvons pas parler ici, vu que nous n'avons pas encore vu le concept d'interruption.
Le 8250 avait un générateur de fréquence interne, ce qui lui permettait de gérer plusieurs bus différents. par exemple, il pouvait gérer le bus RS-232 ou le bus PS/2, alors qu'ils ont tout deux des fréquences différentes. Pour cela, le 8259 recevait une horloge de base sur les entrées dédiées XIN, XOUT, /BAUDOUT, RCLK. Puis, il multipliait cette fréquence par un coefficient, afin de générer la fréquence idéale pour le bus voulu.

### Le remplacement des registres d'émission/réception par une FIFO

Il 8250 et ses successeurs n'étaient pas les seuls UARTs disponibles sur le marché. Entre le Zilog SCC, le MOS Technology 6551, le Zilog Z8440 et bien d'autres, il y avait le choix. Et parmi tout ce zoo d'UART, il faut séparer deux classes. La première est celle qui regroupe le 8250 vu précédemment, le 8251 d'Intel, le Motorola 6850, le 6551 de MOS Technology et quelques autres. Ils disposaient d'un registre d'émission et d'un registre de réception, là où les autres les remplaçaient par une mémoire FIFO.
Le Zilog SCC a remplacé le registre de réception par une mémoire FIFO. L'idée était d'accumuler plusieurs octets avant de prévenir le processeur. Plusieurs trames étaient réceptionnées avant qu'on fasse intervenir le processeur, qui n'était pas interrompu à tout bout de champ. Nous verrons cela plus en détail dans le chapitre sur la synchronisation entre CPU et périphériques. Toujours est-il que le Zilog SCC avait un registre d'émission de un octet et une FIFO de réception de 3 octets. Seule la réception était améliorée.
Le successeur du 8250, le 16 550, a appliqué la même optimisation pour le registre d'émission, qui était remplacé par une mémoire FIFO. Pour rappel, l'UART a une fréquence plus élevée que la liaison série et il communique avec un processeur qui a aussi une fréquence très élevée. Il était alors possible d'envoyer plusieurs trames à l'UART en même temps, ou du moins dans un intervalle de temps très court, largement plus petit que celui nécessaire pour transmettre une trame. Sur le 16 550, le processeur pouvait envoyer 16 trames à la suite, qui sont ensuite transmises une par une par l'UART. Les UARTs suivants implémentaient des FIFOs de 128 octets, comme le 16 850 ou le 16C850.
De nombreux UART implémentaient cette optimisation, que ce soit en réception ou en émission. Il faut dire que sans ça, les transmissions étaient limitées à environ 1000 octets par secondes, pour des raisons liées au système d'exploitation. Pour simplifier, l'OS limite le nombre d'accès à l'UART à 1000 fois par seconde (une interruption toutes les millisecondes). Mais avec l'ajout de mémoires FIFO, le débit s'améliore. Une FIFO peut être intégralement remplie ou vidée en un seul passage du processeur. Par exemple, avec une FIFO de 128, on peut transmettre 128 × 1000 = 125 kibioctets par secondes.