« Fonctionnement d'un ordinateur/Les bus et liaisons point à point (généralités) » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
m →‎Arbitrage du bus : un espèce
Ligne 193 : Ligne 193 :
==Arbitrage du bus==
==Arbitrage du bus==


Sur certains bus, il arrive que plusieurs composants tentent d'envoyer ou de recevoir une donnée sur le bus en même temps : c'est un '''conflit d'accès au bus'''. Cette situation arrive sur de nombreux types de bus, qu'ils soient multiplexés ou non. Sur les bus multipléxés, qui relient plus de deux composants, cette situation est fréquente du fait du nombre de récepteurs/émetteurs potentiels. Mis cela peut aussi arriver sur certains bus dédiés, les bus half-duplex étant des exemples particuliers : il se peut que les deux composants veuillent être émetteurs en même temps, ou récepteurs. Quoiqu'il en soit, ces conflits d'accès posent problème si un composant cherche à envoyer un 1 et l'autre un 0 : tout ce que l’on reçoit à l'autre bout du fil est un espèce de mélange incohérent des deux données envoyées sur le bus par les deux composants. Pour résoudre ce problème, il faut répartir l'accès au bus pour n'avoir qu'un émetteur à la fois. On doit choisir un émetteur parmi les candidats. Ce choix sera effectué différemment suivant le protocole du bus et son organisation, mais ce choix n’est pas gratuit : certains composants devront attendre leur tour pour avoir accès au bus. Les concepteurs de bus ont inventé des méthodes pour gérer ces conflits d’accès, et choisir le plus efficacement possible l’émetteur : on parle d''''arbitrage du bus'''.
Sur certains bus, il arrive que plusieurs composants tentent d'envoyer ou de recevoir une donnée sur le bus en même temps : c'est un '''conflit d'accès au bus'''. Cette situation arrive sur de nombreux types de bus, qu'ils soient multiplexés ou non. Sur les bus multipléxés, qui relient plus de deux composants, cette situation est fréquente du fait du nombre de récepteurs/émetteurs potentiels. Mis cela peut aussi arriver sur certains bus dédiés, les bus half-duplex étant des exemples particuliers : il se peut que les deux composants veuillent être émetteurs en même temps, ou récepteurs. Quoiqu'il en soit, ces conflits d'accès posent problème si un composant cherche à envoyer un 1 et l'autre un 0 : tout ce que l’on reçoit à l'autre bout du fil est une espèce de mélange incohérent des deux données envoyées sur le bus par les deux composants. Pour résoudre ce problème, il faut répartir l'accès au bus pour n'avoir qu'un émetteur à la fois. On doit choisir un émetteur parmi les candidats. Ce choix sera effectué différemment suivant le protocole du bus et son organisation, mais ce choix n’est pas gratuit : certains composants devront attendre leur tour pour avoir accès au bus. Les concepteurs de bus ont inventé des méthodes pour gérer ces conflits d’accès, et choisir le plus efficacement possible l’émetteur : on parle d''''arbitrage du bus'''.


===Méthode d'arbitrage (algorithme)===
===Méthode d'arbitrage (algorithme)===

Version du 11 janvier 2019 à 10:52

Schéma d'un bus.

