Il y a quelques chapitres, nous avons vu la différence entre bus et liaison point à point : là où ces dernières ne connectent que deux composants, les bus de communication en connectent bien plus. Ce faisant, les bus de communication font face à de nouveaux problèmes, inconnus des liaisons point à point. Et ce sont ces problèmes qui font l'objet de ce chapitre. Autant le chapitre précédent valait à la fois pour les liaisons point à point et les bus, autant ce n'est pas le cas de celui-ci. Ce chapitre va parler de ce qui n'est valable que pour les bus de communication, comme leur arbitrage, la détection des collisions, etc. Tous ces problèmes ne peuvent pas survenir, par définition, sur les liaisons point à point.
L'adressage du récepteur.
La trame doit naturellement être envoyée à un récepteur, seul destinataire de la trame. Sur les liaisons point à point, il n'y a pas besoin de préciser quel est le récepteur. Mais sur les bus, c'est une toute autre histoire. Tous les composants reliés aux bus sont de potentiels récepteurs et l'émetteur doit préciser à qui la trame est destinée. Pour résoudre ce problème, chaque composant se voit attribuer une adresse, il est « numéroté ». Cela fonctionne aussi pour les composants qui sont des périphériques.
L'adressage sur les bus parallèles et série.
Sur les bus parallèles, l'adresse est généralement transmise sur des fils à part, sur un sous-bus dédié appelé le bus d'adresse. En général, les adresses sur les bus pour périphériques sont assez petites, de quelques bits dans le cas le plus fréquent, quelques octets tout au plus. Il n'y a pas besoin de plus pour adresser une centaine de composants ou plus. Les seuls bus à avoir des adresses de plusieurs octets sont les bus liés aux mémoires, ou ceux qui ont un rapport avec les réseaux informatiques.
Les bus multiplexés utilisent une astuce pour économiser des fils et des broches. Un bus multiplexé sert alternativement de bus de donnée ou d'adresse, suivant la valeur d'un bit du bus de commande. Ce dernier, le bit "Adress Line Enable" (ALE), précise si le contenu du bus est une adresse ou une donnée : il vaut 1 quand une adresse transite sur le bus, et 0 si le bus contient une donnée. Un défaut de ces bus est que les transferts sont plus lents, car l'adresse et la donnée ne sont pas envoyées en même temps. Un autre problème des bus multiplexé est qu'ils ont a peu-près autant de bits pour coder l'adresse que pour transporter les données. Par exemple, un bus multiplexé de 8 bits transmettra des adresses de 16 bits, mais aussi des données de 16 bits. Ils sont donc moins versatiles, mais cela pose surtout problème sur les bus où l'on peut connecter peu de périphériques. Dans ce cas, les adresses sont très petites et l'économie de fils est donc beaucoup plus faible.
Passons maintenant aux bus série (ou certains bus parallèles particuliers). Pour arriver à destination, la trame doit indiquer l'adresse du composant de destination. Les récepteurs espionnent le bus en permanence pour détecter les trames qui leur sont destinées. Ils lisent toutes 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, ils ne la prennent pas en compte sinon. L'adresse en question est intégrée à la trame et est placée à un endroit précis, toujours le même, pour que le récepteur puisse l'extraire. Le plus souvent, l'adresse de destination est placée au début de la trame, afin qu'elle soit envoyée au plus vite. Ainsi, les périphériques savent plus rapidement si la trame leur est destinée ou non, l'adresse étant connue le plus tôt possible.
Le décodage d'adresse.
Le fait d'attribuer une adresse à chaque composant est une idée simple, mais efficace. Encore faut-il la mettre en œuvre et il existe plusieurs possibilités pour cela. Implémenter l'adressage sur un bus demande à ce que chaque composant sache d'une manière ou d'une autre que c'est à lui que l'on veut parler et pas à un autre. Lorsqu'une adresse est envoyée sur le bus, seul l'émetteur et le récepteur se connectent au bus, les autres composants ne sont pas censés réagir. Et pour cela, il existe deux possibilités : soit on délègue l'adressage au composant, soit on ajoute un circuit qui active le composant adressé et désactive les autres.
Avec la première méthode, les composants branchés sur le bus monitorent en permanence ce qui est transféré sur le bus. Quand un envoi de commande a lieu, chaque composant extrait l'adresse transmise sur le bus et vérifie si c'est bien la sienne. Si c'est le cas, le composant se connecte sur le bus et les autres composants se déconnectent. En conséquence, chaque composant contient un comparateur pour cette vérification d'adresse, dont la sortie commande les circuits trois états qui relient le contrôleur au bus. Cette méthode est particulièrement pratique sur les bus où le bus d'adresse est séparé du bus de données. Si ce n'est pas le cas, le composant doit mémoriser l'adresse transmise sur le bus dans un registre, avant de faire la comparaison? Même chose sur les bus série.
La seconde solution est celle du décodage d'adresse. Elle utilise un circuit qui détermine, à partir de l'adresse, quel est le composant adressé. Seul ce composant sera activé/connecté au bus, tandis que les autres seront désactivés/déconnectés du bus. Pour implémenter la dernière solution, chaque périphérique possède une entrée CS, qui active ou désactive le composant suivant sa valeur. Le composant se déconnecte du bus si ce bit est à 0 et est connecté s'il est à 1. Pour éviter les conflits, un seul composant doit avoir son bit CS à 1. Pour cela, il faut ajouter un circuit qui prend en entrée l'adresse et qui commande les bits CS : ce circuit est un circuit de décodage partiel d'adresse.
L'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.
Les circuits d’interfaçage avec le bus : circuits trois-états et multiplexeurs.
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, elle n'a pas la moindre influence sur 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).
Pour simplifier, on peut voir ceux-ci comme des interrupteurs :
L'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.
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.
É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 ("Chip Select") qui connecte ou déconnecte la mémoire du bus, mais aussi par le bit R/W ("Read/Write") 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.
L'arbitrage du bus.
Sur certains bus, il arrive que plusieurs composants tentent d'envoyer 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 multiplexés, qui relient plus de deux composants, cette situation est fréquente du fait du nombre de récepteurs/émetteurs potentiels. Mais 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.
Quoi qu’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.
Les méthodes d'arbitrage (algorithmes).
Il existe plusieurs méthodes d'arbitrages, qui peuvent se classer en différents types, selon leur fonctionnement.
Pour donner un exemple d'algorithme d'arbitrage, parlons de 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. Cette méthode fort simple convient si les différents composants ont des besoins approximativement équilibrés. Mais elle n'est pas adaptée quand certains composants effectuent beaucoup de transactions que les autres. Les composants gourmands manqueront de débit, alors que les autres monopoliseront le bus pour ne presque rien en faire. Une solution est d'autoriser à un composant de libérer le bus prématurément, s'il n'en a pas besoin. Ce faisant, les composants qui n'utilisent pas beaucoup le bus laisseront la place aux composants plus gourmands.
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 :
Certains protocoles permettent de libérer le bus de force pour laisser la place à un autre composant : on parle alors de bus mastering. 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. Bref, les méthodes d'arbitrage sont nombreuses.
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.
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" transmet à l'arbitre une demande d'accès au bus. Le composant qui veut accéder au bus va placer un sur ce fil 1 quand il veut accéder au bus. Le fil "Grant" permet à l'arbitre de signaler qu'un des composants pourra avoir accès au bus. Le fil est unique "Request" est partagé entre tous les composants (cela remplace l'utilisation d'une porte OU). Par contre, le fil "Grant" relie l'arbitre au premier composant, puis le premier composant au second, le second au troisième, etc. Tous les composants sont reliés en guirlande par ce fil Grant.
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.