Dans un ordinateur, les composants sont placés sur un circuit imprimé (la carte mère), un circuit sur lequel on vient connecter les différents composants d'un ordinateur, et qui relie ceux-ci via divers bus. Si je dis "par divers bus", c'est parce qu'il n'y a pas qu'un seul bus dans un ordinateur, mais plusieurs : un bus pour communiquer avec le disque dur, un bus pour la carte graphique, un pour le processeur, un pour la mémoire, etc. De ce fait, un PC contient un nombre impressionnant de bus, jugez plutôt :

  • le SMBUS, qui est utilisé pour communiquer avec les ventilateurs, les sondes de température et les sondes de tension présentes un peu partout dans notre ordinateur ;
  • les bus USB ;
  • le bus PCI, utilisé pour les cartes son et qui servait autrefois à communiquer avec les cartes graphiques ;
  • le bus AGP, autrefois utilisé pour les cartes graphiques ;
  • le bus PCI Express, utilisé pour communiquer avec des cartes graphiques ou des cartes son ;
  • le bus P-ATA, relié au disque dur ;
  • le bus S-ATA et ses variantes eSATA, eSATAp, ATAoE, utilisés pour communiquer avec le disque dur ;
  • le bus Low Pin Count, qui permet d'accéder au clavier, aux souris, au lecteur de disquette, et aux ports parallèle et série ;
  • le bus ISA et son cousin le bus EISA, autrefois utilisés pour des cartes d'extension ;
  • l'Intel QuickPath Interconnect et l'HyperTransport, qui relient les processeurs récents au reste de l'ordinateur ;
  • le FireWire (1394) ;
  • le bus SCSI et ses variantes (SCSI Parallel, Serial Attached SCSI, iSCSI), qui permettent de communiquer avec des disque durs ;
  • le bus MIDI, une véritable antiquité oubliée de tous, qui servait pour les cartes son ;
  • le fameux RS-232 utilisé dans les ports série ;
  • enfin, le bus IEEE-1284 utilisé pour le port parallèle.

Propriétés d’un bus

Ces bus sont très différents les uns des autres, et ont des caractéristiques très différentes qui sont listées dans le tableau ci-dessous.

Caractéristique Définition
Largeur Nombre maximal de bits transmis simultanément sur le bus.
Débit binaire Nombre de bits que le bus peut transmettre par seconde.
Latence Temps d'attente que met une donnée à être transférée sur le bus.
Caractère synchrone ou asynchrone Certains bus transmettent un signal d'horloge, d'autres non.
Protocole Le protocole d'un bus définit comment les données sont envoyées sur le bus.

Bus série et parallèle

Comparaison entre bus série et parallèle.
Comparaison entre bus série et parallèle.

La plupart des bus peuvent échanger plusieurs bits en même temps et sont appelés bus parallèles. Mais il existe des bus qui ne peuvent échanger qu'un bit à la fois : ce sont des bus série. On pourrait croire qu'un bus série ne contient qu'un seul fil pour transmettre les données, mais il existe des contrexemples. Généralement, c'est le signe que le bus n'utilise pas un codage NRZ, mais une autre forme de codage un peu plus complexe. Par exemple, le bus USB utilise deux fils D+ et D- pour transmettre un bit. Pour faire simple, lorsque le fil D+ est à sa tension maximale, l'autre est à zéro (et réciproquement).

La transmission et la réception sur un bus série demande de faire une conversion entre les données, qui sont codées sur plusieurs bits, et le flux série à envoyer sur le bus. Cela s'effectue généralement en utilisant des registres à décalage, commandés par des circuits de contrôle.

Serial interface
Serial interface

Passons maintenant aux bus parallèles. Pour information, si le contenu d'un bus de largeur de n bits est mis à jour m fois par secondes, alors son débit binaire est de n × m. Mais contrairement à ce qu'on pourrait croire, les bus parallèles ne sont pas plus rapides que les bus série. En effet, le temps d'attente entre deux transmissions est plus grand sur les bus parallèles. Un bus série n'a pas ce genre de problèmes, ce qui surcompense le fait qu’un bus série ne peut envoyer qu'un bit à la fois.

Il existe plusieurs raisons à cette infériorité des temps de latence des bus série. Premièrement, les fils d'un bus ne sont pas identiques électriquement : leur longueur et leur résistance changent très légèrement d'un fil à l'autre. En conséquence, un bit va se propager à des vitesses différentes suivant le fil. On est obligé de se caler sur le fil le plus lent pour éviter des problèmes à la réception. En second lieu, il y a le phénomène de crosstalk. Lorsque la tension à l'intérieur du fil varie (quand le fil passe de 0 à 1 ou inversement), le fil va émettre des ondes électromagnétiques qui perturbent les fils d'à côté. Il faut attendre que la perturbation électromagnétique se soit atténuée pour lire les bits, ce qui limite le nombre de changements d'état du bus par seconde.

Bus synchrones et asynchrones

On peut faire la différence entre bus synchrone et asynchrone, la différence se faisant selon l'usage ou non d'une horloge. La méthode de synchronisation des composants et des communications sur le bus peut ainsi utiliser une horloge, ou la remplacer par des mécanismes autres.

Bus synchrone

Certains bus sont synchronisés sur un signal d'horloge : ce sont les bus synchrones. Avec ces bus, le temps de transmission d'une donnée est fixé une fois pour toute. Le composant sait combien de cycles d'horloge durent une lecture ou une écriture. Sur certains bus, le contenu du bus n'est pas mis à jour à chaque front montant, ou à chaque front descendant, mais aux deux : fronts montant et descendant. De tels bus sont appelés des bus double data rate. Cela permet de transférer deux données sur le bus (une à chaque front) en un seul cycle d'horloge : le débit binaire est doublé sans toucher à la fréquence du bus.

Exemple de lecture sur un bus synchrone.
Exemple de lecture sur un bus synchrone.

Bus asynchrone

À haute fréquence, le signal d'horloge met un certain temps pour se propager à travers le fil d'horloge, ce qui induit un léger décalage entre les composants. Plus on augmente la longueur des fils, plus ces décalages deviendront ennuyeux. Plus on augmente la fréquence, plus la période diminue comparée au temps de propagation de l'horloge dans le fil. Ces phénomènes font qu'il est difficile d'atteindre des fréquences de plusieurs gigahertz sur les bus actuels. Pour ces raisons, certains bus se passent complètement de signal d'horloge, et ont un protocole conçu pour : ce sont les bus asynchrones. Ces bus sont donc très adaptés pour transmettre des informations sur de longues distances (plusieurs centimètres ou plus).

Exemple d'écriture sur un bus asynchrone
Exemple d'écriture sur un bus asynchrone

Bus dédiés et multiplexés

Sur certains bus, on peut connecter un nombre assez important de composants : ce sont les bus multiplexés. Le composant qui envoie une donnée sur le bus est appelé un émetteur, alors que ceux reçoivent les données sont appelés récepteurs. Avec ces bus, un émetteur va envoyer ses données à tous les autres récepteurs. Sur tous ces récepteurs, il se peut que seul l'un d'entre eux soit le destinataire du paquet : les autres vont alors ignorer le paquet, seul le destinataire traitant le paquet. Cependant, il se peut qu'il y aie plusieurs récepteurs comme destinataires : dans ce cas, les destinataires vont tous recevoir la donnée et la traiter. Ces bus permettent donc de faire des envois de données à plusieurs composants en une seule fois.

Bus multiplexés.
Bus multiplexés.

Les bus multiplexés sont à opposer aux bus dédiés, qui se contentent de connecter deux composants entre eux. Les bus dédiés sont classés en trois types, suivant les récepteurs et les émetteurs.

Type de bus Description
Simplex Les informations ne vont que dans un sens : un composant est l'émetteur et l'autre reste à tout jamais récepteur.
Half-duplex Il est possible d'être émetteur ou récepteur, suivant la situation. Par contre, impossible d'être à la fois émetteur et récepteur.
Full-duplex Ce bus permet d'être à la fois récepteur et émetteur. On peut créer un bus full duplex en regroupant deux bus simplex ensemble, un bus pour l'émission et un pour la réception. Mais certains bus n'utilisent pas cette technique et se contentent d'un seul bus bidirectionnel.

Interfaçage avec le bus

Une fois que l'on sait quel composant a accès au bus à un instant donné, il faut trouver un moyen pour que les composants non sélectionnés par l'arbitrage ne puissent pas écrire sur le bus.

Circuits d’interfaçage avec le bus

Une première solution consiste à relier les sorties des composants au bus via un multiplexeur : on est alors certain que seul un composant pourra émettre sur le bus à un moment donné. L'arbitrage du bus choisit quel composant peut émettre, et configure l'entrée de commande du multiplexeur en fonction.

Une autre solution consiste à déconnecter du bus les sorties qui n'envoient pas de données. Plus précisément, leurs sorties peuvent être mises dans un état de haute impédance, qui n'est ni un 0 ni un 1 : quand une sortie est en haute impédance, celle-ci n'a pas la moindre influence sur les composants auxquels elle est reliée. Un composant dont les sorties sont en haute impédance ne peut pas influencer le bus et ne peut donc pas y écrire. Pour mettre une sortie en état de haute impédance, on utilise des circuits trois états, qui possèdent une entrée de donnée, une entrée de commande, et une sortie : suivant ce qui est mis sur l'entrée de commande, la sortie est soit en état de haute impédance (déconnectée du bus), soit dans l'état normal (0 ou 1).

Commande Entrée Sortie
0 0 Haute impédance/Déconnexion
0 1 Haute impédance/Déconnexion
1 0 0
1 0 1

Pour simplifier, on peut voir ceux-ci comme des interrupteurs :

  • si on envoie un 0 sur l'entrée de commande, ces circuits trois états se comportent comme un interrupteur ouvert ;
  • si on envoie un 1 sur l'entrée de commande, ces circuits trois états se comportent comme un interrupteur fermé.
Tampons 3 états.
Tampons 3 états.

Interfaçage avec le bus en lecture et écriture

On peut utiliser ces circuits trois états pour permettre à un composant d'émettre ou de recevoir des données sur un bus. Par exemple, on peut utiliser ces composants pour autoriser les émissions sur le bus, le composant étant déconnecté (haute impédance) s'il n'a rien à émettre. Le composant a accès au bus en écriture seule. L'exemple typique est celui d'une mémoire ROM reliée à un bus de données.

Bus en écriture seule.
Bus en écriture seule.

Une autre possibilité est de permettre à un composant de recevoir des données sur le bus. Le composant peut alors surveiller le bus et regarder si des données lui sont transmises, ou se déconnecter du bus. Le composant a alors accès au bus en lecture seule.

Bus en lecture seule.
Bus en lecture seule.

Évidemment, on peut autoriser lectures et écritures : le composant peut alors aussi bien émettre que recevoir des données sur le bus quand il s'y connecte. On doit alors utiliser deux circuits trois états, un pour l'émission/écriture et un autre pour la réception/lecture. Comme exemple, on pourrait citer les mémoires RAM, qui sont reliées au bus mémoire par des circuits de ce genre. Dans ce cas, les circuits trois états doivent être commandés par le bit CS (qui connecte ou déconnecte la mémoire du bus), mais aussi par le bit R/W qui décide du sens de transfert. Pour faire la traduction entre ces deux bits et les bits à placer sur l'entrée de commande des circuits trois états, on utilise un petit circuit combinatoire assez simple.

Bus en lecture et écriture.
Bus en lecture et écriture.

Le codage des données/commandes sur un bus

La communication sur un bus est légèrement différente de ce qu'on a vu dans les chapitres précédents. Auparavant, quand deux composants devaient communiquer, on se contenter de relier les deux par un fil, et faire en sorte que de nouvelles données soient envoyées à chaque cycle. Mais sur un bus, ce n'est pas toujours le cas : il arrive que le bus soit inutilisé durant un certain temps, sans données transmises. Les composants qui veulent communiquer doivent donc déterminer quand le bus est inutilisé afin de ne pas confondre l'état de repos du bus avec des données. Pour cela, on peut certes ajouter un bit au bus de commande, qui indique si une transmission démarre sur le bus. Mais il y a moyen de se passer de ce genre d'artifice sur les bus série avec des méthodes plus ingénieuses.

Pour les bus série, il faut aussi 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. Pour résoudre ces problèmes, les transmissions sur le bus sont standardisées de manière à rendre l'interprétation du flux de bit claire et sans ambiguïté. Deux composants électroniques communiquent entre eux en s'envoyant des trames, des flux de bits où chaque information nécessaire à la transmission est à une place précise. Dans le cas le plus simple, une trame commence par un octet qui indique le début de la transmission, suivi par l'adresse du récepteur, puis les données à transmettre, et enfin un octet qui indique la fin de la transmission. D'autres trames de ce genre existent, et nous en verrons quelques unes dans ce va suivre. Nous allons d'abord voir comment les bits sont codés sur un bus, avant de voir comment les flux de bits sont segmentés en trames.

Les codes en ligne : le codage des bits sur un bus

Chaque fil d'un bus transmet un signal, qui peut être codé de diverses manières. Il existe des méthodes relativement nombreuses pour coder un bit de données pour le transmettre sur un bus : ces méthodes sont appelées des codages en ligne. Toutes codent celui-ci avec une tension, qui peut prendre un état haut (tension forte) ou un état bas (tension faible, le plus souvent proche de 0 volts). Outre le codage des données, il faut prendre aussi en compte le codage des commandes. En effet, certains bus série utilisent des fils dédiés pour la transmission des bits de données et de commande. Cela permet d'éviter d'utiliser trop de fils pour un même procédé.

Les codes non-différentiels

La plupart des méthodes se contentent d'une seule tension, d'un seul fil, pour coder les bits. La première de ces méthodes, le codage NRZ-L, devrait vous être familière : il s'agit d'utiliser l'état haut pour coder un 1 et l'état bas pour le zéro (ou l'inverse). Le codage NRZ-M fonction ne différemment : un état haut signifie que le bit envoyé est l'inverse du précédent, tandis que l'état bas indique que le bit envoyé est identique au précédent. Le codage NRZ-S est identique au codage NRZ-M si ce n'est que l'état haut et bas sont inversés. Le codage RZ est similaire au codage NRZ, si ce n'est que la tension retourne systématiquement à l'état bas après la moitié d'un cycle d'horloge. Celui-ci permet une meilleure synchronisation avec le signal d'horloge, notamment dans les environnements bruités. Avec le codage Manchester, aussi appelé codage biphasé, un 1 est codé par un front descendant, alors qu'un 0 est codé par un front montant (ou l'inverse, dans certaines variantes). Ce codage s'obtient en faisant un OU logique entre l'horloge et le flux de bits à envoyer (codé en NRZ-L). Diverses variantes existent, qui codent un 1 ou un 0 avec un front, tandis que l'autre bit est codé comme en NRZ-L. Ces différentes méthodes se distinguent par des caractéristiques électriques qui sont à l’avantage ou au désavantage de l'un ou l'autre suivant la situation : meilleur spectre de bande passante, composante continue nulle/non-nulle, etc.

Illustration des différents codes en ligne.
Illustration des différents codes en ligne.

Les codes différentiels

Pour plus de fiabilité, il est possible d'utiliser deux fils pour envoyer un bit (sur un bus série). Ces deux fils ont un contenu qui est inversé électriquement. Dans le détail, le premier utilise une tension positive pour l'état haut et le second une tension négative. Ce faisant, on utilise la différence de tension pour coder le bit. C'est plus fiable que d'utiliser des tensions deux fois plus élevées, pour des raisons que nous passerons sous silence. Un tel codage est appelé un codage différentiel. Il est notamment utilisé sur le protocole USB. Sur ce protocole, deux fils sont utilisés pour transmettre un bit, via codage différentiel. Dans chaque fil, le bit est codé par un codage NRZ-L.

Signal USB : exemple.
Signal USB : exemple.

La gestion des trames

Le codage des trames indique comment l'émetteur doit envoyer les données sur le bus, ce qui permet aux récepteurs de détecter les données transmises et les interpréter. Le terme trame réfère aux données transmises : toutes les informations nécessaires pour une transmission sont regroupées dans ce qu'on appelle une trame qui est envoyée telle quelle sur le bus. Une trame doit fournir plusieurs informations. Premièrement, le codage des trames doit permettre une synchronisation des équipements, qui ne fonctionnent pas forcément à la même vitesse. Cela se fait par la transmission d'un signal de synchronisation ou signal d'horloge dans les trames. Enfin, la trame doit contenir toutes les données de la transmission, et de quoi contrôler celle-ci : indiquer les récepteurs, dire quand commence la transmission et quand elle se termine, etc.

Sur les bus série, ces 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). Pour les bus parallèles, les choses peuvent être plus variables. Il est possible d'envoyer des trames plus grosses que la largeur du bus : la trame est envoyée par paquets de données, octet par octet ou byte par byte. Mais sur certains bus, toute la trame est envoyée d'un coup sur le bus : 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 quand sa transmission est terminée. Ce cas correspond à celui qu'on a vu dans les premiers chapitres, quand on reliait la sortie d'un composant sur l'entrée d'un autre. Par exemple, reprenons le bus d'une mémoire RAM, qui est un exemple de bus parallèle. On a surtout vu le cas où la RAM était connectée à un bus d'adresse, un bus de données et de commande : dans ce cas, la trame proprement dite est composée de l'adresse, des bits de commande et de la donnée, et il est évident que la trame est envoyée en une seule fois. Mais on a aussi vu le cas, plus rare, où les données sont envoyées l'une après l'autre : on envoie l'adresse, puis la donnée, par exemple. Ou encore, on envoie l'adresse haute, puis l'adresse basse, et enfin la donnée. Dans ce cas, la trame complète est la même, mais elle est envoyée en plusieurs fois, par paquets de la taille du bus (ou presque). La même chose est possible avec d'autres bus que le bus mémoire, ainsi qu'avec d'autres formes de données/trames. Si la mémoire avait un bus série, on pourrait lui envoyer la trame bit par bit.

Le codage du début et de la fin de transmission

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. De nos jours, la quasi-totalité des protocoles utilisent la même technique pour délimiter le début et la fin d'une trame. Ils utilisent un octet spécial (ou une suite d'octet), qui indique le début de la trame, et un autre octet pour la fin de la trame. Ces octets de synchronisation, respectivement nommés START et STOP, sont standardisés par le protocole. Entre ces octets de synchronisation, on trouve un en-tête standardisé, les données à transmettre et éventuellement des données de contrôle d'erreur. Un problème peut cependant survenir : il se peut qu'un octet de la trame soit identique à un octet START ou STOP. Mais il y a moyen de résoudre le problème assez simplement. Pour cela, il suffit de faire précéder ces pseudo-octets START/STOP par un octet d'échappement, lui aussi standardisé. Les vrais octets START et STOP ne sont pas précédés de cet octet d'échappement et seront pris en compte, là où les pseudo-START/STOP seront ignoré car précédés de l'octet d'échappement. Il arrive plus rarement que ces octets de START/STOP soient en réalité codés par des bits spéciaux. Un bon exemple est celui du bit I²C, où les bits de START et STOP sont codés par des fronts sur les bus de données et de commande. Il en est de même sur le BUS RS-232 et RS-485, comme nous le verrons plus tard.

Trame
Trame

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. Ce faisant, le récepteur a moins de travail d'analyse à faire : il n'a pas vraiment à analyser le contenu des octets pour savoir quoi faire, mais a juste à compter les octets qu'il reçoit. Cette méthode et la méthode précédente ont des conséquences différentes sur la taille des trames. Si la trame peut être très longue, la longueur doit être codée sur plusieurs octets : cela prend plus de place que l'octet/bit STOP. Par contre, les octets d’échappement sont économisés. Un autre défaut (très théorique) de cette approche est que 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. Mais ce défaut est plus théorique qu'autre chose.

Trame - 2
Trame - 2

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.

Le codage du récepteur

La trame doit naturellement être envoyée à un récepteur, seul destinataire de la trame. Sur les bus où il n'y a toujours que deux composants de connectés, il n'y a pas besoin de préciser quel est le récepteur. On peut déduire assez facilement que le récepteur est l'autre composant, celui qui n'a pas envoyé la donnée : sa détermination est implicite. Mais sur les bus avec plusieurs composants, la trame doit indiquer à qui elle est destinée. Pour cela, chaque composant se voit attribuer une adresse, il est numéroté. La trame a juste à indiquer quelle est l'adresse du composant de destination. Cela fonctionne aussi pour les composants qui sont des périphériques : on a vu dans le chapitre précédent que chaque périphérique/composant se voyait attribuer une adresse. L'adresse en question est intégrée à la trame et est placée à un endroit précis, le plus souvent à son début. La raison à cela est que les récepteurs espionnent le bus en permanence pour détecter les trames qui leur sont destinées. Ils lisent toujours les trames envoyées sur le bus et en extraient l'adresse de destination : si celle-ci leur correspond, ils lisent le reste de la trame, ou se déconnectent du bus sinon. Pour que la transmission soit la plus rapide, il vaut mieux que les périphériques sachent le plus rapidement si la trame leur est destinée, ce qui demande de mettre l'adresse au plus tôt.

La fiabilité des transmissions

ACK TCP

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 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 aux données de base, ces bits étant appelés des bits de correction/détection d'erreur. Ces bits 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. Dans le cas le plus simple, on se contente d'un simple bit de parité. Dans d'autres cas, on peut ajouter une somme de contrôle ou un code de Hamming à la trame. Cela permet de détecter les erreurs de transmission.

Si l'erreur peut être corrigée par le récepteur, tout va bien. 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.

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 deux problèmes précédents peuvent être résolu 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és reçues et celles à renvoyer (modulo les time-out de chaque trame). On peut remarquer qu'avec cette méthode, les trames sont parfois recues dans le désordre, alors qu'elles ont été envoyées dans le désordre. 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 recue. Lui aussi va devoir accumuler les trames recues dans une mémoire, pour les remettre dans l'ordre.

Arbitrage du bus

Sur certains bus, il arrive que plusieurs composants tentent d'envoyer ou de recevoir une donnée sur le bus en même temps : c'est un conflit d'accès au bus. Cette situation arrive sur de nombreux types de bus, qu'ils soient multiplexés ou non. Sur les bus multipléxés, qui relient plus de deux composants, cette situation est fréquente du fait du nombre de récepteurs/émetteurs potentiels. Mis cela peut aussi arriver sur certains bus dédiés, les bus half-duplex étant des exemples particuliers : il se peut que les deux composants veuillent être émetteurs en même temps, ou récepteurs. Quoiqu'il en soit, ces conflits d'accès posent problème si un composant cherche à envoyer un 1 et l'autre un 0 : tout ce que l’on reçoit à l'autre bout du fil est une espèce de mélange incohérent des deux données envoyées sur le bus par les deux composants. Pour résoudre ce problème, il faut répartir l'accès au bus pour n'avoir qu'un émetteur à la fois. On doit choisir un émetteur parmi les candidats. Ce choix sera effectué différemment suivant le protocole du bus et son organisation, mais ce choix n’est pas gratuit : certains composants devront attendre leur tour pour avoir accès au bus. Les concepteurs de bus ont inventé des méthodes pour gérer ces conflits d’accès, et choisir le plus efficacement possible l’émetteur : on parle d'arbitrage du bus.

Méthode d'arbitrage (algorithme)

Il existe plusieurs méthodes d'arbitrages, qui peuvent se classer en différents types, selon leur fonctionnement. Par exemple, certains protocoles permettent de libérer le bus de force pour laisser la place à un autre composant : on parle alors de bus mastering. Certains composants peuvent ainsi préempter le bus : ils donnent l'accès au bus à un composant durant un certain temps fixe, même si un composant qui n'utilise pas totalement le temps qui lui est attribué peut libérer le bus prématurément. Sur certains bus, certains composants sont prioritaires, et les circuits chargés de l'arbitrage libèrent le bus de force si un composant plus prioritaire veut utiliser le bus.

Pour onner un exemple d'algorithme d'arbitrage, nous allons voir l'arbitrage par multiplexage temporel. Celui-ci peut se résumer en une phrase : chacun son tour ! Chaque composant a accès au bus à tour de rôle, durant un temps fixe. Cet arbitrage est mal adapté quand certains composants effectuent beaucoup de transactions sur le bus et d’autres très peu.

Une autre méthode est celle de l'arbitrage par requête, qui se résume à un simple « premier arrivé, premier servi » ! L'idée est que tout composant peut réserver le bus si celui-ci est libre, mais doit attendre si le bus est déjà réservé. Pour savoir si le bus est réservé, il existe deux méthodes :

  • soit chaque composant peut vérifier à tout moment si le bus est libre ou non (aucun composant n'écrit dessus) ;
  • soit on rajoute un bit qui indique si le bus est libre ou occupé : le bit busy.

Arbitrage centralisé ou décentralisé

Une autre classification nous dit si un composant gère le bus, ou si cet arbitrage est délégué aux composants qui accèdent au bus. Dans l'arbitrage centralisé, un circuit spécialisé s'occupe de l'arbitrage du bus. Dans l'arbitrage distribué, chaque composant se débrouille de concert avec tous les autres pour éviter les conflits d’accès au bus : chaque composant décide seul d'émettre ou pas, suivant l'état du bus. Le second critère de classement se base sur l'algorithme d'arbitrage. Un même algorithme peut être implémenté soit de manière centralisée, soit de manière décentralisée.

Pour donner un exemple d'arbitrage centralisé, nous allons aborder l'arbitrage par daisy chain. Il s'agit d'un algorithme centralisé, dans lequel tout composant a une priorité fixe. Dans celui-ci, tous les composants sont reliés à un arbitre, qui dit si l'accès au bus est autorisé. Les composants sont reliés à l'arbitre via deux fils : un fil nommé Request qui part des composants et arrive dans l'arbitre, et un fil Grant qui part de l'arbitre et parcours les composants un par un. Le fil Request indique à l'arbitre qu'un composant souhaite accéder au bus : c'est un signal d'autorisation. Le composant qui veut accéder au bus va placer un sur ce fil 1 quand ils veut accéder au bus. Ce fil est unique, partagé entre tous les composants (cela remplace l'utilisation d'une porte OU). Le fil Grant permet à l'arbitre de signaler qu'un des composant pourra avoir accès au bus. Ce fil relie l'arbitre au premier composant. Celui-ci fournit un fil Grant, qui sort du premier composant pour renter dans le second, et ainsi de suite : tous les composants sont reliés en guirlande par ce fil Grant. Ce fil permet de décider quel composant peut accéder au bus. Par défaut, l'arbitre envoie un 1 quand il accepte un nouvel accès au bus (et un 0 quand il veut bloquer tout nouvel accès. Quand un composant ne veut pas accéder au bus, il transmet le bit reçu sur ce fil tel quel, sans le modifier. Mais s'il veut accéder au bus, il mettra un zéro sur ce fil : les composants précédents verront ainsi un 1 sur le fil, mais les suivants verront un zéro (interdiction d'accès). Ainsi, les composants les plus près du bus, dans l'ordre de la guirlande, seront prioritaires sur les autres.

Daisy Chain.
Daisy Chain.

Exemples de bus

Dans ce qui va suivre, nous allons étudier quelques bus relativement connus, autrefois très utilisés dans les ordinateurs. La plupart de ces bus sont très simples : il n'est pas question d'étudier les bus les plus en vogue à l'heure actuelle, du fait de leur complexité. Nous allons surtout étudier les bus série, les bus parallèles étant plus rares.

Port série RS-232

Port série (RS-232).

Pour donner un premier exemple, nous allons aborder le port RS-232, un bus série utilisé sur les port série qu'on trouve à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre. Ce RS-232 est un bus série asynchrone.

Une transmission sur ce bus se décompose en un bit de start, les données à transmettre, un bit de parité, et un bit de stop. Le bit de start est systématiquement un bit qui vaut 0, tandis que le bit de stop vaut systématiquement 1.

Trame RS-232.
Trame RS-232.
Trame RS-232.
Trame RS-232.

L'envoi et la réception des trames sur ce bus se fait simplement en utilisant un composant nommé UART composé de registres à décalages qui envoient ou réceptionnent les données bit par bit sur le bus. Les données envoyer sont placées dans un registre à décalage, dont le bit de sortie est connecté directement sur le bus série. La réception se fait de la même manière : le bus est conncté à l'entrée d'un registre à décalage. Quelques circuits annexes s'occupent du calcul de la parité et de la détection des bits de start et de stop.

Schema UART.
Schema UART.

Bus I²c

Nous allons maintenant prendre l'exemple typique du bus I2c.

Bus I2C.
Bus I2C.

Celui-ci utilise deux fils pour, respectivement, le transport des données et de l'horloge, nommés SDA (Serial Data Line) et SCL (Serial Clock Line). Ces deux fils sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Les données sont transmises bit par bit, avec pour détail que les données sont maintenues tant que l’horloge est à 1. Dit autrement, le signal de donnée ne montre aucun front entre deux fronts de l'horloge. Retenez bien cette remarque. Un transfert de données commence avec un bit START, qui est codé par une mise à 0 de l'horloge avant le fil de donnée, et se termine avec un bit STOP, qui correspond aux conditions inverses. Les deux correspondent à une violation de la règle qui veut qu'il y aie absence de front sur le signal de données entre deux fronts d'horloge.