Wikilivres
frwikibooks
https://fr.wikibooks.org/wiki/Accueil
MediaWiki 1.45.0-wmf.5
first-letter
Média
Spécial
Discussion
Utilisateur
Discussion utilisateur
Wikilivres
Discussion Wikilivres
Fichier
Discussion fichier
MediaWiki
Discussion MediaWiki
Modèle
Discussion modèle
Aide
Discussion aide
Catégorie
Discussion catégorie
Transwiki
Discussion Transwiki
Wikijunior
Discussion Wikijunior
TimedText
TimedText talk
Module
Discussion module
Programmation C sharp/Les variables et les constantes
0
14122
744729
744725
2025-06-14T14:47:06Z
DavidL
1746
Révocation d’une modification réalisée par [[Special:Contributions/197.149.187.26|197.149.187.26]] ([[User talk:197.149.187.26|discussion]]) et restauration de la dernière version réalisée par [[User:DavidL|DavidL]]
659123
wikitext
text/x-wiki
<noinclude>{{Programmation C sharp}}{{NavTitre|book={{BASEPAGENAME}}|prev=Les espaces de noms|next=Les types de base et les déclarations}}</noinclude>
== Les variables ==
Une variable réserve une place en mémoire pour stocker des données : résultats d'expressions, données lues depuis un fichier, ... Elle est associée à un nom.
=== Déclaration ===
Une variable possède un nom et un type. Ce type détermine ce que peut stocker la variable : un nombre, une chaîne de caractère, un objet d'une classe particulière...
La syntaxe est la suivante :
''type nom ['' = ''expression ]'' ;
Exemples :
<syntaxhighlight lang="csharp">
double prix_unitaire;
int quantite = 50;
string article = "Pommes";
bool rabais = false;
</syntaxhighlight>
Assigner une variable (initialiser) à la déclaration n'est pas obligatoire. Dans ce cas, elle possède la valeur par défaut correspondant au type :
* la valeur par défaut des types numériques et caractères (<code>char</code>) est zéro (0),
* la valeur par défaut du type booléen (<code>bool</code>) est <code>false</code>,
* la valeur par défaut des types références (objet, chaîne de caractère, tableaux) est <code>null</code>,
* la valeur par défaut des types énumérés (<code>enum</code>) est celle qui correspond au type sous-jacent (<code>int</code> par défaut).
Il est possible de regrouper la déclaration de plusieurs variables du même type en utilisant une virgule pour séparer les différentes variables.
Exemple :
<syntaxhighlight lang="csharp">
int quantite = 50, nombre, quantite_en_stock = 100 ;
</syntaxhighlight>
Ou en plaçant une variable par ligne pour plus de clarté :
<syntaxhighlight lang="csharp">
int quantite = 50,
nombre,
quantite_en_stock = 100 ;
</syntaxhighlight>
=== Utilisation ===
Une variable est utilisée pour stocker des résultats intermédiaires, des données qui serviront ultérieurement. La valeur d'une variable peut être modifiée autant de fois que nécessaire.
Exemple :
<syntaxhighlight lang="csharp">
prix_unitaire = 1.50;
if (rabais) prix_unitaire = prix_unitaire - 0.20;
double prix_total = prix_unitaire * quantite ;
</syntaxhighlight>
=== Portée d'une variable ===
La portée d'une variable est l'ensemble des emplacements dans le code où cette variable est accessible. En dehors de cette portée, l'utilisation du même nom correspondra à une autre variable ou plus souvent à aucune variable (erreur de compilation dans ce cas).
La portée d'une variable correspond au bloc d'accolades où elle est déclarée.
Une variable membre d'une classe est déclarée au niveau de la classe et est accessible depuis les autres membres de la classe (méthodes, indexeurs, propriétés, ...). Une variable locale est déclarée à l'intérieur d'un bloc d'accolades (celui de la méthode/propriété membre, ou un bloc interne à celle-ci).
Exemple :
<syntaxhighlight lang="csharp">
public class Exemple
{
// variable membre de la classe :
int nombre = 5; // portée : la classe Exemple
public int MethodeUne()
{
// variable locale à la méthode :
int nombre = 10; // portée : la méthode MethodeUne()
return nombre + // la variable locale
this.nombre; // la variable membre
// retourne 10 + 5 , c'est à dire 15
}
public int MethodeDeux()
{
System.Console.WriteLine("Nombre = " + nombre); // la variable membre
// Nombre = 5
{
int nombre = 20; // variable locale au bloc d'accolades
System.Console.WriteLine("Nombre = " + nombre); // la variable locale
// Nombre = 20
} // la variable locale est hors de portée
System.Console.WriteLine("Nombre = " + nombre); // la variable membre
// Nombre = 5
}
}
</syntaxhighlight>
=== Durée de vie d'une variable ===
La durée de vie d'une variable est la période durant laquelle une variable existe, c'est à dire conserve son emplacement en mémoire et durant laquelle elle peut donc stocker des données.
Pour une variable locale d'une méthode, la durée de vie correspond à celle de l'exécution du bloc de code : elle est allouée au début du bloc de déclaration sur la pile, et libérée de la pile après exécution du bloc.
Pour une variable membre d'une classe, la durée de vie correspond à celle de l'objet (l'instance de la classe) : elle est allouée à la construction d'un objet et libérée lors de la libération de l'objet.
=== Variable en lecture seule ===
Une variable peut être déclarée en lecture seule en utilisant le mot-clé <code>readonly</code>.
Exemple :
<syntaxhighlight lang="csharp">
readonly double taux_tva = 19.6;
</syntaxhighlight>
Il n'est pas obligatoire d'initialiser une variable en lecture seule lors de sa déclaration. Ce qui permet de déterminer la valeur en fonction d'autres données, ou de lire sa valeur depuis un fichier par exemple, et d'empêcher sa modification après affectation.
Exemple :
<syntaxhighlight lang="csharp">
class Facture
{
public readonly double taux_tva;
public Facture(bool taux1)
{
if (taux1) taux_tva = 19.6;
else taux_tva = 5.5;
}
}
</syntaxhighlight>
== Les constantes ==
Une constante nommée est associée à une valeur pour toute la durée de l'application. Sa valeur ne peut changer.
=== Déclaration ===
La syntaxe est similaire à celle de la déclaration d'une variable, excepté que le mot clé <code>const</code> précède la déclaration, et que l'initialisation à la déclaration est obligatoire :
const ''type nom'' = ''expression'' ;
Exemple :
<syntaxhighlight lang="csharp">
const double prix_unitaire_unique = 1.50 ;
const double taux_euro_en_francs = 6.55957 ;
</syntaxhighlight>
Comme les variables il est également possible de regrouper la déclaration de plusieurs constantes du même type :
<syntaxhighlight lang="csharp">
const double
prix_unitaire_unique = 1.50 ,
taux_euro_en_francs = 6.55957 ;
</syntaxhighlight>
'''N.B.''' : Une constante est implicitement statique. Il est donc inutile d'ajouter le mot-clé <code>static</code>, sinon le compilateur génère une erreur.
=== Types des constantes ===
Le mot <code>const</code> ne s'applique qu'aux types standards numériques, booléens, <code>string</code>, et énumérations.
=== Portée et durée de vie d'une constante ===
Les mêmes règles que celles pour les variables s'appliquent également aux constantes.
clr089dwr6hsqgbj4z1gccyhmfra3qz
Le système d'exploitation GNU-Linux/Commandes de base
0
20391
744792
707802
2025-06-15T04:26:51Z
2A01:CB18:1150:4600:C5FF:3372:4F9:EE19
/* mkdir (make directory) */Correction de "si il" en "s'il"
744792
wikitext
text/x-wiki
<noinclude>
{{Linux}}
</noinclude>
== Éléments de syntaxe ==
Les commandes présentées dans les sections qui suivent sont introduites juste après un message d'invite qui dépend de la configuration du shell utilisé. Celui-ci est représenté par un signe dollar $. Il ne doit donc pas être tapé.
Le caractère dièse # marque le début d'un commentaire qui se termine en fin de ligne. Il n'est pas nécessaire de le recopier pour exécuter la commande.
Exemple :
$ pwd # affiche le répertoire courant
Pour tester cette commande, il suffit d'entrer {{touche|p}} {{touche|w}} {{touche|d}} {{touche|↵}}.
Les lignes qui ne sont pas marquées du signe dollar indiquent ce que la commande précédente doit/peut afficher.
Par ailleurs, il existe deux opérateurs de concaténation des commandes :
* ";" : qui enchaine les commandes quelques soient leurs résultats. Exemple : <code>pwd ; pwd</code>.
* "&" : qui stoppe l'enchainement si une commande renvoie une erreur. Exemple : <code>pwd & pwd</code>. De plus, si aucune commande ne figure après le "&", le terminal redonne le focus avant la fin de la première commande, de sorte que l'on peut en taper d'autres si elle est longue (en asynchrone).
** "&&" : idem sans les informations sur les processus.
== pwd (print working directory) ==
Affiche le répertoire courant.
$ pwd
/home/alex
== id ==
Affiche les informations relatives à l'utilisateur connecté.
$ id
uid=1000(alex) gid=1000(alex) groupes=20(dialout), 24(cdrom), 25(floppy), 29(audio), 44(video), 46(plugdev), 106(netdev), 109(powerdev), 1000(alex)
== passwd ==
Permet de changer son mot de passe
<syntaxhighlight lang="bash">
$ passwd
Changing password for alex
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
passwd : le mot de passe a été mis à jour avec succès
</syntaxhighlight>
{{attention|Ne pas utiliser le symbole euros (<code>€</code>) dans les mots de passe, car il est potentiellement mal géré par Linux et pourrait se voir systématiquement refusé.|clear=left}}
== cd (change directory) ==
Pour se placer dans un répertoire
$ cd . # . désigne le répertoire courant
$ cd .. # .. désigne le répertoire parent
$ cd / # / désigne le répertoire racine
$ cd /tmp # désigne le répertoire tmp appartenant à la racine
$ cd tmp # désigne le répertoire tmp du répertoire courant
$ cd ../tmp # désigne le répertoire tmp du répertoire parent du répertoire courant
$ cd ~ # permet de revenir dans son répertoire de travail (home directory)
$ cd # idem
Connecté en root, la commande cd m'amène au répertoire /root qui est le répertoire de travail de l'utilisateur root.
Connecté avec l'utilisateur ''alex'', cd m'amène au répertoire de travail de l'utilisateur ''alex'', à savoir /home/alex.
== ls (list sorting) ==
Liste les fichiers d'un répertoire
$ ls # liste les fichiers non cachés du répertoire courant
$ ls -l # (l : long) : liste détaillée des fichiers du répertoire courant
$ ls -a # (a : all) : liste tous les fichiers, y compris les fichiers cachés
On peut combiner plusieurs options, l'ordre n'est pas important. Les quatre commandes suivantes sont identiques :
$ ls -a -l
$ ls -l -a
$ ls -la
$ ls -al
Lister de façon détaillée (-l) tous les fichiers, même cachés (-a), les plus récents (-t) en derniers (-r), avec leur taille en kilo-octets (-k) lisible facilement avec les unités K pour Kilo, M pour Mega, G pour Giga(-h).
$ ls -lartkh
Pour ne lister que les sous-dossiers :
ls -d */
Pour compter les éléments de la liste :
ls |wc -l
== cat (con''cat''enate) ==
Affiche le contenu d'un fichier ou de plusieurs fichiers concaténés sur la sortie standard (l'écran)
$ cat /etc/crontab # affiche le contenu du fichier /etc/crontab
$ cat /etc/cron.daily /etc/cron.weekly # concatène les deux fichiers et affiche leur contenu
Pour que les modifications de fichiers s'ajoutent en temps réel sur la sortie standard, utiliser :
tail -f /etc/crontab
== mkdir (make directory) ==
Permet de créer un répertoire
$ mkdir rep1 # crée un répertoire rep1 dans le répertoire courant
$ mkdir /rep1 # tente de créer un répertoire rep1 à la racine,
# le système refuse car je ne suis pas connecté en root
Exercice : je suis dans le répertoire ''/var/log'', je souhaite créer un répertoire ''rep2'' dans le répertoire ''/home/alex'', comment faire ?
J'ai trois possibilités :
$ mkdir ../../home/alex/rep2 # on utilise un adressage relatif à la position où je suis :
# on remonte dans l'arborescence jusqu'à la racine puis
# on redescend jusqu'au répertoire alex)
$ mkdir /home/alex/rep2 # on utilise un adressage absolu en partant de la racine
$ mkdir ~/rep2 # on utilise ~ pour désigner le répertoire de travail
l'option -p permet de créer le(s) répertoire(s) parent(s).
$ mkdir -p rep1/rep2 # crée un répertoire parent rep1 s'il n'existe pas,
# et crée dans rep1 un répertoire rep2
== rmdir (remove directory) ==
Supprimer un répertoire vide
$ rmdir rep1
== cp (copy) ==
Copier un fichier
$ cp /etc/passwd /tmp # copie le fichier /etc/passwd dans le répertoire /tmp
$ cp /etc/passwd /tmp/nouveaunom # copie le fichier /etc/passwd dans le répertoire /tmp
# et le renomme en ''nouveaunom''
Attention, si le fichier destination existe déjà, il sera remplacé sans demande de confirmation !
Options courantes :
-i : si le fichier destination existe, demande confirmation avant de remplacer le fichier
=== Copier un dossier (avec son contenu) ===
cp -Rf mondossier /tmp
== rm (remove) ==
Effacer un fichier
$ rm lefichier # efface le fichier ''lefichier''
Attention, le fichier est effacé et sans demander confirmation !
Options courantes :
-i : demande confirmation avant d'effacer le fichier
-f : ne demande pas de confirmation (annule -i)
-r : supprime les répertoires récursivement
=== Exemples ===
* Pour supprimer le répertoire "tmp" et son contenu :
rm -rf tmp
* Pour supprimer les fichiers de log de plus de deux jours :
find /var/log* -mtime +2 -exec rm {} \;
== mv (move) ==
Déplacer ou renommer des fichiers
<syntaxhighlight lang=bash>
$ mv [Option] Source Destination(répertoire)
$ mv [Option] Répertoire Source
$ mv fichier_source fichier_cible # déplacer fichier_source dans fichier_cible
$ mv fichier1 fichier2 # renomme le fichier fichier1 en fichier2
# Attention si fichier2 existe, son contenu sera écrasé et
# remplacé par celui de fichier1
</syntaxhighlight>
Par précaution, on utilise l'option -i qui permet d'être averti par le système avant l'écrasement du fichier destination si celui-ci existe.
<syntaxhighlight lang=bash>
$ mv -i fichier1 fichier2 # demande la confirmation avant d'effacer la destination
$ mv rep1/fic1 rep2/fic2 # déplace le fichier fic1 situé dans le répertoire ''rep1''
# sous le nouveau nom fic2 situé dans le répertoire rep2
$ mv rep1 rep2 # déplace le répertoire rep1 dans le répertoire rep2
# si rep2 n'existe pas, renomme rep1 en rep2
</syntaxhighlight>
{{attention|Si ''le dossier n'est pas vide'', utiliser "-f" pour forcer l'écrasement.}}
{{attention|Pour inclure les fichiers cachés dans les déplacements *, exécuter avant <code>shopt -s dotglob</code>.}}
== ln (link) ==
La commande ln permet de créer des liens, c'est à dire des raccourcis vers des fichiers ou des répertoires.
ln -s ''destination'' ''nom_du_lien''
Exemple
$ ln -s prog1.0 monprogramme
Cette commande crée le lien suivant :
lrwxrwxrwx 1 alex alex 7 2007-10-26 14:25 monprogramme -> prog1.0
== find : recherche par nom de fichier ==
Par exemple pour trouver tous les fichiers .log dans le dossier courant récursivement :
find . -name "*.log"
== grep : recherche par contenu de fichier ==
Par exemple pour trouver tous les fichiers qui contiennent "mon texte" dans le dossier courant récursivement :
grep -r "mon texte" .
En regex en ignorant la casse :
grep -rPi "[a-z_]+" .
== clear et reset ==
* <code>clear</code> masque les données affichées en console, et on peut les retrouver en remontant l'ascenseur.
* <code>reset</code> efface complètement les données affichées en console.
h3e8m6buso0ldb158tn2n6zn7fm1bvg
Modèle:Photo màj
10
22266
744730
744722
2025-06-14T14:47:48Z
DavidL
1746
Révocation d’une modification réalisée par [[Special:Contributions/2600:1003:B881:4C40:A4FF:BC4F:5818:85F|2600:1003:B881:4C40:A4FF:BC4F:5818:85F]] ([[User talk:2600:1003:B881:4C40:A4FF:BC4F:5818:85F|discussion]]) et restauration de la dernière version réalisée par [[User:DavidL|DavidL]]
679628
wikitext
text/x-wiki
<s></s><div style="text-align: center;">'''les 10 dernières mises à jour notables'''</div>
# [[Photographie/Personnalités/S/Southworth & Hawes|Southworth & Hawes]] (2 mars)
# [[Photographie/Fabricants/Showa Optical Works|Showa Optical Works]] (29 février)
# [[Photographie/Personnalités/B/Bradley & Rulofson|Bradley & Rulofson]] (4 janvier, MàJ)
- - - - - - - - - -
# [[Photographie/Personnalités/L/Lucien Lorelle|Lucien Lorelle]] (10 décembre, MàJ)
# [[Photographie/Sociétés et Organisations/Associations et clubs/Groupe des XV|Groupe des XV]] (10 décembre)
# [[Photographie/Fabricants/Sony|MàJ de la liste des APN Sony]] (10 décembre)
# [[Photographie/Personnalités/S/Alfred Stieglitz|Alfred Stieglitz]] (17 novembre)
# [[Photographie/Classement et archivage|Classement et archivage]] (12 novembre)
# [[Photographie/Fabricants/ADOX|ADOX]] (24 août)
# [[Photographie/Personnalités/B/Hippolyte Bayard|Hippolyte Bayard]] (22 août)
{{Boîte déroulante début|titre=mises à jour précédentes|}}
----------
# [[Photographie/Personnalités/C/Charles Louis Chevalier|Famille Chevalier (opticiens)]] (22 août)
# [[Photographie/Personnalités/K/Ronald Kroon|Ronald Kroon]] (8 août)
# [[Photographie/Personnalités/S/Giorgio_Sommer|Giorgio Sommer]] (27 juillet)
# [[Photographie/Personnalités/H/Frank Jay Haynes|Frank Jay Haynes]] (22 juillet)
# [[Photographie/Personnalités/H/Oliver Wendell Holmes Sr.|Oliver Wendell Holmes Sr.]] (6 juin)
# [[Photographie/Personnalités/R/Paul Rudolph|Paul Rudolph]] (26 mai)
# [[Photographie/Personnalités/W/Sabine Weiss|Sabine Weiss]] (1er juillet)
# [[Photographie/Personnalités/B/Frédéric Boissonnas|Frédéric Boissonnas]] (10 février)
# [[Photographie/Personnalités/V/Ariel Varges|Ariel Varges]] (8 février)
# [[Photographie/Personnalités/G/Andreas Gursky|Andreas Gursky]] (9 janvier)
# [[Photographie/Personnalités/E/Antonio Esplugas Puig|Antonio Esplugas Puig]] (8 janvier)
# [[Photographie/Fabricants/Cosina/Cosina CS-3|Cosina CS-3]] (7 janvier)
# [[Photographie/Fabricants/Cosina/Cosina CS-2|Cosina CS-2]] (7 janvier)
# [[Photographie/Fabricants/Cosina/Cosina CS-1|Cosina CS-1]] (7 janvier)
# [[Photographie/Fabricants/Cosina/Cosina CT-1|Cosina CT-1]] (7 janvier)
# [[Photographie/Fabricants/Vivitar/Vivitar XV-2|Vivitar XV-2]] (7 janvier)
# [[Photographie/Fabricants/Fujifilm/Fujifilm Fujica AX-3|Fujica AX-3]] (7 janvier)
# [[Photographie/Fabricants/Mamiya/Mamiya ZE|Mamiya ZE]] (7 janvier)
# [[Photographie/Personnalités/L/G. Lékégian|G. Lékégian]] (3 janvier)
# [[Photographie/Fabricants/Vivitar/Vivitar Zoom Thyristor 285|Vivitar Zoom Thyristor 285]] (3 janvier)
# [[Photographie/Fabricants/Sunpak/Sunpak Autozoom 3600|Sunpak Autozoom 3600]] (2 janvier)
# [[Photographie/Fabricants/Panasonic/Panasonic National PE-3057|National PE-3057]] (2 janvier)
# [[Photographie/Fabricants/Minolta/Minolta Auto 320X|Minolta Auto 320X]] (2 janvier)
# [[Photographie/Fabricants/Mamiya/Mamiya Mamiyalite ZE|Mamiyalite ZE]] (2 janvier)
# [[Photographie/Fabricants/Konica/Konica X-24 Auto|Konica X-24 Auto]] (2 janvier)
# [[Photographie/Fabricants/Fujifilm/Fujifilm Fujica Auto Strobo 300 X|Fujica Auto Strobo 300 X]] (2 janvier)
# [[Photographie/Fabricants/Yashica/Contax TLA 20|Contax TLA 20]] (2 janvier)
# [[Photographie/Personnalités/J/Pierre Jaffeux|Pierre Jaffeux]] (2 janvier)
'''Archives'''
<!--[[Photographie/Mises à jour, archives 2017|2017]] -->
[[Photographie/Mises à jour, archives 2016|2016]]
[[Photographie/Mises à jour, archives 2015|2015]]
[[Photographie/Mises à jour, archives 2014|2014]]
[[Photographie/Mises à jour, archives 2013|2013]]
[[Photographie/Mises à jour, archives 2012|2012]]
[[Photographie/Mises à jour, archives 2011|2011]]
[[Photographie/Mises à jour, archives 2010|2010]]
[[Photographie/Mises à jour, archives 2009|2009]]
[[Photographie/Mises à jour, archives 2008|2008]]
{{Boîte déroulante fin}}
<noinclude>
{{documentation|{{FULLPAGENAME}}}}
{{Photo màj}}
[[Catégorie:Modèles non imprimables du livre de photographie]]
</noinclude>
bc8on7o1rl017lf2arlw8tx5n2o903x
Fonctionnement d'un ordinateur/L'encodage des données
0
65765
744777
738791
2025-06-15T01:36:56Z
Mewtow
31375
/* Les différents codages : analogique, numérique et binaire */
744777
wikitext
text/x-wiki
Vous savez déjà qu'un ordinateur permet de faire plein de choses totalement différentes : écouter de la musique, lire des films/vidéos, afficher ou écrire du texte, retoucher des images, créer des vidéos, jouer à des jeux vidéos, etc. Pour être plus général, on devrait dire qu'un ordinateur manipule des informations, sous la forme de fichier texte, de vidéo, d'image, de morceau de musique, de niveau de jeux vidéos, etc. Dans ce qui suit, nous allons appeler ces informations par le terme '''données'''.
On pourrait définir les ordinateurs comme des appareils qui manipulent des données et/ou qui traitent de l'information, mais force est de constater que cette définition, oh combien fréquente, n'est pas la bonne. Tous les appareils électroniques manipulent des données, même ceux qui ne sont pas des ordinateurs proprement dit : les exemples des décodeurs TNT et autres lecteurs de DVD sont là pour nous le rappeler. Même si la définition d’ordinateur est assez floue et que plusieurs définitions concurrentes existent, il est évident que les ordinateurs se distinguent des autres appareils électroniques programmables sur plusieurs points. Notamment, ils stockent leurs données d'une certaine manière (le codage numérique que nous allons aborder).
==Le codage de l'information==
Avant d'être traitée, une information doit être transformée en données exploitables par l'ordinateur, sans quoi il ne pourra pas en faire quoi que ce soit. Eh bien, sachez qu'elles sont stockées… avec des nombres. Toute donnée n'est qu'un ensemble de nombres structuré pour être compréhensible par l'ordinateur : on dit que les données sont codées par des nombres. Il suffit d'utiliser une machine à calculer pour manipuler ces nombres, et donc sur les données. Une simple machine à calculer devient une machine à traiter de l'information. Aussi bizarre que cela puisse paraitre, un ordinateur n'est qu'une sorte de grosse calculatrice hyper-performante. Mais comment faire la correspondance entre ces nombres et du son, du texte, ou toute autre forme d'information ? Et comment fait notre ordinateur pour stocker ces nombres et les manipuler ? Nous allons répondre à ces questions dans ce chapitre.
Toute information présente dans un ordinateur est décomposée en petites informations de base, chacune représentée par un nombre. Par exemple, le texte sera décomposé en caractères (des lettres, des chiffres, ou des symboles). Pareil pour les images, qui sont décomposées en pixels, eux-mêmes codés par un nombre. Même chose pour la vidéo, qui n'est rien d'autre qu'une suite d'images affichées à intervalles réguliers. La façon dont un morceau d'information (lettre ou pixel, par exemple) est représenté avec des nombres est définie par ce qu'on appelle un codage, parfois appelé improprement encodage. Ce codage va attribuer un nombre à chaque morceau d'information. Pour montrer à quoi peut ressembler un codage, on va prendre trois exemples : du texte, une image et du son.
===Texte : standard ASCII===
Pour coder un texte, il suffit de savoir coder une lettre ou tout autre symbole présent dans un texte normal (on parle de '''caractères'''). Pour coder chaque caractère avec un nombre, il existe plusieurs codages : l'ASCII, l'Unicode, etc.
[[File:ASCII full.svg|vignette|Caractères ASCII imprimables.]]
Le codage le plus ancien, appelé l''''ASCII''', a été inventé pour les communications télégraphiques et a été ensuite réutilisé dans l'informatique et l'électronique à de nombreuses occasions. Il est intégralement défini par une table de correspondance entre une lettre et le nombre associé, appelée la ''table ASCII''. Le standard ASCII originel utilise des nombres codés sur 7 bits (et non 8 comme beaucoup le croient), ce qui permet de coder 128 symboles différents.
Les lettres sont stockées dans l'ordre alphabétique, pour simplifier la vie des utilisateurs : des nombres consécutifs correspondent à des lettres consécutives. L'ASCII ne code pas seulement des lettres, mais aussi d'autres symboles, dont certains ne sont même pas affichables ! Cela peut paraitre bizarre, mais s'explique facilement quand on connait les origines du standard. Ces caractères non-affichables servent pour les imprimantes, FAX et autres systèmes de télécopies. Pour faciliter la conception de ces machines, on a placé dans cette table ASCII des symboles qui n'étaient pas destinés à être affichés, mais dont le but était de donner un ordre à l'imprimante/machine à écrire... On trouve ainsi des symboles de retour à la ligne, par exemple.
[[File:ASCII-Table.svg|centre|vignette|upright=2.0|ASCII-Table]]
La table ASCII a cependant des limitations assez problématiques. Par exemple, vous remarquerez que les accents n'y sont pas, ce qui n'est pas étonnant quand on sait qu'il s'agit d'un standard américain. De même, impossible de coder un texte en grec ou en japonais : les idéogrammes et les lettres grecques ne sont pas dans la table ASCII. Pour combler ce manque, des '''codages ASCII étendus''' ont rajouté des caractères à la table ASCII de base. Ils sont assez nombreux et ne sont pas compatibles entre eux. Le plus connu et le plus utilisé est certainement le codage '''ISO 8859''' et ses dérivés, utilisés par de nombreux systèmes d'exploitation et logiciels en occident. Ce codage code ses caractères sur 8 bits et est rétrocompatible ASCII, ce qui fait qu'il est parfois confondu avec ce dernier alors que les deux sont très différents.
Aujourd'hui, le standard de codage de texte le plus connu est certainement l’'''Unicode'''. L'Unicode est parfaitement compatible avec la table ASCII : les 128 premiers symboles de l’Unicode sont ceux de la table ASCII, et sont rangés dans le même ordre. Là où l'ASCII ne code que l'alphabet anglais, les codages actuels comme l'Unicode prennent en compte les caractères chinois, japonais, grecs, etc.
===Image===
[[File:Imagematricielle.gif|vignette|Image matricielle.]]
Le même principe peut être appliqué aux images : l'image est décomposée en morceaux de même taille qu'on appelle des '''pixels'''. L'image est ainsi vue comme un rectangle de pixels, avec une largeur et une longueur. Le nombre de pixels en largeur et en longueur définit la résolution de l'image : par exemple, une image avec 800 pixels de longueur et 600 en largeur sera une image dont la résolution est de 800*600. Il va de soi que plus cette résolution est grande, plus l'image sera fine et précise. On peut d'ailleurs remarquer que les images en basse résolution ont souvent un aspect dit pixelisé, où les bords des objets sont en marche d'escaliers.
Chaque pixel a une couleur qui est codée par un ou plusieurs nombres entiers. D'ordinaire, la couleur d'un pixel est définie par un mélange des trois couleurs primaires rouge, vert et bleu. Par exemple, la couleur jaune est composée à 50 % de rouge et à 50 % de vert. Pour coder la couleur d'un pixel, il suffit de coder chaque couleur primaire avec un nombre entier : un nombre pour le rouge, un autre pour le vert et un dernier pour le bleu. Ce codage est appelé le '''codage RGB'''. Mais il existe d'autres méthodes, qui codent un pixel non pas à partir des couleurs primaires, mais à partir d'autres espaces de couleur.
Pour stocker une image dans l'ordinateur, on a besoin de connaitre sa largeur, sa longueur et la couleur de chaque pixel. Une image peut donc être représentée dans un fichier par une suite d'entiers : un pour la largeur, un pour la longueur, et le reste pour les couleurs des pixels. Ces entiers sont stockés les uns à la suite des autres dans un fichier. Les pixels sont stockés ligne par ligne, en partant du haut, et chaque ligne est codée de gauche à droite. Les fichiers image actuels utilisent des techniques de codage plus élaborées, permettant notamment décrire une image en utilisant moins de nombres, ce qui prend moins de place dans l'ordinateur.
===Son===
Pour mémoriser du son, il suffit de mémoriser l'intensité sonore reçue par un microphone à intervalles réguliers. Cette intensité est codée par un nombre entier : si le son est fort, le nombre sera élevé, tandis qu'un son faible se verra attribuer un entier petit. Ces entiers seront rassemblés dans l'ordre de mesure, et stockés dans un fichier son, comme du wav, du PCM, etc. Généralement, ces fichiers sont compressés afin de prendre moins de place.
==Le support physique de l'information codée==
Pour pouvoir traiter de l'information, la première étape est d'abord de coder celle-ci, c'est à dire de la transformer en nombres. Et peu importe le codage utilisé, celui-ci a besoin d'un support physique, d'une grandeur physique quelconque. Et pour être franc, on peut utiliser tout et n’importe quoi. Par exemple, certains calculateurs assez anciens étaient des calculateurs pneumatiques, qui utilisaient la pression de l'air pour représenter des chiffres ou nombres : soit le nombre encodé était proportionnel à la pression, soit il existait divers intervalles de pression correspondant chacun à un nombre entier bien précis. Il a aussi existé des technologies purement mécaniques pour ce faire, comme les cartes perforées ou d'autres dispositifs encore plus ingénieux. De nos jours, ce stockage se fait soit par l'aimantation d'un support magnétique, soit par un support optique (les CD et DVD), soit par un support électronique. Les supports magnétiques sont réservés aux disques durs magnétiques, destinés à être remplacés par des disques durs entièrement électroniques (les fameux Solid State Drives, que nous verrons dans quelques chapitres).
Pour les supports de stockage électroniques, très courants dans nos ordinateurs, le support en question est une '''tension électrique'''. Ces tensions sont ensuite manipulées par des composants électriques/électroniques plus ou moins sophistiqués : résistances, condensateurs, bobines, amplificateurs opérationnels, diodes, transistors, etc. Certains d'entre eux ont besoin d'être alimentés en énergie. Pour cela, chaque circuit est relié à une tension qui l'alimente en énergie : la '''tension d'alimentation'''. Après tout, la tension qui code les nombres ne sort pas de nulle part et il faut bien qu'il trouve de quoi fournir une tension de 2, 3, 5 volts. De même, on a besoin d'une tension de référence valant zéro volt, qu'on appelle la '''masse''', qui sert pour le zéro.
Dans les circuits électroniques actuels, ordinateurs inclus, la tension d'alimentation varie généralement entre 0 et 5 volts. Mais de plus en plus, on tend à utiliser des valeurs de plus en plus basses, histoire d'économiser un peu d'énergie. Eh oui, car plus un circuit utilise une tension élevée, plus il consomme d'énergie et plus il chauffe. Pour un processeur, il est rare que les modèles récents utilisent une tension supérieure à 2 volts : la moyenne tournant autour de 1-1.5 volts. Même chose pour les mémoires : la tension d'alimentation de celle-ci diminue au cours du temps. Pour donner des exemples, une mémoire DDR a une tension d'alimentation qui tourne autour de 2,5 volts, les mémoires DDR2 ont une tension d'alimentation qui tombe à 1,8 volts, et les mémoires DDR3 ont une tension d'alimentation qui tombe à 1,5 volts. C'est très peu : les composants qui manipulent ces tensions doivent être très précis.
==Les différents codages : analogique, numérique et binaire==
[[File:Codage d'un chiffre décimal avec une tension.png|vignette|Codage numérique : exemple du codage d'un chiffre décimal avec une tension.]]
Le codage, la transformation d’information en nombre, peut être fait de plusieurs façons différentes. Dans les grandes lignes, on peut identifier deux grands types de codages.
Le '''codage analogique''' utilise des nombres réels : il code l’information avec des grandeurs physiques (quelque chose que l'on peut mesurer par un nombre) comprises dans un intervalle. Par exemple, un thermostat analogique convertit la température en tension électrique pour la manipuler : une température de 0 degré donne une tension de 0 volts, une température de 20 degrés donne une tension de 5 Volts, une température de 40 degrés donnera du 10 Volts, etc. Un codage analogique a une précision théoriquement infinie : on peut par exemple utiliser toutes les valeurs entre 0 et 5 Volts pour coder une information, même des valeurs tordues comme 1, 2.2345646, ou pire…
Le '''codage numérique''' n'utilise qu'un nombre fini de valeurs, contrairement au codage analogique. Pour être plus précis, il code des informations en utilisant des nombres entiers, représentés par des suites de chiffres. Le codage numérique précise comment coder les chiffres avec une tension. Comme illustré ci-contre, chaque chiffre correspond à un intervalle de tension : la tension code pour ce chiffre si elle est comprise dans cet intervalle. Cela donnera des valeurs de tension du style : 0, 0.12, 0.24, 0.36, 0.48… jusqu'à 2 volts.
===Les avantages et désavantages de l'analogique et du numérique===
Un calculateur analogique (qui utilise le codage analogique) peut en théorie faire ses calculs avec une précision théorique très fine, impossible à atteindre avec un calculateur numérique, notamment pour les opérations comme les dérivées, intégrations et autres calculs similaires. Mais dans les faits, aucune machine analogique n'est parfaite et la précision théorique est rarement atteinte, loin de là. Les imperfections des machines posent beaucoup plus de problèmes sur les machines analogiques que sur les machines numériques.
Obtenir des calculs précis sur un calculateur analogique demande non seulement d'utiliser des composants de très bonne qualité, à la conception quasi-parfaite, mais aussi d'utiliser des techniques de conception particulières. Même les composants de qualité ont des imperfections certes mineures, qui peuvent cependant sévèrement perturber les résultats. Les moyens pour réduire ce genre de problème sont très complexes, ce qui fait que la conception des calculateurs analogiques est diablement complexe, au point d'être une affaire de spécialistes. Concevoir ces machines est non seulement très difficile, mais tester leur bon fonctionnement ou corriger des pannes est encore plus complexe.
De plus, les calculateurs analogiques sont plus sensibles aux perturbations électromagnétiques. On dit aussi qu'ils ont une faible immunité au bruit. En effet, un signal analogique peut facilement subir des perturbations qui vont changer sa valeur, modifiant directement la valeur des nombres stockés ou manipulés. Avec un codage numérique, les perturbations ou parasites vont moins perturber le signal numérique. La raison est qu'une variation de tension qui reste dans un intervalle représentant un chiffre ne changera pas sa valeur. Il faut que la variation de tension fasse sortir la tension de l'intervalle pour changer le chiffre. Cette sensibilité aux perturbations est un désavantage net pour l'analogique et est une des raisons qui font que les calculateurs analogiques sont peu utilisés de nos jours. Elle rend difficile de faire fonctionner un calculateur analogique rapidement et limite donc sa puissance.
Un autre désavantage est que les calculateurs analogiques sont très spécialisés et qu'ils ne sont pas programmables. Un calculateur analogique est forcément conçu pour résoudre un problème bien précis. On peut le reconfigurer, le modifier à la marge, mais guère plus. Typiquement, les calculateurs analogiques sont utilisés pour résoudre des équations différentielles couplées non-linéaires, mais n'ont guère d'utilité pratique au-delà. Mais les ingénieurs ne font cela que pour les problèmes où il est pertinent de concevoir de zéro un calculateur spécialement dédié au problème à résoudre, ce qui est un cas assez rare.
===Le choix de la base===
Au vu des défauts des calculateurs analogiques, on devine que la grosse majorité des circuits électronique actuels sont numériques. Mais il faut savoir que les ordinateurs n'utilisent pas la numération décimale normale, celle à 10 chiffres qui vont de 0 à 9. De nos jours, les ordinateurs n'utilisent que deux chiffres, 0 et 1 (on parle de « bit ») : on dit qu'ils comptent en binaire. On verra dans le chapitre suivant comment coder des nombres avec des bits, ce qui est relativement simple. Pour le moment, nous allons justifier ce choix de n'utiliser que des bits et pas les chiffres décimaux (de 0 à 9). Avec une tension électrique, il y a diverses méthodes pour coder un bit : codage Manchester, NRZ, etc. Autant trancher dans le vif tout de suite : la quasi-intégralité des circuits d'un ordinateur se basent sur le '''codage NRZ'''.
Naïvement, la solution la plus simple serait de fixer un seuil en-dessous duquel la tension code un 0, et au-dessus duquel la tension représente un 1. Mais les circuits qui manipulent des tensions n'ont pas une précision parfaite et une petite perturbation électrique pourrait alors transformer un 0 en 1. Pour limiter la casse, on préfère ajouter une sorte de marge de sécurité, ce qui fait qu'on utilise en réalité deux seuils séparés par un intervalle vide. Le résultat est le fameux codage NRZ dont nous venons de parler : la tension doit être en dessous d'un seuil donné pour un 0, et il existe un autre seuil au-dessus duquel la tension représente un 1. Tout ce qu'il faut retenir, c'est qu'il y a un intervalle pour le 0 et un autre pour le 1. En dehors de ces intervalles, on considère que le circuit est trop imprécis pour pouvoir conclure sur la valeur de la tension : on ne sait pas trop si c'est un 1 ou un 0.
: Il arrive que ce soit l'inverse sur certains circuits électroniques : en dessous d'un certain seuil, c'est un 1 et si c'est au-dessus d'un autre seuil c'est 0.
[[File:Codage NRZ.png|centre|vignette|upright=2|Codage NRZ]]
L'avantage du binaire par rapport aux autres codages est qu'il permet de mieux résister aux perturbations électromagnétiques mentionnées dans le chapitre précédent. À tension d'alimentation égale, les intervalles de chaque chiffre sont plus petits pour un codage décimal : toute perturbation de la tension aura plus de chances de changer un chiffre. Mais avec des intervalles plus grands, un parasite aura nettement moins de chance de modifier la valeur du chiffre codé ainsi. La résistance aux perturbations électromagnétiques est donc meilleure avec seulement deux intervalles.
[[File:Comparaison entre codage binaire et décimal pour l'immunité au bruit.png|centre|vignette|upright=2|Comparaison entre codage binaire et décimal pour l'immunité au bruit.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Introduction
| prevText=Introduction
| next=Le codage des nombres
| nextText=Le codage des nombres
}}
</noinclude>
ff6r3nm8x4011ii1spmfaesyd1fr1m1
Fonctionnement d'un ordinateur/Le bus mémoire
0
65769
744791
720344
2025-06-15T02:05:04Z
Mewtow
31375
/* Le préchargement des mémoires Dual et Quad data rate */
744791
wikitext
text/x-wiki
Le bus mémoire des PC modernes est très important pour les performances. Les processeurs sont de plus en plus exigeants et la vitesse de la mémoire commence à être de plus en plus limitante pour leurs performances. La solution la plus évidente est d'augmenter la fréquence des mémoires et/ou de diminuer leur temps d'accès. Mais c'est que c'est plus facile à dire qu'à faire ! Les mémoires actuelles ne peut pas vraiment être rendu plus rapides, compte tenu des contraintes techniques actuelles. La solution actuellement retenue est d'augmenter le débit de la mémoire. Et pour cela, la performance du bus mémoire est primordiale.
Le débit binaire des mémoires actuelles dépend beaucoup de la performance du bus mémoire. La performance d'un bus dépend de son débit binaire, qui lui-même est le produit de sa fréquence et de sa largeur. Diverses technologies tentent d'augmenter le débit binaire du bus mémoire, que ce soit en augmentant sa largeur ou sa fréquence. La largeur du bus mémoire est quelque peu limitée par le fait qu'il faut câbler des fils sur la carte mère et ajouter des broches sur les barrettes de mémoire. Les deux possibilités sont déjà utilisées à fond, les bus actuels ayant plusieurs centaines de fils/broches.
==Les bus mémoire à multiples canaux==
Pour commencer, mettons de côté la fréquence, et intéressons-nous à la largeur du bus mémoire. Les PC actuels ont des bus d’une largeur de 64 bits minimum, avec cependant possibilité de passer à 128, 192, voire 256 bits ! C'est ce qui se cache derrière les technologies ''dual-channel'', ''triple-channel'' ou ''quad-channel''.
Le bus mémoire a une taille de 64 bits par barrette de mémoire, avec quelques contraintes de configuration. Le ''dual-channel'' permet de connecter deux barrettes de 64 bits, à un bus de 128 bits. Ainsi, on lit/écrit 64 bits de poids faible depuis la première barrette, puis les 64 bits de poids fort depuis la seconde barrette. Le ''triple-channel'' fait de même avec trois barrettes de mémoire, le ''quad-channel'' avec quatre barrettes de mémoire. Ces techniques augmentent la largeur du bus, donc influencent le débit binaire, mais n'ont pas d'effet sur le temps de latence de la mémoire. Et ce ne sont pas les seules techniques dans ce genre.
[[File:Dual channel slots.jpg|vignette|Dual channel slots]]
Pour en profiter, il faut placer les barrettes mémoire d'une certaine manière sur la carte mère. Typiquement, une carte mère ''dual channel'' a deux slots mémoires, voire quatre. Quand il y en a deux, tout va bien, il suffit de placer une barrette dans chaque slot. Mais dans le cas où la carte mère en a quatre, les slots sont d'une couleur différent pour indiquer comment les placer. Il faut placer les barrettes dans les slots de la même couleur pour profiter du ''dual channel''.
==Les mémoires ''Dual'' et ''Quad data rate''==
Accroître plus la largeur du bus a trop de désavantages : il faudrait câbler beaucoup trop de fils. Une autre solution est d'augmenter la fréquence du bus, mais cela demande alors d'augmenter la fréquence de la mémoire, qui ne suit pas. Mais il existe une solution alternative, qui est une sorte de mélange des deux techniques. Cette technique s'appelle le préchargement, ''prefetching'' en anglais. Elle donne naissance aux mémoires '''mémoires Dual Data Rate''', aussi appelées mémoires DDR. Il s'agit de mémoires SDRAM améliorées, avec une interface avec la mémoire légèrement bidouillée.
===Les mémoires sans préchargement===
Les mémoires sans préchargement sont appelées des '''mémoires SDR (''Single Data Rate'')'''. Avec elles, le plan mémoire et le bus vont à la même fréquence et ils ont la même largeur (le nombre de bits transmit en une fois). Par exemple, si le bus mémoire a une largeur de 64 bits et une fréquence de 100 MHz, alors le plan mémoire fait de même.
[[File:Préchargement mémoire sur des DDR.png|centre|vignette|upright=2|Mémoire SDR.]]
Toute augmentation de la fréquence et/ou de la largeur du bus se répercute sur le plan mémoire et réciproquement. Problème, le plan mémoire est difficile à faire fonctionner à haute fréquence, mais peut avoir une largeur assez importante sans problèmes. Pour le bus, c'est l'inverse : le faire fonctionner à haute fréquence est possible, bien que cela requière un travail d'ingénierie assez conséquent, alors qu'en augmenter la largeur poserait de sérieux problèmes.
===Les mémoires avec préchargement===
L'idée du préchargement est un compromis idéal entre les deux contraintes précédentes : on augmente la largeur du plan mémoire sans en augmenter la fréquence, mais on fait l'inverse pour le bus. En faisant cela, le plan mémoire a une fréquence inférieure à celle du bus, mais a une largeur plus importante qui compense exactement la différence de fréquence. Si le plan mémoire a une largeur de N fois celle du bus, le bus a une fréquence N plus élevée pour compenser.
Sur les '''mémoires DDR (''Double Data Rate'')''', le plan mémoire est deux fois plus large que le bus, mais a une fréquence deux fois plus faible. Les données lues ou écrites dans le plan mémoire sont envoyées en deux fois sur le bus, ce qui est compensé par le fait qu'il soit deux fois plus rapide. Ceci dit, il faut trouver un moyen pour découper un mot mémoire de 128 bits en deux blocs de 64, à envoyer sur le bus dans le bon ordre. Cela se fait dans l'interface avec le bus, grâce à une sorte de mémoire tampon un peu spéciale, dans laquelle on accumule les 128 bits lus ou à écrire.
[[File:Mémoire DDR.jpg|centre|vignette|upright=2|Mémoire DDR.]]
: Sur les mémoires DDR dans les ordinateurs personnels, seul un signal d'horloge est utilisé, que ce soit pour le bus, le plan mémoire, ou le contrôleur. Seulement, le bus et les contrôleurs mémoire réagissent à la fois sur les fronts montants et sur les fronts descendants de l'horloge. Le plan mémoire, lui, ne réagit qu'aux fronts montants.
Il existe aussi des '''mémoires quad data rate''', pour lesquelles la fréquence du bus est quatre fois celle du plan mémoire. Évidemment, la mémoire peut alors lire ou écrire 4 fois plus de données par cycle que ce que le bus peut supporter.
[[File:Mémoire QDR.png|centre|vignette|upright=2|Mémoire QDR.]]
Vous remarquerez que le préchargement se marie extrêmement bien avec le mode rafale.
Le préchargement augmente donc le débit théorique maximal. Sur les mémoires sans préchargement, le débit théorique maximal se calcule en multipliant la largeur du bus de données par sa fréquence. Par exemple, une mémoire SDRAM fonctionnant à 133 Mhz et qui utilise un bus de 8 octets, aura un débit de 8 * 133 * 1024 * 1024 octets par seconde, ce qui fait environ du 1 giga-octets par secondes. Pour les mémoires DDR, il faut multiplier la largeur du bus mémoire par la fréquence, et multiplier le tout par deux pour obtenir le débit maximal théorique. En reprenant notre exemple d'une mémoire DDR fonctionnant à 200 Mhz et utilisée en simple channel utilisera un bus de 8 octets, ce qui donnera un débit de 8 * 200 * 1024 * 1024 octets par seconde, ce qui fait environ du 2.1 gigaoctets par secondes.
==Les bus mémoire à base de liaisons point à point : les barrettes FB-DIMM==
Dans le cas le plus fréquent, toutes les barrettes d'un PC sont reliées au même bus mémoire, comme indiqué dans le schéma ci-dessous. Le bus mémoire est un bus parallèle, avec tous les défauts que ca implique quand on travaille à haute fréquence. Diverses contraintes électriques assez compliquées à expliquer font que les bus parallèles ont du mal à fonctionner à haute fréquence, la stabilité de transmission du signal est altérée.
[[File:Bus mémoire.PNG|centre|vignette|upright=2|Bus mémoire]]
[[File:FB-DIMM - principe.PNG|vignette|FB-DIMM - principe]]
Les barrettes mémoire '''FB-DIMM''' contournent le problème en utilisant plusieurs liaisons point à point. Il y a deux choses à comprendre. La première est que chaque barrette est connectée à la suivante par une liaison point à point, comme indiqué ci-dessous. Il n'y a pas de bus sur lequel on connecte toutes les barrettes, mais une série de plusieurs liaisons point à point. Les commandes/données passent d'une barrette à l'autre jusqu'à destination. Par exemple, une commande SDRAM part du contrôleur mémoire, passe d'une barrette à l'autre, avant d'arriver à la barrette de destination. Même chose pour les données lues depuis les DRAM, qui partent de la barrette, passent d'une barrette à la suivante, jusqu’à arriver au contrôleur mémoire.
Ensuite, les liaisons point à point sont au nombre de deux par barrette : une pour la lecture (''northbound channel''), l'autre pour l'écriture (''southbound channel''). Chaque barrette est reliée aux liaisons point à point par un circuit de contrôle qui fait l'interface. Le circuit de contrôle s'appelle l''''''Advanced Memory Buffer''''', il vérifie si chaque transmission est destinée à la barrette, et envoie la commande/donnée à la barrette suivante si ce n'est pas le cas.
[[File:FB-DIMM system organization.svg|centre|vignette|upright=2|Bus mémoire pour les barrettes FB-DIMM, schéma détaillé.]]
L'avantage de cette organisation est que l'on peut facilement brancher beaucoup de barrettes mémoire sur la carte mère. Avec un bus parallèle, il est difficile de mettre plus de 4 barrettes mémoire. Plus on insère de barrettes de mémoire, plus la stabilité du signal transmis avec un bus parallèle se dégrade. Cela ne pose pas de problème quand on rajoute des barrettes sur la carte mère, car elles sont conçues pour que le signal reste exploitable même si tous les slots mémoire sont remplis. Mais cela fait qu'on a rarement plus de 4 slots mémoire par carte mère. Avec des barrettes FB-DIMM, on peut monter facilement à 8 ou 16 barrettes.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=L'interface d'une mémoire électronique
| prevText=L'interface d'une mémoire électronique
| next=Les cellules mémoires
| nextText=Les cellules mémoires
}}
</noinclude>
sqiwju0os2wmf7ni6m1cwwiwjhz48ii
Jeu de rôle sur table — Jouer, créer/Probabilités des dés en jeu de rôle
0
67268
744754
724887
2025-06-14T21:24:45Z
Cdang
1202
/* Épreuves successives */ doublon
744754
wikitext
text/x-wiki
<noinclude>{{NavTitre|book={{BASEPAGENAME}}|prev=Le hasard dans les jeux de rôle|next=Créer des éléments du monde}}</noinclude>
[[Fichier:Dadosvariasfaces.jpg|vignette|Dés utilisés en jeux de rôle.]]
Nous avons vu précédemment quand et comment le hasard intervient dans une partie de jeu de rôle (lire ''[[../Le hasard dans les jeux de rôle|Le hasard dans les jeux de rôle]]'').
Le hasard étant dans l'idéal utilisé à des moments critiques de la partie, son usage doit être abondamment testé. Mais une bonne compréhension de la notion de probabilité et l'utilisation de quelques outils permet d'orienter les choix au moment de la conception. Je me permet de remettre deux citations :
{{citation bloc | les êtres humains dévient quasi-systématiquement de la rationalité lorsqu’ils font face à des dilemmes décisionnels impliquant des probabilités.
| 2 = Romain Ligneul
| 3 = The house always wins<ref>{{lien web
| libellé = Ligneul 2016 | id = Ligneul2016
| url = http://coginnov.org/the-house-always-wins-2/
| titre = The house always wins | langue = fr
| site = Cog Innov
| date = 2016-06-14 | consulté le = 2017-01-05
| auteur = Romain Ligneul
}}.</ref>
}}
{{citation bloc | 1 = Pour clore le sujet, n’oubliez pas que les dés sont vos instruments. Apprenez à les utiliser correctement et ils vous aideront énormément.
| 2 = Gary Gygax | 3 = Guide du maître<ref name="dmg1p6">{{ouvrage
| prénom1 = Gary | nom1 = Gygax
| titre = Guide du maître
| collection = Règles avancées de Donjons & Dragons 1{{re}} éd.
| éditeur = TSR
| année = 1987
| passage = 6
}}.</ref>.}}
== Bon, qu'est-ce qu'on veut ? ==
Vous être en train d'écrire des règles ou bien vous envisagez de modifier des règles existantes. Vous savez déjà :
* ce que vous voulez reproduire autour de la table : immersion, ambiance, suspense, surprise, rebondissement narratif…
* à quel moment intervient le choix ;
* quelle est la complexité de la procédure.
Vous vous posez la question : de quelle manière vais-je générer le hasard ? Jets de dés ? Sous une caractéristique, pour dépasser un seuil, ''roll & keep''..? Tirage de cartes ? De jetons ?
Vous voulez évaluer plusieurs solutions pour guider votre choix.
; Question préliminaire
:''La'' première question est :
:: Quelle doit être la fréquence de tel résultat (réussite, échec, avec ou sans complication, avec ou sans éclat) ?
:Le mieux est de l'exprimer sous la forme d'un adjectif : '''exceptionnel, rare, occasionnel, fréquent'''.
: Ensuite, il faut associer des résultats de tirage à chaque adjectif.
L'objectif de ce chapitre est de vous aider à mettre en correspondance un adjectif et des résultats de tirage, par l'intermédiaire de nombres appelés « probabilités ».
Pour cela, nous utiliserons du simple comptage, quelques représentations (tableau, graphiques) et l'utilisation que quelques logiciels : tableur (''Libreoffice Calc'' mais ça marche aussi avec ''Microsoft Office Excel'') et simulateur ''AnyDice''<ref>http://anydice.com/</ref> de Jasper Flick. On pourra aussi regarder le site ''Dice.run''<ref>https://dice.run/</ref> et ''Troll''<ref>http://www.diku.dk/~torbenm/Troll/</ref>.
== Quelques ''fumbles'' ==
Pour mettre en évidence l'importance du sujet, citons quelques erreurs de conception, quelques « maladresses » ''({{lang|en|fumbles}})'' :
* les livres-jeux de la série « Défis fantastiques » (Gallimard jeunesse) : lors d'un combat, une différence de deux points d'Habileté entre les combattants condamne le plus faible, or la création des personnages est en général aléatoire et le héros a autant de chances d'avoir 7 que 12 en Habileté ; il est donc impossible d'équilibrer les livres en conservant ses règles de création aléatoires<ref>{{lien web
| url = http://www.la-taverne-des-aventuriers.com/t3119-projet-dfix-discussion-pour-reequilibrer-les-dfs
| titre = Projet DFix : discussion pour rééquilibrer les DFs
| site = La Taverne des aventuriers
| consulté le = 2018-04-17
}}</ref> ;
* dans ''RuneQuest IV'' (Mongoose 2006) : lorsqu'un personnage ayant 100 % s'oppose à un personnage ayant 20 %, il gagne dans 93 % des cas ; lorsqu'un personnage ayant 101 % s'oppose à un personnage ayant 20 %, il gagne dans 62 % des cas<ref>{{lien web
| url = https://sites.google.com/site/simonhibbs/skills100
| titre = Skills over 100 % – A rules patch for Mongoose ''RuneQuest''
| site = simonhibbs sur site.google.com
| date = 2006 | consulté le = 2018-04-17
}}.</ref> ;
* à ''Donjons & Dragons 5'' (Wizards of the Coast 2014), la règle d'avantage/désavantage (lancer deux d20 et conserver soit le plus élevé, soit le plus faible) équivaut à un bonus/malus de ±5 alors que les autres cas prévoient un modificateur de ±3 au maximum<ref>{{article
| prénom1 = David | nom1 = Burckle
| périodique = Casus Belli
| numméro = 11 | volume = 4
| mois = septembre | année = 2014
| titre = Player’s Handbook : la fureur du dragon
| passage = 38–41.
}}</ref> ;
* à ''Wuxia'' (Studio Mammouth 2005), la probabilité d'avoir une maladresse (échec critique) augmente avec la valeur de l'attribut : plus un personnage a de chances de réussir une action, plus il a de risque de faire une maladresse.
== Quelques notions de base ==
=== Qu'est-ce qu'une probabilité ? ===
Un événement aléatoire est un événement dont on ne peut pas prédire exactement le résultat. L'exemple le plus simple est le pile ou face : je sais que le résultat ne peut être que pile ou que face, mais si la pièce est équilibrée, je ne peux pas prédire avec certitude le résultat d'un tirage.
Si j'effectue une tirage, j'ai un résultat (pile ou face). Si j'effectue deux tirages, j'ai deux résultats qui peuvent être identiques (pile-pile ou bien face-face) ou qui peuvent être différents (pile puis face, ou bien face puis pile).
Si j'effectue dix tirages, je m'attend à avoir un assortiment de pile et de face mais pas dix pile ni dix face — une telle situation est possible mais est peu probable.
Si j'effectue cent tirages, alors j'aurai à peu près autant de pile que de face. Pas ''exactement'' autant mais ''à peu près''.
{{définition |définition=La '''probabilité''' d'un résultat est la proportion idéale d'un résultat lorsque l'on fait un grand nombre de tirages.}}
C'est une proportion donc en mathématique, elle peut s'exprimer sous la forme d'une fraction. Pour pile ou face, on a « une chance sur deux » d'avoir pile et « une chance sur deux » d'avoir face. La probabilité d'avoir pile est donc ''p'' = ½, que l'on peut aussi écrire ''p'' = 0,5 (puisque 1 ÷ 2 = 0,5). De même pour face. Comme les probabilités de pile et de face sont identiques, on dit que les événements sont équiprobables.
La probabilité s'exprime souvent pour cent tirages : « pour cent », « pourcent », %. Le pourcentage consiste simplement à multiplier la probabilité par 100 :
: ''p'' = ½ = 0,5 = 50 %.
Donc si je fais un seul tirage, je ne sais pas ce que je vais obtenir, je sais juste que j'ai autant de chances d'obtenir un pile qu'un face. Mais si je fais 100 ou {{formatnum:1000}} tirages, je sais que je vais avoir à peu près respectivement 50 et 500 pile. C'est la fameuse loi des grands nombres : les proportions de résultats observés s'approchent des proportions idéales (des probabilités) lorsque l'on a un grand nombre d'événements (de tirages).
Dans le contexte présent, le terme « fréquence » est un synonyme de « probabilité ».
=== Probabilités et statistiques ===
En mathématiques, les probabilités sont la partie théorique de l'étude du hasard ; les statistiques sont l'étude pratique des événements. Par exemple, je veux savoir si ma pièce est équilibrée ou faussée :
* si je fais un seul tirage, je ne peux rien dire ;
* si je fais deux tirages, pas plus : il est aussi fréquent d'avoir deux fois le même résultat que d'avoir deux résultats différents ;
* si je fais dix tirages et que j'ai 7 pile et 3 face, je ne peux toujours rien dire ; en revanche, si j'ai 10 pile, je vais commencer à douter ;
* combien faut-il de tirages pour pouvoir déterminer si ma pièce est équilibrée ou faussée ? C'est à ce type de questions que répondent les statistiques.
Les statistiques sont également utilisée pour les sondages d'opinion et pour déterminer l'erreur faite sur une mesure en général.
Donc, cela ne nous concerne pas. Nous considérerons toujours que nos dés sont idéaux et pas pipés. Mais c'était l'occasion de faire un point de vocabulaire, ce qui est toujours utile si vous désirez faire des recherche sur Internet.
=== Dénombrement ===
Pour déterminer les proportions idéales, les probabilités, il suffit, dans notre cas, de compter les situations possibles. Si les pièces et les dés sont équilibrés, si les jetons et les cartes sont indiscernables et bien mélangés, chaque situation a les mêmes chances de survenir que les autres.
Donc, pour nous, les probabilités se résument à du comptage, à du dénombrement.
==== Moyenne, écart type, médiane et quartiles ====
Dans le cas de valeurs aléatoires, on indique fréquemment la moyenne et l'écart type. Tenez-vous le pour dit : ce n'est pas pertinent ici.
En effet, la moyenne est très populaire en France car on s'en sert pour les bulletins de notes à l'école mais c'est un médiocre outil en statistiques. Concrètement, elle correspond à deux types de situation :
* « situation d'équilibre » (barycentre) : ce type de situation ne nous concerne pas ;
* « lissage » : on recherche un phénomène constant ayant le même effet que notre phénomène variable, par exemple pour mon éclairage de vélo, je cherche le courant continu (la pile) qui, en alimentant l'ampoule,donne le même éclairage que le courant alternatif (la dynamo) ; cela nous intéresse lorsque l'on considère une succession de tests (donc une succession de résultats variables), c'est le seul cas pour nous où la notion de moyenne est pertinente.
L'écart type indique si les valeurs sont très dispersées ou bien si au contraire elles sont « ramassées » autour d'une valeur « centrale ». Cette indication est utile mais le calcul de l'écart type est relativement compliqué pour ce que l'on a à faire (et il n'est vraiment pertinent que pour un type de loi dite « loi normale » ou « gaussienne »).
Nous nous référerons quant à nous à la notion de quartile. Il s'agit simplement de couper la liste des événements possibles en quatre part égales. La frontière centrale est appelée la médiane et sa valeur est souvent égale à la moyenne. Il existe plusieurs méthodes pour déterminer les quartiles mais cela importe peu, les différences ne sont pas significatives pour ce que nous avons à faire.
{{boîte déroulante/début|titre=Quartiles}}
Considérons le jet de 2d6. Les différents jets possibles sont :
{|class=wikitable
! scope="col" | Dé 1
! scope="col" | Dé 2
! scope="col" | Somme
|-
| rowspan="6"| 1
|1 || 2
|-
| 2 || 3
|-
| 3 || 4
|-
| 6 || 5
|-
| 5 || 6
|-
| 6 || 7
|-
| 2 || 1 || 3
|-
| … || … || …
|-
| rowspan="2" | 6 || 5 || 11
|-
| 6 || 12
|}
La liste des résultats possibles est donc :
: 2 - 3 - 4 - 5 - 6 - 7 - 3 - 4 - 5 - 6 - 7 - 8 - 4 - 5 - 6 - 7 - 8 - 9 - 5 - 6 - 7 - 8 - 9 - 10 - 6 - 7 - 8 - 9 - 10 - 11 - 7 - 8 - 9 - 10 - 11 - 12.
Si nous classons la liste par ordre croissant, nous avons :
: 2 - 3 - 3 - 4 - 4 - 4 - 5 - 5 - 5 - 5 - 6 - 6 - 6 - 6 - 6 - 7 - 7 - 7 - 7 - 7 - 7 - 8 - 8 - 8 - 8 - 8 - 9 - 9 - 9 - 9 - 10 - 10 - 10 - 11 - 11 - 12.
Nous avons 36 résultats que nous découpons en 4 groupes de 9, les séparations sont marquées par une barre verticale épaisse « ❙ » :
: 2 - 3 - 3 - 4 - 4 - 4 - 5 - 5 - 5 ❙ 5 - 6 - 6 - 6 - 6 - 6 - 7 - 7 - 7 ❙ 7 - 7 - 7 - 8 - 8 - 8 - 8 - 8 - 9 ❙ 9 - 9 - 9 - 10 - 10 - 10 - 11 - 11 - 12.
Les quartiles sont donc :
* extrémité basse : 2 ;
* premier '''quartile''' : 5 ;
* deuxième quartile, '''médiane''' : 7 ;
* troisième '''quartile''' : 9 ;
* extrémité haute : 12.
{{boîte déroulante/fin}}
Mais n'oublions pas un indicateur simple et utile : la plage de valeurs, c'est-à-dire la valeur la plus basse et la valeur la plus haute. C'était d'ailleurs de cette manière qu'étaient notés les dés dans ''AD&D 1'' : « 1–8 » indiquait 1d8 et « 2–8 » indiquait 2d4 (mais cette notation est trompeuse car on pourrait croire que la seule différence est la suppression de la valeur 1).
== Considérations de conception ''({{lang|en|game design}})'' et de mise en œuvre ''({{lang|en|game play}})'' ==
Maintenant que nous avons effleuré la notion de probabilité, vous, meneur de jeu et créateur, devez considérer la place du hasard dans votre manière de jouer — ce qui était donc l'objet du chapitre précédent ''[[../Le hasard dans les jeux de rôle|Le hasard dans les jeux de rôle]]'' — et bien considérer les éléments suivants : à partir du moment où l'on décide de s'en remettre au dés, alors
* les règles façonnent le monde fictionnel, la diégèse : la probabilité des jets définit ce qui est rare ou fréquent dans votre monde<ref>{{chapitre
| prénom1 = Olivier | nom1 = Caïra
| titre ouvrage = Jeux de rôle. Les forges de la fiction
| titre chapitre = Les règles comme matrice du monde
| passage = 218-220
| éditeur = CNRS | isbn = 978-2-271-06497-4
| année = 2007
}}.</ref> ;
* les règles façonnent la fiction : la probabilité des jets définit si la fiction s'oriente plus souvent dans un sens ou dans un autre ;
* les règles sont l'essence du jeu (c'est le sens du mot ''{{lang|en|game}}'' dans ''{{lang|en|roleplaying game}}'') : la probabilité des jets influe sur le caractère ludique.
Donc à la question préalablement posée :
: Qu'est-ce qui est exceptionnel, rare, occasionnel, fréquent ?
répond la question :
: Quelle probabilité associer aux adjectifs « exceptionnel », « rare », « occasionnel » et « fréquent » ?
Cela fait écho à une autre question :
: Quelle est la probabilité pour qu'un personnage « moyen » réussisse une action de difficulté « moyenne » ?
:(Sur ce point, voir ''[[../Le hasard dans les jeux de rôle#Estimer ses chances|Le hasard dans les jeux de rôle → Estimer ses chances]]''.)
Nous proposons de prendre quelques exemple réel pour appréhender le sujet :
{| class="wikitable"
|+ Probabilités dans le monde réel
|-
! scope="col" rowspan="2" | Situation
! scope="col" colspan="2" | Probabilité
|-
! scope="col" | de réussite
! scope="col" | d'échec
|-
! scope="row" | Survivre à une <br />roulette russe
| 83 %<br /> (5/6) || 17 %<br /> (1/6)
|-
! scope="row" | Survivre à une roulette <br />russe avec deux balles
| 67 %<br /> (4/6) || 33 %<br /> (2/6)
|-
! scope="row" | Avoir un jour de pluie<br /> à Brest
| 58 %<br /> (210/365) || 43 %<br /> (155/365)
|-
! scope="row" | Pile ou face
| 50 %<br /> (1/2) || 50 %<br /> (1/2)
|-
! scope="row" | Avoir un beau soleil<br /> en été en France métropolitaine<ref>Réussite : pas de couverture nuageuse. Échec : couverture nuageuse avec ou sans pluie. Bien évidemment, il s'agit d'une moyenne, la couverture nuageuse n'étant pas uniforme. Pour plus de précisions, on pourra voir la carte ''Pourcentage du nombre de jours de ciel clair en été en France (nébulosité inférieure ou égale à 2/10)'' sur la page {{lien web
| url = https://www.meteo-paris.com/actualites/vacances-d-ete-en-france-nos-regions-ont-du-talent-23-avril-2020.html
| titre = Vacances d'été en France : nos régions ont du talent !
| site = Météo Paris
| date = 2020-04-23 | consulté le = 2022-06-14
}}.</ref>
| 28 %<br /> (29/92) || 72 %<br /> (63/92)
|-
! scope="row" | Avoir un jour de pluie<br /> au Cap Corse
| 18 %<br /> (67/365) || 82 %<br /> (75/90)
|-
! scope="row" | Avoir un beau soleil<br /> en hiver en France métropolitaine
| 17 %<br /> (15/90) || 83 %<br /> (75/90)
|-
! scope="row" | Traverser un champ d'astéroïdes<br /> avec un cargo corellien YT-1300
| {{formatnum:0.03}} %<br /> (1/{{formatnum:3720}}) || {{formatnum:99.97}} %<br />
|-
! scope="row" | Six bons numéros<br /> au Loto (rang 1)
| {{formatnum:0.000005}} %<br /> (1/{{formatnum:19000000}}) || {{formatnum:99.999995}} %<br />
|-
! scope="row" | Mourir d'un accident <br /> de voiture lors d'un <br /> trajet de 100 km dans l'UE
| {{formatnum:0.0000008}} %<br /> (0,8/{{formatnum:1000000}}) || {{formatnum:99.9999992}} %<br />
|}
Notez que les termes « réussite » et « échec » ne désignent pas un événement positif ou négatif mais les probabilités de survenue ou non de l'événement.
La fréquence de survenue des événements dépend aussi de la fréquence des jets. Par exemple, si une partie comprend quatre combats, que chaque combat nécessite environ 10 jets par personne et qu'il y a quatre PJ, il y aura donc 160 jets de combat par partie. S'il y a 1 % de risque de faire une maladresse ''({{lang|en|fumble}})'', cela signifie qu'il y aura environ 1 à 2 maladresse en combat par partie. Si l'unique magicien jette 10 sorts par partie, il y aura une maladresse magique toutes les 10 parties environ.
Si maintenant la maladresse survient avec un 1 sur 1d20 (5 %), cela signifie environ 8 maladresses en combat par partie et une maladresse magique toutes les 2 parties.
Vous pouvez formaliser vos réflexions en remplissant le tableau suivant : « si j'estime qu'un événement rare intervient une fois toutes les trois parties (''m'' = 1/3 = 0,33) et que l'on fait dix jets de dés par parties (''n'' = 10), alors la probabilité de l'événement est ''m''/''n'' = 0,33/10 = 0,033 = 3,3 % ». « Si j'estime qu'un événement occasionnel arrive une fois par parties (''m'' = 1) et que l'on fait 5 jets par partie (''n'' = 5), alors la probabilité de l'événement est ''m''/''n'' = 1/5 = 0,2 = 20 % ».
{| class="wikitable"
|+ Probabilité associée aux adjectifs
|-
! scope="col" rowspan="2" | Qualificatif
! scope="col" rowspan="2" | Nombre de fois<br />par partie ''(m)''
! scope="col" rowspan="2" | Nombre de jets<br />par partie ''(n)''
! scope="col" colspan="2"| Probabilité
|-
! scope="col" | Fraction (''m''/''n'')
! scope="col" | Pourcentage (%)
|-
! scope="row" | exceptionnel
| || || ||
|-
! scope="row" | rare
| || || ||
|-
! scope="row" | occasionnel
| || || ||
|-
! scope="row" | fréquent
| || || ||
|-
| colspan="5" align="center"| <small>Rappel : P (%) = 100 × ''m''/''n''</small>
|}
Le « nombre de jets ''n'' » dépend du type de test : jet d'attaque, jet de perception, jet de diplomatie… Il faut ensuite regarder comment on obtient les pourcentage déterminés avec la procédure de résolution choisie (type de dés, combien j'en jette, comment je lis les résultats).
Ce qui suit va vous permettre ensuite de voir si les méthodes de génération de hasard choisies correspondent aux valeurs de ce tableau.
== Courbe plate ou courbe en cloche ? ==
[[fichier:Probabilite 1d20 3d6 avec centiles 30 50 70.svg|vignette|upright=2|Génération de valeurs entre 1 et 20 avec 1d20, 2d10 et 3d6 ; courbes des fréquences des valeurs (fonctions de masse, en haut) et courbes des fréquences cumulées (fonction de répartition, en bas).]]
Si l'on ne jette qu'un seul dé, chaque valeur a autant de chances que les autres d'apparaître. Si l'on trace la fréquence de sorties de chaque valeur (la « fonction de masse »), on a une courbe plate. C'est le cas du dé à vingt faces (d20), courbe bleue dans la figure du haut ci-contre, chaque valeur a 100/20 = 5 % de chances de sortir.
Si l'on jette 3d6 (pour obtenir des valeurs entre 3 et 18), courbe rouge dans la figure du haut, on voit que les valeurs centrales 10 et 11 ont plus de chances de sortir que les valeurs extrêmes 3 et 18. Les valeurs 3 et 18 ne sont obtenues qu'avec une seule combinaison de dés, respectivement (1, 1, 1) et (6, 6, 6), sur 6<sup>3</sup> = 216 combinaisons soit une fréquence de 100 × 1/216 = 0,5 % ; la valeur 10 peut s'obtenir par 27 combinaisons : (1, 3, 6), (1, 6, 3), (2, 2, 6), (2, 3, 5), … soit 100 × 27 / 216 = 12,5 %. La fréquence de sortie des valeurs a une forme « en cloche ». Nous avons déjà évoqué ceci dans le chapitre précédent ''[[../Le_hasard_dans_les_jeux_de_rôle#Linéarité_des_chances|Le hasard dans les jeux de rôle > Linéarité des chances]]''.
Cette considération a une certaine importance dans l'histoire du jeu de rôle. En effet, jusque dans les années 1960, on ne connaissait que les dés à six faces. Un dé ne permet de générer que six valeurs. Ainsi, une table d'événements aléatoires ne peut contenir que six entrées et une échelle de chances de réussite ne peut avoir que sept échelons espacés d'environ 17 % (100/6) :
* aucune chance de réussite, 0 % ;
* faire 1 avec le dé : 100/6 ≃ 17 % de chances ;
* faire 1 ou 2 avec le dé : 100 × 2 / 6 ≃ 33 % de chances ;
* faire 1 à 3 : 50 % ;
* faire 1 à 4 : environ 67 % ;
* faire 1 à 5 : env. 83 % ;
* faire 1 à 6 : 100 %.
Les joueurs de jeux de guerre ''({{lang|en|wargames}})'' voulaient pouvoir générer plus d'événements et avoir plus de niveaux de puissance pour comparer les unités de leurs armées. En ajoutant plusieurs dés, on obtient surtout des valeurs moyennes et le résultat est donc plus prédictible ; les résultats extrêmes sont peu probables, la procédure génère donc peu de surprise, de chaos. C'est ainsi que les joueurs commencèrent à introduire les dés à 20 faces, et en particulier un des co-créateurs du premier jeu de rôle, ''Donjons & Dragons'', Gary Gygax<ref>{{ouvrage
| prénom1 = Michael | nom1 = Witwer
| titre = L'Empire de l'imaginaire | sous-titre = Gary Gygax et la naissance de ''Dungeons & Dragons''
| éditeur = Sycko | année = 2018 | isbn = 979-10-94206-18-8
| passage = 93-94
}}.</ref>, en 1974. Gary Gygax fait d'ailleurs figurer cette histoire de formes de courbes dans le ''Guide du maître de Donjons & Dragons'' en 1979<ref name="dmg1p6" />.
Cependant, si l'on regarde bien la manière dont les dés sont utilisés réellement en jeux de rôle, la différence entre les deux manières de faire, 1d20 ou 3d6, ne réside pas à proprement parler dans le fait que les valeurs extrêmes sortent avec autant de chances ou bien plus rarement que les valeurs médianes. En effet, la plupart du temps, on considère un jet sous une valeur (faire une valeur ou moins) ou bien un jet au dessus d'un seuil (faire une valeur ou plus). Il ne faut donc pas considérer les fréquences d'apparition des valeurs, les courbes du haut, mais les fréquences cumulées, les courbes du bas dans la figure ci-dessus. La courbe pour 1d20 est une droite et la courbe pour 3d6 forme un « S » (courbe sigmoïde) mais la différence entre les deux courbes n'est pas si grande que cela. Les courbes se rejoignent au début (0 % de réussite) et à la fin (100 % de réussite) ainsi qu'au milieu (50 % de réussite).
La différence entre les deux situations tient plus dans le fait que :
* avec 1d20, les probabilités montent régulièrement, chaque fois que l'on monte de 1, on gagne 5 % ;
* avec 3d6, lorsque l'on passe de 1 à 2 on gagne 1,4 %, pour passer de 10 à 11 on gagne 12,5 %.
Avec une courbe en S (3d6), il est donc plus difficile de prédire ses chances de réussite, de savoir s'il est intéressant de gagner un bonus de + 1 ou si un malus de –1 est vraiment pénalisant. En revanche, avec une courbe en S, il est facile de réserver des résultats rares ; par exemple si l'on dit que « 3 est une réussite critique », alors cette réussite critique n'arrivera qu'une fois tous les 216 jets en moyenne. Avec une courbe droite, 1d20, les valeurs extrêmes ont 5 % de chances de survenir, si l'on dit que « 1 est une réussite critique » alors la réussite critique arrivera assez fréquemment, une fois tous les 20 jets en moyenne ; il faut donc prévoir une règle complémentaire si l'on veut que l'événement soit plus rare, par exemple « obtenir un avec 1d20 et réussir un jet de confirmation » (donc faire deux jets).
Pour le reste, si l'on veut qu'un événement ait une probabilité de 30 % environ, on considérera un résultat de 1 à 6 sur 1d20 (que l'on note couramment « 1-6 » ou bien « 6– » pour « 6 ou moins ») et un résultat de 3 à 8 (3-8, 8–) avec 3d6. Si l'on veut une probabilité de 50 %, on aura dans les deux cas 10– (1-10 sur 1d20 et 3-10 sur 3d6) et pour une probabilité de 70 %, on prendra 1-14 (14–) sur 1d20 et 3-12 (12–) sur 3d6. Ces trois situations (les 30<sup>e</sup>, 50<sup>e</sup> et 70<sup>e</sup> centiles) sont représentés par les lignes pontillées sur la figure ci-dessus.
[[Fichier:Fonction repartition 3d6 approximation Z.svg|vignette|upright=2|Approximation de la fonction de répartition de 3d6 par une courbe trilinéaire (en Z).]]
À bien y regarder, la courbe de répartition en S pour 3d6 peut être décomposée en trois parties :
* entre les valeurs 3 et 6 (environ 10 % des situations), une partie droite ;
* entre les valeurs 3 et 14 (environ 80 % des situations), une autre partie droite ;
* entre les valeurs 14 et 18 (environ 10 % des situations), une dernière partie droite ;
la courbe en S est donc très proche d'une courbe en Z. Nous voyons que la partie centrale, qui correspond à 8 jets sur 10, est très proche de la courbe de 1d10 + 5. Donc dans 8 jets sur 10 (plage de valeurs 6-14), on pourrait remplacer le jet de 3d6 par un jet de 1d10 + 5, donc une probabilité linéaire. La courbe en cloche est donc la plupart du temps identique à une courbe plate, avec une progression d'environ 10 % de chances pour chaque incrément de 1. Dans les parties extrêmes, moins de 6 et plus de 14 (plages 3-6 et 14-18), la progression est d'environ 2,5 % pour chaque +1.
Cette vision simplifiée permet de mieux appréhender ses chances de réussite. Cependant, il faudrait faire cette analyse pour chaque situation où l'on ajoute plusieurs dés : 2d6, 2d10…
Donc outre ces deux différences mentionnées ci-dessus, à savoir :
* la difficulté de prévoir comment évoluent les chances de réussite avec 3d6 et
* la nécessité d'avoir une règle supplémentaire pour que les résultats critiques soient rares avec 1d20,
les deux autres différences importantes sont :
* on trouve plus facilement des d6 que des d20 ;
* 3d6 impose de devoir faire des additions.
Les différences sont donc notables mais ne résident pas là où se l'imaginent la plupart des joueurs et joueuses.
== Les jets de dés ==
{{note|1=La probabilité d'obtenir une valeur donnée avec un jet (par exemple obtenir un 2 avec 2d4) est intéressant lorsque l'on veut construire une table (table aléatoire ou table de résolution) ou bien pour les règles d'échec critique (maladresse) et de réussite critique (action d'éclat, coup de maître) : ces événements surviennent souvent lorsque le jet donne la valeur la plus petite ou la valeur la plus grande.
Mais la plupart du temps, on s'intéresse aux probabilité de faire moins qu'une valeur, ou bien de faire plus qu'une valeur.}}
=== Jet d'un dé unique ===
==== Histogramme vertical ====
[[Fichier:Probas 1d4.svg |vignette |upright=2 |Empilement de dés (gauche) et histogramme (droite).]]
Chaque événement est l'obtention d'une face. On peut donc représenter la valeur « une occurrence » par la face du dé.
Lorsque l'on s'intéresse à l'obtention d'une valeur, il suffit de représenter le dé au dessus du nombre correspondant.
Lorsque l'on veut obtenir « une valeur ou moins », on empile les dés correspondant à ce critère. Par exemple, pour le critère « 3 ou moins », on empile les dés montrant les faces « 1 », « 2 » et « 3 ».
On peut directement transformer ce dessin en diagramme barres, encore appelé « histogramme ».
Nous constatons que les tests « faire moins que » (règle du type « jet sous une caractéristique », par exemple ''Basic Role-Playing'', ''RuneQuest'', ''L'Appel de Cthulhu''…) et les tests « faire plus que » (règle du type « dépasser une seuil », par exemple ''d20 System'', ''D&D 3.X'', ''Pathfinder''…) sont symétriques : on obtient l'une en retournant l'autre. Pour simplifier, nous n'étudierons par la suite que la première situation, « faire moins que ».
Donc, l'histogramme du haut (« 1d4 = ''i'' ») se lit de la manière suivante (lecture des barres de gauche à droite) : « j'ai 25 % d'obtenir un “1” avec 1d4 », « j'ai 25 % d'obtenir un “2” avec 1d4 », « j'ai 25 % d'obtenir un “3” avec 1d4 » et « j'ai 25 % d'obtenir un “4” avec 1d4 ». La probabilité est uniforme, « plate ».
Le second histogramme (« 1d4 ≤ ''i'' ») se lit (de gauche à droite) : « j'ai 25 % d'obtenir un “1” ou moins avec 1d4 », « j'ai 50 % d'obtenir un “2” ou moins avec 1d4 », « j'ai 75 % d'obtenir un “3” ou moins avec 1d4 », « j'ai 100 % d'obtenir un “4” ou moins avec 1d4 ».
Une inégalité stricte — « 1d4 < ''i'' » — équivaut à tout décaler d'une barre : « obtenir strictement moins que “2” avec 1d4 » équivaut à « obtenir un “1” ou moins avec 1d4 ».
{{clr}}
==== Valeurs caractéristiques de la distribution ====
[[Fichier:Boite a moustaches d4.svg |vignette |upright=2 |Boite à moustaches pour 1d4.]]
Les extrema sont évidemment « 1 » et « 4 ».
Si on coupe la liste des événements possibles en deux parts égales :
: « 1 », « 2 » ❙ « 3 », « 4 »
on voit que la médiane, représentée par le trait vertical « ❙ », est située entre « 2 » et « 3 ». On prend par convention la valeur « 2,5 ».
Si l'on sépare la liste en quatre parts égales, on a :
: « 1 » ❙ « 2 » ❙ « 3 » ❙ « 4 »
Les quartiles, représentés par « ❙ », valent donc « 1,5 », « 2,5 » (médiane) et « 3,5 ».
On peut représenter ceci par une « boîte à moustaches ».
==== Tableau ====
La colonne « Événements » désigne la face sur laquelle tombe le dé, la colonne « Résultat » indique ce qui est attendu. Dans le cas d'un dé unique, le résultat '''2''' s'obtient avec l'événement « 2 » (la face « 2 » du dé) ; mais si l'on veut avoir pour résultat '''2 ou moins''', cela s'obtient pour deux événements : « 1 » ou « 2 ».
{| class="wikitable"
|+ Probabilités d'avoir un résultat pour 1d4
|-
! scope="col" rowspan="2" | Résultat
! scope="col" rowspan="2" | Événements
! scope="col" rowspan="2" | Nombre<br /> d'occurrences
! scope="col" colspan="3"| Probabilité
|-
! scope="col" | fraction
! scope="col" | pour 1
! scope="col" | pour 100
|-
! scope="row" | 1
| {{surligné|#00FFFF|« 1 »}} || 1 || 1/4 || 0,25 || 25 %
|-
! scope="row" | 2
| {{surligné|#00FFFF|« 2 »}} || 1 || 1/4 || 0,25 || 25 %
|-
! scope="row" | 3
| {{surligné|#00FFFF|« 3 »}} || 1 || 1/4 || 0,25 || 25 %
|-
! scope="row" | 4
| {{surligné|#00FFFF|« 4 »}} || 1 || 1/4 || 0,25 || 25 %
|}
{| class="wikitable"
|+ Probabilités de faire une valeur ou moins pour 1d4
|-
! scope="col" rowspan="2" | Résultat
! scope="col" rowspan="2" | Événements
! scope="col" rowspan="2" | Nombre<br /> d'occurrences
! scope="col" colspan="3"| Probabilité
|-
! scope="col" | fraction
! scope="col" | pour 1
! scope="col" | pour 100
|-
! scope="row" | 1
| {{surligné|#00FFFF|« 1 »}} || 1 || 1/4 || 0,25 || 25 %
|-
! scope="row" | 2
| {{surligné|#00FFFF|« 1 » ou « 2 »}} || 2 || 2/4 || 0,5 || 50 %
|-
! scope="row" | 3
| {{surligné|#00FFFF|« 1 » ou « 2 » ou « 3 »}} || 3 || 3/4 || 0,75 || 75 %
|-
! scope="row" | 4
| {{surligné|#00FFFF|« 1 » ou « 2 » ou « 3 » ou « 4 »}} || 4 || 4/4 || 1 || 100 %
|}
{| class="wikitable"
|+ Probabilités de faire une valeur ou plus pour 1d4
|-
! scope="col" rowspan="2" | Résultat
! scope="col" rowspan="2" | Événements
! scope="col" rowspan="2" | Nombre<br /> d'occurrences
! scope="col" colspan="3"| Probabilité
|-
! scope="col" | fraction
! scope="col" | pour 1
! scope="col" | pour 100
|-
! scope="row" | 1
| {{surligné|#00FFFF|« 1 » ou « 2 » ou « 3 » ou « 4 »}} || 4 || 4/4 || 1 || 100 %
|-
! scope="row" | 2
| {{surligné|#00FFFF|« 2 » ou « 3 » ou « 4 »}} || 3 || 3/4 || 0,75 || 75 %
|-
! scope="row" | 3
| {{surligné|#00FFFF|« 3 » ou « 4 »}} || 2 || 2/4 || 0,5 || 50 %
|-
! scope="row" | 4
| {{surligné|#00FFFF|« 4 »}} || 1 || 1/4 || 0,25 || 25 %
|}
==== Histogramme horizontal ====
La colonne « Événements » prend une forme qui peut directement se transposer histogramme — en graphique à barres :
; Avoir un résultat donné
: '''1''' {{Avancement|25|barre 7|taille=50}}
: '''2''' {{Avancement|25|barre 7|taille=50}}
: '''3''' {{Avancement|25|barre 7|taille=50}}
: '''4''' {{Avancement|25|barre 7|taille=50}}
; Avoir une valeur ou moins
: '''1''' {{Avancement|25|barre 7|taille=50}}
: '''2''' {{Avancement|50|barre 7|taille=50}}
: '''3''' {{Avancement|75|barre 7|taille=50}}
: '''4''' {{Avancement|100|barre 7|taille=50}}
; Avoir une valeur ou plus
: '''1''' {{Avancement|100|barre 7|taille=50}}
: '''2''' {{Avancement|75|barre 7|taille=50}}
: '''3''' {{Avancement|50|barre 7|taille=50}}
: '''4''' {{Avancement|25|barre 7|taille=50}}
==== Arbre ====
[[Fichier:Arbre de proba 1d4.svg |vignette |upright=2 |Arbre de probabilité d'un dé à quatre faces.]]
La dernière manière de représenter un jet consiste à faire un graphe de type « arbre ». Chaque événement possible (chaque face) est une branche partant de la « racine » (le point situé en haut du graphe).
La ligne « résultat : “1d4 = ''i''” » se lit de la manière suivante : « si l'événement est “1”, alors le résultat est “1” » — c'est-à-dire « on lit la face du dé ».
La ligne « résultat : “1d4 ≤ ''i''” » se lit de la manière suivante : « si l'événement est “1”, alors le résultat est positif pour les limites “1”, “2”, “3” et “4” » — c'est-à-dire « si on obtient un “1” avec le dé, on réussit le jet pour les caractéristiques valant “1”, “2”, “3” et “4” ».
Ce type de représentation prend son intérêt lorsque l'on fait plusieurs tests l'un après l'autre.
{{clr}}
=== Somme de deux dés identiques ===
Nous considérons maintenant 2d4.
[[Fichier:Probas 2d4.svg |vignette |upright=2 |Empilement des dés (gauche) et histogrammes (droite) pour 2d4.]]
==== Histogrammes ====
La probabilité d'obtenir une valeur ont une forme triangulaire. Il est donc plus probable d'avoir une valeur moyenne — « 5 » — que d'avoir des valeurs extrêmes — « 2 », « 8 ».
La probabilité de faire une valeur ou moins a une forme de S, appelée « sigmoïde ». Ainsi, lorsque l'on passe de « 2 » à « 3 », la probabilité change peu (+6 % environ) ; en revanche, lorsque l'on passe de « 4 » à « 5 », la probabilité varie de manière importante (+ 25 %).
{{clr}}
[[Fichier:Arbre de proba 2d4.svg |vignette |upright=2 |Arbre de probabilité de 2d4.]]
==== Arbre de probabilités ====
Notre arbre a maintenant deux « étages » : le premier étage est le résultat du premier dé, le second étage est le résultat du second dé. Nous avons 4 × 4 = 16 événements possible, un événement étant la somme de deux dés.
{{clr}}
[[Fichier:Boite a moustaches 2d4.svg |vignette |Boîte à moustaches pour 2d4.]]
[[Fichier:Probas cumul 2d4 et boite a moustaches.svg|vignette |Rapport entre la courbe de probabilités cumulées et a boîte à moustaches.]]
==== Valeurs caractéristiques de la distribution ====
Classons les valeurs obtenues par ordre croissant et séparons la liste en quatre parties égales :
: « 2 » « 3 » « 3 » « 4 » ❙ « 4 » « 4 » « 5 » « 5 » ❙ « 5 » « 5 » « 6 » « 6 » ❙ « 6 » « 7 » « 7 » « 8 »
Nous avons donc :
* minimum : 2 ;
* 1{{er}} quartile : 4 ;
* médiane : 5 ;
* 3{{e}} quartile = 6
* maximum : 8.
Par rapport à 1d4, nous voyons que la partie centrale de la boîte à moustache, qui contient la moitié des événements, est plus ramassée par rapport à l'étendue totale.
Nous profitons de cette situation pour mettre en évidence le rapport entre la courbe des probabilités cumulées et la boîte à moustaches. En effet, puisque les quartiles correspondent à des probabilités cumulées de 25 %/50 %/75 %, on peut les obtenir en regardant les valeurs des dés qui donnent les probabilités cumulées mentionnées. Notons toutefois que les valeurs trouvées par cette méthode sont approximatives (3,5/4,5/5,5 alors que les vraies valeurs sont 4/5/6). Cette différence s'atténue lorsque le nombre de dés augmente.
{{clr}}
==== Tableau ====
Nous construisons d'abord un tableau des événements.
{| class="wikitable"
|+ Somme de deux dés à quatre faces (2d4)
|-
! scope="col" | dé 1
! scope="col" | dé 2
! scope="col" | événement<br /> (dé 1 + dé 2)
|-
| rowspan="4" | 1
|1 || 2
|-
| 2 || 3
|-
| 3 || 4
|-
| 4 || 5
|-
| rowspan="4" | 2
|1 || 3
|-
| 2 || 4
|-
| 3 || 5
|-
| 4 || 6
|-
| rowspan="4" | 3
|1 || 4
|-
| 2 || 5
|-
| 3 || 6
|-
| 4 || 7
|-
| rowspan="4" | 4
|1 || 5
|-
| 2 || 6
|-
| 3 || 7
|-
| 4 || 8
|}
Nous pouvons aussi construire ce tableau comme une matrice : le contenu de la cellule est la somme du titre de la ligne et du titre de la colonne.
{| class="wikitable"
|+ Somme de deux dés à quatre faces (2d4)
|-
| rowspan="2" colspan="2" | \
! scope="col" colspan="4" | dé n<sup>o</sup> 1
|-
! scope="col" | 1
! scope="col" | 2
! scope="col" | 3
! scope="col" | 4
|-
! scope="row" rowspan="4" |dé<br /> n<sup>o</sup> 2
! scope="row" | 1
| 2 || 3 || 4 || 5
|-
! scope="row" | 2
| 3 || 4 || 5 || 6
|-
! scope="row" | 3
| 4 || 5 || 6 || 7
|-
! scope="row" | 4
| 5 || 6 || 7 || 8
|}
Puis nous construisons un tableau des occurrences :
{| class="wikitable"
|+ Probabilités d'avoir un résultat pour 2d4
! scope="col" rowspan="2" | Résultat
! scope="col" rowspan="2" | Événements
! scope="col" rowspan="2" | Nombre<br /> d'occurrences
! scope="col" colspan="3" | Probabilités
|-
! scope="col" | fraction
! scope="col" | pour 1
! scope="col" | pour 100
|-
! scope="row" | 2
| {{surligné|#00FFFF|« 1 + 1 »}} || 1 || 1/16 || {{formatnum:0.0625}} || 6,25
|-
! scope="row" | 3
| {{surligné|#00FFFF|« 1 + 2 »}} {{surligné|#00FFFF|« 2 + 1 »}} || 2 || 2/16 || {{formatnum:0.125}} || 12,5
|-
! scope="row" | 4
| {{surligné|#00FFFF|« 1 + 3 »}} {{surligné|#00FFFF|« 2 + 2 »}} {{surligné|#00FFFF|« 3 + 1 »}} || 3 || 3/16 || {{formatnum:0.1875}} || 18,75
|-
! scope="row" | 5
| {{surligné|#00FFFF|« 1 + 4 »}} {{surligné|#00FFFF|« 2 + 3 »}} {{surligné|#00FFFF|« 3 + 2 »}} {{surligné|#00FFFF|« 4 + 1 »}} || 4 || 4/16 || {{formatnum:0.25}} || 25
|-
! scope="row" | 6
| {{surligné|#00FFFF|« 2 + 4 »}} {{surligné|#00FFFF|« 3 + 3 »}} {{surligné|#00FFFF|« 4 + 2 »}} || 3 || 3/16 || {{formatnum:0.1875}} || 18,75
|-
! scope="row" | 7
| {{surligné|#00FFFF|« 3 + 4 »}} {{surligné|#00FFFF|« 4 + 3 »}} || 2 || 2/16 || {{formatnum:0.125}} || 12,5
|-
! scope="row" | 8
| {{surligné|#00FFFF|« 4 + 4 »}} || 1 || 1/16 || {{formatnum:0.0625}} || 6,25
|}
Pour la table des probabilités cumulées, nous n'indiquons pas le détail de chaque événement mais directement la somme afin d'économiser de la place : nous n'indiquons par « “1 + 2” “2 + 1” » mais directement « “3” “3” ».
{| class="wikitable"
|+ Probabilités de faire une valeur ou moins pour 2d4
! scope="col" rowspan="2" | Résultat
! scope="col" rowspan="2" | Événements
! scope="col" rowspan="2" | Nombre<br /> d'occurrences
! scope="col" colspan="3" | Probabilités
|-
! scope="col" | fraction
! scope="col" | pour 1
! scope="col" | pour 100
|-
! scope="row" | 2
| {{surligné|#00FFFF|« 2 »}} || 1 || 1/16 || {{formatnum:0.0625}} || 6,25
|-
! scope="row" | 3
| {{surligné|#00FFFF|« 2 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 3 »}} || 3 || 3/16 || {{formatnum:0.1875}} || 12,5
|-
! scope="row" | 4
| {{surligné|#00FFFF|« 2 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} || 6 || 6/16 || {{formatnum:0.375}} || 37,5
|-
! scope="row" | 5
| {{surligné|#00FFFF|« 2 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} || 10 || 10/16 || {{formatnum:0.625}} || 62,5
|-
! scope="row" | 6
| {{surligné|#00FFFF|« 2 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 6 »}} || 14 || 13/16 || {{formatnum:0.8125}} || 81,25
|-
! scope="row" | 7
| {{surligné|#00FFFF|« 2 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 7 »}} {{surligné|#00FFFF|« 7 »}} || 15 || 15/16 || {{formatnum:0.9375}} || 93,75
|-
! scope="row" | 8
| {{surligné|#00FFFF|« 2 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 3 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 4 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 5 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 6 »}} {{surligné|#00FFFF|« 7 »}} {{surligné|#00FFFF|« 7 »}} {{surligné|#00FFFF|« 8 »}} || 16 || 16/16 || 1 || 100
|}
=== Somme de trois dés identiques ===
[[Fichier:Probas 3d4.svg |vignette |upright=3 |Probabilités pour 3d4.]]
La démarche est la même que ci-dessus. Les choses deviennent juste de plus en plus longues à faire à la main. Voici le début du tableau des événements :
{| class="wikitable"
|+ Somme de trois dés à quatre faces (3d4)
|-
! scope="col" | dé 1
! scope="col" | dé 2
! scope="col" | dé 3
! scope="col" | événement<br /> (dé 1 + dé 2 + dé 3)
|-
| rowspan="8" | 1
| rowspan="4" | 1
| 1 || 3
|-
| 2 || 4
|-
| 3 || 5
|-
| 4 || 6
|-
| rowspan="4" | 2
| 1 || 4
|-
| 2 || 5
|-
| 3 || 6
|-
| 4 || 7
|-
| … || … || … || …
|}
Il y a au total 4 × 4 × 4 = 64 événements (combinaisons de dés) possibles.
L'histogramme des probabilités (3d4 = ''i'') forme une courbe en cloche et l'histogramme des probabilités cumulées (3d4 ≤ ''i'') forme une sigmoïde un peu plus aplaties aux extrémités et un peu plus pentue au milieu. Les quartiles valent :
* minimum : 3 ;
* 1{{er}} quartile : 5,5 ;
* médiane : 7 ;
* 3{{e}} quartile : 8,5 ;
* maximum : 12.
== Somme de dés identiques en général ==
=== Probabilités ===
Quel que soit le type de dé, à partir du moment où il est numéroté de manière « classique », on a :
* 1d a une probabilité uniforme ; les probabilités cumulées forment une droite ;
* 2d a une probabilité triangulaire ; les probabilités cumulées forment une sigmoïde ;
* 3d et plus : la probabilité est une courbe en cloche et les probabilités cumulées forment une sigmoïde.
Vous trouverez ci-dessous les courbes pour des dés classiques (dés cubiques à six faces).
<gallery>
File:Probabilite nd6 fonction masse.svg | ''n''d6 = ''i''
File:Probabilite nd6 fonction densite.svg | ''n''d6 ≤ ''i''
File:Boite a moustaches 1d6 a 5d6.svg | Boîtes à moustaches pour ''n''d6
</gallery>
Pour des raisons de lisibilité, nous n'avons pas représenté les barres mais nous avons relié le sommet des barres par des traits. Cette manière de faire n'est pas recommandée mais facilite la lecture. Pour les boîtes à moustache, nous avons découpé les domaines de valeurs en quatre parties égales par des traits bleus afin de mettre en évidence le phénomène de concentration des tirages.
Cela est aussi vrai pour certains dés « spéciaux » comme les dF (pour ''Fudge'' et ''FATE'').
=== Quelle influence cela a-t-il sur le jeu ? ===
==== Plusieurs manière d'avoir un jet dans une plage de valeurs ====
[[Fichier:Probabilite d20 2d10 3d6.svg |vignette |upright=2 |Comparaison des probabilités entre 1d20, 2d10 et 3d6.]]
[[Fichier:Boite a moustaches 1d20 2d10 3d6.svg |vignette |upright=2 |Boîtes à moustaches pour 1d20, 2d10 et 3d6.]]
Comme nous l'avons relevé dans le chapitre précédent ''([[../Le hasard dans les jeux de rôle|Le hasard dans les jeux de rôle]])'', les deux choses importantes sont :
* le choix des joueuses, ce qui inclue la capacité des joueuses à estimer leurs chances ;
* le fait de savoir si un événement est rare ou fréquent.
Pour une échelle de valeurs donnée, mettons entre 1 et 20, il y a plusieurs manière de générer du hasard : 1d20, 2d10, 3d6…
Avec 1d20, on a autant de chances d'avoir un résultat faible, moyen ou fort. Avec 2d10 ou 3d6, on a plus de chances d'avoir un résultat moyen — « 10 » ou « 11 » — et peu de chances d'avoir un résultat extrême. De fait, si l'on utilise la somme de plusieurs dés, on peut être plus confiant dans le fait d'avoir un résultat moyen, mais les joueuses le savent-elles ? Et d'un point du vue du jeu, est-il vraiment intéressant d'avoir une grande dispersion des valeurs, d'avoir régulièrement des valeurs très élevées ou très basses ?
Un choix de conception ''({{lang|en|game design}})'' intéressant…
==== Table d'événements aléatoire ====
Si l'on crée une table d'événements aléatoire<ref>Lire par exemple {{lien web
| url = http://acritarche.tumblr.com/post/107413760823/mes-outils-jdr-partie-5-les-tables-al%C3%A9atoires
| titre = Mes outils “jdr” (partie 5 : les tables aléatoires)
| auteur = Bastien « Acritarche » Wautoz
| site = Contes des ères abyssales
| date = 2015-01-07 | consulté le = 2018-04-19
}}.</ref>, il faut être conscient que si l'on jette plusieurs dés, alors les valeurs centrales sont plus probables. Ainsi, on mettra les événements les plus fréquents et les plus intéressants pour la narration. On peut par exemple classer dans un tableau à deux entrées : une entrée « fréquence » et une entrée « intérêt narratif ». Ce tableau permet d'évaluer la pertinence de l'événement.
{| class="wikitable"
|+ Table d'évaluation
|-
| rowspan="2" colspan="2" | \
! scope="col" colspan="3" | Intérêt narratif
|-
! scope="col" | Banal
! scope="col" | Intéressant
! scope="col" | Riche
|-
! scope="row" rowspan="3" | fré-<br />quence
! scope="row" | Rare
| 0 || 1 || 2
|-
! scope="row" | Occasionnel
| 1 || 2 || 3
|-
! scope="row" | Fréquent
| 2 || 3 || 4
|}
Chaque événement est placé dans cette table d'évaluation et se voit donc associer un score. Puis, les événements sont classés par ordre décroissant de pertinence et placés comparés à la liste des valeurs classées par ordre de probabilité décroissante. Par exemple, avec 2d6 :
{| class="wikitable"
|+ Classement par probabilité décroissante (2d4)
|-
! scope="col" | Valeur
! scope="col" | Probabilité
! scope="col" | Événement
|-
! scope="row" | 5
| 25 % ||
|-
! scope="row" | 6
| 19 % ||
|-
! scope="row" | 4
| 19 % ||
|-
! scope="row" | 7
| 13 % ||
|-
! scope="row" | 3
| 13 % ||
|-
! scope="row" | 8
| 6 % ||
|-
! scope="row" | 2
| 6 % ||
|}
Concrètement, dans le tableau précédent, on place les événements ayant des scores élevés en haut du tableau et les événements ayant un score faible en bas.
Cette table est ensuite triée par ordre de valeur de dé croissante pour en faciliter la lecture.
==== Progression des chances ====
Imaginons que l'on résolve une situation par un jet sous une caractéristique. Avec un seul dé, chaque fois que le PJ progresse de 1 dans sa caractéristique, ses chances de réussite progressent de la même quantité, par exemple +5 % avec 1d20.
En revanche, si l'on additionne plusieurs dés, la courbe sigmoïde fait que :
* aux faibles valeurs, les chances de réussite progressent lentement ;
* aux valeurs médianes, la progression est très rapide ;
* aux fortes valeurs, la progression est lente.
D'un point de vue stratégique, il faut donc rapidement sortir de la zone basse et dépasser la partie médiane. En revanche, il devient moins intéressant d'investir dans une caractéristique déjà élevée. Bien sûr, cela change avec la présence de bonus-malus ; ainsi, dans une situation assortie d'un malus, une caractéristique élevée « devient » une caractéristique moyenne…
'''Exemple'''
Une caractéristique est cotée sur 20 environ. L'action est réussie si un jet de dé(s) est inférieur ou égal à la caractéristique.
{| class="wikitable"
|+ 1d20 + mod. ≤ car.
|-
! scope="col" rowspan="2" | Caractéristique
! scope="col" colspan="3" | Modificateur de difficulté
|-
! scope="col" | +2
! scope="col" | 0
! scope="col" | –2
|-
! scope="row" | 10
| 60 % || 50 % || 40 %
|-
! scope="row" | 13
| 75 % || 65 % || 55 %
|-
! scope="row" | 15
| 85 % || 75 % || 65 %
|}
{| class="wikitable"
|+ 2d10 + mod. ≤ car.
|-
! scope="col" rowspan="2" | Caractéristique
! scope="col" colspan="3" | Modificateur de difficulté
|-
! scope="col" | +2
! scope="col" | 0
! scope="col" | –2
|-
! scope="row" | 10
| 72 % || 45 % || 28 %
|-
! scope="row" | 13
| 85 % || 72 % || 55 %
|-
! scope="row" | 15
| 94 % || 85 % || 72 %
|}
{| class="wikitable"
|+ 3d6 + mod. ≤ car.
|-
! scope="col" rowspan="2" | Caractéristique
! scope="col" colspan="3" | Modificateur de difficulté
|-
! scope="col" | +2
! scope="col" | 0
! scope="col" | –2
|-
! scope="row" | 10
| 74 % || 50 % || 26 %
|-
! scope="row" | 13
| 95 % || 84 % || 63 %
|-
! scope="row" | 15
| 99 % || 95 % || 84 %
|}
Lorsque nous sommes du « côté droite » de la sigmoïde (au-dessus de la médiane), donc pour des caractéristiques de 13 ou 15, la situation à 3d6 est plus favorable que celle à 2d10 qui l'est encore plus que celle à 1d20.
Avec ''AnyDice'', nous avons utilisé l'affichage de données « Data: At most » et les expressions <code>output 1d20</code>, <code>output 2d10</code> et <code>output 3d6</code>. Plutôt que de chercher les lignes « 10 » (ainsi que « 8 » et « 12 » pour les modificateurs ±2), « 13 » (et « 11 ») et « 15 » (et « 17 ») sur les histogramme (ou le texte de la section « Export »), on peut aussi taper des expressions du type <code>output 1d20 - 2 <= 10</code>, les chances de réussite apparaissent sur la ligne « 1 ».
'''Exemple'''
Considérons le ''d20 system''. Une caractéristique génère un modificateur allant de –5 à +5 ; s'y ajoute normalement les points de compétence et divers bonus. L'action est réussie si un jet de dés modifié est supérieur à un degré de difficulté (DD).
Les caractéristiques sont cotées sur 20 pour des humains et voici un extrait de la table des modificateurs de caractéristique et la table des degrés de difficulté, tirées du DRS<ref>''Document de référence du système'', publié selon la licence ludique libre (OGL).</ref> :
<table border="0">
<tr><td valign="bottom">
{| class="wikitable" style="float:left"
|+ Table des modificateurs de caractéristique (extrait)
! scope="col" colspan="2" | Caractéristique
|-
! scope="col" | Valeur
! scope="col" | Modificateur
|-
| 8-9 || –1
|-
| 10-11 || 0
|-
| 12-13 || +1
|-
| 14-15 || +2
|-
| 16-17 || +3
|}
</td><td valign="bottom">
{| class="wikitable" style="float:right"
|+ Table des degrés de difficulte
|-
! scope="col" | Difficulté <br />de la tâche
! scope="col" | DD
|-
| Très facile || 5
|-
| Facile || 10
|-
| Moyen || 15
|-
| Difficile || 20
|-
| Très difficile || 25
|-
| Presque impossible || 30
|}
</tr>
</table>
Nous sélectionnons quelques cas pour l'étude.
{| class="wikitable"
|+ 1d20 + mod. de car. ≥ DD
|-
! scope="col" rowspan="2" | Modificateur de<br />Caractéristique
! scope="col" colspan="3" | Degré de difficulté
|-
! scope="col" | 5
! scope="col" | 10
! scope="col" | 15
|-
! scope="row" | 0
| 80 % || 55 % || 30 %
|-
! scope="row" | +1
| 85 % || 60 % || 35 %
|-
! scope="row" | +2
| 90 % || 65 % || 40 %
|}
{| class="wikitable"
|+ 2d10 + mod. de car. ≥ DD
|-
! scope="col" rowspan="2" | Modificateur de<br />Caractéristique
! scope="col" colspan="3" | Degré de difficulté
|-
! scope="col" | 5
! scope="col" | 10
! scope="col" | 15
|-
! scope="row" | 0
| 94 % || 64 % || 21 %
|-
! scope="row" | +1
| 97 % || 72 % || 28 %
|-
! scope="row" | +2
| 99 % || 79 % || 36 %
|}
{| class="wikitable"
|+ 3d6 + mod. de car. ≥ DD
|-
! scope="col" rowspan="2" | Modificateur de<br />Caractéristique
! scope="col" colspan="3" | Degré de difficulté
|-
! scope="col" | 5
! scope="col" | 10
! scope="col" | 15
|-
! scope="row" | 0
| 98 % || 62 % || 9 %
|-
! scope="row" | +1
| 99 % || 74 % || 16 %
|-
! scope="row" | +2
| 100 % || 84 % || 26 %
|}
Pour une caractéristique ayant un modificateur nul (0) et un DD 10, les chances de réussite sont plus élevées avec 2d10 et 3d6. Lorsque le DD varie de ±5, les chances de réussite varient de +35/–25 % avec 1d20, de +30/–43 % avec 2d10 et de +36/–53 % avec 3d6. Les DD élevés sont donc défavorables avec 2d10 et ''a fortiori'' avec 3d6.
Lorsque le modificateur augmente, la situation est d'autant plus favorable que le nombre de dés augmente pour les DD faibles (5 ou 10) ; elle est d'autant plus défavorable que le nombre de dés augmente pour les DD forts.
Avec ''AnyDice'', nous avons utilisé l'affichage de données « Data: At least » et les expressions <code>output 1d20</code>, <code>output 1d20 + 1</code> et <code>output 1d20 + 2</code> ; <code>output 2d10</code>, <code>output 2d10 + 1</code> et <code>output 2d10 + 2</code> ; <code>output 3d6</code>, <code>output 3d6 + 1</code> et <code>output 3d6 + 2</code>. Plutôt que de chercher les lignes « 5 », « 10 » et « 15 » sur les histogramme (ou le texte de la section « Export »), on peut aussi taper <code>output 1d20 >= 5</code>, <code>output 1d20 >= 10</code> etc. qui donne directement la probabilité de réussite sur la ligne « 1 ».
Notez que dans les deux situations considérées, nous reconstruisons une « table des difficultés » telle que présentée dans la section ''[[../Le hasard dans les jeux de rôle#Estimer ses chances|Estimer ses chances]]'' du chapitre précédent.
==== Maladresses et coups de maître ====
Les situations générant des maladresses ''({{lang|en|fumbles}})'' et des coups de maître (réussites critiques) sont souvent les valeurs extrêmes « naturelles » des jets — « naturel » signifiant la valeur indiquée par la face du dés sans appliquer de modificateur. Dans le cas d'un dé unique ou d'une somme de dés, les deux valeurs extrêmes — le minimum et le maximum — ont les mêmes probabilités de survenue. Il s'agit donc :
{| class="wikitable"
|+ Valeurs extrêmes des jets
|-
! scope="col" | Jet
! scope="col" | Minimum
! scope="col" | Maximum
! scope="col" | Probabilité
|-
! scope="row" | 1d20
| 1 || 20 || 5 % (1/20)
|-
! scope="row" | 2d10
| 2 || 20 || 1 % (1/100)
|-
! scope="row" | 3d6
| 3 || 18 || 0,462 % (1/216)
|}
Donc avec 1d20, il y aura probablement plusieurs maladresses et coups de maître par partie. Avec 3d6, cela sera plus rare.
Pour raréfier les situations extrêmes avec 1d20, on peut rajouter une deuxième condition, par exemple pour une maladresse : « faire la valeur extrême et confirmer en échouant à un second jet » et pour un coup de maître : « faire la valeur extrême et confirmer en réussissant à un second jet ».
Pour rendre plus fréquent les situations extrêmes avec 3d6, on peut augmenter la plage, par exemple respectivement « 3-4 » et « 17-18 » soit environ 2% (1/50).
== Variantes d'une somme de dés identiques ==
=== Soustraction de dés ===
[[Fichier:Probas moins 2d4.svg |vignette |upright=2 |La probabilité de {–2d4} est identique à la probabilité de {2d4 – 10}.]]
Certains système soustraient des dés. L'opération est en fait identique à ajouter des dés. En effet, on peut remarquer que {–1d4} a les mêmes probabilités que {1d4 – 5}, que {–2d4} a les mêmes probabilités que {2d4 – 10} et de manière générale, {–''n''dX} a les mêmes probabilités que {''n''dX – ''n''⋅(X + 1)}.
Ainsi, la probabilité de {1d4 – 1d4} est identique à la probabilité de {2d4 – 5}. Soustraire un dé revient simplement à enlever une valeur constante à la somme des dés.
Si les dés sont identiques, la distribution est centrée sur zéro.
{{clr}}
[[Fichier:Probas abs 1d10 moins 1d10.svg |vignette |Probabilités de la valeur absolue de la différence de deux dés à dix faces, <nowiki>|</nowiki>1d10 – 1d10<nowiki>|</nowiki>, les faces « 0 » valant zéro.]]
Le jeu ''Qin'' (7{{e}} Cercle, 2009) utilise un « dé yin-yang » : on jette deux d10 et le résultat le plus élevé est soustrait du résultat le plus faible, et le résultat « 0 » est la valeur zéro (et non dix comme habituellement). Le jet est ainsi toujours positif ou nul. D'un point de vue mathématiques, cela revient à prendre la valeur absolue de la différence.
Puisque nous avons ici pour la première fois une distribution dissymétrique, nous pouvons illustrer la différence entre moyenne et médiane. D'un point de vue calcul, la moyenne s'obtient par :
: Somme des événements/nombre d'événements
Pour simplifier, nous ne considérons pas des d10 mais des d4. Nous avons donc :
[[Fichier:Moyenne abs 1d4 moins 1d4.svg |vignette |Illustration de la notion de moyenne comme point d'équilibre de l'histogramme.]]
{| class="wikitable"
|+ Moyenne pour <nowiki>|</nowiki>1d4 – 1d4<nowiki>|</nowiki>
|-
! scope="col" | Dé 1
! scope="col" | Dé 2
! scope="col" | Événement
|-
! scope="row" rowspan="4" | 1
! scope="row" | 1
| 0
|-
! scope="row" | 2
| 1
|-
! scope="row" | 3
| 2
|-
! scope="row" | 4
| 3
|-
! scope="row" rowspan="4" | 2
! scope="row" | 1
| 1
|-
! scope="row" | 2
| 0
|-
! scope="row" | 3
| 1
|-
! scope="row" | 4
| 2
|-
! scope="row" rowspan="4" | 3
! scope="row" | 1
| 2
|-
! scope="row" | 2
| 1
|-
! scope="row" | 3
| 0
|-
! scope="row" | 4
| 1
|-
! scope="row" rowspan="4" | 4
! scope="row" | 1
| 3
|-
! scope="row" | 2
| 2
|-
! scope="row" | 3
| 1
|-
! scope="row" | 4
| 0
|-
! scope="row" colspan="2" | Somme
| 20
|-
! scope="row" colspan="2" | Moyenne
| {{formatnum:1.25}}
|}
En effet :
: somme des événements : (0 + 1 + 2 + 3 + 1 + 0 + 1 + 2 + 2 + 1 + 0 + 1 + 3 + 2 + 1 + 0) = 20
: nombre d'événements : 16
: moyenne : 20/16 = {{formatnum:1.25}}
Représentons chaque événement par une brique, les briques ayant toutes la même masse et formant des piles régulièrement espacées sur un plateau. Chaque pile correspond à la valeur d'un événement, donc une valeur entre 0 et 3 ; elles sont classées par ordre croissant. Pour chaque position, on empile autant de brique qu'il y a d'événement ayant la valeur correspondante ; on obtient de fait l'histogramme. Le point d'équilibre du plateau est situé entre le centre de la pile « 1 » et le centre de la pile « 2 », plus proche du 1. Si l'on repère les positions par rapport au centre de la pile « 0 », le point d'équilibre est à l'abscisse « 1,25 ».
La médiane, quant à elle, vaut 1 :
: 0 0 0 0 1 1 1 1 ❙ 1 1 2 2 2 2 3 3
Dans ''AnyDice'', la moyenne est la première valeur entre parenthèses indiquée après le titre de l'affichage.
Dans le tableur ''LibreOffice Calc'', la moyenne s'obtient avec l'expression <code>=moyenne(C2:C17)</code>.
{{clr}}
[[Fichier:Probas d yin yang.svg |vignette |Probabilités du « dé yin-yang » ''(Qin : Les Royaumes combattants)''.]]
Pour en revenir au dé yin-yang, la règle indique qu'un double « 0 » est une maladresse tandis qu'un autre double est un coup de maître. Le code pour gérer ceci est indiqué [[#Dé_yin-yang_(Qin)|en annexe]]. Dans le graphique ci-contre, la maladresse est représentée par la valeur –1 et le coup de maître par la valeur 10, avec une étiquette « crit. » (réussite ou échec critique).
{{clr}}
=== Somme de dés différents ===
Si l'on somme deux dés différents, on obtient une répartition en trapèze. La probabilité cumulée reste une sigmoïde dont la partie centrale est rectiligne. La situation est donc intermédiaire entre le dé seul et la somme de deux dés identiques :
* pour une table aléatoire, les probabilités d'apparition des événements sont identiques pour la plage centrale ;
* pour un jet de type test, les probabilités évoluent de manière linéaire dans la partie centrale (le gain d'un point permet de progresser de manière uniforme) tout en ayant des probabilités plus faibles pour les valeurs extrêmes.
Si l'on somme plus de dés, nous approchons une courbe en cloche. C'est donc une alternative si le but est d'avoir ce type de courbe.
<gallery>
File:Probas_1d4_plus_1d6.svg | 1d4 + 1d6
File:Probas_2d4_plus_1d6.svg | 2d4 + 1d6
File:Probas_1d4_plus_2d6.svg | 1d4 + 2d6
File:Probas_1d4_plus_1d6_plus_1d8.svg | 1d4 + 1d6 + 1d8
File:Boite a moustaches somme deux ou trois des differents.svg | Boîtes à moustache
</gallery>
Les histogrammes et valeurs ci-dessus peuvent s'obtenir avec ''AnyDice'' en utilisant les syntaxes :
<syntaxhighlight lang="text">
output 1d4 + 1d6
output 2d4 + 1d6
output 1d4 + 2d6
output 1d4 + 1d6 + 1d8
</syntaxhighlight>
=== Comparaison de deux dés ===
Dans certains jeux, on compare deux jets de dés. C'est typiquement le cas des jeux où les dés représentent la capacité du personnage, soit que la caractéristique s'exprime sous la forme de dés ''(d6 System'', ''Savage World'', ''Earthdawn''…'')'', soit que la caractéristique donne un bonus aux dés ''(d20 System)''. Dans ces jeux là, lorsque deux personnages s'opposent, le test consiste souvent à comparer les deux jets :
* dé<sub>1</sub> > dé<sub>2</sub> : le personnage 1 remporte la confrontation ;
* dé<sub>1</sub> = dé<sub>2</sub> : égalité ;
* dé<sub>1</sub> < dé<sub>2</sub> : le personnage 2 remporte la confrontation.
Ces conditions peuvent s'écrire respectivement :
* dé<sub>1</sub> – dé<sub>2</sub> > 0 ;
* dé<sub>1</sub> – dé<sub>2</sub> = 0 ;
* dé<sub>1</sub> – dé<sub>2</sub> < 0.
Nous voyons que cette situation est similaire à la soustraction de deux jets.
Par exemple, deux personnages font un bras de fer ; la force de l'un est de 1d6 et la force de l'autre de 1d8, le résultat le plus élevé l'emporte. On compare donc 1d6 à 1d8, ce qui revient à comparer {1d8 – 1d6} à zéro, soit, d'après la section précédente, à comparer {1d8 + 1d6 – 7} à zéro.
Dans le les livres-jeux et le jeu de rôle ''Défis fantastiques'', lors d'un combat, on calcul la force d'attaque (FA) en additionnant 2d6 à la caractéristique ''habileté''. Le personnage ayant la FA la plus élevée remporte l'assaut et inflige des dégâts à l'autre. On compare donc 2d6 + Hab<sub>1</sub> à 2d6 + Hab<sub>2</sub>. Cela revient à comparer {2d6 – 2d6} à {Hab<sub>2</sub> – Hab<sub>1</sub>} donc, au final, à comparer {4d6 – 14}<ref>14 = 4 × 3,5, soit quatre fois la moyenne d'un dé.</ref> à {Hab<sub>2</sub> – Hab<sub>1</sub>}.
: {2d6 + Hab<sub>1</sub>} > {2d6 + Hab<sub>2</sub>} ⇔ {2d6 – 2d6} > {Hab<sub>2</sub> – Hab<sub>1</sub>} ⇔ {4d6 – 14} > {Hab<sub>2</sub> – Hab<sub>1</sub>}
<gallery>
Probas DF diffhab 0.svg | Cas où les combattants ont la même Habileté
Probas DF diffhab 1.svg | Cas où les combattants ont 1 point de différence en Habileté
Probas DF diffhab 2.svg | Cas où les combattants ont 2 points de différence en Habileté
Probas DF diffhab 3.svg | Cas où les combattants ont 3 points de différence en Habileté
</gallery>
Dans le cas des livres-jeux, si l'on n'utilise pas la règle de ''chance'', un coup porté fait perdre 2 points d{{'}}''endurance'' à l'adversaire. On peut donc calculer le nombre de points d'endurance perdus en moyenne pour 10 assauts :
: si un personnage touche à 100 %, il inflige 2 × 10 = 20 points d{{'}}''endurance'' pour 10 assauts ; s'il ne touche que 56 % du temps, il n'inflige en moyenne que 0,56 × 20 ≃ 11 points de dégât.
{| class="wikitable"
|+ Probabilités lors d'un combat à ''Défis fantastiques''
|-
! scope="col" rowspan="2" | Hab<sub>2</sub> – Hab<sub>1</sub>
! scope="col" colspan="2" | Probabilité de toucher
! scope="col" colspan="2" | Perte d{{'}}''endurance'' pour 10 assauts
|-
! scope="col" | combattant 1
! scope="col" | combattant 2
! scope="col" | combattant 1
! scope="col" | combattant 2
|-
! scope="row" | 0
| 44 % || 44 % || 9 || 9
|-
! scope="row" | 1
| 34 % || 56 % || 11 || 7
|-
! scope="row" | 2
| 24 % || 66 % || 13 || 5
|-
! scope="row" | 3
| 16 % || 76 % || 15 || 3
|}
Nous voyons qu'avec une différence de deux points d{{'}}''habileté'', le personnage le plus faible est touché deux fois plus souvent, il perd son ''endurance'' deux fois plus rapidement que le personnage le plus fort.
Dans ''AnyDice'', pour avoir les courbes, on peut utiliser l'une des deux solutions :
<syntaxhighlight lang="text">
output 2d6-2d6 named "2d6 – 2d6"
output 4d6-14 named "2d6 – 2d6"
</syntaxhighlight>
Pour avoir directement les probabilités de toucher, on peut utiliser :
<syntaxhighlight lang="text">
output (2d6-2d6) > 2 named "1 touche pour Hab2 – Hab1 = 2"
output (4d6-14) > 2 named "1 touche pour Hab2 – Hab1 = 2"
</syntaxhighlight>
On peut calculer toutes les valeurs d'un seul coup en utilisant une boucle ''({{lang|en|loop}})'' :
<syntaxhighlight lang="text">
loop N over {0..3}{
output (4d6-14)>N named "1 touche pour Hab2 – Hab1 = [N]"
output (4d6-14)<N named "2 touche pour Hab2 – Hab1 = [N]"
}
</syntaxhighlight>
La commande <code>loop N over {0..3}{ ''instructions'' }</code> répète les instructions contenues entre accolades pour N prenant successivement les valeurs 0, 1, 2 et 3. Dans le nom de l'affichage <code>named"…"</code>, il faut mettre le nom de la variable N entre crochet pour qu{{'}}''AnyDice'' sache qu'il s'agit bien de la variable et pas d'une lettre à afficher.
=== Jet sans limite ===
[[Fichier:Probas 1d4 sans limite.svg |vignette |upright=2 |Jet sans limite avec 1d4 incluant les résultats négatifs.]]
[[Fichier:Probas 1d4 explosif.svg |vignette |upright=2 |Probabilités pour 1d4 « explosif » (il n'est sans limite que vers le haut).]]
Le jet sans limite (JSL) (anglais : ''{{lang|en|open-ended die}}'', littéralement jet à fin ouverte), ou « jet explosif », est un mécanisme de jeu de rôle permettant d'étendre la plage de valeurs données par un jet de dé(s). Lorsque l'on obtient un des extrêmes de la plage, on relance les dés ; le résultat est alors ajouté au résultat précédent si le premier jet était maximal, retranché si le jet était minimal. Ce mécanisme est récursif, ce qui explique le terme de « sans limite ».
Exemple avec 1d10 :
* si le jet donne un résultat entre 2 et 9, je garde le résultat tel quel ;
* si le résultat est un 10, je tire de nouveau un d10 et le résultat est ajouté à ce 10 ; si le deuxième jet est un 10 (on a donc 20 pour l'instant), je tire un troisième dé que j'ajoute…
* si le premier résultat est un 1, je retire un d10 et le résultat est déduit de ce 1 ; si le deuxième jet est un 10 (on a donc –9 pour l'instant), je tire un troisième dé que je retranche…
Les principaux jeux utilisant ce système sont ''Rolemaster'' et les jeux dérivés avec un d100 (jet ouvert sur les résultats 1-5 et 95-00) : ''Jeu de rôle des Terres du Milieu'', ''Spacemaster'' et ''HARP''. C'est une manière de gérer les maladresses et coups de maître. D'autre jeux n'appliquent le système que « vers le haut ». Par exemple le ''D6 System'', et ''Star Wars'' qui en est à l'origine, applique ce système à un seul des dés à six faces lancés, le « dé libre » ''(wild die)''. ''Savage World'' a le système « d'as ». ''Anima Beyond Fantasy'' permet les jets ouverts sur des dés à cent faces : il est donc possible d'obtenir des résultats allant dans les 300, 400… Ce qui permet des actions ''a priori'' physiquement impossibles.
''AnyDice'' propose la fonction ''explode'' pour faire des jets ouverts « vers le haut » :
<syntaxhighlight lang="text">
output [explode d6] named "d6 explosif"
</syntaxhighlight>
donne un d6 qui explose sur les « 6 ». Comme le processus ne s’arrête virtuellement jamais, AnyDice s'arrête à trois lancers supplémentaires appelé « profondeur » ''({{lang|en|depth}})'' ; on peut modifier ce nombre de lancer supplémentaires. Par exemple pour avoir 5 relancés avec 1d4
<syntaxhighlight lang="text">
set "explode depth" to 5
output [explode d4] named "d4 explosif"
</syntaxhighlight>
Notez la différence entre <code>[explode 2d6]</code> — qui « explose » sur un « 12 » — et <code>2d[explode d6]</code> — qui jette deux d6 explosifs et en fait la somme.
Si on veut avoir un jet sans limite à la manière de ''Rolemaster'', il faut créer sa propre fonction. Par exemple, pour 1d10 :
<syntaxhighlight lang="text">
function: jsl N:n {
if N = 10 { result: N + [explode d10] }
if N = 1 { result: N - [explode d10] }
result: N
}
set "maximum function depth" to 3
output [jsl d10]
</syntaxhighlight>
Nous voyons ici qu'il est possible de créer des fonctions avec ''AnyDice''. La syntaxe générale est :
function: ''NomDeLaFonction'' ''VariableEntrée'' {
''instructions''
result: ''expression''
}
et elle s'utilise ensuite comme toutes les fonctions
[''NomDeLaFonction'' ''Valeur'']
dans la fonction, la commande <code>result:</code> provoque la sortie de la fonction avec pour valeur l'expression qui suit les deux points.
{{note|La variable d'entrée est toujours en majuscules.}}
Dans le cas présent, on définit la fonction : ''jsl''. On demande à ''AnyDice'' de tracer la fonction ''jsl''(d10) par la syntaxe :
<syntaxhighlight lang="text">
output [jsl d10]
</syntaxhighlight>
Pour cette fonction, on a :
* ''NomDeLaFonction'' : <code>jsl</code> ;
* ''VariableEntrée'' : <code>N</code> ; comme ici la variable est une séquence (une suite de nombre, puisqu'il s'agit des résultats possibles d'un dé), on ajoute <code>:n</code> à ce nom.
La fonction a donc pour paramètre d'entrée N = d10. Si (''{{lang|en|if}}'' en anglais) cette valeur N est égale à 10, alors on lui ajoute le résultat de la fonction ''explode''(d10). Si cette valeur N est égale à 1, alors on lui enlève le résultat de la fonction ''explode''(d10). Les instructions correspondantes sont
<syntaxhighlight lang="text">
if N = 10 { result: N + [explode d10] }
if N = 1 { result: N - [explode d10] }
</syntaxhighlight>
Si aucune des conditions n'est réalisée, c'est-à-dire si le résultat est entre 2 et 9, alors l'exécution n'a pas rencontré de commande de sortie <code>result:</code> donc on applique la dernière ligne qui renvoie la valeur de N :
<syntaxhighlight lang="text">
result: N
</syntaxhighlight>
=== Choix de dés parmi plusieurs ===
[[Fichier:Probas choix 1d4 parmi 2.svg |vignette|upright=2 | Choix d'un dé parmi deux dés à quatre faces. Certains quartiles sont identiques à la médiane ou à un extremum.]]
Nous étudions ici le cas où l'on jette plusieurs dés et où l'on n'en retient que certains, en général le plus élevé ou le plus faible, parfois le dé médian (c'est-à-dire ni le plus élevé, ni le plus faible parmi trois dés). Par exemple :
* dans ''AD&D1'', une des méthodes de détermination des caractéristiques consiste a additionner les trois meilleurs d6 parmi 4 ;
* dans ''D&D5'', un avantage permet de choisir le meilleur de deux d20 et un désavantage impose de choisir le pire ;
* dans ''Savage World'', les personnages joker jettent 1d6 en plus de leur dé de compétence et choisissent le meilleur ;
* dans ''Usagi Yojimbo 2'', on choisit le meilleur résultat entre le dé de caractéristique et le dé de compétence ;
* dans ''P'tites sorcières'', on somme deux dés à six faces, mais selon la puissance du personnage, il s'agit des deux pires dés parmi trois ou quatre (personnage faible) ; simplement de deux dés (personnage moyen) ; des deux meilleurs dés parmi trois ou quatre (personnage fort) ;
* le système natif du ''Livre des cinq anneaux'' est appelé ''{{lang|en|roll & keep}}'' et consiste à conserver ''m'' dés parmi ''n'' jetés (noté ''m'' k ''n'').
{| class="wikitable" style="float:right; margin-left: 10px;"
|+ État du tableur
|-
|
! scope="col" | A
! scope="col" | B
! scope="col" | C
|-
! scope="row" | 1
| '''dé 1''' || '''dé 2''' || '''événement'''
|-
! scope="row" | 2
| 1 || 1 || <code>=max(A2:B2)</code>
|-
! scope="row" | 3
| 1 || 2 ||
|}
[[Fichier:1d choisi parmi 2 differents.svg|vignette|upright=2 |Probabilité d'avoir une valeur en choisissant la plus haute parmi avec deux dés différents.]]
[[Fichier:1d choisi parmi 3 differents fonction de masse.svg|vignette|upright=2 |''Idem'' avec trois dés différents.]]
Avec un tableur, la colonne ''événement'' utilise la fonction <code>max()</code> si l'on veut le dé le plus élevé, <code>min()</code> si l'on veut le dé le plus faible et <code>mediane()</code> si l'on veut le dé médian. Par exemple, pour avoir le dé le plus élevé parmi deux : <code>=max(A2:B2)</code>. On crée ensuite le second tableau ''résultats''/''nb. d'occ''/''cumul'' habituel.
Avec ''AnyDice'', on utilise les fonctions <code>highest … of …</code> (le plus grand), <code>lowest … of …</code> (le plus petit) et <code>middle … of …</code> (le médian). Par exemple, pour avoir le d20 le plus petit parmi deux ou pour avoir la somme des trois d6 les plus élevés parmi 4 :
<syntaxhighlight lang="text">
output [lowest 1 of 2d20]
output [highest 3 of 4d6]
</syntaxhighlight>
Si les dés sont différents, on utilise <code>highest of … and …</code> et consort. Par exemple, pour avoir le plus petit entre 1d4 et d16, ou le plus grand entre 1d4, 1d6 et 1d8 :
<syntaxhighlight lang="text">
output [lowest of 1d4 and 1d6]
output [highest of 1d4 and [highest of 1d6 and 1d8]]
</syntaxhighlight>
Dans le cas du meilleur ou du pire dé parmi 2d20 (avantage ou désavantage à ''D&D5''), nous avons les quartiles suivants.
{| class="wikitable"
|+ Quartiles lorsque l'on choisit 1d20 parmi 2
|-
! scope="col" | Situation
! scope="col" | 1{{er}} quartile
! scope="col" | Médiane
! scope="col" | 3{{e}} quartile
|-
! scope="row" | Meilleur parmi 2d20
| 10,75 || 15 || 18
|-
! scope="row" | 1d20 seul
| 5 || 10 || 15
|-
! scope="row" | Pire parmi 2d20
| 3 || 6 || 10,75
|}
Nous remarquons que par rapport à 1d20 seul, le choix du meilleur ou du pire parmi deux décale les quartiles d'une valeur allant de –4,25 à +5,25, la médiane étant décalée de –4/+5. Cela justifie la remarque préliminaire dans la section [[#Quelques fumbles|''Quelques'' fumbles]].
Le tableau ci-dessous indique les quartiles pour le choix de 2d6 parmi 2 à 4, méthode utilisée par ''P'tites Sorcières''.
[[Fichier:2d6 choisis parmi n.svg |vignette |upright=1.5 |Somme de 2d6 choisis parmi plusieurs.]]
{| class="wikitable"
|+ Quartiles pour ''P'tites sorcières''
|-
! scope="col" | Dés
! scope="col" | 1{{er}} quartile
! scope="col" | Médiane
! scope="col" | 3{{e}} quartile
|-
! scope="row" | 2 pires parmi 4d6
| 3 || 4 || 6
|-
! scope="row" | 2 pires parmi 3d6
| 4 || 5 || 7
|-
! scope="row" | 2d6
| 5 || 7 || 9
|-
! scope="row" | 2 meilleurs parmi 3d6
| 7 || 9 || 10
|-
! scope="row" | 2 meilleurs parmi 4d6
| 8 || 10 || 11
|}
La table des difficultés pour ''P'tites Sorcières'' est la suivante :
{| class="wikitable"
|+ Probabilités de réussite dans les ''P'tites sorcières''
|-
! scope = "col" rowspan="2" | Possibilité
! scope = "col" colspan="5" | Difficulté
|-
! scope = "col" | immanquable<br />3
! scope = "col" | facile<br /> 5
! scope = "col" | moyenne<br /> 7
! scope = "col" | difficile<br /> 9
! scope = "col" | exceptionnelle<br />11
|-
! scope="row" | mauvaise
| 68 % || 31 % || 9 % || 2 % || 0,1 %
|-
! scope="row" | faible
| 80 % || 48 % || 19 % || 5 % || 0,5 %
|-
! scope="row" | moyenne
| 92 % || 72 % || 42 % || 17 % || 3 %
|-
! scope="row" | bonne
| 98 % || 89 % || 68 % || 36 % || 7 %
|-
! scope="row" | excellente
| 99,6 % || 96 % || 83 % || 52 % || 13 %
|}
== Avoir deux dés identiques ==
Le système de ''Reign'' (2007)<ref>{{lien web
| url = http://www.legrog.org/jeux/reign
| titre = Reign
| site = GRoG
| consulté le = 2018-08-21
}}</ref> consiste à jeter un nombre de d10 dépendant de la puissance du personnage ; l'action réussit si l'on a au moins une paire (deux dés identiques). La réussite est d'autant meilleure que l'on a de dés ayant la même valeur et que cette valeur est élevée.
La réussite simple (avoir au moins une paire) est similaire au problème dit du [[w:paradoxe des l'anniversaires|paradoxes de l'anniversaires]]. La manière la plus simple consiste à considérer les risques d'échec c'est-à-dire de n'avoir aucune paire.
Avec 2d10, le premier dé à 10 possibilités ; pour qu'il n'y ait pas de paire, le second dé n'a que 9 possibilités. Donc sur 10 × 10 = 100 combinaisons possibles
: {1 ; 1}, {1 ; 2}, …, {4 ; 7}, … {0 ; 8}, {0 ; 9}, {0 ; 0},
il y a 10 × 9 = 90 combinaisons perdantes
: {1 ; 2}, {1 ; 3}, …, {4 ; 7}, … {0 ; 8}, {0 ; 9}
et logiquement 100 – 90 = 10 combinaisons gagnantes
: {1 ; 1}, {2 ; 2}, …, {9 ; 9}, {0 ; 0}.
Nous avons donc 90 % d'échec et 10 % de réussite. Avec 3d10, il y a 10 × 10 × 10 = {{formatnum:1000}} combinaisons possibles
: {1 ; 1 ; 1}; {1 ; 1 ; 2}, … {0 ; 0 ; 9}, {0 ; 0; 0}
et 10 × 9 × 8 = 720 combinaisons perdantes
: {1 ; 2 ; 3}, {1 ; 2 ; 4}, … {0 ; 9 ; 7}, {0 ; 9 ; 8}
donc 72 % d'échec et 28 % de réussite. Avec ''n'' dés, il y a 10<sup>''n''</sup> combinaisons possibles et 10 × 9 × … × (10 – ''n'' + 1) situations d'échec ; cette dernière valeur s'appelle un « arrangement » en mathématiques et se note A<sup>''n''</sup><sub>10</sub>. On voit qu'à partir de 11 dés, il y a nécessairement au moins une réussite (puisqu'on ne peut pas avoir 11 valeurs différentes avec des d10).
== Pioche de jetons ou de cartes ==
Lorsque l'on pioche des objets identiques au toucher en aveugle (pions, pierres, jetons) ou bien des cartes dont le dos est identique, les probabilités de piocher un jeton ou une carte sont tout simplement obtenues en comptant les objets identiques.
Par exemple, si un sac contient 20 jetons blancs et 10 jetons noirs, soit 30 jetons au total, alors :
* on a 20/30 ≃ 0,67 = 67 % de chances de tirer un jeton blanc ;
* on a 10/30 ≃ 0,33 = 33 % de chances de tirer un jeton noir.
Si l'on fait plusieurs tirages successifs sans remettre les jetons, par exemple tirer trois jetons d'un sac, les proportions changent à chaque tirage. Ce point est traité plus loin dans la section ''[[#Épreuves successives|Épreuves successives]]''.
Si l'on considère un jeu de 32 cartes classique, alors :
* on a une chance sur quatre de tirer chaque couleur (trèfle, carreau, cœur, pique) donc une probabilité de 1/4 = 0,25 = 25 % ;
* il y a quatre rois donc on a une probabilité de 4/32 ≃ 0,13 = 13 % de chances de tirer un roi ;
* les probabilités de tirer une figure (valet, dame, roi) sont de 12/32 ≃ 0,38 = 38 %.
== Épreuves successives ==
[[Fichier:Arbre de proba bernoulli 40pc 3 epreuves.svg |vignette |upright=3 |Arbre de probabilité d'un processus de Bernoulli avec trois épreuves, chaque épreuve ayant une probabilité ''p'' = 0,4 de réussir (40 %) et ''q'' = 0,6 d'échouer.]]
[[Fichier:Probas epreuve Bernoulli 40pc.svg |vignette |upright=1.5 |Probabilité de réussir l'épreuve pour la première fois au bout de ''n'' essais (haut), et probabilité de réussir au moins une épreuve en ''n'' essais (bas), pour un processus de bernoulli avec 40 % de réussite.]]
Le terme « épreuves successives » désigne le fait de recourir plusieurs fois au hasard dans un but unique. Il peut s'agir :
* de refaire un jet jusqu'à le réussir (ou échouer) ;
* d'un combat : on a fréquemment deux jets ou plus par tour de jeu ;
* d'un système dans lequel l'on jette plusieurs dés et l'on dénombre les réussites (et éventuellement les complications) ;
* d'enchaîner les jets pour progresser vers un objectif.
Le terme « jet » peut bien sûr être remplacé par n'importe quel mode de génération de hasard.
La manière la plus simple de représente cette situation est d'utiliser un arbre de probabilités.
Dans la situation la plus simple, chaque jet a deux issues : réussite ou échec. En probabilités, on parle de « processus de Bernoulli » ; l'arbre de probabilités est un arbre binaire (il n'y a que deux branches à chaque nœud).
Lorsque l'on est à une extrémité (une feuille de l'arbre), la probabilité d'obtenir cette situation est le produit des probabilités en parcourant l'arbre depuis la racine. Si par exemple on effectue trois jets successifs avec 40 % de réussite (et donc 60 % d'échec) :
* à chaque branche, on a une probabilité ''p'' = 0,4 de réussir et ''q'' = 0,6 d'échouer ;
* la probabilité d'échouer à tous les jets est donc de 0,6 × 0,6 × 0,6 = 0,216 = 21,6 % ;
* la probabilité d'avoir au moins une réussite est donc le complément : 1 – 0,216 = 0,784 ; 100 – 21,6 = 78,4 % ;
* la probabilité de n'avoir que des réussites est de 0,064 = 6,4 % ;
* la probabilité d'avoir au moins un échec est donc le complément : 1 – 0,064 = 0,936 ; 100 – 0,4,6 = 93,6 % ;
* trois situations ont une réussite et une seule ; chaque situation a une probabilité 0,4 × 0,6 × 0,6 = 0,144 = 14,4 % de chances de survenir, il y a donc au total 3 × 0,144 = 0,432 = 43,2 % d'avoir une réussite et une seule.
De manière générale, si la probabilité de réussite est ''p'', alors la probabilité qu'il réussisse au ''n''-ième essai (après ''n'' – 1 échecs) vaut :
: <math>\mathrm{P}(n) = (1 - p)^{n - 1} \times p</math>.
En terme de probabilités, c'est une loi binomiale avec ''k'' = ''n'' – 1.
La probabilité cumulée tend vers 1 (100 %), on peut donner le nombre d'essai médian
: N tel que ∑<sub>1</sub><sup>N</sup> P(''n'') ≥ 0,5 ;
on sait que dans la moitié des cas, il faut faire moins d'essais que N pour réussir une épreuve. Le nombre moyen d'essais pour que la réussite soit supérieure à 99 % (par exemple)
: N tel que ∑<sub>1</sub><sup>N</sup> P(''n'') ≥ 0,99.
Le nombre moyen d'essais pour qu'il réussisse vaut
: N<sub>moy</sub> = ∑ ''n'' × P(''n'').
La moyenne est ici intéressante en tant que « lissage » : c'est une « fréquence équivalente » c'est-à-dire que sur un très grand nombre de processus, on a à peu près autant de réussite que si l'on réussissait une fois tous les N<sub>moy</sub>. Par exemple, si on a 5 % de réussir une épreuve, alors il faut en moyenne 20 essais pour réussir, cela signifie que sur un grand nombre de processus, c'est comme si on réussissait un jet tous les 20 jets.
{| class="wikitable"
|+ Nombre d'essais pour réussir une action
|-
! scope="col" | Probabilité de réussite<br />sur un essai
! scope="col" | Nombre d'essais<br /> médian
! scope="col" | Nombre d'essais<br /> moyen
! scope="col" | Nombre d'essais sûr<br /> (99 % de réussite)
|-
! scope="row" | 5 %
| 14 || 20,0 || 90
|-
! scope="row" | 10 %
| 7 || 10,0 || 44
|-
! scope="row" | 20 %
| 4 || 5,0 || 21
|-
! scope="row" | 30 %
| 2 || 3,3 || 13
|-
! scope="row" | 40 %
| 2 || 2,5 || 10
|-
! scope="row" | 50 %
| 1 || 2,0 || 7
|-
! scope="row" | 60 %
| 1 || 1,7 || 6
|-
! scope="row" | 70 %
| 1 || 1,4 || 4
|-
! scope="row" | 80 %
| 1 || 1,3 || 3
|-
! scope="row" | 90 %
| 1 || 1,1 || 2
|}
{{...}}
== Les outils informatiques ==
Nous avons présenté précédemment des méthodes « manuelles » pour aborder les probabilités afin de vous aider à comprendre les principes. Mais la mise en œuvre de ces méthodes est fastidieuse lorsque le nombre de dés augmente : pour 3d6, on a déjà 6<sup>3</sup> = 6 × 6 × 6 = 216 possibilités : {1 ; 1 ; 1}, {1 ; 1; 2}, {1 ; 1; 3}, … {1 ; 2 ; 1}, … {6 ; 6 ; 5}, {6 ; 6 ; 6}. Le tableau commence à être fastidieux à construire ! Et si l'on enchaîne cinq jets consécutifs, ou que l'on jette cinq dés avec pour chaque dé une échec ou une réussite, la dernière ligne de l'arbre contient 32 éléments pour 52 éléments au total dans l'arbre, il faut une grande feuille et pas mal de temps…
Nous présentons donc ici des outils informatiques pour nous aider dans cette tâche.
=== Avec ''AnyDice'' ===
==== Jet d'un dé unique ====
Avec un navigateur Internet, ouvrir la page http://www.anydice.com/ puis :
* dans la grande zone de texte, taper : <code>output 1d4 named "dé à quatre faces"</code> ; cela signifie « sortir les résultats pour 1d4 et afficher le titre “dé à quatre faces” » ;
* cliquer sur le bouton <code>[Calculate]</code> (« calculer ») ;
* dans la zone d'outils <code>View</code>, jouez avec les boutons <code>[Table]</code> (table et histogramme horizontal) et <code>[Graph]</code> (graphique) ;
* dans la zone d'outils <code>Data</code>, jouez avec les boutons <code>[Normal]</code> (probabilités d'obtenir une valeur), <code>[At least]</code> (au moins) et <code>[At most]</code> (au plus).
Vous retrouvez les différentes représentations présentées ci-dessus.
Notez que ma commande <code>named "…"</code> est optionnelle.
==== Somme de deux dés identiques ====
Pour deux dés à quatre faces, la syntaxe est évidemment
<syntaxhighlight lang="text">
output 2d4 named "2d4"
</syntaxhighlight>
==== Somme de trois dés identiques ====
Pour trois dés à quatre faces, la commande est évidemment :
<syntaxhighlight lang="text">
output 3d4 named "3d4"
</syntaxhighlight>
==== Somme de dés identiques en général ====
Pour simuler un pile ou face, on peut indiquer un dé dont les faces valent « 0 » ou « 1 » :
<syntaxhighlight lang="text">
output 1d{0, 1} named "pile ou face"
output 1d{0..1} named "pile ou face"
</syntaxhighlight>
Un dF (dé Fudge ou FATE) est un dé dont deux faces sont marquées « – », deux sont vierges et deux sont marquées « + ». De la manière dont ils s'utilisent, on peut considérer que les faces valent respectivement « –1 », « 0 » et « +1 » ; comme les faces vont par paire, c'est comme s'il n'y avait que trois faces soit :
<syntaxhighlight lang="text">
output 1d{-1, 0, 1} named "dF"
output 1d{-1..1} named "dF"
</syntaxhighlight>
==== Soustraction de dés ====
On peut simplement écrire :
<syntaxhighlight lang="text">
output -2d4 named "–2d4"
output 1d6-1d6 named "1d6 – 1d6"
</syntaxhighlight>
==== Dé yin-yang ====
Le dé « yn-yang » est utilisé dans le jeu ''Qin''. On jette deux d10 (un noir et un blanc), le résultat est la valeur absolue de la différence ce qui dans ''AnyDice'' se note :
<syntaxhighlight lang="text">
output [absolute d{0..9}-d{0..9}] named "dé yin-yang"
</syntaxhighlight>
Nous utilisons donc ici une fonction de ''AnyDice'' appelée ''absolute''. Notez la syntaxe utilisée pour appeler une fonction : en mathématiques, on note ƒ(''x'') pour appliquer la fonction ƒ à la valeur ''x'' ; dans ''AnyDice'', cela se note <code>[f x]</code> ou, pour utiliser du pseudo-code :
[''NomDeLaFonction'' ''Valeur'']
avec ici :
* ''NomDeLaFonction'' : <code>absolute</code> ;
* ''Valeur'' : <code>d{0..9}-d{0..9}</code>.
==== Divers ====
'''Somme du dé le plus élevé et du dé le moins élevé'''
On jette plusieurs dés à dix faces et on additionne le plus élevé et le plus faible :
<syntaxhighlight lang="text">
loop N over {2..9} {
TOTO:Nd10
output [highest 1 of TOTO] + [lowest 1 of TOTO] named "[N]d10"
}
</syntaxhighlight>
Nous utilisons des variables : N qui est le nombre de dés jetés et TOTO qui est le résultat du jet de N dés. Les noms des variables sont nécessairement en lettres capitales.
La commande <code>loop N over {''n''<sub>1</sub>..''n''<sub>2</sub>} {''liste d'instructions''}</code> crée une boucle, la variable N prenant les valeurs entières de ''n''<sub>1</sub> à ''n''<sub>2</sub>, la ''liste d'instructions'' est exécutée pour chaque valeur successive de N.
L'affectation d'une valeur à une variable se fait avec la syntaxe <code>NOM:''valeur''</code>. Ici, la commande <code>TOTO:Nd10</code> indique que la variable TOTO contient le résultat du jet de N dés à dix faces.
La commande <code>highest ''m'' of NOM</code> prend les ''m'' plus grandes valeurs de la série NOM ; la commande <code>lowest ''m'' of NOM</code> prend les ''m'' plus petites valeurs de la série NOM. Donc le code <code>[highest 1 of TOTO] + [lowest 1 of TOTO]</code> fait la somme de la plus grande valeur et de la plus petite valeur de la série TOTO.
=== Avec un tableur ===
==== Jet d'un dé unique ====
[[Fichier:Liste 1 4 LibreOffice Calc.png |vignette |Créer une liste d'entiers de 1 à 4 en « étirant » un début de liste.]]
Avec un tableur, nous commençons par créer une la liste des événements, en colonne. Il est bien sûr possible de taper les valeurs successivement dans les cases mais cela devient laborieux lorsque le nombre de faces augmente. Pour se simplifier la tâche :
* dans la case en haut à gauche (colonne « A » ligne « 1 »), taper le nom de la liste : « événements » ;
* dans les cases situées en dessous (colonne « A », lignes « 2 » et « 3 »), taper les deux premières valeurs, respectivement « 1 » et « 2 » ;
* sélectionner les deux valeurs : cliquer sur la case « A2 » et faire glisser la souris en maintenant le bouton appuyé jusqu'à la case « A3 », puis relâcher le bouton ; les deux cases sont grisées ;
* positionner le pointeur de la souris sur le petit carré noir qui apparaît en bas à droite de la sélection ; le curseur se transforme en une petite croix ;
* cliquer et faire glisser la souris pour étendre la zone de saisie jusqu'à la case « A5 » ; une étiquette jaune indique la valeur « 4 » ;
* lâcher le bouton de la souris, la liste est créée.
{| class="wikitable" style="float:right; margin-left: 10px;"
|+ État du tableur
|-
|
! scope="col" | A
! scope="col" | B
|-
! scope="row" | 1
| '''événements''' ||
|-
! scope="row" | 2
| 1 ||
|-
! scope="row" | 3
| 2 ||
|-
! scope="row" | 4
| 3 ||
|-
! scope="row" | 5
| 4 ||
|-
! scope="row" | 6
| ||
|-
! scope="row" | 7
| '''résultats''' || '''nb d'occ.'''
|-
! scope="row" | 8
| 1 ||
|-
! scope="row" | 9
| 2 ||
|-
! scope="row" | 10
| 3 ||
|-
! scope="row" | 11
| 4 ||
|}
Pour plus de lisibilité, on peut mettre le titre « événements » en gras.
{{clr}}
[[Fichier:Selectionner fonction frequence LibreOffice Calc.png |vignette |upright=2 |Sélection de l'outil « Assistant fonctions ».]]
On crée ensuite en dessous une seconde liste identique mais intitulée « résultats » — a différence entre la liste « événements » et la liste « résultats » sera plus claire lorsque l'on passera à deux dés. La liste « résultats » indique les résultats qu'il est possible d'obtenir aux dés.
On crée ensuite une liste « nb d'occ. » à droite de la liste « résultats ». Une fois le titre créé, on sélectionne les cellules B8 à B11 et l'on tape :
<syntaxhighlight lang="text">
=frequence(
</syntaxhighlight>
puis on clique sur le bouton « Assistant fonction » <code>[''ƒx'']</code>.
{{clr}}
[[Fichier:Assistant fonctions frequence LibreOffice Calc.png |vignette |upright=2 |Boîte de dialogue « Assistant fonction » pour la fonction <code>FREQUENCE</code>.]]
Cela ouvre la boîte de dialogue « Assistant fonction ». Pour remplir les champs « données » et « classes », on peut simplement sélectionner les liste en cliquant sur la première cellule et en faisant bouger le pointeur de la souris jusqu'à la dernière cellule en maintenant le bouton enfoncé puis de relâcher le bouton. Sinon, la syntaxe est simplement « première cellule — deux-points — dernière cellule » soit respectivement <code>A2:A5</code> et <code>A8:A11</code>.
Il faut ensuite s'assurer que le case « Matrice » en bas à gauche est cochée. Puis, on clique sur le bouton <code>[OK]</code> ; la liste se remplit du nombre d'occurrences, soit ici des « 1 » partout.
{{clr}}
{| class="wikitable" style="float:right; margin-left: 10px;"
|+ État du tableur
|-
|
! scope="col" | A
! scope="col" | B
! scope="col" | C
|-
! scope="row" | 1
| '''événements''' || ||
|-
! scope="row" | 2
| 1 || ||
|-
! scope="row" | 3
| 2 || ||
|-
! scope="row" | 4
| 3 || ||
|-
! scope="row" | 5
| 4 || ||
|-
! scope="row" | 6
| || ||
|-
! scope="row" | 7
| '''résultats''' || '''nb d'occ.''' || '''cumul'''
|-
! scope="row" | 8
| 1 || 1 || 1
|-
! scope="row" | 9
| 2 || 1 || 2
|-
! scope="row" | 10
| 3 || 1 || 3
|-
! scope="row" | 11
| 4 || 1 || 4
|}
Nous créons ensuite une liste nommée « cumul » dans laquelle nous allons indiquer les nombres d'occurrence cumulés, c'est-à-dire les probabilités d'avoir une valeur ou moins. Pour cela, il suffit d'écrire dans la première cellule, <code>C8</code> :
<syntaxhighlight lang="text">
=frequence(A$2:A$5 ; A8)
</syntaxhighlight>
et d'appuyer sur la touche <code>[Entrée↵]</code> du clavier. Cette expression signifie : « calcule le nombre de valeurs inférieures à la valeur de la cellule <code>A8</code> qui apparaissent dans la liste <code>A$2:A$5</code> ».
Les signes dollar « <code>$</code> » dans l'expression « <code>A$2:A$5</code> » indiquent que les valeurs qui suivent (les numéros de ligne « 2 » et « 5 ») ne doivent pas varier.
Puis, on clique dans la cellule A8, on place le pointeur de la souris sur le petit carré noir en bas à droite — le pointeur se transforme en croix « + » — et on double-clique. Cela étire la liste.
{{clr}}
[[Fichier:Selectionner diagramme LibreOffice Calc.png |vignette |upright=2 |Sélectionner l'outil « Diagramme ».]]
Pour tracer le premier histogramme, on sélectionne les deux listes « résultats » et « nb d'occ. », c'est-à-dire la zone <code>A7:B11</code>, puis on clique sur le bouton <code>[Diagramme]</code>. Cela ouvre la boîte de dialogue « Assistant de diagramme ».
{{clr}}
[[Fichier:Histogramme occurrences d4 LibreOffice Calc.png |vignette |upright=2 |Histogramme des occurrences.]]
Pour la première étape « 1. Type de diagramme », on s'assure que l'on a bien sélectionné « Colonne » pour un histogramme vertical, « Barre » pour un histogramme horizontal.
À la deuxième étape « 2. Plage de données », il faut cocher les deux cases « Première lige comme étiquette » et « Première colonne comme étiquette ». On peut ensuite cliquer sur le bouton <code>[Terminer]</code>.
{{clr}}
[[Fichier:Histogramme occurrences cumulees d4 LibreOffice Calc.png |vignette |upright=2 |Histogramme des occurrences.]]
Pour créer l'histogramme des occurrences cumulées (« faire un valeur ou moins »), on fait de même mais en sélectionnant deux colonnes disjointes. Pour se faire, il faut sélectionner la seconde colonne en maintenant la touche <code>[Ctrl]</code> (« contrôle ») du clavier enfoncée. La plage de données se note alors : <code>A7;A11 ; C7:C11</code>.
{{clr}}
[[Fichier:Fonction quartile LibreOffice Calc.png |vignette |upright=2 |Utilisation de la fonction <code>QUARTILE</code>.]]
{| class="wikitable" style="float:right; margin-left: 10px;"
|+ État du tableur
|-
|
! scope="col" | A
! scope="col" | B
! scope="col" | C
|-
! scope="row" | 1
| '''événements''' || ||
|-
! scope="row" | 2
| 1 || ||
|-
! scope="row" | 3
| 2 || ||
|-
! scope="row" | 4
| 3 || ||
|-
! scope="row" | 5
| 4 || ||
|-
! scope="row" | 6
| || ||
|-
! scope="row" | 7
| '''résultats''' || '''nb d'occ.''' || '''cumul'''
|-
! scope="row" | 8
| 1 || 1 || 1
|-
! scope="row" | 9
| 2 || 1 || 2
|-
! scope="row" | 10
| 3 || 1 || 3
|-
! scope="row" | 11
| 4 || 1 || 4
|-
! scope="row" | 12
| || ||
|-
! scope="row" | 13
| '''quartiles''' || ||
|-
! scope="row" | 14
| 1,75 || ||
|-
! scope="row" | 15
| 2,5 || ||
|-
! scope="row" | 16
| 3,25 || ||
|}
Pour avoir les quartiles, il suffit de taper :
<syntaxhighlight lang="text">
=quartile(A2:A4 ; 1)
=quartile(A2:A4 ; 2)
=quartile(A2:A4 ; 3)
</syntaxhighlight>
On retrouve bien la médiane (deuxième quartile) à 2,5.
Comme mentionné plus haut, les valeurs des autres quartiles sont légèrement différentes de celle que l'on trouve « à la main » (1,75 pour 1,5 et 3,25 pour 3,5) car la méthode utilisée par ''LibreOffice Calc'' est différente mais cela a peu d'importance.
{{clr}}
==== Tableur avec les données d{{'}}''AnyDice'' ====
[[Fichier:Import de texte separateur virgule LibreOffice Calc.png |vignette |upright=2 |Import de texte dans ''LibreOffice Calc'' avec le séparateur virgule (CSV).]]
[[Fichier:Probabilites 1d4 AnyDice LibreOffice Calc.png |vignette |upright=2 |Probabilités pour un dé à quatre faces. Données exportées d{{'}}''AnyDice'' vers ''LibreOffice Calc''.]]
Vous pouvez utiliser ''AnyDice'' pour générer les données et ensuite utiliser le tableur pour d'autres calculs et tracés. Pour cela :
* dans ''AnyDice'', mettez vous dans le mode « Data : Normal » et cliquez sur le bouton « View : Export ».
La fenêtre de résultats contient alors le texte :
<syntaxhighlight lang="text">
"dé à quatre faces",2.5,1.118033988749895,1,4
#,%
1,25
2,25
3,25
4,25
</syntaxhighlight>
Vous pouvez copier ce texte et le coller dans votre tableur :
* lorsque vous collez le texte, cela ouvre la boîte de dialogue « Import de texte » ;
* dans la zone « Option de séparateur », assurez-vous que la case « Virgule » est cochée ;
* cliquez sur le bouton <code>[OK]</code> ;
* assurez-vous que tous les nombres sont alignés à droite des cellules ; si ce n'est pas le cas, il est possible que le séparateur décimal pour votre tableur soit la virgule (alors que c'est le point pour ''AnyDice'') ; dans ce cas-là :
** dans Libre-Office Calc, cliquez sur le menu « Édition » puis choisissez l'option « Rechercher & remplacer… »,
** dans la boîte de dialogue « Rechercher & remplacer » qui s'ouvre, tapez « <code>.</code> » (point de ponctuation, sans les guillemets) dans le champ « Rechercher », tapez « <code>,</code> » (virgule) dans le champ « Remplacer par » et cliquez sur le bouton <code>[tout remplacer]</code>.
Refaites l'opération avec le mode « Data : At most » d{{'}}''AnyDice''.
{{note|Nous avons ici directement les probabilités en pourcents.}}
{{clr}}
==== Somme de deux dés identiques ====
[[File:Somme 2d4 LibreOffice Calc.png |vignette |upright=2 |Somme des deux dés.]]
On commence par créer le tableau des événements. Pour cela :
* écrire les noms des colonnes en gras : '''dé 1''', '''dé 2''' et '''2d4''' ;
* dans les cellules <code>A2</code> et <code>A3</code>, écrire <code>1</code> ;
* sélectionner ces deux cellules <code>A2:A3</code>, cliquer sur le petit carré noir en bas à droite de la sélection et amenez-le deux cellules plus bas ; vous avez maintenant quatre fois le nombre « 1 » ;
* recommencez en dessous avec les nombres « 2 », « 3 » et « 4 » ;
* dans la colonne B, créez une liste de « 1 » à « 4 » ; copiez cette liste et collez-là trois fois ;
* dans la cellule <code>C2</code>, tapez : <code>=somme(</code> puis sélectionnez les cellules <code>A2</code> et <code>B2</code>, puis appuyez sur la touche <code>Entrée↵</code> du clavier ;
* re-sélectionnez la cellule <code>C2</code> et double-cliquez sur le petit carré noir en bas à droite de la sélection ; cela « propage » la formule aux cellules situées en dessous.
{{note|1=On peut aussi utiliser la formule <code>=A2+B2</code> à la place de la fonction <code>SOMME</code>.}}
{{clr}}
[[Fichier:Selectionner fonction frequence LibreOffice Calc 2e exemple.png |vignette |upright=2 |Sélectionner l'outil « Assistant fonction » pour la fonction <code>FREQUENCE</code>.]]
[[Fichier:Probabilites 2d4 LibreOffice Calc.png |vignette |upright=2 |Résultat final.]]
On crée ensuite la table des occurrences :
* dans la ligne 19, écrire les noms des colonnes en gras : '''résultats''', '''nb d'occ.''', '''cumul''' ;
* dans les cellules <code>A20</code> à <code>A27</code>, créer une liste de 2 à 8 : écrire « 2 » dans la cellule <code>A20</code>, « 3 » dans la cellule <code>A21</code>, sélectionnez ces deux cellules et étirez la sélection avec le petit carré noir ;
* sélectionnez les cellules <code>A20:A26</code>, tapez <code>=frequence(</code >et cliquez sur le bouton « Assistant fonctions »
* dans la boîte de dialogue « Assistant fonction » qui s'ouvre :
** dans le camp « données », sélectionnez les cellules <code>C2:C17</code>,
** dans le camp « classes », sélectionnez les cellules <code>A20:A26</code>,
** cochez la case « Matrice »,
** cliquez sur le bouton <code>[OK]</code> ;
* dans la cellule <code>C20</code>, tapez <code>=frequence(C$2:C$17 ; C20)</code> ;
* re-sélectionnez la cellule <code>C20</code> et double-cliquez sur le petit carré noir en bas à droite de la sélection.
Vous avez toutes les données, il vous reste à tracer les histogrammes et à déterminer les quartiles.
----
''Sections de l'ancien article, à intégrer.''
== Un peu de probabilité… ==
=== Choix de certains dés parmi plusieurs ''(roll & keep)'' ===
[[Fichier:3d6 et 3 meillleurs de 4d6.svg|thumb|Probabilité d'avoir un score en jetant trois dés, et en jetant quatre dés et en sommant les trois meilleurs]]
[[Fichier:Boite a moustaches 3d6 et 3 meilleurs 4d6.svg|vignette|Boîtes à moustaches correspondantes.]]
Dans certains systèmes, lors de la création des personnages, on jette quatre dés à six faces et on retient les trois meilleurs scores. De fait, les résultats obtenus sont meilleurs que si l'on jetait 3d6. Les courbes de probabilité sont représentées ci-contre.
{| class="wikitable"
|+ Comparaison des méthodes
! Jet !! Valeur la<br />plus probable !! Moyenne
|-
! scope="row" | 3d6
| 10–11 || 10,5
|-
! scope="row" | 3 meilleurs parmi 4d6
| 13 || 12,24
|}
{| class="wikitable"
|+ Quartiles des méthodes
|-
! scope="col" | Dés
! scope="col" | Minimum
! scope="col" | Premier<br /> quartile
! scope="col" | Médiane
! scope="col" | Troisième<br /> quartile
! scope="col" | Maximum
! scope="col" | Écart<br /> interquartile
|-
! scope="row" | 3d6
| 3 || 8 || 10,5 || 13 || 18 || 5
|-
! scope="row" | 3 meilleurs parmi 4d6
| 3 || 10 || 12 || 14 || 18 || 4
|}
[[Fichier:1d6 choisi parmi n fonction de masse.svg|vignette|Probabilité d'avoir une valeur en choisissant le meilleur d6 parmi ''n'']]
Dans certains jeux (''Shadowrun'', jeux du ''Monde des ténèbres'', comme ''Vampire : la Mascarade''), la capacité à réussir l'action est également exprimée par un nombre de dés, mais on ne fait pas la somme des dés : on fixe un seuil dans la gamme de valeurs d'un dé (par exemple entre 1 et 6 pour des d6), et l'action est réussie si au moins un dé atteint ou dépasse cette valeur. Bien évidemment, plus le joueur jette de dés, plus il a de chance de réussir au moins un jet.
{| class="wikitable"
|+ Quartiles pour le meilleur dé par mi ''n''
|-
! scope="col" | Nombre<br /> de dés
! scope="col" | Minimum
! scope="col" | Premier<br /> quartile
! scope="col" | Médiane
! scope="col" | Troisième<br /> quartile
! scope="col" | Maximum
! scope="col" | Écart<br /> interquartile
|-
! scope="row" | 1
| 1 || 2 || 3,5 || 5 || 6 || 3
|-
! scope="row" | 2
| 1 || 3,5 || 5 || 6 || 6 || 2,5
|-
! scope="row" | 3
| 1 || 4 || 5 || 6 || 6 || 2
|-
! scope="row" | 4
| 1 || 5 || 6 || 6 || 6 || 1
|-
! scope="row" | 5
| 1 || 5 || 6 || 6 || 6 || 1
|}
[[Image:1d choisi parmi 2 differents.svg|vignette|250px|Probabilité d'avoir une valeur en choisissant la plus haute parmi avec deux dés différents.]]
[[Fichier:1d choisi parmi 3 differents fonction de masse.svg|vignette|''Idem'' avec trois dés différents.]]
Dans d'autres jeux, comme par exemple ''Usagi Yojimbo'' 2{{e}} édition, on jette deux ou plusieurs dés pouvant être différents (par exemple 1d4 et 1d6), et on ne retient que la plus haute valeur. Si les deux dés sont identiques (même nombre de faces ''n''), il y a ''n''² combinaisons possibles :
* la valeur « 1 » n'a qu'une seule chance de sortir (1 et 1), soit une probabilité 1/''n''<sup> 2</sup> ;
* la valeur la plus haute ''n'' a 2''n'' – 1 possibilités de sortir (un des dés est indique ''n'', l'autre peut prendre toutes les valeurs possibles), la probabilité est donc (2''n'' – 1)/''n''<sup> 2</sup> ;
* entre ces deux valeurs, la probabilité croît linéairement (pour une valeur ''i'', un des dés doit avoir la valeur ''i'' et l'autre une valeur inférieure ou égale à ''i'').
Si les dés n'ont pas le même nombre de faces, ''n''<sub>1</sub> et ''n''<sub>2</sub> (''n''<sub>1</sub> < ''n''<sub>2</sub>) :
* de 1 à ''n''<sub>1</sub>, on a comme ci-dessus un comportement linéairement croissant ;
* entre ''n''<sub>1</sub>+1 et ''n''<sub>2</sub>, seul compte le score du dé ayant le plus de faces, on a donc une probabilité uniforme valant 1/''n''<sub>2</sub>.
[[Fichier:2d6 choisis parmi n fonction de masse.svg|thumb|200px|Probabilités en sommant deux dés choisis (les plus mauvais ou les meilleurs) parmi ''n''<br />Probabilité d'avoir un score (haut) et probabilité d'atteindre ou de dépasser un score (bas)]]
Dans le jeu de rôle ''P'tites sorcières'', on somme deux dés à six faces, mais selon la puissance du personnage, il s'agit
* des deux pires dés parmi trois ou quatre (personnage faible) ;
* simplement de deux dés (personnage moyen) ;
* des deux meilleurs dés parmi trois ou quatre (personnage fort).
{| class="wikitable"
|+ Statistiques du système de ''P'tites sorcières''
! Jet !! Valeur la<br /> plus probable !! Moyenne
|-
! scope="row" | 2 pires parmi 4
| 4 || 4,65
|-
! scope="row" | 2 pires parmi 3
| 5 || 5,54
|-
! scope="row" | 2d6
| 7 || 7
|-
! scope="row" | 2 meilleurs parmi 3
| 9 || 8,46
|-
! scope="row" | 2 meilleurs parmi 4
| 10 || 9,34
|}
{| class="wikitable"
|+ Quartiles pour ''P'tites sorcières''
|-
! scope="col" | Dés
! scope="col" | Minimum
! scope="col" | Premier<br /> quartile
! scope="col" | Médiane
! scope="col" | Troisième<br /> quartile
! scope="col" | Maximum
! scope="col" | Écart<br /> interquartile
|-
! scope="row" | 2 pires parmi 4d6
| 2 || 3 || 4 || 6 || 12 || 3
|-
! scope="row" | 2 pires parmi 3d6
| 2 || 4 || 5 || 7 || 12 || 3
|-
! scope="row" | 2d6
| 2 || 5 || 7 || 9 || 12 || 4
|-
! scope="row" | 2 meilleurs parmi 3d6
| 2 || 7 || 9 || 10 || 12 || 3
|-
! scope="row" | 2 meilleurs parmi 4d6
| 2 || 8 || 10 || 11 || 12 || 3
|}
On voit que les médianes sont différentes des moyennes, ce qui est caractéristique des distributions asymétriques.
Voir également ci-dessous ''[[#Dépasser un seuil avec au moins un dé parmi n|Dépasser un seuil avec au moins un dé parmi ''n'']]''.
== Probabilité de réussite d'un test ==
=== Probabilité de réussite et équilibre du jeu ===
Dans une partie de jeu de rôle, la résolution d'une situation hasardeuse est un « jeu dans le jeu ». La manière dont on résout la situation, le test — combien de dés et quels dés on jette par exemple — participe à la construction de l'ambiance, en particulier si le joueur doit faire des choix (notion de stratégie) ; par exemple, le joueur dispose d'une réserve de dés de bonus, doit-il en utiliser un dans la situation présente ou doit-il le garder pour plus tard ?
Mais d'un point de vue du résultat pur, seule importe la probabilité de réussite. Ce point est important pour ''l'équilibre'' du jeu : si les jets sont trop difficiles, les joueurs risquent l'écœurement, s'ils sont trop faciles, il n'y aura plus d'enjeu. En particulier, si une situation de vie ou de mort se joue sur un jet, alors une probabilité de 50 % signifie que le personnage a une chance sur deux de mourir, ou bien que statistiquement la moitié de l'équipe va y passer.
Si par contre le personnage a droit à plusieurs essais, alors la probabilité de réussite détermine le nombre d'essais que devra tenter le personnage pour réussir. Par exemple, si un personnage à 50 % de chances de réussite, alors :
* il réussit du premier coup dans 50 % des cas ;
* si le premier essai est un échec, il a 50 % de chances de réussite au second essai, soit une probabilité de 0,5 × 0,5 = 0,25 = 25 % (donc 75 % de réussite en 2 essais ou moins ;
* …
De manière générale, si la probabilité de réussite est ''p'', alors la probabilité qu'il réussisse au ''n''-ième essai (après ''n'' – 1 échecs) vaut :
: <math>\mathrm{P}(n) = (1 - p)^{n - 1} \times p</math>.
C'est une loi binomiale avec ''k'' = ''n'' – 1.
La probabilité cumulée tend vers 1 (100 %), on peut donner le nombre moyen d'essais pour qu'il réussisse,
: {{surligner|''n''}} = ∑ ''n'' × P(''n'')
ainsi que le nombre d'essai pour que la réussite soit supérieure à 99 % (par exemple)
: N tel que ∑<sub>1</sub><sup>N</sup> P(''n'') ≥ 0,99
{| class="wikitable"
|+ Nombre d'essais pour réussir une action
|-
! scope="col" | Probabilité de réussite<br />sur un essai
! scope="col" | Nombre d'essais<br /> moyen
! scope="col" | Nombre d'essais<br /> à 99 % de réussite
|-
! scope="row" | 5 %
| 20,0 || 90
|-
! scope="row" | 10 %
| 10,0 || 44
|-
! scope="row" | 20 %
| 5,0 || 21
|-
! scope="row" | 30 %
| 3,3 || 13
|-
! scope="row" | 40 %
| 2,5 || 10
|-
! scope="row" | 50 %
| 2,0 || 7
|-
! scope="row" | 60 %
| 1,7 || 6
|-
! scope="row" | 70 %
| 1,4 || 4
|-
! scope="row" | 80 %
| 1,3 || 3
|-
! scope="row" | 90 %
| 1,1 || 2
|}
Cela permet d'interpréter les notions de « personnage moyen », « caractéristique moyenne », « difficulté moyenne ». On prend par exemple une référence haute : si l'on considère qu'un expert à 90 % de chances de réussir du premier coup une opération de difficulté moyenne, alors il mettra en moyenne 1,1 essai pour réussir, et réussira « à coup sûr » (à 99 % de chances) en deux coups.
Un « personnage moyen » (ayant un score de 50 %) mettra deux fois plus de temps en moyenne pour réussir ; et il réussira à coup sûr en 7 coups. Par exemple, dans une classe de mathématique, les « élèves moyens » mettent en moyenne deux fois plus de temps que leur professeur pour réussir un exercice « moyen », et le dernier met sept fois plus de temps. Et si un mécanicien (ayant un score de 90 % en mécanique) met une heure à effectuer une réparation de difficulté moyenne, par exemple changer les plaquettes de frein, alors amateur éclairé (ayant 50 %) mettra deux heures en moyenne, sept heures au pire des cas.
=== Expression de la probabilité ===
Le cas le plus simple est celui des systèmes donnant directement le pourcentage de réussite (''Basic System'', ''Megaversal system'', ''Rêve de dragon'', ''Mega 2''…). Mais dans tous les cas, on peut déterminer la probabilité et l'exprimer en pourcentage.
[[Fichier:Probabilite cumulee d20 2d10 3d6.svg|vignette|Probabilité de faire une valeur ou moins (≤) avec 1d20, 2d10 et 3d6. Les fonctions de densité ont une forme en S caractéristique.]]
Dans un certain nombre de jeux, un jet est réussi si la valeur des dés est inférieure à la valeur de la caractéristique ou de la compétence. On a donc des probabilités cumulées : si la caractéristique vaut 5, alors la probabilité de réussite est la somme des probabilités d'avoir1, 2, 3, 4 et 5. La courbe, également appelée fonction de densité F, a une forme de S caractéristique (sigmoïde). Dans le cas des ''Défis fantastiques, le jeu de rôle'' ou de ''SimulacreS'', le jet se fait avec deux dés à six faces, on a donc le cumul suivant<ref>Dans le cas des ''Défis fantastiques'', le test est réussi si le jet est inférieur ou égal (≤) à la valeur, ta,dis que dans ''SimulacreS'', le jet doit être strictement inférieur (<). La table dconsidère une inégalité large (≤), il suffit de décaler d'une ligne les pourcentages dans le cas d'une inégalité stricte (<).</ref>.
{| class="wikitable"
|+ Cumul des probabilités de 2d6
|-
! scope = "col" | Valeur<br />(caractéristique)
! scope = "col" | Probabilité
! scope = "col" | Probabilité cumulée<br />(Probabilité de réussite)
|-
! scope = "row" | 2
| 2,8 % || 2,8 %
|-
! scope = "row" | 3
| 5,6 % || 2,8 + 5,6 = 8,3 %
|-
! scope = "row" | 4
| 8,3 % || 8,3 + 8,3 = 16,7 %
|-
| … || … || …
|-
! scope = "row" | 12
| 2,8 % || 100 %
|}
Cette table donne donc les pourcentages de réussite du test
Reperenons l'exemple de la comparaison sntre 1d20, 2d10 et 3d6. Si l'on considère un seuil de 5, on réussit si l'on fait 1, 2, 3, 4 ou 5 ; il faut donc sommer, cumuler les probabilités. Avec 1d20, on a 5 × 5 % = 25 % de chances de réussite ; avec 2d10, on a 1 + 2 + 3 + 4 = 10 % de chance de réussite. Ceci est synthétisé sur la figure ci-contre.
Si l'on considère un jet sous une ''capacité'' (ou au-dessus d'une ''capacité''), on voit qu'en utilisant 2d10 au lieu de 1d20, pour un référentiel de niveaux de difficulté donné, on défavorise les personnages faibles (un personnage ayant une ''capacité'' en dessous de 11 a moins de chances de réussir l'action avec 2d10 qu'avec 1d20) ; par contre, cela favorise les personnages forts ; ceci s'accentue encore plus en utilisant 3d6. Il est cependant possible d'adapter le référentiel des niveaux de difficulté de manière à retrouver les mêmes probabilités quel que soit le système choisi.
Dans certains jeux, le résultat des dés doit contraire dépasser une valeur seuil, qui est d'autant plus élevée que l'action est difficile. La probabilité de réussite est donc le complémentaire de la fonction de densité, 1 – F (ou 100 – F en %), on a toujours une courbe en S, mais cette fois-ci décroissante.
Dans le cas du ''d20 system'', les capacités du personnage sont évaluées par un malus/bonus déterminé essentiellement par une caractéristique et le degré de maitrise d'une compétence. La difficulté intrinsèque de l'action est donnée par un degré de difficulté (DD). La table suivante traduit l'ensemble en termes de pourcentage de réussite.
{| class="wikitable"
|+ Probabilités de réussite dans le ''d20 system''
|-
! scope = "col" rowspan="2" | Malus/bonus
! scope = "col" colspan="5" | Degré de difficulté
|-
! scope = "col" | 0
! scope = "col" | 10
! scope = "col" | 20
! scope = "col" | 30
! scope = "col" | 40
|-
! scope="row" | –5
| 80 % || 30 % || 0 % || 0 % || 0 %
|-
! scope="row" | –3
| 90 % || 40 % || 0 % || 0 % || 0 %
|-
! scope="row" | 0
| 100 % || 50 % || 5 % || 0 % || 0 %
|-
! scope="row" | +3
| 100 % || 70 % || 20 % || 0 % || 0 %
|-
! scope="row" | +5
| 100 % || 80 % || 30 % || 0 % || 0 %
|-
! scope="row" | +10
| 100 % || 100 % || 55 % || 5 % || 0 %
|-
! scope="row" | +20
| 100 % || 100 % || 100 % || 55 % || 5 %
|}
[[Fichier:2d6 choisis parmi n probabilites cumulees.svg|vignette|Probabilité de réussite d'un test dans ''Les Contes ensorcelés/Les P'tites Sorcières'']]
Dans le cas des ''Contes ensorcelés/P'tites sorcières'', on a la table suivante ; les valeurs ont été arrondis à l'unité, sauf pour les valeurs inférieures à 1 % ou supérieures à 99 %.
{| class="wikitable"
|+ Probabilités de réussite dans les ''P'tites sorcières''
|-
! scope = "col" rowspan="2" | Possibilité
! scope = "col" colspan="5" | Difficulté
|-
! scope = "col" | immanquable<br />3
! scope = "col" | facile<br /> 5
! scope = "col" | moyenne<br /> 7
! scope = "col" | difficile<br /> 9
! scope = "col" | exceptionnelle<br />11
|-
! scope="row" | mauvaise
| 68 % || 31 % || 9 % || 2 % || 0,1 %
|-
! scope="row" | faible
| 80 % || 48 % || 19 % || 5 % || 0,5 %
|-
! scope="row" | moyenne
| 92 % || 72 % || 42 % || 17 % || 3 %
|-
! scope="row" | bonne
| 98 % || 89 % || 68 % || 36 % || 7 %
|-
! scope="row" | excellente
| 99,6 % || 96 % || 83 % || 52 % || 13 %
|}
'''Jet sans limite'''
[[Image:Jet sans limite depasser.png|200px|thumb|Probabilité de dépasser un seuil avec un jet sans limite d'un d20]]
Si l'on considère la probabilité de dépasser un seuil (il s'agit de l'intégrale de la courbe précédente), on constate donc que :
* sur une tranche de dix-neuf valeurs, la pente est constante ;
* toutes les vingt valeurs, on a un plateau horizontal.
La moyenne du jet sans limite est de 10,4 contre 10,5 pour un jet de d20 normal.
Avec un dé à cent faces, en rejouant les score 1–5 et 96–00, on obtient un profil similaire. Les valeurs interdites sont : …, –95, 5, 96, 196, …
Les valeurs entre 6 et 95 ont 1 % de chances de sortir. Pour avoir une valeur entre 101 et 191, il faut obtenir un 1–5 au premier jet ; il existe 5 combinaisons possible par valeur (par exemple pour 110 : 96 + 14, 97 + 13, 98 + 12, 99 + 11, 00 + 10), soit 0,05 % de chances d'obtenir ce score (chaque combinaison ayant 0,01{{2}} soit 0,01 % de chances de sortir).
Entre 96 et 101, les probabilité croissent de manière linéaire de 0 à 0,05 % de chances :
* on ne peut jamais obtenir 96 (puisque le 96 est rejoué) ;
* le 97 peut s'obtenir avec une seule combinaison : 96 + 01, soit 0,01 % de chances ;
* le 98 peut s'obtenir de deux manières : 96 + 02 et 97 + 01, soit 0,02 % de chances ;
* …
De la même manière, les probabilités entre 191 et 196, les chances passent linéairement de 0,05 % à 0.
Pour les échecs critiques, le calcul est similaire :
* des scores entre 5 et 0, on passe linéairement de 0 à 0,05 % de chances ;
* entre 0 et –90, les chances sont de 0,05 % ;
* entre –90 et –95, les chances passent linéairement de 0,05 % à 0.
'''Nombre de dés variable'''
[[Image:probabilite nd6.png|300px|thumb|probabilités comparées d'avoir un résultat avec ''n''d6]]
Dans certains jeux (''Star Wars 1{{e}} éd.'', ''D6 System'', ''Le Livre des cinq anneaux''), la capacité du personnage à résoudre une action est donnée par un nombre de dés, le seuil à franchir étant un nombre fixe représentant la difficulté de l'action ; par exemple un personnage peu doué aura 1d6 et un personnage très doué aura 5d6, et un seuil de difficulté moyen est 9. Ou alors (''RuneQuest'', ''Avant Charlemagne'', ''Empire galactique'', ''Trauma''), la difficulté est représentée par un nombre de dés, et la capacité d'un personnage est un nombre fixe ; par exemple, un personnage moyen a une capacité de 9, une action simple se joue à 1d6 et une action complexe à 5d6. On peut faire les mêmes calculs que précédemment, les résultats sont présentés sur la figure ci-contre.
Comme précédemment, nous remarquons que plus on somme de dés, plus on se rapproche d'une courbe en cloche (théorème central limite).
Nous avons représenté sur la figure du bas la probabilité de faire ''moins'' qu'une valeur :
* si le nombre de dés représente la difficulté et qu'il faut faire moins qu'un seuil, alors une courbe donnée représente les chances de réussite pour une difficulté donnée (nombre de dés) en fonction du score de la capacité (seuil) ;
* si le nombre de dés représente la capacité et qu'il faut faire au-delà d'un seuil, alors une courbe correspond aux probabilités d'échec pour une capacité fixée (nombre de dés) en fonction de la difficulté (seuil).
[[Image:Probabilite depasser score nd6.png|300px|thumb|Probabilité de dépasser un score donné avec ''n''d6]]
Nous représentons ci-dessous les résultats d'une manière différente : on regarde l'évolution de la probabilité de dépasser un seuil donné en fonction du nombre de dés ; le seuil est le nombre indiqué à côté de la courbe. Chaque courbe représente les chances de réussite d'une action de difficulté donnée (le seuil est fixé) en fonction de la puissance du personnage (nombre de dés).
Par exemple, le score « 1 » ne peut s'obtenir qu'avec 1d6, et l'on fait obligatoirement 1 ou plus ; « 1 » est donc représenté par un point unique (1d6, 100 %) de couleur bleu marine.
La probabilité de faire 6 ou plus passe de 16,67 % avec 1d6 à 99,99 % avec 5d6 (courbe marron).
Un seuil de 18 ne peut pas être atteint avec 1 ou 2d6 ; les probabilités de réussite passent de 0,46 % avec 3d6 à 50 % avec 5d6 (courbe bleu ciel).
'''Un dé parmi ''n'' '''
[[Image:Probabilite 1d6 parmi n.png|250px|thumb|probabilité de dépasser un seuil donné avec 1d6 parmi ''n'']]
Dans certains jeux (''Shadowrun'', jeux du ''Monde des ténèbres'', comme ''Vampire : la Mascarade''), la capacité à réussir l'action est également exprimée par un nombre de dés, mais on ne fait pas la somme des dés : on fixe un seuil dans la gamme de valeurs d'un dé (par exemple entre 1 et 6 pour des d6), et l'action est réussie si au moins un dé atteint ou dépasse cette valeur. Bien évidemment, plus le joueur jette de dés, plus il a de chance de réussir au moins un jet.
Dans certains cas, le nombre de dés dépassant le seuil donne la qualité de la réussite.
On voit qu'avec ce système, les personnages puissants (ayant un grand nombre de dés) sont surtout favorisés pour les hauts seuils : ils n'ont pas tellement plus de chance de réussir les actions faciles que les personnages faibles, mais réussissent bien mieux les actions difficiles.
[[Image:1d choisi parmi 2 differents.svg|thumb|250px|Probabilité d'avoir une valeur en choisissant la plus haute parmi avec deux dés différents]]
Dans certains jeux (par exemple ''Usagi Yojimbo''), on jette deux ou plusieurs dés pouvant être différents (par exemple 1d4 et 1d6), et on ne retient que la plus haute valeur. Si les deux dés sont identiques (même nombre de faces ''n''), il y a ''n''² combinaisons possibles :
* la valeur « 1 » n'a qu'une seule chance de sortir (1 et 1), soit une probabilité 1/''n''<sup> 2</sup> ;
* la valeur la plus haute ''n'' a 2''n'' – 1 possibilités de sortir (un des dés est indique ''n'', l'autre peut prendre toutes les valeurs possibles), la probabilité est donc (2''n'' – 1)/''n''<sup> 2</sup> ;
* entre ces deux valeurs, la probabilité croît linéairement (pour une valeur ''i'', un des dés doit avoir la valeur ''i'' et l'autre une valeur inférieure ou égale à ''i'').
Si les dés n'ont pas le même nombre de faces, ''n''<sub>1</sub> et ''n''<sub>2</sub> (''n''<sub>1</sub> < ''n''<sub>2</sub>) :
* de 1 à ''n''<sub>1</sub>, on a comme ci-dessus un comportement linéairement croissant ;
* entre ''n''<sub>1</sub> + 1 et ''n''<sub>2</sub>, seul compte le score du dé ayant le plus de faces, on a donc une probabilité uniforme valant 1/''n''<sub>2</sub>.
[[Image:1d choisi parmi 2 differents depasser score.svg|thumb|250px|Probabilité de dépasser un score en choisissant le meilleur dé parmi deux.]]
La probabilité d'atteindre ou de dépasser un score est la somme des probabilités pour les valeurs supérieures ou égalees à ce score. On a donc logiquement (intégrales classiques en mathématiques) :
* une branche de parabole sur la partie où la probabilité croît linéairement :
* un segment de droite sur la partie où la probabilité est constante ;
* une rupture de pente entre les deux, correspondant à la discontinuité dans la progression des probabilités.
[[Image:1d choisi parmi 3 differents.svg|thumb|250px| Probabilité de faire un score (haut) ou de dépasser un score en choisissant le meilleur dé parmi trois (identiques ou différents).]]
La figure ci-contre montre les probabilités lorsque l'on a trois dés, pouvant être différents.
On peut classer les combinaisons de dé par ordre de moyenne croissante.
== Jeux n'utilisant pas de dés ==
Il existe quelques rares jeux de rôle n'utilisant pas de dés. On peut citer ''Prince Valiant'', qui utilise des pièces de monnaies (pile ou face), ou encore ''Ambre'', qui se joue sans aucun recours au hasard chiffré (ni dés, ni cartes, ni pièces). Dans ce dernier cas, on se contente de comparer les traits : « le plus fort gagne », ou bien, en cas d'égalité trop sensible, c'est le plus rusé qui l'emporte.
Il y a aussi des jeux qui n'utilisent pas de dés, mais qui les remplacent par des [[w:fr:cartes à jouer|cartes à jouer]] ; si les probabilités en sont changées, le mécanisme n'est pas fondamentalement différent. La plus grosse différence est que, souvent, le joueur peut décider de la carte qu'il joue selon ce qu'il a en main, ce qui lui donne plus de maîtrise sur la destinée de son personnage. Les jeux les plus renommés dans cette catégorie sont ''Château Falkenstein'' et ''Miles Christi''. ''Deadlands'' et les jeux qui en sont issus (la gamme ''Savage Worlds'') mêlent dés et cartes.
Certains jeux utilisent des cartes de tarot, notamment ''Ambre'' et ''Chimères'' ; servant avant tout d'aides à la créativité, elles peuvent aussi partiellement remplacer les dés.
{{loupe|w:fr:Mécanisme de simulation dans les jeux de rôle#Gestion des résultats des actions}}
== Annexes ==
=== Compléments sur AnyDice ===
==== Fonction ''explode''() ====
Il est possible de recréer la fonction ''explode''() ; c'est parfaitement inutile mais nous le faisons à titre d'exercice, pour mieux comprendre ''AnyDice''. C'est une fonction s'appelant elle-même (fonction récursive) :
* ''NomDeLaFonction'' : <code>jslexplode</code> ;
* ''VariableEntrée'' : <code>N</code> avec la précision <code>:n</code>.
La fonction a donc pour paramètre d'entrée N = d10. Si cette valeur N est égale à 10, alors on lui ajoute le résultat de la fonction ''jslexplode''(d10), on parle « d'appel récursif » (la fonction s'appelle elle-même) :
<syntaxhighlight lang="text">
if N = 10 { result: N + [jslexplode d10] }
</syntaxhighlight>
Si cette condition n'est réalisée, c'est-à-dire si le résultat est entre 1 et 9, alors l'exécution n'a pas rencontré la commande de sortie <code>result:</code> donc on applique la dernière ligne qui renvoie la valeur de N :
<syntaxhighlight lang="text">
result: N
</syntaxhighlight>
La fonction complète est donc :
<syntaxhighlight lang="text">
function: jslexplode N:n {
if N = 10 { result: N + [jslexplode d10] }
result: N
}
set "maximum function depth" to 3
output [jslexplode d10]
</syntaxhighlight>
==== Fonction de plusieurs variables ====
Puisque l'on en est aux fonctions, notez que l'expression peut comporter plusieurs variables d'entrée ; ''AnyDice'' reconnaît automatiquement les variables utilisée dans la fonction. Par exemple :
<syntaxhighlight lang="text">
function: A et B {
result: A+B
}
output [2 et 5]
</syntaxhighlight>
va bien calculer 2 + 5. On peut même écrire :
<syntaxhighlight lang="text">
function: ajouter A et B {
result: A+B
}
output [ajouter 2 et 5]
</syntaxhighlight>
==== Dé yin-yang ''(Qin)'' ====
Le dé yin-yang utilisé dans le jeu ''Qin'' est décrit dans la section ''[[#Soustraction de dés|Soustraction de dés]]''. Le résultat de la soustraction est compris entre 0 et 9; nous utilisons donc a valeur –1 pour indique une maladresse et la valeur 10 pour indiquer un coup de maître.
L'opérateur « & » (esperluette) correspond à l'opérateur booléen « et » ''({{lang|en|and}})''.
<syntaxhighlight lang="text">
A: d{0..9}
B: d{0..9}
function: deyinyang X:n Y:n {
if X=0 & Y=0 {result: -1}
if X=Y {result: 10}
result: [absolute X-Y]}
output [deyinyang A B] named "dé yin-yang"
</syntaxhighlight>
=== Bibliographie ===
* {{ouvrage
| prénom1 = Greg | nom1 = Costikyan |lien auteur1 = w:fr:Greg Costikyan
| titre = Uncertainty in Games
| langue = en
| éditeur = MIT Press
| année = 2013
| isbn = 978-02-6201896-8
| pages = 152
| présentation en ligne = https://mitpress.mit.edu/books/uncertainty-games
}}
== Notes et références ==
{{références}}
== Voir aussi ==
=== Liens externes ===
* {{lien web
| url = http://litteraction.fr/article/litterature-interactive/systeme-additif-simple
| titre = Système additif simple
| auteur = Caïthness
| site = Littéraction
| date = 2012-08-12 | consulté le = 2013-10-24
}}
* {{article
| libellé = Hetland 2013
| prénom1 = Magnus Lie | nom1 = Hetland
| titre = Simulating Ability | sous-titre = Representing Skills in Games
| langue = en
| périodique = arXiv
| année = 2013 | mois = juillet | jour = 26
| url = https://arxiv.org/abs/1307.0201
| doi = 10.1007/978-3-642-40790-1_22
}}
* {{lien web
| url = http://www.limbicsystemsjdr.com/pourquoi-nous-lancons-des-des/
| titre = Pourquoi nous lançons les dés
| auteur = Frédéric Sintes
| site = Limbic system
| date = 2012-05-23 | consulté le = 2017-09-15
}}
* {{lien web
| url = https://perso.univ-rennes1.fr/denis.poinsot/Statistiques_%20pour_statophobes/STATISTIQUES%20POUR%20STATOPHOBES.pdf
| auteur = Denis Poinçot
| titre = Statistiques pour les statophobes
| date = 2004 | consulté le = 2017-12-06
| site = Université de Rennes
| format = PDF
}}
; Memento Ludi (Sébastien « Wenlock » Delfino), « [http://mementoludi.net/index.php/tag/jeter-les-des-ne-me-suffit-plus/ Jeter les dés ne suffit plus] »
* {{lien web
| url = http://mementoludi.net/index.php/2016/06/21/jeter-les-des-ne-me-suffit-plus-ou-pourquoi-le-gameplay-est-important/
| titre = Jeter les dés ne suffit plus ou “pourquoi le ''gameplay'' est important”
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-06-21 | consulté le = 2017-08-09
}}
* {{lien web
| url = http://mementoludi.net/index.php/2016/06/21/jeter-les-des-ne-me-suffit-plus-1-definir-le-gameplay/
| titre = 1. Définir le ''gameplay''
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-06-21 | consulté le = 2017-08-09
}}
* {{lien web
| url = http://mementoludi.net/index.php/2016/06/22/jeter-les-des-ne-me-suffit-plus-2-parametres-de-gameplay-jouabilite/
| titre = 2. Paramètres de ''gameplay'' et jouabilité
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-06-22 | consulté le = 2017-08-09
}}
* {{lien web
| url = http://mementoludi.net/index.php/2016/06/24/jeter-les-des-ne-me-suffit-plus-3-lagentivite-le-systeme-monde/
| titre = 3. L'agentivité et le système-monde
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-06-24 | consulté le = 2017-08-09
}}
* {{lien web
| url = http://mementoludi.net/index.php/2016/06/26/jeter-les-des-ne-me-suffit-plus-4-au-hasard-des-jeux/
| titre = 4. Au hasard des jeux
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-06-26 | consulté le = 2017-08-09
}}
* {{lien web
| url = http://mementoludi.net/index.php/2016/06/27/jeter-les-des-ne-me-suffit-plus-5-ou-se-cache-le-gameplay-du-jeu-de-roles/
| titre = 5. Où se cache le ''gameplay'' du jeu de rôles ?
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-06-27 | consulté le = 2017-08-09
}}
* {{lien web
| url = http://mementoludi.net/index.php/2016/09/05/jeter-les-des-ne-me-suffit-plus-6-les-protagonistes/
| titre = 6. Les protagonistes
| auteur = Sébastien Delfino
| site = Memento Ludi
| date = 2016-09-05 | consulté le = 2017-08-09
}}
; Probabilités à des jeux particuliers
* {{lien web
| url = http://www.datagenetics.com/blog/january42012/
| titre = Yahtzee Probability
| langue = en
| site = DataGenetics
| date = 2012-01 | consulté le = 2018-04-11
}}
* {{lien web
| url = http://abrobecker.free.fr/text/poker.htm
| titre = Yam, Poker et Probabilités
| auteur = Alain Brobecker
| site = abrobecker.free.fr
| date = 2004-08 | consulté le = 2018-04-11
}}
----
''[[../Le hasard dans les jeux de rôle/]]'' < [[Jeu de rôle sur table — Jouer, créer|↑]] > ''[[../Créer des éléments du monde /]]''
{{DEFAULTSORT:Probabilites des des en jeu de role}}
[[Catégorie:Jeu de rôle sur table — Jouer, créer (livre)]]
i9oxulx7zztbii04f4gfkz5k4sh1h5y
Les cartes graphiques/Le multi-GPU
0
67391
744766
716904
2025-06-15T01:18:00Z
Mewtow
31375
744766
wikitext
text/x-wiki
[[File:R700 interconnect.svg|droite|thumb|Illustration du multi-GPU où deux cartes graphiques communiquent via un lien indépendant du bus PCIExpress. On voit que le débit du lien entre les deux cartes graphique est ajouté au débit du bus PCIExpress.]]
Les techniques dites de multi-GPU, tels le SLI et le Crossfire, permettent de mettre plusieurs cartes graphiques dans un PC pour gagner en performances. Bien que tombées en désuétude pour le grand public, elles ont eu leur heure de gloire durant les années 2000.
Contrairement à ce qu'on pourrait penser, le multi-GPU n'est pas une technique récente. Pensez donc qu'en 1998, il était possible de combiner dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx (un ancien fabricant de cartes graphiques, aujourd'hui racheté par NVIDIA). Autre exemple : dans les années 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome.
Le multi-GPU était destiné aux jeux vidéo, même si les applications de réalité virtuelle, l'imagerie médicale haute précision ou les applications de conception par ordinateur pouvaient en tirer profit. C'est ce genre de choses qui se cachent derrière les films d'animation ou les effets spéciaux créés par ordinateur : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets, ce qui demande la coopération de plusieurs cartes graphiques.
Le multi-GPU peut se présenter sous plusieurs formes, la plus simple consistant à placer plusieurs GPU sur une même carte graphique. Mais il est aussi possible d'utiliser plusieurs cartes graphiques séparées, connectées à la carte mère via PCI-Express. Si les deux cartes ont besoin d’échanger des informations, les transferts passent par le bus PCI-Express ou par un connecteur qui relie les deux cartes (ce qui est souvent plus rapide). Il n'y a pas de différences de performances avec la solution utilisant des cartes séparées reliées avec un connecteur. Tout le problème des solutions multi-GPU est de répartir les calculs sur plusieurs cartes graphiques, ce qui est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients, que nous allons aborder de suite.
==Le ''Split Frame Rendering''==
Le '''''Split Frame Rendering''''' découpe l'image en morceaux, qui sont répartis sur des cartes graphiques différentes. Ce principe a été décliné en plusieurs versions, et nous allons les passer en revue. Nous pouvons commencer par faire la différence entre les méthodes de distribution statiques et dynamiques. Avec les méthodes statiques, la manière de découper l'image est toujours la même : celle-ci sera découpée en blocs, en lignes, en colonnes, etc; de la même façon quel que soit l'image. Avec les techniques dynamiques, le découpage s'adapte en fonction de la complexité de l'image. Nous allons commencer par aborder les méthodes statiques.
===Le ''Scanline interleave''===
Historiquement, la première technique multi-GPU inventée s'appelait le '''''Scan Line Interleave''''' et elle fût utilisée par les cartes graphiques Voodoo 2. Avec cette technique, chaque carte graphique calculait une ligne sur deux, la première carte rendait les lignes paires et l'autre les lignes impaires. Notons que cette technique ressemble à la technique de l'entrelacement vue dans le tout premier chapitre. Cependant, on peut adapter la technique à un nombre arbitraire de GPU, en faisant calculer par chaque GPU une ligne sur 3, 4, 5, etc.
[[File:Scanline interleave.png|centre|vignette|upright=2.0|Scanline interleave]]
Cette technique avait un avantage certain quand la résolution des images était limitée par la quantité de mémoire vidéo, ce qui était le cas de la Voodoo 2, qui ne pouvait pas dépasser une résolution de 800 * 600. Avec le ''scan line interleave'', les deux ''framebuffers'' des deux cartes étaient combinés en un seul ''framebuffer'' plus gros, capable de supporter des résolutions plus élevées. Cette technique a toutefois un gros défaut : l’utilisation de la mémoire vidéo n'est pas optimale. Comme vous le savez, la mémoire vidéo sert à stocker les objets géométriques de la scène à rendre, les textures, et d'autres choses encore. Avec le ''scan line interleave'', chaque objet et texture est présent dans la mémoire vidéo de chaque carte graphique. Il faut dire que ces objets et textures sont assez grands : la carte graphique devant rendre une ligne sur deux, il est très rare qu'un objet doive être rendu totalement par une des cartes et pas l'autre. Avec d'autres techniques, cette consommation de mémoire peut être mieux gérée.
===Le ''Checker board''===
La technique du '''''Checker Board''''' découpe l'image non en lignes, mais en carrés de plusieurs pixels. Dans le cas le plus simple, les carrés ont une taille fixe, de 16 pixels de largeur par exemple. Si les carrés sont suffisamment gros, il arrive qu'ils puissent contenir totalement un objet géométrique. Dans ces conditions, une seule carte graphique devra calculer cet objet géométrique et charger ses données, qui ne seront donc pas dupliquées dans les deux cartes. Le gain en terme de mémoire peut être appréciable si les blocs sont suffisamment gros. Mais il arrive souvent qu'un objet soit à la frontière entre deux blocs : il doit donc être rendu par les deux cartes, et sera stocké dans les deux mémoires vidéos.
Pour plus d'efficacité, on peut passer d'un découpage statique, où tous les carrés ont la même taille, à un découpage dynamique, dans lequel on découpe l'image en rectangles dont la longueur et la largeur varient. En faisant varier le mieux possible la taille et la longueur de ces rectangles, on peut faire en sorte qu'un maximum de rectangles contiennent totalement un objet géométrique. Le gain en terme de mémoire et de rendu peut être appréciable. Néanmoins, découper des blocs dynamiquement est très complexe, et le faire efficacement est un casse-tête pour les développeurs de drivers.
===Le ''Screen spiting''===
Il est aussi possible de simplement couper l'image en deux : la partie haute de l'image ira sur un GPU, et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU, en découpant l'image en autant de parties qu'il y a de GPU. Vu que de nombreux objets n'apparaissent que dans une portion de l'image, le drivers peut ainsi répartir les données de l'objet pour éviter toute duplication entre cartes graphiques. Cela demande du travail au driver, mais cela en vaut la peine, le gain en terme de mémoire étant appréciable.
[[File:Screen spliting.png|centre|vignette|upright=2.0|Screen spliting]]
Le découpage de l'image peut reposer sur une technique statique : la moitié haute de l'image pour le premier GPU, et le bas pour l'autre. Ceci dit, quelques complications peuvent survenir dans certains jeux, les FPS notamment, où le bas de l'image est plus chargé que le haut. C'est en effet dans le bas de l'image qu'on trouve un sol, des murs, les ennemis, ou d'autres objets géométriques complexes texturés, alors que le haut représente le ciel ou un plafond, assez simple géométriquement et aux textures simples. Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, et une des cartes 3D finira par attendre l'autre.
Mieux répartir les calculs devient alors nécessaire. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse. Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l’exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Comme cela, si vous voulez tirer une roquette sur une ennemi qui vient de prendre un jumper (vous ne jouez pas à UT ou Quake ?), vous ne subirez pas un gros coup de lag parce que le découpage statique était inadapté. Dans ce cas, c'est le driver qui gère ce découpage : il dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soit que ces algorithmes ne sont pas parfaits.
==L'''Alternate Frame Rendering''==
L''''alternate Frame Rendering''' consiste à répartir des images complètes sur les différents GPUs. Dans sa forme la plus simple, un GPU calcule une image, et l'autre GPU calcule la suivante en parallèle. Les problèmes liés à la répartition des calculs entre cartes graphiques disparaissent alors. Cette technique est supportée par la majorité des cartes graphiques actuelles. Cette technique a été inventé par ATI, sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256. Évidemment, on retrouve un vieux problème présent dans certaines des techniques vues avant : chaque objet géométrique devra être présent dans la mémoire vidéo de chaque carte graphique, vu qu'elle devra l'afficher à l'écran. Il est donc impossible de répartir les différents objets dans les mémoires des cartes graphiques. Mais d'autres problèmes peuvent survenir.
Un des défauts de cette approche est le '''micro-stuttering'''. Dans des situations où le processeur est peu puissant, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins imprévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l’œil nu. Cela cause une impression de micro-saccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable. Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images, et éviter les variations, le mieux est d’empêcher des images de s'afficher trop tôt. Si une image a été calculée en très peu de temps, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du framerate mesuré précédemment.
Ensuite, il arrive que deux images soient dépendantes les unes des autres : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'Open GL qu'on appelle le Render To Texture, ainsi que dans quelques autres situations. Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.
{{NavChapitre | book=Les cartes graphiques
| prev=L'antialiasing
| prevText=L'antialiasing
| next=Le support matériel du lancer de rayons
| nextText=Le support matériel du lancer de rayons
}}{{autocat}}
fte0jx7o96e4abv5ast0y1xsb2wchck
744767
744766
2025-06-15T01:18:27Z
Mewtow
31375
744767
wikitext
text/x-wiki
[[File:R700 interconnect.svg|droite|thumb|Illustration du multi-GPU où deux cartes graphiques communiquent via un lien indépendant du bus PCIExpress. On voit que le débit du lien entre les deux cartes graphique est ajouté au débit du bus PCIExpress.]]
Les techniques dites de multi-GPU, tels le SLI et le Crossfire, permettent de mettre plusieurs cartes graphiques dans un PC pour gagner en performances. Bien que tombées en désuétude pour le grand public, elles ont eu leur heure de gloire durant les années 2000.
Contrairement à ce qu'on pourrait penser, le multi-GPU n'est pas une technique récente. Pensez donc qu'en 1998, il était possible de combiner dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx (un ancien fabricant de cartes graphiques, aujourd'hui racheté par NVIDIA). Autre exemple : dans les années 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome.
Le multi-GPU était destiné aux jeux vidéo, même si les applications de réalité virtuelle, l'imagerie médicale haute précision ou les applications de conception par ordinateur pouvaient en tirer profit. C'est ce genre de choses qui se cachent derrière les films d'animation ou les effets spéciaux créés par ordinateur : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets, ce qui demande la coopération de plusieurs cartes graphiques.
Le multi-GPU peut se présenter sous plusieurs formes, la plus simple consistant à placer plusieurs GPU sur une même carte graphique. Mais il est aussi possible d'utiliser plusieurs cartes graphiques séparées, connectées à la carte mère via PCI-Express. Si les deux cartes ont besoin d’échanger des informations, les transferts passent par le bus PCI-Express ou par un connecteur qui relie les deux cartes (ce qui est souvent plus rapide). Tout le problème des solutions multi-GPU est de répartir les calculs sur plusieurs cartes graphiques, ce qui est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients, que nous allons aborder de suite.
==Le ''Split Frame Rendering''==
Le '''''Split Frame Rendering''''' découpe l'image en morceaux, qui sont répartis sur des cartes graphiques différentes. Ce principe a été décliné en plusieurs versions, et nous allons les passer en revue. Nous pouvons commencer par faire la différence entre les méthodes de distribution statiques et dynamiques. Avec les méthodes statiques, la manière de découper l'image est toujours la même : celle-ci sera découpée en blocs, en lignes, en colonnes, etc; de la même façon quel que soit l'image. Avec les techniques dynamiques, le découpage s'adapte en fonction de la complexité de l'image. Nous allons commencer par aborder les méthodes statiques.
===Le ''Scanline interleave''===
Historiquement, la première technique multi-GPU inventée s'appelait le '''''Scan Line Interleave''''' et elle fût utilisée par les cartes graphiques Voodoo 2. Avec cette technique, chaque carte graphique calculait une ligne sur deux, la première carte rendait les lignes paires et l'autre les lignes impaires. Notons que cette technique ressemble à la technique de l'entrelacement vue dans le tout premier chapitre. Cependant, on peut adapter la technique à un nombre arbitraire de GPU, en faisant calculer par chaque GPU une ligne sur 3, 4, 5, etc.
[[File:Scanline interleave.png|centre|vignette|upright=2.0|Scanline interleave]]
Cette technique avait un avantage certain quand la résolution des images était limitée par la quantité de mémoire vidéo, ce qui était le cas de la Voodoo 2, qui ne pouvait pas dépasser une résolution de 800 * 600. Avec le ''scan line interleave'', les deux ''framebuffers'' des deux cartes étaient combinés en un seul ''framebuffer'' plus gros, capable de supporter des résolutions plus élevées. Cette technique a toutefois un gros défaut : l’utilisation de la mémoire vidéo n'est pas optimale. Comme vous le savez, la mémoire vidéo sert à stocker les objets géométriques de la scène à rendre, les textures, et d'autres choses encore. Avec le ''scan line interleave'', chaque objet et texture est présent dans la mémoire vidéo de chaque carte graphique. Il faut dire que ces objets et textures sont assez grands : la carte graphique devant rendre une ligne sur deux, il est très rare qu'un objet doive être rendu totalement par une des cartes et pas l'autre. Avec d'autres techniques, cette consommation de mémoire peut être mieux gérée.
===Le ''Checker board''===
La technique du '''''Checker Board''''' découpe l'image non en lignes, mais en carrés de plusieurs pixels. Dans le cas le plus simple, les carrés ont une taille fixe, de 16 pixels de largeur par exemple. Si les carrés sont suffisamment gros, il arrive qu'ils puissent contenir totalement un objet géométrique. Dans ces conditions, une seule carte graphique devra calculer cet objet géométrique et charger ses données, qui ne seront donc pas dupliquées dans les deux cartes. Le gain en terme de mémoire peut être appréciable si les blocs sont suffisamment gros. Mais il arrive souvent qu'un objet soit à la frontière entre deux blocs : il doit donc être rendu par les deux cartes, et sera stocké dans les deux mémoires vidéos.
Pour plus d'efficacité, on peut passer d'un découpage statique, où tous les carrés ont la même taille, à un découpage dynamique, dans lequel on découpe l'image en rectangles dont la longueur et la largeur varient. En faisant varier le mieux possible la taille et la longueur de ces rectangles, on peut faire en sorte qu'un maximum de rectangles contiennent totalement un objet géométrique. Le gain en terme de mémoire et de rendu peut être appréciable. Néanmoins, découper des blocs dynamiquement est très complexe, et le faire efficacement est un casse-tête pour les développeurs de drivers.
===Le ''Screen spiting''===
Il est aussi possible de simplement couper l'image en deux : la partie haute de l'image ira sur un GPU, et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU, en découpant l'image en autant de parties qu'il y a de GPU. Vu que de nombreux objets n'apparaissent que dans une portion de l'image, le drivers peut ainsi répartir les données de l'objet pour éviter toute duplication entre cartes graphiques. Cela demande du travail au driver, mais cela en vaut la peine, le gain en terme de mémoire étant appréciable.
[[File:Screen spliting.png|centre|vignette|upright=2.0|Screen spliting]]
Le découpage de l'image peut reposer sur une technique statique : la moitié haute de l'image pour le premier GPU, et le bas pour l'autre. Ceci dit, quelques complications peuvent survenir dans certains jeux, les FPS notamment, où le bas de l'image est plus chargé que le haut. C'est en effet dans le bas de l'image qu'on trouve un sol, des murs, les ennemis, ou d'autres objets géométriques complexes texturés, alors que le haut représente le ciel ou un plafond, assez simple géométriquement et aux textures simples. Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, et une des cartes 3D finira par attendre l'autre.
Mieux répartir les calculs devient alors nécessaire. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse. Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l’exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Comme cela, si vous voulez tirer une roquette sur une ennemi qui vient de prendre un jumper (vous ne jouez pas à UT ou Quake ?), vous ne subirez pas un gros coup de lag parce que le découpage statique était inadapté. Dans ce cas, c'est le driver qui gère ce découpage : il dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soit que ces algorithmes ne sont pas parfaits.
==L'''Alternate Frame Rendering''==
L''''alternate Frame Rendering''' consiste à répartir des images complètes sur les différents GPUs. Dans sa forme la plus simple, un GPU calcule une image, et l'autre GPU calcule la suivante en parallèle. Les problèmes liés à la répartition des calculs entre cartes graphiques disparaissent alors. Cette technique est supportée par la majorité des cartes graphiques actuelles. Cette technique a été inventé par ATI, sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256. Évidemment, on retrouve un vieux problème présent dans certaines des techniques vues avant : chaque objet géométrique devra être présent dans la mémoire vidéo de chaque carte graphique, vu qu'elle devra l'afficher à l'écran. Il est donc impossible de répartir les différents objets dans les mémoires des cartes graphiques. Mais d'autres problèmes peuvent survenir.
Un des défauts de cette approche est le '''micro-stuttering'''. Dans des situations où le processeur est peu puissant, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins imprévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l’œil nu. Cela cause une impression de micro-saccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable. Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images, et éviter les variations, le mieux est d’empêcher des images de s'afficher trop tôt. Si une image a été calculée en très peu de temps, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du framerate mesuré précédemment.
Ensuite, il arrive que deux images soient dépendantes les unes des autres : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'Open GL qu'on appelle le Render To Texture, ainsi que dans quelques autres situations. Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.
{{NavChapitre | book=Les cartes graphiques
| prev=L'antialiasing
| prevText=L'antialiasing
| next=Le support matériel du lancer de rayons
| nextText=Le support matériel du lancer de rayons
}}{{autocat}}
q5u1t80zmatvbuxpvxchwh9dmbwmszl
744768
744767
2025-06-15T01:20:34Z
Mewtow
31375
/* L'Alternate Frame Rendering */
744768
wikitext
text/x-wiki
[[File:R700 interconnect.svg|droite|thumb|Illustration du multi-GPU où deux cartes graphiques communiquent via un lien indépendant du bus PCIExpress. On voit que le débit du lien entre les deux cartes graphique est ajouté au débit du bus PCIExpress.]]
Les techniques dites de multi-GPU, tels le SLI et le Crossfire, permettent de mettre plusieurs cartes graphiques dans un PC pour gagner en performances. Bien que tombées en désuétude pour le grand public, elles ont eu leur heure de gloire durant les années 2000.
Contrairement à ce qu'on pourrait penser, le multi-GPU n'est pas une technique récente. Pensez donc qu'en 1998, il était possible de combiner dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx (un ancien fabricant de cartes graphiques, aujourd'hui racheté par NVIDIA). Autre exemple : dans les années 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome.
Le multi-GPU était destiné aux jeux vidéo, même si les applications de réalité virtuelle, l'imagerie médicale haute précision ou les applications de conception par ordinateur pouvaient en tirer profit. C'est ce genre de choses qui se cachent derrière les films d'animation ou les effets spéciaux créés par ordinateur : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets, ce qui demande la coopération de plusieurs cartes graphiques.
Le multi-GPU peut se présenter sous plusieurs formes, la plus simple consistant à placer plusieurs GPU sur une même carte graphique. Mais il est aussi possible d'utiliser plusieurs cartes graphiques séparées, connectées à la carte mère via PCI-Express. Si les deux cartes ont besoin d’échanger des informations, les transferts passent par le bus PCI-Express ou par un connecteur qui relie les deux cartes (ce qui est souvent plus rapide). Tout le problème des solutions multi-GPU est de répartir les calculs sur plusieurs cartes graphiques, ce qui est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients, que nous allons aborder de suite.
==Le ''Split Frame Rendering''==
Le '''''Split Frame Rendering''''' découpe l'image en morceaux, qui sont répartis sur des cartes graphiques différentes. Ce principe a été décliné en plusieurs versions, et nous allons les passer en revue. Nous pouvons commencer par faire la différence entre les méthodes de distribution statiques et dynamiques. Avec les méthodes statiques, la manière de découper l'image est toujours la même : celle-ci sera découpée en blocs, en lignes, en colonnes, etc; de la même façon quel que soit l'image. Avec les techniques dynamiques, le découpage s'adapte en fonction de la complexité de l'image. Nous allons commencer par aborder les méthodes statiques.
===Le ''Scanline interleave''===
Historiquement, la première technique multi-GPU inventée s'appelait le '''''Scan Line Interleave''''' et elle fût utilisée par les cartes graphiques Voodoo 2. Avec cette technique, chaque carte graphique calculait une ligne sur deux, la première carte rendait les lignes paires et l'autre les lignes impaires. Notons que cette technique ressemble à la technique de l'entrelacement vue dans le tout premier chapitre. Cependant, on peut adapter la technique à un nombre arbitraire de GPU, en faisant calculer par chaque GPU une ligne sur 3, 4, 5, etc.
[[File:Scanline interleave.png|centre|vignette|upright=2.0|Scanline interleave]]
Cette technique avait un avantage certain quand la résolution des images était limitée par la quantité de mémoire vidéo, ce qui était le cas de la Voodoo 2, qui ne pouvait pas dépasser une résolution de 800 * 600. Avec le ''scan line interleave'', les deux ''framebuffers'' des deux cartes étaient combinés en un seul ''framebuffer'' plus gros, capable de supporter des résolutions plus élevées. Cette technique a toutefois un gros défaut : l’utilisation de la mémoire vidéo n'est pas optimale. Comme vous le savez, la mémoire vidéo sert à stocker les objets géométriques de la scène à rendre, les textures, et d'autres choses encore. Avec le ''scan line interleave'', chaque objet et texture est présent dans la mémoire vidéo de chaque carte graphique. Il faut dire que ces objets et textures sont assez grands : la carte graphique devant rendre une ligne sur deux, il est très rare qu'un objet doive être rendu totalement par une des cartes et pas l'autre. Avec d'autres techniques, cette consommation de mémoire peut être mieux gérée.
===Le ''Checker board''===
La technique du '''''Checker Board''''' découpe l'image non en lignes, mais en carrés de plusieurs pixels. Dans le cas le plus simple, les carrés ont une taille fixe, de 16 pixels de largeur par exemple. Si les carrés sont suffisamment gros, il arrive qu'ils puissent contenir totalement un objet géométrique. Dans ces conditions, une seule carte graphique devra calculer cet objet géométrique et charger ses données, qui ne seront donc pas dupliquées dans les deux cartes. Le gain en terme de mémoire peut être appréciable si les blocs sont suffisamment gros. Mais il arrive souvent qu'un objet soit à la frontière entre deux blocs : il doit donc être rendu par les deux cartes, et sera stocké dans les deux mémoires vidéos.
Pour plus d'efficacité, on peut passer d'un découpage statique, où tous les carrés ont la même taille, à un découpage dynamique, dans lequel on découpe l'image en rectangles dont la longueur et la largeur varient. En faisant varier le mieux possible la taille et la longueur de ces rectangles, on peut faire en sorte qu'un maximum de rectangles contiennent totalement un objet géométrique. Le gain en terme de mémoire et de rendu peut être appréciable. Néanmoins, découper des blocs dynamiquement est très complexe, et le faire efficacement est un casse-tête pour les développeurs de drivers.
===Le ''Screen spiting''===
Il est aussi possible de simplement couper l'image en deux : la partie haute de l'image ira sur un GPU, et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU, en découpant l'image en autant de parties qu'il y a de GPU. Vu que de nombreux objets n'apparaissent que dans une portion de l'image, le drivers peut ainsi répartir les données de l'objet pour éviter toute duplication entre cartes graphiques. Cela demande du travail au driver, mais cela en vaut la peine, le gain en terme de mémoire étant appréciable.
[[File:Screen spliting.png|centre|vignette|upright=2.0|Screen spliting]]
Le découpage de l'image peut reposer sur une technique statique : la moitié haute de l'image pour le premier GPU, et le bas pour l'autre. Ceci dit, quelques complications peuvent survenir dans certains jeux, les FPS notamment, où le bas de l'image est plus chargé que le haut. C'est en effet dans le bas de l'image qu'on trouve un sol, des murs, les ennemis, ou d'autres objets géométriques complexes texturés, alors que le haut représente le ciel ou un plafond, assez simple géométriquement et aux textures simples. Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, et une des cartes 3D finira par attendre l'autre.
Mieux répartir les calculs devient alors nécessaire. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse. Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l’exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Comme cela, si vous voulez tirer une roquette sur une ennemi qui vient de prendre un jumper (vous ne jouez pas à UT ou Quake ?), vous ne subirez pas un gros coup de lag parce que le découpage statique était inadapté. Dans ce cas, c'est le driver qui gère ce découpage : il dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soit que ces algorithmes ne sont pas parfaits.
==L'''Alternate Frame Rendering''==
L''''alternate Frame Rendering''' (AFR) consiste à répartir des images complètes sur les différents GPUs. Dans sa forme la plus simple, un GPU calcule une image, et l'autre GPU calcule la suivante en parallèle. Les problèmes liés à la répartition des calculs entre cartes graphiques disparaissent alors. L'AFR a été inventé par ATI, sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256.
Évidemment, on retrouve un problème présent dans les techniques vues avant : chaque carte graphique a un exemplaire de la géométrie de la scène 3D dans sa mémoire vidéo, vu qu'elle doit l'afficher à l'écran. Il est donc impossible de répartir les différents objets dans les mémoires des cartes graphiques. Mais d'autres problèmes peuvent survenir.
Un des défauts de cette approche est le '''micro-stuttering'''. Dans des situations où le processeur est peu puissant, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins imprévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l’œil nu. Cela cause une impression de micro-saccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable.
Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images, et éviter les variations, le mieux est d’empêcher des images de s'afficher trop tôt. Si une image a été calculée en très peu de temps, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du framerate mesuré précédemment.
Ensuite, il arrive que deux images soient dépendantes les unes des autres : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'Open GL qu'on appelle le Render To Texture, ainsi que dans quelques autres situations. Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.
{{NavChapitre | book=Les cartes graphiques
| prev=L'antialiasing
| prevText=L'antialiasing
| next=Le support matériel du lancer de rayons
| nextText=Le support matériel du lancer de rayons
}}{{autocat}}
3wnaviuq5la3ml4sc0hwpjr7x0y5i7y
744772
744768
2025-06-15T01:24:32Z
Mewtow
31375
/* L'Alternate Frame Rendering */
744772
wikitext
text/x-wiki
[[File:R700 interconnect.svg|droite|thumb|Illustration du multi-GPU où deux cartes graphiques communiquent via un lien indépendant du bus PCIExpress. On voit que le débit du lien entre les deux cartes graphique est ajouté au débit du bus PCIExpress.]]
Les techniques dites de multi-GPU, tels le SLI et le Crossfire, permettent de mettre plusieurs cartes graphiques dans un PC pour gagner en performances. Bien que tombées en désuétude pour le grand public, elles ont eu leur heure de gloire durant les années 2000.
Contrairement à ce qu'on pourrait penser, le multi-GPU n'est pas une technique récente. Pensez donc qu'en 1998, il était possible de combiner dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx (un ancien fabricant de cartes graphiques, aujourd'hui racheté par NVIDIA). Autre exemple : dans les années 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome.
Le multi-GPU était destiné aux jeux vidéo, même si les applications de réalité virtuelle, l'imagerie médicale haute précision ou les applications de conception par ordinateur pouvaient en tirer profit. C'est ce genre de choses qui se cachent derrière les films d'animation ou les effets spéciaux créés par ordinateur : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets, ce qui demande la coopération de plusieurs cartes graphiques.
Le multi-GPU peut se présenter sous plusieurs formes, la plus simple consistant à placer plusieurs GPU sur une même carte graphique. Mais il est aussi possible d'utiliser plusieurs cartes graphiques séparées, connectées à la carte mère via PCI-Express. Si les deux cartes ont besoin d’échanger des informations, les transferts passent par le bus PCI-Express ou par un connecteur qui relie les deux cartes (ce qui est souvent plus rapide). Tout le problème des solutions multi-GPU est de répartir les calculs sur plusieurs cartes graphiques, ce qui est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients, que nous allons aborder de suite.
==Le ''Split Frame Rendering''==
Le '''''Split Frame Rendering''''' découpe l'image en morceaux, qui sont répartis sur des cartes graphiques différentes. Ce principe a été décliné en plusieurs versions, et nous allons les passer en revue. Nous pouvons commencer par faire la différence entre les méthodes de distribution statiques et dynamiques. Avec les méthodes statiques, la manière de découper l'image est toujours la même : celle-ci sera découpée en blocs, en lignes, en colonnes, etc; de la même façon quel que soit l'image. Avec les techniques dynamiques, le découpage s'adapte en fonction de la complexité de l'image. Nous allons commencer par aborder les méthodes statiques.
===Le ''Scanline interleave''===
Historiquement, la première technique multi-GPU inventée s'appelait le '''''Scan Line Interleave''''' et elle fût utilisée par les cartes graphiques Voodoo 2. Avec cette technique, chaque carte graphique calculait une ligne sur deux, la première carte rendait les lignes paires et l'autre les lignes impaires. Notons que cette technique ressemble à la technique de l'entrelacement vue dans le tout premier chapitre. Cependant, on peut adapter la technique à un nombre arbitraire de GPU, en faisant calculer par chaque GPU une ligne sur 3, 4, 5, etc.
[[File:Scanline interleave.png|centre|vignette|upright=2.0|Scanline interleave]]
Cette technique avait un avantage certain quand la résolution des images était limitée par la quantité de mémoire vidéo, ce qui était le cas de la Voodoo 2, qui ne pouvait pas dépasser une résolution de 800 * 600. Avec le ''scan line interleave'', les deux ''framebuffers'' des deux cartes étaient combinés en un seul ''framebuffer'' plus gros, capable de supporter des résolutions plus élevées. Cette technique a toutefois un gros défaut : l’utilisation de la mémoire vidéo n'est pas optimale. Comme vous le savez, la mémoire vidéo sert à stocker les objets géométriques de la scène à rendre, les textures, et d'autres choses encore. Avec le ''scan line interleave'', chaque objet et texture est présent dans la mémoire vidéo de chaque carte graphique. Il faut dire que ces objets et textures sont assez grands : la carte graphique devant rendre une ligne sur deux, il est très rare qu'un objet doive être rendu totalement par une des cartes et pas l'autre. Avec d'autres techniques, cette consommation de mémoire peut être mieux gérée.
===Le ''Checker board''===
La technique du '''''Checker Board''''' découpe l'image non en lignes, mais en carrés de plusieurs pixels. Dans le cas le plus simple, les carrés ont une taille fixe, de 16 pixels de largeur par exemple. Si les carrés sont suffisamment gros, il arrive qu'ils puissent contenir totalement un objet géométrique. Dans ces conditions, une seule carte graphique devra calculer cet objet géométrique et charger ses données, qui ne seront donc pas dupliquées dans les deux cartes. Le gain en terme de mémoire peut être appréciable si les blocs sont suffisamment gros. Mais il arrive souvent qu'un objet soit à la frontière entre deux blocs : il doit donc être rendu par les deux cartes, et sera stocké dans les deux mémoires vidéos.
Pour plus d'efficacité, on peut passer d'un découpage statique, où tous les carrés ont la même taille, à un découpage dynamique, dans lequel on découpe l'image en rectangles dont la longueur et la largeur varient. En faisant varier le mieux possible la taille et la longueur de ces rectangles, on peut faire en sorte qu'un maximum de rectangles contiennent totalement un objet géométrique. Le gain en terme de mémoire et de rendu peut être appréciable. Néanmoins, découper des blocs dynamiquement est très complexe, et le faire efficacement est un casse-tête pour les développeurs de drivers.
===Le ''Screen spiting''===
Il est aussi possible de simplement couper l'image en deux : la partie haute de l'image ira sur un GPU, et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU, en découpant l'image en autant de parties qu'il y a de GPU. Vu que de nombreux objets n'apparaissent que dans une portion de l'image, le drivers peut ainsi répartir les données de l'objet pour éviter toute duplication entre cartes graphiques. Cela demande du travail au driver, mais cela en vaut la peine, le gain en terme de mémoire étant appréciable.
[[File:Screen spliting.png|centre|vignette|upright=2.0|Screen spliting]]
Le découpage de l'image peut reposer sur une technique statique : la moitié haute de l'image pour le premier GPU, et le bas pour l'autre. Ceci dit, quelques complications peuvent survenir dans certains jeux, les FPS notamment, où le bas de l'image est plus chargé que le haut. C'est en effet dans le bas de l'image qu'on trouve un sol, des murs, les ennemis, ou d'autres objets géométriques complexes texturés, alors que le haut représente le ciel ou un plafond, assez simple géométriquement et aux textures simples. Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, et une des cartes 3D finira par attendre l'autre.
Mieux répartir les calculs devient alors nécessaire. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse. Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l’exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Comme cela, si vous voulez tirer une roquette sur une ennemi qui vient de prendre un jumper (vous ne jouez pas à UT ou Quake ?), vous ne subirez pas un gros coup de lag parce que le découpage statique était inadapté. Dans ce cas, c'est le driver qui gère ce découpage : il dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soit que ces algorithmes ne sont pas parfaits.
==L'''Alternate Frame Rendering''==
L''''alternate Frame Rendering''' (AFR) consiste à répartir des images complètes sur les différents GPUs. Dans sa forme la plus simple, un GPU calcule une image, et l'autre GPU calcule la suivante en parallèle. Les problèmes liés à la répartition des calculs entre cartes graphiques disparaissent alors. L'AFR a été inventé par ATI, sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256.
Évidemment, on retrouve un problème présent dans les techniques vues avant : chaque carte graphique a un exemplaire de la géométrie de la scène 3D dans sa mémoire vidéo, vu qu'elle doit l'afficher à l'écran. Il est donc impossible de répartir les différents objets dans les mémoires des cartes graphiques. Mais d'autres problèmes peuvent survenir.
Un des défauts de cette approche est le '''micro-stuttering'''. Dans des situations où le processeur est peu puissant, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins imprévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l’œil nu. Cela cause une impression de micro-saccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable.
Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images, et éviter les variations, le mieux est d’empêcher des images de s'afficher trop tôt. Si une image a été calculée en très peu de temps, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du framerate mesuré précédemment.
Ensuite, il arrive que deux images soient dépendantes les unes des autres : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'Open GL qu'on appelle le Render To Texture, ainsi que dans quelques autres situations. Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.
{{NavChapitre | book=Les cartes graphiques
| prev=L'antialiasing
| prevText=L'antialiasing
}}{{autocat}}
tkh3k2tfd0evb8sbjq7fzrzvsq2vdd6
Les cartes graphiques/Le pipeline géométrique d'avant DirectX 10
0
67393
744774
742664
2025-06-15T01:27:20Z
Mewtow
31375
744774
wikitext
text/x-wiki
Le pipeline graphique a beaucoup changé dans le temps et est devenu de plus en plus programmable. Et cela s'est beaucoup ressentit dans la partie du pipeline graphique dédiée au traitement de la géométrie. De circuits fixes de T&L, il a intégré des ''vertex shaders'', puis des ''geometry shaders'', ''hull shaders'', ''domain shaders'', ''primitive shaders'', ''mesh shaders'' et bien d'autres. L'évolution s'est faite en plusieurs grandes périodes, qu'on peut distinguer très nettement.
La toute première période est celle où les circuits géométriques n'étaient pas programmables du tout. C’était l'époque ancienne des '''circuits de ''Transform & Lightning''''', introduit sur la Geforce 256 de NVIDIA. Le pipeline géométrique se résumait alors aux quatre étapes suivantes :
* L'étape de '''chargement des sommets/triangles''', qui sont lus depuis la mémoire vidéo et injectés dans le pipeline graphique.
* L'étape de '''transformation''' effectue deux changements de coordonnées pour chaque sommet. Premièrement, elle place les objets au bon endroit dans la scène 3D, ce qui demande de mettre à jour les coordonnées de chaque sommet de chaque modèle. C'est la première étape de calcul : l'''étape de transformation des modèles 3D''. Ensuite, elle effectue un changement de coordonnées pour centrer l'univers sur la caméra, dans la direction du regard. C'est l'étape de ''transformation de la caméra''.
* La phase d''''éclairage''' (en anglais ''lighting'') attribue une couleur à chaque sommet, qui définit son niveau de luminosité : est-ce que le sommet est fortement éclairé ou est-il dans l'ombre ?
* La phase d''''assemblage des primitives''' regroupe les sommets en triangles.
Les deux étapes du milieu étaient le fait d'un circuit de T&L (''Transform & Lightning'') unique, qui n'était pas programmable. Il était accompagné de deux circuits fixes pour les deux étapes restantes. L''''input assembler'' charge les vertices depuis la mémoire vidéo, l'assembleur de primitive regroupe les sommets en triangles avant la rastérisation.
: Dans les tableaux qui vont suivre, les circuits non-programmables sont indiqués en rouge.
{|class="wikitable"
|-
! colspan="4" | Avant l'arrivée des ''shaders''
|-
| class="f_rouge" | ''Input assembly''
| class="f_rouge" | ''Transform & Lighting''
| class="f_rouge" | ''Primitive assembly''
|}
La première période a été très courte : à peine deux générations de cartes graphiques. Rapidement, dès la Geforce 3, les circuits de T&L ont été remplacés par des processeurs de ''shaders'' programmables, avec l'introduction des ''vertex shaders''. Ils s'occupaient donc des phases de transformation et d'éclairage, comme les circuits de T&L qu'ils remplaçaient. Les processeurs de ''shaders'' étaient toujours accompagnés de l''''input assembler'' et de l'assembleur de primitive.
{|class="wikitable"
|-
! colspan="4" | Après la Geforce 3, avant DirectX 10
|-
| class="f_rouge" | ''Input assembly''
| ''Vertex shader''
| class="f_rouge" | ''Primitive assembly''
|}
S'en est suivi une longue période où le traitement de la géométrie se résumait aux ''vertex shaders''. Durant cette période, le pipeline graphique ne manipulait que des sommets, les triangles n'existaient qu'en sortie du pipeline géométrique. L'introduction des techniques de tesselation, qui agissent directement sur des primitives, a changé la donne. Nous détaillerons ces techniques de tesselation dans le prochain chapitre. Mais leur introduction ne s'est pas faite sans heurts.
Initialement, les concepteurs de cartes graphiques ont tenté d'ajouter des nouveaux ''shaders'' pour gérer la tesselation. Sont d'abord apparus les ''geometry shaders'', puis le ''hull/domain shaders''. Il s'agit là de la troisième période, qui visait à améliorer le pipeline graphique précédent. Mais le résultat était assez désastreux. Les programmeurs avaient beaucoup de mal à utiliser la tesselation ou les nouveaux ''shaders'', ce qui fait que ces technologies ont été peu utilisées.
{|class="wikitable"
|-
! colspan="7" | DirectX 10
|-
| class="f_rouge" | ''Input assembly''
| ''Vertex shader''
| colspan="4" | ''Geometry shader''
| class="f_rouge" | ''Primitive assembly''
|-
! colspan="7" | DirectX 11
|-
| class="f_rouge" | ''Input assembly''
| ''Vertex shader''
| ''Hull shader''
| class="f_rouge" | Tesselation
| ''Domain shader''
| ''Geometry shader''
| class="f_rouge" | ''Primitive assembly''
|}
Depuis les années 2018-2020, le pipeline géométrique a fortement évolué et a été revu de fond en comble. L'arrivée des ''primitive shaders'' d'AMD et des ''mesh shaders'' de NVIDIA a modifié en profondeur le pipeline géométrique, qui est devenu plus simple, plus puissant et plus flexible. De nombreuses étapes ont disparues ou sont devenus programmables. A vrai dire, les étapes d'assemblage de primitives et d'''input assembly'' ont disparues.
Dans ce chapitre, nous allons voir l'ancien pipeline géométrique, celui de la première et de la seconde période. Nous allons nous concentrer sur l'''input assembler'' et l'assemblage de primitives. Nous avons déjà vu les étapes de transformation et l'éclairage dans le chapitre sur les bases du rendu 3D, nous n'allons pas revenir dessus dans le détail. Toujours est-il que l'ancien pipeline géométrique ne traitait que des sommets, les triangles n'existaient qu'une fois l'assemblage de primitive effectué. Le pipeline géométrique après Direct X 10 fonctionne sur un principe totalement différent, mais ce sera le sujet du prochain chapitre.
==L'''input assembler''==
L'''input assembler'' charge les sommets de la mémoire vidéo dans les unités de traitement des sommets (circuit de T&L ou processeurs de''vertex shader''). C'est une unité d'accès mémoire un peu particulière, qui contient des circuits assez classiques pour ce genre de circuits : des circuits de calcul d'adresse, des circuits pour commander la mémoire VRAM, un contrôleur mémoire, diverses mémoires tampons, etc. Pour faire son travail, il a besoin de l'adresse des données géométriques en mémoire, leur taille et éventuellement du type des données qu'on lui envoie (sommets codées sur 32 bits, 64, 128, etc). Ces informations sont mémorisées dans des registres, qui sont configurés par le pilote de périphérique.
[[File:Input assembler.png|centre|vignette|upright=2.0|Input assembler]]
Les objets géométriques et la scène 3D sont mémorisés dans la mémoire vidéo, sous une forme plus ou moins structurée. Rappelons que les objets 3D sont représentés comme un assemblage de triangles collés les uns aux autres, l'ensemble formant un maillage. La position d'un triangle est déterminée par la position de chacun de ses sommets. Avec trois coordonnées x, y et z pour un sommet, un triangle demande donc 9 cordonnées. De plus, il faut ajouter des informations sur la manière dont les sommets sont reliés entre eux, quel sommet est relié à quel autre, comment les arêtes sont connectées, etc. Toutes ces informations sont stockées dans un tableau en mémoire vidéo : le '''tampon de sommets'''.
Le contenu du tampon de sommet dépend de la représentation utilisée. Il y a plusieurs manières de structure les informations dans le tampon de sommet, qui ont des avantages et inconvénients divers. Toutes ces représentations cherchent à résoudre un problème bien précis : comment indiquer comment les sommets doivent être reliés entre triangles.
[[File:Cube colored.png|vignette|Cube en 3D]]
Le point crucial est qu'un sommet est très souvent partagé par plusieurs triangles. Par exemple, prenez le cube de l'image ci-contre. Le sommet rouge du cube appartient aux 3 faces grise, jaune et bleue, et sera présent en trois exemplaires dans le tampon de sommets : un pour la face bleue, un pour la jaune, et un pour la grise. Pour éviter ce gâchis, les concepteurs d'API et de cartes graphiques ont inventé des représentations pour les maillages, qui visent à limiter la consommation de mémoire ou faciliter la traversée du tampon de sommet.
===Les techniques anciennes : ''Triangle strip'' et ''Triangle fan''===
Pour gérer le partage des sommets entre triangles, la représentation la plus simple est appelée le '''maillage sommet-sommet''' (''Vertex-Vertex Meshes''). L'idée est que chaque sommet précise, en plus de ses trois coordonnées, quels sont les autres sommets auxquels il est relié. Les sommets sont regroupés dans un tableau et les autres sommets sont identifiés par leur position dans le tableau, leur indice.
[[File:Vertex-Vertex Meshes (VV).png|centre|vignette|upright=1.5|Vertex-Vertex Meshes (VV)]]
Les informations sur les triangles sont implicites et doivent être reconstruites à partir des informations présentes dans le tampon de sommets. Autant dire que niveau praticité et utilisation de la puissance de calcul, cette technique est peu efficace. Par contre, le tampon de sommet a l'avantage, avec cette technique, d'utiliser peu de mémoire. Les informations sur les arêtes et triangles étant implicites, elles ne sont pas mémorisées, ce qui économise de la place.
Mais il existe des méthodes pour que les informations sur les arêtes soient codées de manière explicite. L'idée est que deux sommets consécutifs dans le tampon de sommet soient reliés par une arête. Ainsi, les informations sur les arêtes n'ont plus à être codées dans le tampon de sommet, mais sont implicitement contenues dans l'ordre des sommets. Ces représentations sont appelées des ''Corner-tables''. Dans le domaine du rendu 3D, deux techniques de ce genre ont été utilisées : la technique des ''triangle fans'' et celle des ''triangle strips''.
La technique des '''triangles strip''' optimise le rendu de triangles placés en série, qui ont une arête et deux sommets en commun. L'optimisation consiste à ne stocker complètement que le premier triangle le plus à gauche, les autres triangles étant codés avec un seul sommet. Ce sommet est combiné avec les deux derniers sommets chargés par l'input assembler pour former un triangle. Pour gérer ces triangles strips, l'input assembler doit mémoriser dans un registre les deux derniers sommets utilisées. En mémoire, le gain est énorme : au lieu de trois sommets pour chaque triangle, on se retrouve avec un sommet pour chaque triangle, sauf le premier de la surface.
[[File:Triangle strip.png|centre|vignette|Triangle strip]]
La technique des '''triangles fan''' fonctionne comme pour le triangle strip, sauf que le sommet n'est pas combiné avec les deux sommets précédents. Supposons que je crée un premier triangle avec les sommets v1, v2, v3. Avec la technique des triangles strips, les deux sommets réutilisés auraient été les sommets v2 et v3. Avec les triangles fans, les sommets réutilisés sont les sommets v1 et v3. Les triangles fans sont utiles pour créer des figures comme des cercles, des halos de lumière, etc.
[[File:Triangle fan.png|centre|vignette|upright=2.0|Triangle fan]]
===Le tampon d'indices===
Enfin, nous arrivons à la dernière technique, qui permet de limiter l'empreinte mémoire tout en facilitant la manipulation de la géométrie. Cette technique est appelée la '''représentation face-sommet'''. Elle consiste à stocker les informations sur les triangles et sur les sommets séparément. Le tampon de sommet contient juste les coordonnées des sommets, mais ne dit rien sur la manière dont ils sont reliés. Les informations sur les triangles sont quant à elles mémorisées dans un tableau séparé appelé le ''tampon d'indices''. Ce dernier n'est rien de plus qu'une liste de triangles.
Dit comme cela, on ne voit pas vraiment où se trouve le gain en mémoire. On se retrouve avec deux tableaux : un pour les indices, un pour les vertices. Mais l'astuce tient au codage des données dans le tampon d'indices. Dans le tampon d'indices, un sommet n'est pas codé par ses trois coordonnées. Les sommets étant partagés entre plusieurs triangles, il y aurait beaucoup de redondance avec cette méthode. Pour un sommet partagé entre N triangles, on aurait N copies du sommet, une par triangle. Pour éviter cela, chaque sommet est codé par un indice, un numéro qui indique la position du sommet dans le tampon de sommet. Avec la technique du tampon d'indice, les coordonnées sont codées en un seul exemplaire, mais le tampon d'indice contiendra N exemplaires de l'indice. L'astuce est qu'un indice prend moins de place qu'un sommet : entre un indice et trois coordonnées, le choix est vite fait. Et entre 7 exemplaires d'un sommet, et 7 exemplaires d'un indice et un sommet associé, le gain en mémoire est du côté de la solution à base d'index.
: On pourrait remplacer les indices par des pointeurs, ce qui donnerait un cas particulier d'une structure de données connue sous le nom de vecteur de Liffe. Mais ce n'est pas très pratique et n'est pas utilisé dans le domaine du rendu 3D.
[[File:Mesh fv.jpg|centre|vignette|upright=2|Représentation face-sommet.]]
Avec un tampon d'indices, un sommet peut être chargé plusieurs fois depuis la mémoire vidéo. Pour exploiter cette propriété, les cartes graphiques intercalent une mémoire cache pour mémoriser les sommets déjà chargés : le '''cache de sommets'''. Chaque sommet est stocké dans ce cache avec son indice en guise de Tag. Pour profiter le plus possible de ce cache, les concepteurs de jeux vidéo peuvent changer l'ordre des sommets en mémoire. Sur les cartes graphiques assez anciennes, ce cache est souvent très petit, à peine 30 à 50 sommets. Et c'était de plus un cache très simple, allant d'une simple mémoire FIFO à des caches basiques (pas de politique de remplacement complexe, caches directement adressé, ...). Notons que ce cache a cependant été fortement modifié depuis que les unités de ''vertex shader'' ont été fusionnées avec les unités de ''pixel shaders''. Un tel cache se mariait bien avec des unités géométriques séparées des circuits de gestion des pixels, en raison de sa spécialisation.
[[File:Vertex cache.png|centre|vignette|upright=2.0|Cache de sommets.]]
==L'étape de T&L et les ''vertex shaders''==
L'étape de transformation-projection regroupe plusieurs manipulations différentes, mais qui ont pour point commun de demander des changements de repères. Par changement de repères, on veut dire que l'on passe d'un système de coordonnées à un autre. En tout, il existe trois changements de repères distincts qui sont regroupés dans l''''étape de transformation''' : un premier qui place chaque objet 3D dans la scène 3D, un autre qui centre la scène 3D du point de vue de la caméra, et un autre qui corrige la perspective.
Un changement de coordonnée s'effectue assez simplement en multipliant le vecteur (X, Y, Z) des coordonnées d'un sommet par une matrice adéquate. Il existe des matrices pour la translation, la mise à l'échelle, d'autres pour la rotation, une autre pour la transformation de la caméra, une autre pour l'étape de projection, etc. Un petit problème est que les matrices qui le permettent sont des matrices avec 4 lignes et 4 colonnes, et que la multiplication demande que le nombre de coordonnées du vecteur soit égal au nombre de colonnes. Pour résoudre ce petit problème, on ajoute une 4éme coordonnée aux sommets, la coordonnée homogène, qui ne sert à rien, et est souvent mise à 1, par défaut. Mais oublions de détail.
L'étape de transformation est souvent fusionnée avec l'étape d'éclairage par sommet, les deux demandant de faire des calculs assez similaires. Sur PC, la geforce 256 a été la première à intégrer une unité non-programmable pour gérer transformation et éclairage, appelée '''unité de T&L''' (''Transform & Lighting''). L'unité de T&L incorporait un ou plusieurs circuits de multiplication de matrices spécialisés pour l'étape de transformation. Ils sont composés d'un gros paquet de multiplieurs et d'additionneurs flottants. Elles prennent en entrée les données provenant de l'''input assembler'', ainsi que les matrices nécessaires, et fournissent en sortie les coordonnées des sommets transformés.
L'unité de T&L est devenue programmable dès la Geforce 3, première carte graphique à supporter les ''vertex shaders''. Il y a peu de généralités spécifiques pour les processeurs de ''vertex shaders'', tout a déjà été dit dans le chapitre sur les processeurs de ''shaders''. Tout au plus peut on dire que les cartes graphiques avaient autrefois des processeurs spécialisés dans l’exécution des ''vertex shaders'', distincts des processeurs pour les ''pixel shaders''. Mais cette époque est révolue avec les cartes graphiques actuelles. Par contre, on peut étudier un exemple de processeur de ''vertex shader'' d’antan.
==L'assemblage de primitives==
En sortie de l'étage précédent, on n'a que des sommets éclairés et colorisés, pas des triangles. Et les sommets ne sont pas dans l'ordre : deux sommets qui en sortent à la suite ne sont pas forcément dans le même triangle. Pour recréer des triangles, on doit lire les sommets dans l'ordre adéquat, par paquets de trois pour obtenir des triangles. C'est le rôle de l''''étape d'assemblage de primitives''' (''primitive assembly''), qui regroupe les sommets appartenant au même triangle, à la même primitive. L'assemblage des primitives est réalisée par un circuit fixe, non-programmable, qui utilise le tampon d'indice pour regrouper les sommets en primitives.
L'étape d'assemblage de primitives est suivie par un '''tampon de primitives''', dans lequel les triangles sont accumulés avant d'entrer dans le rastériseur. Le contenu du tampon de primitive varie suivant la carte graphique, mais il y a deux possibilités principales. La première est simplement un paquet de sommets avec un petit tampon d'indices associé. L'autre est simplement un paquet de sommets, avec des sommets dupliqués s'ils sont partagés par plusieurs triangles. La première solution fait un meilleur usage de la mémoire du tampon de primitive, l'autre est plus simple et plus rapide, plus simple d'utilisation pour le rastériseur.
[[File:Carte graphique en rendu immédiat.png|centre|vignette|upright=2|Carte graphique en rendu immédiat]]
{{NavChapitre | book=Les cartes graphiques
| prev=Le processeur de commandes
| prevText=Le processeur de commandes
| next=Le pipeline géométrique après DirectX 10
| nextText=Le pipeline géométrique après DirectX 10
}}{{autocat}}
iy0slhxvoezdjzi35d8lsfnhe31us3x
Les cartes graphiques/Les Render Output Target
0
67394
744770
743151
2025-06-15T01:23:44Z
Mewtow
31375
/* Le cache de profondeur */
744770
wikitext
text/x-wiki
Pour rappel, les étapes précédentes du pipeline graphiques manipulaient non pas des pixels, mais des fragments. Pour rappel, la distinction entre fragment et pixel est pertinente quand plusieurs objets sont l'un derrière l'autre. Si vous tracez une demi-droite dont l'origine est la caméra, et qui passe par le pixel, il arrive qu'elle intersecte la géométrie en plusieurs points. La couleur finale dépend de la couleur de tous ces points d'intersection. Intuitivement, l'objet le plus proche est censé cacher les autres et c'est donc lui qui décide de la couleur du pixel, mais cela demande de déterminer quel est l'objet le plus proche. De plus, certains objets sont transparents et la couleur finale est un mélange de la couleur de plusieurs points d'intersection.
Tout demande de calculer un pseudo-pixel pour chaque point d'intersection et de combiner leurs couleurs pour obtenir le résultat final. Les pseudo-pixels en question sont des '''fragments'''. Chaque fragment possède une position à l'écran, une coordonnée de profondeur, une couleur, ainsi que quelques autres informations potentiellement utiles. Les fragments attribués à un même pixel, qui sont à la même position sur l'écran, sont donc combinés pour obtenir la couleur finale de ce pixel. Pour résumer, la profondeur des fragments doit être gérée, de même que la transparence, etc.
Et c'est justement le rôle de l'étage du pipeline que nous allons voir maintenant. Ces opérations sont réalisées dans un circuit qu'on nomme le '''Raster Operations Pipeline''' (ROP), aussi appelé ''Render Output Target'', situé à la toute fin du pipeline graphique. Dans ce qui suit, nous utiliserons l'abréviation ROP pour simplifier les explications. Le ROP effectue quelques traitements sur les fragments, avant d'enregistrer l'image finale dans la mémoire vidéo.
==Les fonctions des ROP==
Les ROP incorporent plusieurs fonctionnalités qui sont assez diverses. Leur seul lien est qu'il est préférable de les implémenter en matériel plutôt qu'en logiciel, et en-dehors des unités de textures. Il s'agit de fonctionnalités assez simples, basiques, mais nécessaires au fonctionnement de tout rendu 3D. Elles ont aussi pour particularité de beaucoup accéder à la mémoire vidéo. C'est la raison pour laquelle le ROP est situé en fin de pipeline, proche de la mémoire vidéo. Voyons quelles sont ces fonctionnalités.
===La gestion de la profondeur (tests de visibilité)===
Le premier rôle du ROP est de trier les fragments du plus proche au plus éloigné, pour gérer les situations où un triangle en cache un autre (quand un objet en cache un autre, par exemple). Prenons un mur rouge opaque qui cache un mur bleu. Dans ce cas, un pixel de l'écran sera associé à deux fragments : un pour le mur rouge, et un pour le bleu. Vu que le mur de devant est opaque, seul le fragment de ce mur doit être choisi : celui du mur qui est devant. Et il s'agit là d'un exemple simple, mais il est fréquent qu'un objet soit caché par plusieurs objets. En moyenne, un objet est caché par 3 à 4 objets dans un rendu 3d de jeu vidéo.
Pour cela, chaque fragment a une coordonnée de profondeur, appelée la coordonnée z, qui indique la distance de ce fragment à la caméra. La coordonnée z est un nombre qui est d'autant plus petit que l'objet est près de l'écran. La profondeur est calculée à la rastérisation, ce qui fait que les ROP n'ont pas à la calculer, juste à trier les fragments en fonction de leur profondeur.
: On peut préciser qu'il existe des techniques alternatives pour coder la coordonnée de profondeur, qui se distinguent par le fait que la coordonnée z n'est pas proportionnelle à la distance entre le fragment et la caméra. Avec eux, la précision est meilleure pour les fragments proches de la caméra, et plus faible pour les fragments éloignés. Mais il s'agit là de détails assez mathématiques que je me permets de passer sous silence. Dans ce qui suit, nous allons juste parler de profondeur pour regrouper toutes ces techniques, conventionnelles ou alternatives.
[[File:Z-buffer no text.jpg|vignette|Z-buffer correspondant à un rendu]]
Pour savoir quels fragments sont à éliminer (car cachés par d'autres), la carte graphique utilise ce qu'on appelle un '''tampon de profondeur'''. Il s'agit d'un tableau, stocké en mémoire vidéo, qui mémorise la coordonnée z de l'objet le plus proche pour chaque pixel.
Par défaut, ce tampon de profondeur est initialisé avec la valeur de profondeur maximale. Au fur et à mesure que les objets seront calculés, le tampon de profondeur est mis à jour, conservant ainsi la trace de l'objet le plus proche de la caméra. Si jamais un fragment a une coordonnée z plus grande que celle du tampon de profondeur, cela veut dire qu'il est situé derrière un objet déjà rendu et le tampon de profondeur n'a pas à être mis à jour. Dans le cas contraire, le fragment est plus près de la caméra et sa coordonnée z remplace l'ancienne valeur z dans le tampon de profondeur.
[[File:Z-buffer.svg|centre|vignette|upright=2.0|Illustration du processus de mise à jour du Z-buffer.]]
Rappelons que la coordonnée de profondeur est codée sur quelques bits, allant de 16 bits pour les anciennes cartes graphiques, à 24/32 bits pour les cartes plus récentes. De nos jours, les Z-buffer de 16 bits sont abandonnés et toutes les cartes graphiques utilisent des coordonnées z de 24 à 32 bits. La raison est que les Z-buffer de 16 bits ont une précision insuffisante, ce qui fait que des artefacts peuvent survenir. Si deux objets sont suffisamment proches, le tampon de profondeur n'a pas la précision suffisante pour discriminer les deux objets. Pour lui, les deux objets sont à la même place. Conséquence : il faut bien choisir un des deux objets et ce choix se fait pixel par pixel, ce qui fait des artefacts visuels apparaissent. On parle alors de '''''z-fighting'''''. Voici ce que cela donne :
[[File:Z-fighting.png|centre|vignette|Z-fighting]]
===La gestion de la transparence : test alpha et ''alpha blending''===
En premier lieu, les ROPs s'occupent de la gestion de la transparence. La transparence/opacité d'un pixel/texel est codée par un nombre, la '''composante alpha''', qui est ajouté aux trois couleurs RGB. Plus la composante alpha est élevée, plus le pixel est opaque. En clair, tout fragment contient une quatrième couleur en plus des couleurs RGB, qui indique si le fragment est plus ou moins transparent. La gestion de la transparence par les ROP est le fait de plusieurs fonctionnalités distinctes, les deux principales étant le test alpha et l'''alpha blending''.
L''''''alpha test''''' est une technique qui permet d'annuler le rendu d'un fragment en fonction de sa transparence. Si la composante alpha est en-dessous d'un seuil, le fragment est simplement abandonné. Chaque fragment passe une étape de test alpha qui vérifie si la valeur alpha est au-dessus de ce seuil ou non. S'il ne passe pas le test, le fragment est abandonné, il ne passe pas à l'étape de test de profondeur, ni aux étapes suivantes. Il s'agit d'une technique binaire de gestion de la transparence, qui est complétée par d'autres techniques. De nos jours, cette technologie est devenue obsolète.
Elle optimisait le rendu de textures où les pixels sont soit totalement opaques, soit totalement transparents. Un exemple est le rendu du feuillage dans un jeu 3D : on a une texture de feuille plaquée sur un rectangle, les portions vertes étant totalement opaques et le reste étant totalement transparent. L'avantage est que cela évitait de mettre à jour le tampon de profondeur pour des fragments totalement transparents.
Maintenant, le test alpha ne permet pas de gérer des situations où on voit quelque chose à travers un objet transparent. Si un fragment transparent est placé devant un autre fragment, la couleur du pixel sera un mélange de la couleur du fragment transparent, et de la couleur du (ou des) fragments placé·s derrière. Le calcul à effectuer est très simple, et se limite en une simple moyenne pondérée par la transparence de la couleur des deux pixels. On parle alors d''''''alpha blending'''''.
[[File:Texture splatting.png|centre|vignette|upright=2.0|Application de textures.]]
Les fragments arrivant par paquets, calculés uns par uns par les unités de texture et de shaders, le calcul des couleurs est effectué progressivement. Pour cela, la carte graphique doit mettre en attente les résultats temporaires des mélanges pour chaque pixel. C'est le rôle du '''tampon de couleur''', l'équivalent du tampon de profondeur pour les couleurs des pixels. À chaque fragment reçu, le ROP lit la couleur du pixel associé dans le tampon de couleur, fait ou non la moyenne pondérée avec le fragment reçu et enregistre le résultat. Ces opérations de test et d'''alpha blending'' sont effectuées par un circuit spécialisé qui travaille en parallèle des circuits de calcul de la profondeur.
Il faut noter que le rendu de la transparence se marie assez mal avec l'usage d'un tampon de profondeur. Le tampon de profondeur marche très bien quand on a des fragments totalement opaques : il a juste à mémoriser la coordonnée z du pixel le plus proche. Mais avec des fragments transparents, les choses sont plus compliquées, car plusieurs fragments sont censés être visibles, et on ne sait pas quelle coordonnée z stocker. L'interaction entre profondeur et transparence est réglée par diverses techniques. Avec l'''alpha blending'', c'est la cordonnée du fragment le plus proche qui est mémorisée dans le tampon de profondeur.
===Le tampon de ''stencil''===
Le '''''stencil''''' est une fonctionnalité des API graphiques et des cartes graphiques depuis déjà très longtemps. Il sert pour générer des effets graphiques très variés, qu'il serait vain de lister ici. Il a notamment été utilisé pour combattre le phénomène de ''z-fighting'' mentionné plus haut, il est utilisé pour calculer des ombres volumétriques (le moteur de DOOM 3 en faisait grand usage à la base), des réflexions simples, des lightmaps ou shadowmaps, et bien d'autres.
Pour le résumer, on peut le voir comme une sorte de tampon de profondeur programmable, dans la coordonnée z est remplacée par une valeur arbitraire, dont le programmeur peut faire ce qu'il veut. La valeur est de plus une valeur entière, pas flottante. L'idée est que chaque pixel/fragment se voit attribuer une valeur entière, généralement codée sur un octet, que les programmeurs peuvent faire varier à loisir. L'octet ajouté est appelé l''''octet de ''stencil'''''. L'octet a une certaine valeur, qui est calculée par la carte graphique au fur et à mesure que les fragments sont traités. Il ne remplace pas la coordonnée de profondeur, mais s'ajoute à celle-ci.
L'ensemble des octets de ''stencil'' est mémorisée dans un tableau en mémoire vidéo, avec un octet par pixel du ''framebuffer''. Le tableau porte le nom de '''tampon de ''stencil'''''. Il s'agit d'un tableau distinct du tampon de profondeur ou du tampon de couleur, du moins en théorie. Dans les faits, les techniques liées au tampon de ''stencil'' font souvent usage du tampon de profondeur, pour beaucoup d'effets graphiques avancés. Aussi, le tampon de ''stencil'' est souvent fusionné avec le tampon de profondeur. L'ensemble forme un tableau qui associe 32 bits à chaque" pixel : 24 bits pour une coordonnée z, 8 pour l'octet de ''stencil''.
Chaque fragment a sa propre valeur de ''stencil'' qui est calculée par la carte graphique, généralement par les ''shaders''. Lors du passage d'un fragment les ROPs, la carte graphique lit le pixel associé dans le tampon de ''stencil''. Puis il compare l'octet de ''stencil'' avec celui du fragment traité. Si le test échoue, le fragment ne passe pas à l'étape de test de profondeur et est abandonné. S'il passe, le tampon de ''stencil'' est mis à jour.
Par mis à jour, on veut dire que le ROP peut faire diverses manipulations dessus : l'incrémenter, le décrémenter, le mettre à 0, inverser ses bits, remplacer par l'octet de ''stencil'' du fragment, etc. Les opérations possibles sont bien plus nombreuses qu'avec le tampon de profondeur, qui se contente de remplacer la coordonnée z par celle du fragment. C'est toujours possible, on peut remplacer l'octet de ''stencil'' dans le tampon de ''stencil'' par celui du fragment s'il passe le test. Mais pour les techniques de rendu plus complexes, c'est une autre opération qui est utilisée, comme incrémenter l'octet dans le tampon de ''stencil''.
===Les effets de brouillard===
Les '''effets de brouillard''' sont des effets graphiques assez intéressants. Ils sont nécessaires dans certains jeux vidéo pour l'ambiance (pensez à des jeux d'horreur comme Silent Hill), mais ils ont surtout été utilisés pour économiser des calculs. L'idée est de ne pas calculer les graphismes au-delà d'une certaine distance, sans que cela se voie.
L'idée est d'avoir un ''view frustum'' limité : le plan limite au-delà duquel on ne voit pas les objets est assez proche de la caméra. Mais si le plan limite est trop proche, cela donnera une cassure inesthétique dans le rendu. Pour masquer cette cassure, les programmeurs ajoutaient un effet de brouillard. Les objets au-delà du plan limite étaient totalement dans le brouillard, puis ce brouillard se réduisait progressivement en se rapprochant de la caméra, avant de s'annuler à partir d'une certaine distance.
Pour calculer le brouillard, on mélange la couleur finale du pixel avec une ''couleur de brouillard'', la couleur de brouillard étant pondérée par la profondeur. Au-delà d'une certaine distance, l'objet est intégralement dans le brouillard : le brouillard domine totalement la couleur du pixel. En dessous d'une certaine distance, le brouillard est à zéro. Entre les deux, la couleur du brouillard et de l'objet devront toutes les deux être prises en compte dans les calculs. La formule de calcul exacte varie beaucoup, elle est souvent linéaire ou exponentielle.
Notons que ce calcul implique à la fois de l'''alpha blending'' mais aussi la coordonnée de profondeur, ce qui en fait que son implémentation dans les ROPs est l'idéal. Aussi, les premières cartes graphiques calculaient le brouillard dans les ROP, en fonction de la coordonnée de profondeur du fragment. De nos jours, il est calculé par les ''pixel shaders'' et les ROP n'incorporent plus de technique de brouillard spécialisée. Vu que les pixels shaders peuvent s'en charger, cela fait moins de circuits dans les ROPs pour un cout en performance mineur. Et ce d'autant plus que les effets de brouillard sont devenus assez rares de nos jours. Autant les émuler dans les pixels shaders que d'utiliser des circuits pour une fonction devenue anecdotique.
===Les autres fonctions des ROPs===
Les ROPs gèrent aussi des techniques de '''''dithering''''', qui permettent d'adoucir des images lorsqu'elles sont redimensionnées et stockées avec une précision plus faible que la précision de calcul.
Les ROPS implémentent aussi des techniques utilisées sur les ''blitters'' des anciennes cartes d'affichage 2D, comme l'application d''''opérations logiques''' sur chaque pixel enregistré dans le framebuffer. Les opérations logiques en question peuvent prendre une à deux opérandes. Les opérandes sont soit un pixel lu dans le ''framebuffer'', soit un fragment envoyé au ROP. Les opérations logiques à une opérande peuvent inverser, mettre à 0 ou à 1 le pixel dans le framebuffer, ou faire la même chose sur le fragment envoyé en opérande. Les opérations à deux opérandes lisent un pixel dans le framebuffer, et font un ET/OU/XOR avec le fragment opérande (une des deux opérandes peut être inversée). Elles sont utilisées pour faire du traitement d'image ou du rendu 2D, rarement pour du rendu 3D.
Les ROPs gèrent aussi des '''masques d'écritures''', qui permettent de décider si un pixel doit être écrit ou non en mémoire. Il est possible d'inhiber certaines écritures dans le tampon de profondeur ou le tampon de couleur, éventuellement le tampon de stencil. Inhiber la mise à jour d'un pixel dans le tampon de profondeur est utile pour gérer la transparence. Si un pixel est transparent, même partiellement, il ne faut pas mettre à jour le tampon de profondeur, et cela peut être géré par ce système de masquage. Les masquages des couleurs permettent de ne modifier qu'une seule composante R/G/B au lieu de modifier les trois en même temps, pour faire certains effets visuels.
==L'architecture matérielle d'un ROP==
Les ROP contiennent des circuits pour gérer la profondeur des fragments. Il effectuent un test de profondeur, à savoir que les fragments correspondant à un même pixel sont comparés pour savoir lequel est devant l'autre. Ils contiennent aussi des circuits pour gérer la transparence des fragments. Le ROP gère aussi l'antialiasing, de concert avec l'unité de rastérisation. D'autres fonctionnalités annexes sont parfois implémentées dans les ROP. Par exemple, les vielles cartes graphiques implémentaient les effets de brouillards dans les ROPs. Le tout est suivi d'une unité qui enregistre le résultat final en mémoire, où masques et opérations logiques sont appliqués.
Les différentes opérations du ROP doivent se faire dans un certain ordre. Par exemple, gérer la transparence demande que les calculs de profondeur se fassent globalement après ou pendant l'''alpha blending''. Ou encore, les masques et opérations logiques se font à la toute fin du rendu. L'ordre des opérations est censé être le suivant : test ''alpha'', test du ''stencil'', test de profondeur, ''alpha blending''. Du moins, la carte graphique doit donner l'impression que c'est le cas. Elle peut optimiser le tout en traitant le tampon de profondeur, de couleur et de ''stencil'' en même temps, mais donner les résultats adéquats.
[[File:Render Output Pipeline-processor.png|centre|vignette|upright=2|Render Output Pipeline-processor]]
[[File:GeForce 6800 Pixel blending.png|droite|thumb|R.O.P des GeForce 6800.]]
Un ROP est typiquement organisé comme illustré ci-dessous et ci-contre. Il récupère les fragments calculés par les pixels shaders et/ou les unités de texture, via un circuit d'interconnexion spécialisé. Chaque ROP est connecté à toutes les unités de ''shader'', même si la connexion n'est pas forcément directe. Toute unité de ''shader'' peut envoyer des pixels à n'importe quel ROP. Les circuits d'interconnexion sont généralement des réseaux d'interconnexion de type ''crossbar'', comme illustré ci-contre (le premier rectangle rouge).
Notons que les circuits de gestion de la profondeur et de la transparence sont séparés dans les schémas ci-contre et ci-dessous. Il s'agit là d'une commodité qui ne reflète pas forcément l'implémentation matérielle. Et si ces deux circuits sont séparés, ils communiquent entre eux, notamment pour gérer la profondeur des fragments transparents.
Les circuits de gestion de la profondeur et de la couleur gèrent diverses techniques de compression pour économiser de la mémoire et de la bande passante mémoire. Ajoutons à cela que ces deux unités contiennent des caches spécialisés, qui permettent de réduire fortement les accès mémoires, très fréquents dans cette étape du pipeline graphique.
Il est à noter que sur certaines cartes graphiques, l'unité en charge de calculer les couleurs peut aussi servir à effectuer des comparaisons de profondeur. Ainsi, si tous les fragments sont opaques, on peut traiter deux fragments à la fois. C'était le cas sur la Geforce FX de Nvidia, ce qui permettait à cette carte graphique d'obtenir de très bonnes performances dans le jeu DOOM3.
==Le circuit de gestion de la profondeur==
La profondeur est gérée par un circuit spécialisé, qui met à jour le tampon de profondeur. Pour chaque fragment, le ROP lit le tampon de profondeur, récupère la coordonnée z du pixel de destination dedans, compare celle-ci avec celle du fragment, et décide s'il faut mettre à jour ou non le tampon de profondeur. En conséquence, ce circuit effectue beaucoup de lectures et écritures en mémoire vidéo. Or, la bande passante mémoire est limitée et de nombreuses optimisations permettent d'optimiser le tout.
===La z-compression===
Une première solution pour économiser la bande passante mémoire est la technique de '''z-compression''', qui compresse le tampon de profondeur. Les techniques de ''z-compression'' découpent le tampon de profondeur en ''tiles'', en blocs carrés, qui sont compressés séparément les uns des autres. Par exemple, la ''z-compression'' des cartes graphiques ATI radeon 9800, découpait le tampon de profondeur en ''tiles'' de 8 * 8 fragments, et les encodait avec un algorithme nommé DDPCM (''Differential differential pulse code modulation''). Le découpage en ''tiles'' ressemble à ce qui est utilisé pour les textures, pour les mêmes raisons : le calcul d'adresse est simplifié, compression et décompression sont plus rapides, etc.
Précisons que cette compression ne change pas la taille occupée par le tampon de profondeur, mais seulement la quantité de données lue/écrite dans le tampon de profondeur. La raison à cela est simple : les ''tiles'' ont une place fixe en mémoire. Par exemple, si une ''tile'' non-compressée prend 64 octets, on trouvera une ''tile'' tous les 64 octets en mémoire vidéo, afin de simplifier les calculs d'adresse, afin que le ROP sache facilement où se trouve la ''tile'' à lire/écrire. Avec une vraie compression, les ''tiles'' se trouveraient à des endroits très variables d'une image à l'autre. Par contre, la z-compression réduit la quantité de données écrite dans le tampon de profondeur. Par exemple, au lieu d'écrire une ''tile'' non-compressée de 64 octets, on écrira une ''tile'' de seulement 6 octets, les 58 octets restants étant pas lus ou écrits. On obtient un gain en performance, pas en mémoire.
Le format de compression ajoute souvent deux bits par ''tile'', qui indiquent si la ''tile'' est compressée ou non, et si elle vaut zéro ou non. Le bit qui indique si la ''tile'' est compressée permet de laisser certaines ''tiles'' non-compressés, dans le cas où la compression ne permet pas de gagner de la place. Pour le bit qui indique si la ''tile'' ne contient que des 0, elle accélère la remise à zéro du tampon de profondeur. Au lieu de réellement remettre tout le tampon de profondeur à 0, il suffit de réécrire un bit par bloc. Le gain en nombre d'accès mémoire peut se révéler assez impressionnant.
[[File:AMD HyperZ.svg|centre|vignette|upright=2|AMD HyperZ]]
===Le cache de profondeur===
Une autre solution complémentaire ajoute une ou plusieurs mémoires caches dans le ROP, dans le circuit de profondeur. Ce '''cache de profondeur''' stocke des portions du tampon de profondeur qui ont été lues ou écrite récemment. Comme cela, pas besoin de les recharger plusieurs fois : on charge un bloc une fois pour toutes, et on le conserve pour gérer les fragments qui suivent.
Sur certaines cartes graphiques, les données dans le cache de profondeur sont stockées sous forme compressées dans le cache de profondeur, là encore pour augmenter la taille effective du cache. D'autres cartes graphiques ont un cache qui stocke des données décompressées dans le cache de profondeur. Tout est question de compromis entre accès rapide au cache et augmentation de la taille du cache.
Il faut savoir que les autres unités de la carte graphique peuvent lire le tampon de profondeur, en théorie. Cela peut servir pour certaines techniques de rendu, comme pour le ''shadowmapping''. De ce fait, il arrive que le cache de profondeur contienne des données qui sont copiées dans d'autres caches, comme les caches des processeurs de shaders. Le cache de profondeur n'est pas gardé cohérent avec les autres caches du GPU, ce qui signifie que les écritures dans le cache de profondeur ne sont pas propagées dans les autres caches du GPU. Si on modifie des données dans ce cache, les autres caches qui ont une copie de ces données auront une version périmée de la donnée. C'est souvent un problème, sauf dans le cas du cache de profondeur, pour lequel ce n'est pas nécessaire. Cela évite d'implémenter des techniques de cohérence des caches couteuses en circuits et en performance, alors qu'elles n'auraient pas d'intérêt dans ce cas précis.
{{NavChapitre | book=Les cartes graphiques
| prev=Les unités de texture
| prevText=Les unités de texture
| next=Le support matériel du lancer de rayons
| nextText=Le support matériel du lancer de rayons
}}{{autocat}}
tv4l5xma8ztpfl7vet51j9ox70mg038
Fonctionnement d'un ordinateur/Les codes de détection/correction d'erreur
0
69061
744778
739727
2025-06-15T01:38:09Z
Mewtow
31375
744778
wikitext
text/x-wiki
Dans le chapitre précédent, nous avons vu comment l'ordinateur faisait pour coder des nombres. Les nombres en question sont mémorisés dans des mémoires plus ou moins complexes. Et ces mémoires ne sont pas des dispositifs parfaits, elles peuvent subir des corruptions. Les corruptions en question se traduisent le plus souvent par l'inversion d'un bit : un bit censé être à 0 passe à 1, ou inversement. Le terme anglais pour ce genre de corruption est un '''''bitflip''''', mais nous utiliserons le terme général "erreur", pour désigner ces ''bitflips'' .
Pour donner un exemple, on peut citer l'incident du 18 mai 2003 dans la petite ville belge de Schaerbeek. Lors d'une élection, la machine à voter électronique enregistra un écart de 4096 voix entre le dépouillement traditionnel et le dépouillement électronique. La faute à un rayon cosmique, qui avait modifié l'état d'un bit de la mémoire de la machine à voter.
Mais qu'on se rassure : certains codages des nombres permettent de détecter et corriger ces ''bitflips''. Pour cela, les '''codes de détection et de correction d'erreur''' ajoutent des bits de correction/détection d'erreur aux données. Les bits en question sont calculés à partir des données à transmettre/stocker et 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. Ils sont peu utilisées dans les ordinateurs grand public, mais elles sont très importantes dans les domaines demandant des ordinateurs fiables, comme dans l'automobile, l'aviation, le spatial, l'industrie, etc. Et ce chapitre va expliquer ce qu'elles sont, et aussi comment les circuits élaborés permettent de s'en protéger.
Dans ce qui suit, nous parlerons parfois de codes ECC, bien que ce soit un abus de langage : ECC est l'abréviation de ''Error Correction Code'', mais certains de ces codes se contentent de détecter qu'une erreur a eu lieu, sans la corriger. Ceci étant dit, les codes ECC sont utilisés sur les mémoires comme les mémoires RAM, parfois sur les disques durs ou les SSDs, afin d'éviter des corruptions de données. Ils sont aussi utilisés quand on doit transmettre des données, que ce soit sur les bus de communication ou sur un support réseau. Par exemple, les données transmises via internet incorporent un code ECC pour détecter les erreurs de transmission, idem pour les transmissions sur un réseau local.
==Le bit de parité==
Nous allons commercer par aborder le '''bit de parité/imparité'''. Le bit de parité est un bit ajouté à la donnée à mémoriser/transmettre. Sa valeur est telle que le nombre stocké (bit de parité inclus) contient toujours un nombre pair de bits à 1. Ainsi, le bit de parité vaut 0 si le nombre contient déjà un nombre pair de 1, et 1 si le nombre de 1 est impair.
Si un bit s'inverse, quelle qu'en soit la raison, la parité du nombre total de 1 est modifié : ce nombre deviendra impair si un bit est modifié. Et ce qui est valable avec un bit l'est aussi pour 3, 5, 7, et pour tout nombre impair de bits modifiés. Mais tout change si un nombre pair de bit est modifié : la parité ne changera pas. Il permet de détecter des corruptions qui touchent un nombre impair de bits. Si un nombre pair de bit est modifié, il est impossible de détecter l'erreur avec un bit de parité. Ainsi, on peut vérifier si un bit (ou un nombre impair) a été modifié : il suffit de vérifier si le nombre de 1 est impair. Il faut noter que le bit de parité, utilisé seul, ne permet pas de localiser le bit corrompu.
Le '''bit d'imparité''' est similaire au bit de parité, si ce n'est que le nombre total de bits doit être impair, et non pair comme avec un bit de parité. Sa valeur est l'inverse du bit de parité du nombre : quand le premier vaut 1, le second vaut 0, et réciproquement. Mais celui-ci n'est pas meilleur que le bit de parité : on retrouve l'impossibilité de détecter une erreur qui corrompt un nombre pair de bits.
[[File:Bit de parité.jpg|vignette|Valeurs valides et invalides avec un bit de parité. Les valeurs valides sont en vert, les autres en noir.]]
Il est maintenant temps de parler de si un bit de parité est efficace ou non. Que ce soit avec un bit de parité ou d'imparité, environ la moitié des valeurs encodées sont invalides. En effet, si on prend un nombre codé sur N bits, bit de parité, inclut, on pourra encoder 2^n valeurs différentes. La moitié d'entre elle aura un bit de parité à 0, l'autre un bit de parité à 1. Et la moitié aura un nombre de bit à 1 qui soit pair, l'autre un nombre impair. En faisant les compte, seules la moitié des valeurs seront valides. Le diagramme ci-contre montre le cas pour trois bits, avec deux bits de données et un bit de parité.
==L'octet/mot de parité et ses variantes==
L''''octet de parité''' est une extension de la technique du bit de parité, qui s'applique à plusieurs octets. L'idée de base est de calculer un bit de parité par octet, et c'est plus ou moins ce que fait le mot de parité, mais avec quelques subtilités dans les détails.
[[File:Illustration checksum 1.png|centre|vignette|upright=2|Illustration du mot de parité.]]
La technique s'applique en général sur toute donnée qu'on peut découper en blocs d'une taille fixe. Dans les exemples qui vont suivre, les blocs en question seront des octets, pour simplifier les explications, mais il est parfaitement possible de prendre des blocs plus grands, de plusieurs octets. La méthode fonctionne de la même manière. On parle alors de '''mot de parité''' et non d'octet de parité.
===Le calcul du mot de parité===
Le calcul du mot de parité se calcule en disposant chaque octet l'un au-dessus des autres, le tout donnant un tableau dont les lignes sont des octets. Le mot de parité se calcule en calculant le bit de parité de chaque colonne du tableau, et en le plaçant en bas de la colonne. Le résultat obtenu sur la dernière ligne est un octet de parité.
* 1100 0010 : nombre ;
* 1000 1000 : nombre ;
* 0100 1010 : nombre ;
* 1001 0000 : nombre ;
* 1000 1001 : nombre ;
* 1001 0001 : nombre ;
* 0100 0001 : nombre ;
* 0110 0101 : nombre ;
* ------------------------------------
* 1010 1100 : '''octet de parité'''.
Le calcul de l'octet de parité se fait en utilisant des opérations XOR. Pour rappel, une opération XOR est équivalente à une addition binaire dans laquelle on ne tiendrait pas compte des retenues. L'opération prend deux bits et effectue le calcul suivant :
* 0 <math>\oplus</math> 0 = 0 ;
* 0 <math>\oplus</math> 1 = 1 ;
* 1 <math>\oplus</math> 0 = 1 ;
* 1 <math>\oplus</math> 1 = 0.
En faisant un XOR entre deux octets, on obtient l'octet de parité des deux octets opérandes. Et cela se généralise à N opérandes : il suffit de faire un XOR entre les opérandes pour obtenir l'octet de parité de ces N opérandes. Il suffit de faire un XOR entre les deux premières opérandes, puis de faire un XOR entre le résultat et la troisième opérande, puis de refaire un XOR entre le nouveau résultat et le quatrième opérande, et ainsi de suite. Le calcul du mot de parité se fait aussi avec des opérations XOR, cela marche au-delà de l'octet.
===La récupération des données manquantes/effacées===
L'avantage de cette technique est qu'elle permet de reconstituer une donnée manquante. Par exemple, dans l'exemple précédent, si une ligne du calcul disparaissait, on pourrait la retrouver à partir du mot de parité. Il suffit de déterminer, pour chaque colonne, quel valeur 0/1 est compatible avec la valeur du bit de parité associé. C'est d'ailleurs pour cette raison que le mot de parité est utilisé sur les disques durs montés en RAID 3, 5 6, et autres. Grâce à elle, si un disque dur ne fonctionne plus, on peut retirer le disque dur endommagé et reconstituer ses données.
Pour cela, il faut faire faire XOR entre les données non-manquantes et le mot de parité. Pour comprendre pourquoi cela fonctionne, il faut savoir deux choses : faire un XOR entre un nombre et lui-même donne 0, faire un XOR entre une opérande et zéro redonne l'opérande comme résultat. Si on XOR un nombre avec le mot de parité, cela va annuler la présence de ce nombre (son XOR) dans le mot de parité : le résultat correspondra au mot de parité des nombres, nombre xoré exclu. Ce faisant, en faisant un XOR avec tous les nombres connus, ceux-ci disparaîtront du mot de parité, ne laissant que le nombre manquant. Un exemple sera certainement plus parlant.
Prenons le cas où on calcule l'octet de parité de quatre octets nommés O1, O2, O3 et O4, et notant le résultat <math>O_\text{parité}</math>. On a alors :
: <math>O_\text{parité} = O_1 \oplus O_2 \oplus O_3 \oplus O_4</math>
Maintenant, imaginons que l'on veuille retrouver la valeur du second octet O2, qui a été corrompu ou perdu. Dans ce cas, on fait un XOR avec O1, O3 et enfin O4 :
: <math>O_\text{parité} \oplus O_1 \oplus O_3 \oplus O_4</math>
On injecte alors l'équation <math>O_\text{parité} = O_1 \oplus O_2 \oplus O_3 \oplus O_4</math>.
: <math>(O_1 \oplus O_2 \oplus O_3 \oplus O_4) \oplus O_1 \oplus O_3 \oplus O_4</math>
On réorganise les termes :
: <math>(O_1 \oplus O_1) \oplus (O_2 \oplus O_2) \oplus (O_4 \oplus O_4) \oplus O_2</math>
On se rappelle que A XOR A = 0, ce qui simplifie grandement le tout :
: <math>O_2</math>
On retrouve bien l'octet manquant.
==La combinaison d'un mot de parité avec plusieurs bits de parité==
Avec un octet/mot de parité, on peut détecter qu'une erreur a eu lieu, mais aussi récupérer un octet/mot effacé. Mais si un bit est modifié, on ne peut pas corriger l'erreur. En effet, on ne sait pas détecter quel octet a été modifié par l'erreur. Maintenant, ajoutons un bit de parité à chaque octet, en plus de l'octet de parité.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || 1 || 0 || 1 || 1 || 0 || 0 || 1
! 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité
! 1 !! 0 !! 1 !! 0 !! 0 !! 1 !! 0 !! 1 !!
|}
En faisant cela, on peut détecter qu'un bit a été modifié, mais aussi corriger l'erreur assez simplement. En cas d'erreur, deux bits de parité seront faussés : celui associé à l'octet, celui dans l'octet de parité. On peut alors détecter le bit erroné. Une autre méthode est de regarder les bits de parité associés aux octets, pour détecter l'octet erroné. Reste alors à corriger l'erreur, en supprimant l'octet invalide et en récupérant l'octet initial en utilisant le mot de parité.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || 1 || 0 || bgcolor="#00FF00" | 0 || 1 || 0 || 0 || 1
! style="background: #00FF00;" | 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! 0 !! 1 !! style="background: #00FF00;" | 0 !! 0 !! 1 !! 0 !! 1 !!
|}
===Le nombre de bits de parité total est élevé===
Le tout demande d'utiliser beaucoup de bits de parité. Pour N octets, il faut un bit de parité par octet et un octet de parité, ce qui donne N + 8 bits de parité. Pour 64 bits, soit 8 octets, cela fait 16 bits de parité nécessaires, soit 25% de bits en plus. Maintenant, prenons le cas général où on n'utilise pas des octets, mais des mots de M bits, plus longs ou plus courts qu'un octet. Dans ce cas, on a N + M bits de parité.
Notons que la technique peut s'appliquer avec des octets ou des ''nibbles'', si on organise les bits correctement. Par exemple, prenons un ''nibble'' (4 bits). On peut l'organiser en un carré de deux bits de côté et ajouter : un bit de parité par colonne, un bit de parité par colonne (le mot de parité). Le tout donne 4 bits de parité, pour 4 bits de données : on double la taille de la donnée. Il est aussi possible de faire pareil avec un octet, l'organisant en deux lignes de 4 bits. Le résultat est de 6 bits de parité, ce qui est un petit peu mieux qu'avec un ''nibble'' : on passe à 3/4 de bits de plus.
{|class="wikitable"
|+ Exemple d'octet avec parité ajoutée
|-
| 0 || 0 || 1 || 1
! 0
|-
| 1 || 0 || 0 || 1
! 0
|-
! 1 !! 0 !! 1 !! 0 !!
|}
Il est possible de faire la même chose pour des données de plusieurs octets. Pour un nombre de 16 bits, l'idéal est de faire 4 lignes de 4 bits chacune, ce qui fait 8 bits de parité au total. Pour 32 bits, on passe à 12 bits de parité, etc. Au total, voici la quantité de bits de parité nécessaires suivant la longueur de la donnée :
{|class="wikitable"
|+ Exemple d'octet avec parité ajoutée
|-
! 4 !! 8 !! 16 !! 32 !! 64 !! 128 !! 256 !! ...
|-
| 4 || 6 || 8 || 12 || 16 || 24 || 32 || ...
|}
Il existe cependant des techniques plus économes, que nous allons voir dans ce qui suit. Par plus économes, il faut comprendre qu'elles utilisent moins de bits de parité, pour une fiabilité identique, voire meilleure. C'est là un défaut de la technique précédente : elle utilise beaucoup de bits de parités pour pas grand chose.
===Les capacités de correction de la technique===
Notons que cette solution permet de corriger plus d'une erreur. Dans le pire des cas, on peut détecter et corriger une erreur. Si toutes les erreurs sont toutes dans le même mot/octet, alors on peut récupérer l'octet manquant. Le bit de parité permet de détecter un nombre impair d'erreur, soit 1, 3, 5, 7, ... erreurs. Il faut donc que le nombre d'erreurs dans l'octet soit impair.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || bgcolor="#00FF00" | 1 || 0 || bgcolor="#00FF00" | 0 || 1 || 0 || 0 || style="background: #00FF00;" | 1
! style="background: #00FF00;" | 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! style="background: #00FF00;" | 0 !! 1 !! style="background: #00FF00;" | 0 !! 0 !! 1 !! 0 !! style="background: #00FF00;" | 1 !!
|}
Par contre, si le nombre d'erreurs dans un octet est pair, alors le bit de parité associé à l'octet ne remarque pas l'erreur. On sait sur quelles colonnes sont les erreurs, pas la ligne.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || bgcolor="#00FF00" | 1 || 0 || bgcolor="#00FF00" | 0 || 1 || 0 || 0 || 1
! 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! style="background: #FF0000;" | 0 !! 1 !! style="background: #FF0000;" | 0 !! 0 !! 1 !! 0 !! 1 !!
|}
De même, si les erreurs touchent deux octets, alors on ne peut rien corriger. On peut détecter les erreurs, mais pas les corriger.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || bgcolor="#00FF00" | 1 || 0 || 1 || 1
! style="background: #FF0000" | 0
|-
| Octet 3
| 1 || bgcolor="#00FF00" | 1 || 0 || 0 || 1 || 0 || 0 || 1
! style="background: #FF0000" | 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! style="background: #FF0000" | 0 !! 1 !! 0 !! style="background: #FF0000" | 0 !! 1 !! 0 !! 1 !!
|}
Pour résumer, pour des mots de M bits, on peut corriger entre 1 et M/2 erreurs. Pour un octet, cela permet de détecter 1 erreur, 3/5/7 erreurs si elles ont lieu dans le même octet.
===La protection des bits de parité===
Avec la méthode précédente, les bits de parité ne sont pas protégés : la moindre corruption des bits de parité fait que la méthode ne marche plus. Pour cela, il y a une solution toute simple : calculer un bit de parité qui tient compte de tous les bits de parité. Ainsi, si un bit de parité est corrompu, alors ce super-bit de parité détecterait l'erreur.
Dans les tableaux précédents, cela revient à ajouter un bit de parité dans la case tout en bas à droite. Le bit de parité calculé à partie des autres bits de parité est en rouge dans le tableau suivant. Il peut se calculer de plusieurs manières. La plus simple est calculer le bit de parité des 6 bits de parité, ceux de l'octet de parité et les autres. En faisant ainsi, on ganratit que tous les bits de parités sont protégés.
{|class="wikitable"
|+ Exemple avec un octet
|-
| 0 || 0 || 1 || 1 || class="f_vert" | 0
|-
| 1 || 0 || 0 || 1 || class="f_vert" | 0
|-
| class="f_vert" | 1 || class="f_vert" | 0|| class="f_vert" | 1 || class="f_vert" | 0 || class="f_rouge" | 0
|}
Avec cette technique, il faut faire la différence entre les '''bits de parité primaire''', qui calculent la parité de tout ou partie des données, et le '''bit de parité secondaire''', qui est calculé à partie des bits de parité primaire. Généralement, les codes correcteurs/détecteurs d'erreur avec des bits de parité secondaires sont assez peu efficaces, que ce soit en termes de fiabilité ou d'économies de bits. Ils utilisent beaucoup de bits et ne protègent que peu contre les erreurs. De plus, les bits de parité secondaires ne font que repousser le problème : le bit de parité secondaire peut être modifié lui aussi ! Les bits de parité secondaires ne protègent que contre la modification des bits de parité primaire, mais pas de leurs modifications propres. Mais qu'on se rassure : on peut protéger les bits de parité primaire sans recourir à des bits de parité secondaires, avec le codage que nous allons voir dans ce qui suit.
==Les codes de Hamming==
Le '''code de Hamming''' se base sur l'usage de plusieurs bits de parité pour un seul nombre. Chaque bit de parité est calculé à partir d'un sous-ensemble des bits. Chaque bit de parité a son propre sous-ensemble, tous étant différents, mais pouvant avoir des bits en commun. Le but étant que deux sous-ensembles partagent un bit : si ce bit est modifié, cela modifiera les deux bits de parité associés. Et la modification de ce bit est la seule possibilité pour que ces deux bits soient modifiés en même temps : si ces deux bits de parité sont modifiés en même temps, on sait que le bit partagé a été modifié.
Pour résumer, un code de Hamming utilise plusieurs bits de parité, calculés chacun à partir de bits différents, souvent partagés entre bits de parité. Mais cela est aussi vrai pour la technique précédente. Un point important est que si un bit de parité est corrompu et change de valeur, les autres bits de parité ne le seront pas et c'est ce qui permettra de détecter l'erreur. Si un bit de données est inversé, plusieurs bits de parité sont touchés, systématiquement. Donc si un seul bit de parité est incompatible avec les bits de données, alors on sait qu'il a été inversé et qu'il est l'erreur. Pas besoin de faire comme avec la technique précédente, avec un mot de parité complété avec des bits de parité, avec un bit de parité secondaire.
[[File:Hamming(7,4).svg|vignette|Hamming(7,4)]]
Le code de Hamming le plus connu est certainement le '''code 7-4-3''', un code de Hamming parmi les plus simples à comprendre. Celui-ci prend des données sur 4 bits, et leur ajoute 3 bits de parité, ce qui fait en tout 7 bits : c'est de là que vient le nom de 7-4-3 du code. Chaque bit de parité se calcule à partir de 3 bits du nombre, mais aussi des autres bits de parité. Pour poursuivre, nous allons noter les bits de parité p1, p2 et p3, tandis que les bits de données seront notés d1, d2, d3 et d4.
{|class="wikitable"
|-
! Bits de parité incorrects
! Bit modifié
|-
| Les trois bits de parité : p1, p2 et p3
| Bit d4
|-
| p1 et p2
| d1
|-
| p2 et p3
| d3
|-
| p1 et p3
| d2
|}
Il faut préciser que toute modification d'un bit de donnée entraîne la modification de plusieurs bits de parité. Si un seul bit de parité est incorrect, il est possible que ce bit de parité a été corrompu et que les données sont correctes. Ou alors, il se peut que deux bits de données ont été modifiés, sans qu'on sache lesquels.
Le '''code 8-4-4''' est un code 7-4-3 auquel on a ajouté un bit de parité supplémentaire. Celui-ci est calculé à partir de tous les bits, bits de parités ajoutés par le code 7-4-3 inclus. Ainsi, on permet de se prémunir contre une corruption de plusieurs bits de parité.
[[File:Hamming(8,4).svg|centre|vignette|Hamming(8,4)]]
Évidemment, il est possible de créer des codes de Hamming sur un nombre plus grand que bits. Le cas le plus classique est le code 11-7-4.
[[File:Hamming(11,7).svg|centre|vignette|Hamming(11,7)]]
Les codes de Hamming sont généralement plus économes que la technique précédente, avec un mot de parité combiné à plusieurs bits de parité. Par exemple, pour 4 bits, le code de Hamming 7-4-3 n'utilise que 3 bits de parité, contre 4 avec l'autre technique. Pour 7 bits, elle n'en utilise que 4, contre 6. Voici un tableau qui donne combien on peut protéger avec N bits de parité en utilisant un code de Hamming. On voit que les codes de Hamming sont bien plus économes que le mot de parité, tout en étant tout aussi puissant (ou presque).
{|class="wikitable"
|-
! Bits de parité !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9
|-
| Données || 1 || 4 || 11 || 26 || 57 || 120 || 247 || 502
|}
==Les sommes de contrôle==
Les sommes de contrôle sont des techniques de correction d'erreur, où les bits de correction d'erreur sont ajoutés à la suite des données. Les bits de correction d'erreur, ajoutés à la fin du nombre à coder, sont appelés la '''somme de contrôle'''. La vérification d'une erreur de transmission est assez simple : on calcule la somme de contrôle à partir des données transmises et on vérifie qu'elle est identique à celle envoyée avec les données. Si ce n'est pas le cas, il y a eu une erreur de transmission.
Techniquement, les techniques précédentes font partie des sommes de contrôle au sens large, mais il existe un sens plus restreint pour le terme de somme de contrôle. Il est souvent utilisé pour regrouper des techniques telle l'addition modulaire, le CRC, et quelques autres. Toutes ont en commun de traiter les données à coder comme un gros nombre entier, sur lequel on effectue des opérations arithmétiques pour calculer les bits de correction d'erreur. La seule différence est que l'arithmétique utilisée est quelque peu différente de l'arithmétique binaire usuelle. Dans les calculs de CRC, on utilise une arithmétique où les retenues ne sont pas propagées, ce qui fait que les additions et soustractions se résument à des XOR.
La première méthode consiste à diviser les données à envoyer par un nombre entier arbitraire et à utiliser le reste de la division euclidienne comme somme de contrôle. Cette méthode, qui n'a pas de nom, est similaire à celle utilisée dans les '''Codes de Redondance Cyclique'''.
Avec cette méthode, on remplace la division par une opération légèrement différente. L'idée est de faire comme une division, mais dont on aurait remplacé les soustractions par des opérations XOR. Nous appellerons cette opération une pseudo-division dans ce qui suit. Une pseudo-division donne un quotient et un reste, comme le ferait une division normale. Le calcul d'un CRC pseudo-divise les données par un diviseur et on utilise le reste de la pseudo-division comme somme de contrôle.
Il existe plusieurs CRC différents et ils se distinguent surtout par le diviseur utilisé, qui est standardisé pour chaque CRC. La technique peut sembler bizarre, mais cela marche. Cependant, expliquer pourquoi demanderait d'utiliser des concepts mathématiques de haute volée qui n'ont pas leur place dans ce cours, comme la division polynomiale, les codes linéaires ou encore les codes polynomiaux cycliques.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Le codage des nombres
| prevText=Le codage des nombres
| next=Les portes logiques
| nextText=Les portes logiques
}}
</noinclude>
0en7jvxe3ghox00blmc7551taefxszm
744779
744778
2025-06-15T01:38:54Z
Mewtow
31375
/* La récupération des données manquantes/effacées */
744779
wikitext
text/x-wiki
Dans le chapitre précédent, nous avons vu comment l'ordinateur faisait pour coder des nombres. Les nombres en question sont mémorisés dans des mémoires plus ou moins complexes. Et ces mémoires ne sont pas des dispositifs parfaits, elles peuvent subir des corruptions. Les corruptions en question se traduisent le plus souvent par l'inversion d'un bit : un bit censé être à 0 passe à 1, ou inversement. Le terme anglais pour ce genre de corruption est un '''''bitflip''''', mais nous utiliserons le terme général "erreur", pour désigner ces ''bitflips'' .
Pour donner un exemple, on peut citer l'incident du 18 mai 2003 dans la petite ville belge de Schaerbeek. Lors d'une élection, la machine à voter électronique enregistra un écart de 4096 voix entre le dépouillement traditionnel et le dépouillement électronique. La faute à un rayon cosmique, qui avait modifié l'état d'un bit de la mémoire de la machine à voter.
Mais qu'on se rassure : certains codages des nombres permettent de détecter et corriger ces ''bitflips''. Pour cela, les '''codes de détection et de correction d'erreur''' ajoutent des bits de correction/détection d'erreur aux données. Les bits en question sont calculés à partir des données à transmettre/stocker et 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. Ils sont peu utilisées dans les ordinateurs grand public, mais elles sont très importantes dans les domaines demandant des ordinateurs fiables, comme dans l'automobile, l'aviation, le spatial, l'industrie, etc. Et ce chapitre va expliquer ce qu'elles sont, et aussi comment les circuits élaborés permettent de s'en protéger.
Dans ce qui suit, nous parlerons parfois de codes ECC, bien que ce soit un abus de langage : ECC est l'abréviation de ''Error Correction Code'', mais certains de ces codes se contentent de détecter qu'une erreur a eu lieu, sans la corriger. Ceci étant dit, les codes ECC sont utilisés sur les mémoires comme les mémoires RAM, parfois sur les disques durs ou les SSDs, afin d'éviter des corruptions de données. Ils sont aussi utilisés quand on doit transmettre des données, que ce soit sur les bus de communication ou sur un support réseau. Par exemple, les données transmises via internet incorporent un code ECC pour détecter les erreurs de transmission, idem pour les transmissions sur un réseau local.
==Le bit de parité==
Nous allons commercer par aborder le '''bit de parité/imparité'''. Le bit de parité est un bit ajouté à la donnée à mémoriser/transmettre. Sa valeur est telle que le nombre stocké (bit de parité inclus) contient toujours un nombre pair de bits à 1. Ainsi, le bit de parité vaut 0 si le nombre contient déjà un nombre pair de 1, et 1 si le nombre de 1 est impair.
Si un bit s'inverse, quelle qu'en soit la raison, la parité du nombre total de 1 est modifié : ce nombre deviendra impair si un bit est modifié. Et ce qui est valable avec un bit l'est aussi pour 3, 5, 7, et pour tout nombre impair de bits modifiés. Mais tout change si un nombre pair de bit est modifié : la parité ne changera pas. Il permet de détecter des corruptions qui touchent un nombre impair de bits. Si un nombre pair de bit est modifié, il est impossible de détecter l'erreur avec un bit de parité. Ainsi, on peut vérifier si un bit (ou un nombre impair) a été modifié : il suffit de vérifier si le nombre de 1 est impair. Il faut noter que le bit de parité, utilisé seul, ne permet pas de localiser le bit corrompu.
Le '''bit d'imparité''' est similaire au bit de parité, si ce n'est que le nombre total de bits doit être impair, et non pair comme avec un bit de parité. Sa valeur est l'inverse du bit de parité du nombre : quand le premier vaut 1, le second vaut 0, et réciproquement. Mais celui-ci n'est pas meilleur que le bit de parité : on retrouve l'impossibilité de détecter une erreur qui corrompt un nombre pair de bits.
[[File:Bit de parité.jpg|vignette|Valeurs valides et invalides avec un bit de parité. Les valeurs valides sont en vert, les autres en noir.]]
Il est maintenant temps de parler de si un bit de parité est efficace ou non. Que ce soit avec un bit de parité ou d'imparité, environ la moitié des valeurs encodées sont invalides. En effet, si on prend un nombre codé sur N bits, bit de parité, inclut, on pourra encoder 2^n valeurs différentes. La moitié d'entre elle aura un bit de parité à 0, l'autre un bit de parité à 1. Et la moitié aura un nombre de bit à 1 qui soit pair, l'autre un nombre impair. En faisant les compte, seules la moitié des valeurs seront valides. Le diagramme ci-contre montre le cas pour trois bits, avec deux bits de données et un bit de parité.
==L'octet/mot de parité et ses variantes==
L''''octet de parité''' est une extension de la technique du bit de parité, qui s'applique à plusieurs octets. L'idée de base est de calculer un bit de parité par octet, et c'est plus ou moins ce que fait le mot de parité, mais avec quelques subtilités dans les détails.
[[File:Illustration checksum 1.png|centre|vignette|upright=2|Illustration du mot de parité.]]
La technique s'applique en général sur toute donnée qu'on peut découper en blocs d'une taille fixe. Dans les exemples qui vont suivre, les blocs en question seront des octets, pour simplifier les explications, mais il est parfaitement possible de prendre des blocs plus grands, de plusieurs octets. La méthode fonctionne de la même manière. On parle alors de '''mot de parité''' et non d'octet de parité.
===Le calcul du mot de parité===
Le calcul du mot de parité se calcule en disposant chaque octet l'un au-dessus des autres, le tout donnant un tableau dont les lignes sont des octets. Le mot de parité se calcule en calculant le bit de parité de chaque colonne du tableau, et en le plaçant en bas de la colonne. Le résultat obtenu sur la dernière ligne est un octet de parité.
* 1100 0010 : nombre ;
* 1000 1000 : nombre ;
* 0100 1010 : nombre ;
* 1001 0000 : nombre ;
* 1000 1001 : nombre ;
* 1001 0001 : nombre ;
* 0100 0001 : nombre ;
* 0110 0101 : nombre ;
* ------------------------------------
* 1010 1100 : '''octet de parité'''.
Le calcul de l'octet de parité se fait en utilisant des opérations XOR. Pour rappel, une opération XOR est équivalente à une addition binaire dans laquelle on ne tiendrait pas compte des retenues. L'opération prend deux bits et effectue le calcul suivant :
* 0 <math>\oplus</math> 0 = 0 ;
* 0 <math>\oplus</math> 1 = 1 ;
* 1 <math>\oplus</math> 0 = 1 ;
* 1 <math>\oplus</math> 1 = 0.
En faisant un XOR entre deux octets, on obtient l'octet de parité des deux octets opérandes. Et cela se généralise à N opérandes : il suffit de faire un XOR entre les opérandes pour obtenir l'octet de parité de ces N opérandes. Il suffit de faire un XOR entre les deux premières opérandes, puis de faire un XOR entre le résultat et la troisième opérande, puis de refaire un XOR entre le nouveau résultat et le quatrième opérande, et ainsi de suite. Le calcul du mot de parité se fait aussi avec des opérations XOR, cela marche au-delà de l'octet.
===La récupération des données manquantes/effacées===
L'avantage de cette technique est qu'elle permet de reconstituer une donnée manquante. Par exemple, dans l'exemple précédent, si une ligne du calcul disparaissait, on pourrait la retrouver à partir du mot de parité. Il suffit de déterminer, pour chaque colonne, quel valeur 0/1 est compatible avec la valeur du bit de parité associé. C'est d'ailleurs pour cette raison que le mot de parité est utilisé sur les disques durs montés en RAID 3, 5 6, et autres. Grâce à elle, si un disque dur ne fonctionne plus, on peut retirer le disque dur endommagé et reconstituer ses données.
Pour cela, il faut faire faire XOR entre les données non-manquantes et le mot de parité. Pour comprendre pourquoi cela fonctionne, il faut savoir deux choses :
* faire un XOR entre un nombre et lui-même donne 0 ;
* faire un XOR entre une opérande et zéro redonne l'opérande comme résultat.
Si on XOR un nombre avec le mot de parité, cela va annuler la présence de ce nombre (son XOR) dans le mot de parité : le résultat correspondra au mot de parité des nombres, nombre xoré exclu. Ce faisant, en faisant un XOR avec tous les nombres connus, ceux-ci disparaîtront du mot de parité, ne laissant que le nombre manquant. Un exemple sera certainement plus parlant.
Prenons le cas où on calcule l'octet de parité de quatre octets nommés O1, O2, O3 et O4, et notant le résultat <math>O_\text{parité}</math>. On a alors :
: <math>O_\text{parité} = O_1 \oplus O_2 \oplus O_3 \oplus O_4</math>
Maintenant, imaginons que l'on veuille retrouver la valeur du second octet O2, qui a été corrompu ou perdu. Dans ce cas, on fait un XOR avec O1, O3 et enfin O4 :
: <math>O_\text{parité} \oplus O_1 \oplus O_3 \oplus O_4</math>
On injecte alors l'équation <math>O_\text{parité} = O_1 \oplus O_2 \oplus O_3 \oplus O_4</math>.
: <math>(O_1 \oplus O_2 \oplus O_3 \oplus O_4) \oplus O_1 \oplus O_3 \oplus O_4</math>
On réorganise les termes :
: <math>(O_1 \oplus O_1) \oplus (O_2 \oplus O_2) \oplus (O_4 \oplus O_4) \oplus O_2</math>
On se rappelle que A XOR A = 0, ce qui simplifie grandement le tout :
: <math>O_2</math>
On retrouve bien l'octet manquant.
==La combinaison d'un mot de parité avec plusieurs bits de parité==
Avec un octet/mot de parité, on peut détecter qu'une erreur a eu lieu, mais aussi récupérer un octet/mot effacé. Mais si un bit est modifié, on ne peut pas corriger l'erreur. En effet, on ne sait pas détecter quel octet a été modifié par l'erreur. Maintenant, ajoutons un bit de parité à chaque octet, en plus de l'octet de parité.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || 1 || 0 || 1 || 1 || 0 || 0 || 1
! 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité
! 1 !! 0 !! 1 !! 0 !! 0 !! 1 !! 0 !! 1 !!
|}
En faisant cela, on peut détecter qu'un bit a été modifié, mais aussi corriger l'erreur assez simplement. En cas d'erreur, deux bits de parité seront faussés : celui associé à l'octet, celui dans l'octet de parité. On peut alors détecter le bit erroné. Une autre méthode est de regarder les bits de parité associés aux octets, pour détecter l'octet erroné. Reste alors à corriger l'erreur, en supprimant l'octet invalide et en récupérant l'octet initial en utilisant le mot de parité.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || 1 || 0 || bgcolor="#00FF00" | 0 || 1 || 0 || 0 || 1
! style="background: #00FF00;" | 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! 0 !! 1 !! style="background: #00FF00;" | 0 !! 0 !! 1 !! 0 !! 1 !!
|}
===Le nombre de bits de parité total est élevé===
Le tout demande d'utiliser beaucoup de bits de parité. Pour N octets, il faut un bit de parité par octet et un octet de parité, ce qui donne N + 8 bits de parité. Pour 64 bits, soit 8 octets, cela fait 16 bits de parité nécessaires, soit 25% de bits en plus. Maintenant, prenons le cas général où on n'utilise pas des octets, mais des mots de M bits, plus longs ou plus courts qu'un octet. Dans ce cas, on a N + M bits de parité.
Notons que la technique peut s'appliquer avec des octets ou des ''nibbles'', si on organise les bits correctement. Par exemple, prenons un ''nibble'' (4 bits). On peut l'organiser en un carré de deux bits de côté et ajouter : un bit de parité par colonne, un bit de parité par colonne (le mot de parité). Le tout donne 4 bits de parité, pour 4 bits de données : on double la taille de la donnée. Il est aussi possible de faire pareil avec un octet, l'organisant en deux lignes de 4 bits. Le résultat est de 6 bits de parité, ce qui est un petit peu mieux qu'avec un ''nibble'' : on passe à 3/4 de bits de plus.
{|class="wikitable"
|+ Exemple d'octet avec parité ajoutée
|-
| 0 || 0 || 1 || 1
! 0
|-
| 1 || 0 || 0 || 1
! 0
|-
! 1 !! 0 !! 1 !! 0 !!
|}
Il est possible de faire la même chose pour des données de plusieurs octets. Pour un nombre de 16 bits, l'idéal est de faire 4 lignes de 4 bits chacune, ce qui fait 8 bits de parité au total. Pour 32 bits, on passe à 12 bits de parité, etc. Au total, voici la quantité de bits de parité nécessaires suivant la longueur de la donnée :
{|class="wikitable"
|+ Exemple d'octet avec parité ajoutée
|-
! 4 !! 8 !! 16 !! 32 !! 64 !! 128 !! 256 !! ...
|-
| 4 || 6 || 8 || 12 || 16 || 24 || 32 || ...
|}
Il existe cependant des techniques plus économes, que nous allons voir dans ce qui suit. Par plus économes, il faut comprendre qu'elles utilisent moins de bits de parité, pour une fiabilité identique, voire meilleure. C'est là un défaut de la technique précédente : elle utilise beaucoup de bits de parités pour pas grand chose.
===Les capacités de correction de la technique===
Notons que cette solution permet de corriger plus d'une erreur. Dans le pire des cas, on peut détecter et corriger une erreur. Si toutes les erreurs sont toutes dans le même mot/octet, alors on peut récupérer l'octet manquant. Le bit de parité permet de détecter un nombre impair d'erreur, soit 1, 3, 5, 7, ... erreurs. Il faut donc que le nombre d'erreurs dans l'octet soit impair.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || bgcolor="#00FF00" | 1 || 0 || bgcolor="#00FF00" | 0 || 1 || 0 || 0 || style="background: #00FF00;" | 1
! style="background: #00FF00;" | 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! style="background: #00FF00;" | 0 !! 1 !! style="background: #00FF00;" | 0 !! 0 !! 1 !! 0 !! style="background: #00FF00;" | 1 !!
|}
Par contre, si le nombre d'erreurs dans un octet est pair, alors le bit de parité associé à l'octet ne remarque pas l'erreur. On sait sur quelles colonnes sont les erreurs, pas la ligne.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || 1 || 0 || 1 || 1
! 0
|-
| Octet 3
| 1 || bgcolor="#00FF00" | 1 || 0 || bgcolor="#00FF00" | 0 || 1 || 0 || 0 || 1
! 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! style="background: #FF0000;" | 0 !! 1 !! style="background: #FF0000;" | 0 !! 0 !! 1 !! 0 !! 1 !!
|}
De même, si les erreurs touchent deux octets, alors on ne peut rien corriger. On peut détecter les erreurs, mais pas les corriger.
{|class="wikitable" style="text-align:center;"
|+ Exemple avec quatre octets
|-
! colspan="9" | Octets !! Bit de parité de chaque octet
|-
| Octet 1
| 0 || 0 || 1 || 1 || 1 || 1 || 1 || 1
! 0
|-
| Octet 2
| 0 || 0 || 1 || 0 || bgcolor="#00FF00" | 1 || 0 || 1 || 1
! style="background: #FF0000" | 0
|-
| Octet 3
| 1 || bgcolor="#00FF00" | 1 || 0 || 0 || 1 || 0 || 0 || 1
! style="background: #FF0000" | 1
|-
| Octet 4
| 0 || 1 || 1 || 0 || 1 || 0 || 0 || 0
! 1
|-
! Octet de parité !! 1 !! style="background: #FF0000" | 0 !! 1 !! 0 !! style="background: #FF0000" | 0 !! 1 !! 0 !! 1 !!
|}
Pour résumer, pour des mots de M bits, on peut corriger entre 1 et M/2 erreurs. Pour un octet, cela permet de détecter 1 erreur, 3/5/7 erreurs si elles ont lieu dans le même octet.
===La protection des bits de parité===
Avec la méthode précédente, les bits de parité ne sont pas protégés : la moindre corruption des bits de parité fait que la méthode ne marche plus. Pour cela, il y a une solution toute simple : calculer un bit de parité qui tient compte de tous les bits de parité. Ainsi, si un bit de parité est corrompu, alors ce super-bit de parité détecterait l'erreur.
Dans les tableaux précédents, cela revient à ajouter un bit de parité dans la case tout en bas à droite. Le bit de parité calculé à partie des autres bits de parité est en rouge dans le tableau suivant. Il peut se calculer de plusieurs manières. La plus simple est calculer le bit de parité des 6 bits de parité, ceux de l'octet de parité et les autres. En faisant ainsi, on ganratit que tous les bits de parités sont protégés.
{|class="wikitable"
|+ Exemple avec un octet
|-
| 0 || 0 || 1 || 1 || class="f_vert" | 0
|-
| 1 || 0 || 0 || 1 || class="f_vert" | 0
|-
| class="f_vert" | 1 || class="f_vert" | 0|| class="f_vert" | 1 || class="f_vert" | 0 || class="f_rouge" | 0
|}
Avec cette technique, il faut faire la différence entre les '''bits de parité primaire''', qui calculent la parité de tout ou partie des données, et le '''bit de parité secondaire''', qui est calculé à partie des bits de parité primaire. Généralement, les codes correcteurs/détecteurs d'erreur avec des bits de parité secondaires sont assez peu efficaces, que ce soit en termes de fiabilité ou d'économies de bits. Ils utilisent beaucoup de bits et ne protègent que peu contre les erreurs. De plus, les bits de parité secondaires ne font que repousser le problème : le bit de parité secondaire peut être modifié lui aussi ! Les bits de parité secondaires ne protègent que contre la modification des bits de parité primaire, mais pas de leurs modifications propres. Mais qu'on se rassure : on peut protéger les bits de parité primaire sans recourir à des bits de parité secondaires, avec le codage que nous allons voir dans ce qui suit.
==Les codes de Hamming==
Le '''code de Hamming''' se base sur l'usage de plusieurs bits de parité pour un seul nombre. Chaque bit de parité est calculé à partir d'un sous-ensemble des bits. Chaque bit de parité a son propre sous-ensemble, tous étant différents, mais pouvant avoir des bits en commun. Le but étant que deux sous-ensembles partagent un bit : si ce bit est modifié, cela modifiera les deux bits de parité associés. Et la modification de ce bit est la seule possibilité pour que ces deux bits soient modifiés en même temps : si ces deux bits de parité sont modifiés en même temps, on sait que le bit partagé a été modifié.
Pour résumer, un code de Hamming utilise plusieurs bits de parité, calculés chacun à partir de bits différents, souvent partagés entre bits de parité. Mais cela est aussi vrai pour la technique précédente. Un point important est que si un bit de parité est corrompu et change de valeur, les autres bits de parité ne le seront pas et c'est ce qui permettra de détecter l'erreur. Si un bit de données est inversé, plusieurs bits de parité sont touchés, systématiquement. Donc si un seul bit de parité est incompatible avec les bits de données, alors on sait qu'il a été inversé et qu'il est l'erreur. Pas besoin de faire comme avec la technique précédente, avec un mot de parité complété avec des bits de parité, avec un bit de parité secondaire.
[[File:Hamming(7,4).svg|vignette|Hamming(7,4)]]
Le code de Hamming le plus connu est certainement le '''code 7-4-3''', un code de Hamming parmi les plus simples à comprendre. Celui-ci prend des données sur 4 bits, et leur ajoute 3 bits de parité, ce qui fait en tout 7 bits : c'est de là que vient le nom de 7-4-3 du code. Chaque bit de parité se calcule à partir de 3 bits du nombre, mais aussi des autres bits de parité. Pour poursuivre, nous allons noter les bits de parité p1, p2 et p3, tandis que les bits de données seront notés d1, d2, d3 et d4.
{|class="wikitable"
|-
! Bits de parité incorrects
! Bit modifié
|-
| Les trois bits de parité : p1, p2 et p3
| Bit d4
|-
| p1 et p2
| d1
|-
| p2 et p3
| d3
|-
| p1 et p3
| d2
|}
Il faut préciser que toute modification d'un bit de donnée entraîne la modification de plusieurs bits de parité. Si un seul bit de parité est incorrect, il est possible que ce bit de parité a été corrompu et que les données sont correctes. Ou alors, il se peut que deux bits de données ont été modifiés, sans qu'on sache lesquels.
Le '''code 8-4-4''' est un code 7-4-3 auquel on a ajouté un bit de parité supplémentaire. Celui-ci est calculé à partir de tous les bits, bits de parités ajoutés par le code 7-4-3 inclus. Ainsi, on permet de se prémunir contre une corruption de plusieurs bits de parité.
[[File:Hamming(8,4).svg|centre|vignette|Hamming(8,4)]]
Évidemment, il est possible de créer des codes de Hamming sur un nombre plus grand que bits. Le cas le plus classique est le code 11-7-4.
[[File:Hamming(11,7).svg|centre|vignette|Hamming(11,7)]]
Les codes de Hamming sont généralement plus économes que la technique précédente, avec un mot de parité combiné à plusieurs bits de parité. Par exemple, pour 4 bits, le code de Hamming 7-4-3 n'utilise que 3 bits de parité, contre 4 avec l'autre technique. Pour 7 bits, elle n'en utilise que 4, contre 6. Voici un tableau qui donne combien on peut protéger avec N bits de parité en utilisant un code de Hamming. On voit que les codes de Hamming sont bien plus économes que le mot de parité, tout en étant tout aussi puissant (ou presque).
{|class="wikitable"
|-
! Bits de parité !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9
|-
| Données || 1 || 4 || 11 || 26 || 57 || 120 || 247 || 502
|}
==Les sommes de contrôle==
Les sommes de contrôle sont des techniques de correction d'erreur, où les bits de correction d'erreur sont ajoutés à la suite des données. Les bits de correction d'erreur, ajoutés à la fin du nombre à coder, sont appelés la '''somme de contrôle'''. La vérification d'une erreur de transmission est assez simple : on calcule la somme de contrôle à partir des données transmises et on vérifie qu'elle est identique à celle envoyée avec les données. Si ce n'est pas le cas, il y a eu une erreur de transmission.
Techniquement, les techniques précédentes font partie des sommes de contrôle au sens large, mais il existe un sens plus restreint pour le terme de somme de contrôle. Il est souvent utilisé pour regrouper des techniques telle l'addition modulaire, le CRC, et quelques autres. Toutes ont en commun de traiter les données à coder comme un gros nombre entier, sur lequel on effectue des opérations arithmétiques pour calculer les bits de correction d'erreur. La seule différence est que l'arithmétique utilisée est quelque peu différente de l'arithmétique binaire usuelle. Dans les calculs de CRC, on utilise une arithmétique où les retenues ne sont pas propagées, ce qui fait que les additions et soustractions se résument à des XOR.
La première méthode consiste à diviser les données à envoyer par un nombre entier arbitraire et à utiliser le reste de la division euclidienne comme somme de contrôle. Cette méthode, qui n'a pas de nom, est similaire à celle utilisée dans les '''Codes de Redondance Cyclique'''.
Avec cette méthode, on remplace la division par une opération légèrement différente. L'idée est de faire comme une division, mais dont on aurait remplacé les soustractions par des opérations XOR. Nous appellerons cette opération une pseudo-division dans ce qui suit. Une pseudo-division donne un quotient et un reste, comme le ferait une division normale. Le calcul d'un CRC pseudo-divise les données par un diviseur et on utilise le reste de la pseudo-division comme somme de contrôle.
Il existe plusieurs CRC différents et ils se distinguent surtout par le diviseur utilisé, qui est standardisé pour chaque CRC. La technique peut sembler bizarre, mais cela marche. Cependant, expliquer pourquoi demanderait d'utiliser des concepts mathématiques de haute volée qui n'ont pas leur place dans ce cours, comme la division polynomiale, les codes linéaires ou encore les codes polynomiaux cycliques.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Le codage des nombres
| prevText=Le codage des nombres
| next=Les portes logiques
| nextText=Les portes logiques
}}
</noinclude>
ag73ztk6ch76k57xmjwfr9om8lqqv24
Fonctionnement d'un ordinateur/La loi de Moore et les tendances technologiques
0
70026
744788
740763
2025-06-15T01:59:47Z
Mewtow
31375
/* Les mémoires RAM ne sont pas concernées par la loi de Moore */
744788
wikitext
text/x-wiki
Il va de soi que les nouveaux processeurs sont plus puissants que les anciens, pareil pour les mémoires. La raison à cela vient des optimisations apportées par les concepteurs de processeurs. La plupart de ces optimisations ne sont cependant possibles qu'avec la miniaturisation des transistors, qui leur permet d'aller plus vite. Et cela se voit dans les données empiriques. Il est intéressant de regarder comment les mémoires et processeurs ont évolué dans le temps.
Pour les processeurs, la loi de Moore a des conséquences qui sont assez peu évidentes. Certes, on peut mettre plus de transistors dans un processeur, mais en quoi cela se traduira par de meilleures performances ? Pour comprendre l'influence qu'à eu la loi de Moore sur les processeurs modernes, regardons ce graphique, qui montre les relations entre nombre de transistors, fréquence du processeur, performance d'un seul cœur, nombre de cœurs, et consommation énergétique.
[[File:50 Years Of Microprocessor Trend Data.png|centre|vignette|upright=2|50 years of microprocessor trend data, par Karl Rupp.]]
Globalement, on voit que le nombre de transistors augmente de façon exponentielle : doubler tous les X années donne une courbe exponentielle, d'où l'échelle semi-logarithmique du graphique. Mais pour le reste, quelque chose s'est passé en 2005 : les courbes n'ont pas la même pente avant et après 2005. Que ce soit la fréquence, la performance d'un seul cœur, la consommation électrique, tout. Et le nombre de cœurs explose au même moment. Tout cela fait penser que toutes ces caractéristiques étaient liées entre elles et augmentaient exponentiellement, mais il y a un après 2005. Reste à expliquer pourquoi, ce qui est le sujet de ce chapitre, sans compter qu'on détaillera tout ce qui a trait à la consommation énergétique.
==La miniaturisation des transistors est la cause des tendances technologiques==
Avant toute chose, nous devons faire quelques rappels sur les transistors MOS, sans lesquels les explications qui vont suivre seront compliquées. Un transistor MOSFET a de nombreuses caractéristiques : ses dimensions, mais aussi d'autres paramètres plus intéressants. Par exemple, il est intéressant de regarder la consommation d'énergie d'un transistor, à savoir combien de watts ils utilise pour faire ce qu'on lui demande. Pour cela, il faudra parler rapidement de certaines de ses caractéristiques comme sa capacité électrique. Rien de bien compliqué, rassurez-vous.
===Les caractéristiques d'un transistor : finesse de gravure, capacité, etc===
[[File:Transistor CMOS - 1.png|vignette|Transistor CMOS - 1]]
Un transistor MOS est composé de deux morceaux de conducteurs (l'armature de la grille et la liaison drain-source) séparés par un isolant. Les dimensions d'un transistors sont au nombre de deux : la distance entre source et drain, la distance entre grille et semi-conducteur. Les deux sont regroupées sous le terme de '''finesse de gravure''', bien que cela soit un terme impropre.
Nous avons dit plus haut qu'un transistor MOS est composé de deux (semi-)conducteurs séparés par un isolant. Tout cela ressemble beaucoup à un autre composant électronique appelé le '''condensateur''', qui sert de réservoir à électricité. On peut le charger en électricité, ou le vider pour fournir un courant durant une petite durée de temps. L'intérieur d'un condensateur est formé de deux couches de métal conducteur, séparées par un isolant électrique. Les charge s'accumulent dans les couche de métal quand on charge le condensateur en électricité. L'intérieur d'un transistor MOS est donc similaire à celui d'un condensateur, si ce n'est qu'une couche métallique est remplacée par un morceau de semi-conducteur. Tout cela fait qu'un transistor MOS incorpore un pseudo-condensateur caché entre la grille et la liaison source-drain, qui porte le nom de capacité parasite du transistor.
[[File:Plaatcondensator2.GIF|centre|vignette|upright=2|Condensateur et accumulation des charges électrique sur les plaques métalliques.]]
Tout condensateur possède une caractéristique importante : sa '''capacité électrique'''. Il s'agit simplement de la quantité d'électrons/charges qu'il peut contenir en fonction de la tension. Il faut savoir que la quantité de charge contenue dans un condensateur est proportionnelle à la tension, la capacité est le coefficient de proportionnalité entre les deux. Tout cela est sans doute plus clair avec une équation :
: <math>Q = C U</math>, avec Q la quantité de charges contenues dans le condensateur, U la tension, et C la capacité.
[[File:Capacity.svg|vignette|Charge/décharge d'un condensateur.]]
La capacité d'un transistor MOS a une influence directe sur la fréquence à laquelle il peut fonctionner. Pour changer l'état d'un transistor MOS, il faut soit charger la grille, soit la décharger. Et pour remplir le transistor, il faut fournir une charge égale à celle donnée par l'équation précédente.
Si on met ce processus en équations, on s’aperçoit qu'on se trouve avec des charges ou décharges exponentielles. Mais par simplicité, on considère que le temps de charge/décharge d'un condensateur est proportionnel à sa capacité (pour être précis, proportionnel au produit 5 RC, avec R la résistance des fils). Tout ce qu'il faut retenir est que plus la capacité est faible, plus le transistor est rapide et plus il peut fonctionner à haute fréquence.
===Les lois de Dennard, ce qui se cache derrière la loi de Moore===
La loi de Moore est le résultat d'une tendance technologique bien précise : les dimensions d'un transistors se réduisent avec les progrès de la miniaturisation. Elles sont réduites de 30% tous les deux ans. Pour le dire autrement, elles sont multipliées par 0.7 tous les deux ans.
[[File:Comparison semiconductor process nodes.svg|centre|vignette|upright=2.0|Évolution de la finesse de gravure au cours du temps pour les processeurs.]]
Les processeurs sont des composants qui ont actuellement une forme carrée, les transistors sont tous placés sur un plan et ne sont pas empilés les uns sur les autres. Ils occupent donc une certaine aire sur la surface du processeur. Si la taille des transistors est réduite de 30% tous les 2 ans, l'aire que prend un transistor sur la puce est quand à elle divisée par 30% * 30% <math>\approx</math> 50%. En conséquence, on peut mettre deux fois plus de transistors sur la même puce électronique : on retrouve la loi de Moore.
Cela a aussi des conséquences sur la tension d'alimentation nécessaire pour faire fonctionner le transistor. Sans rentrer dans les détails, la tension est elle aussi proportionnelle aux dimensions du transistor. La raison technique, que vous comprendrez si vous avez eu des cours d'électricité avancés durant votre scolarité, est que le champ électrique ne change pas dans le transistor, et que la tension est le produit du champ électrique par la distance. Là encore, la tension d'alimentation est réduite de 30% tous les deux ans.
[[File:Parallel plate capacitor.svg|vignette|Condensateur plan]]
La miniaturisation a une influence directe sur la capacité électrique du transistor. Pour comprendre pourquoi, il faut savoir que le condensateur formé par la grille, l'isolant et le morceau de semi-conducteur est ce que l'on appelle un ''condensateur plan''. La capacité de ce type de condensateur dépend de la surface de la plaque de métal (la grille), du matériau utilisé comme isolant et de la distance entre la grille et le semi-conducteur. On peut calculer cette capacité comme suit, avec A la surface de la grille, e un paramètre qui dépend de l'isolant utilisé et d la distance entre le semi-conducteur et la grille (l'épaisseur de l'isolant, quoi).
: <math>C = \frac{A}{d} \times e</math>
Le coefficient e (la permittivité électrique) reste le même d'une génération de processeur à l'autre, même si les fabricants ont réussi à le faire baisser un peu grâce à matériaux particuliers. Mais laissons cela de côté : dans les faits, seuls les coefficients S et d vont nous intéresser. Si la finesse de gravure diminue de 30%, la distance d diminue du même ordre, la surface A diminue du carré de 30%, c’est-à-dire qu'elle sera approximativement divisée par 2. La capacité totale sera donc divisée par 30% tous les deux ans.
Réduire la capacité des transistors a un impact indirect très fort sur la fréquence à laquelle on peut faire fonctionner le transistor. En effet, la période de l'horloge correspond ''grosso modo'' au temps qu'il faut pour remplir ou vider l'armature de la grille, et on sait que le temps de charge/décharge d'un condensateur est approximativement proportionnel à sa capacité. La capacité et le temps de charge/décharge est donc réduit de 30% tous les deux ans. La fréquence étant inversement proportionnelle au temps de remplissage du condensateur, elle est donc augmentée de 1/0.7 = <math>\approx</math> 40%.
Tout ce qu'on vient de dire plus a été formalisé sous le nom de '''lois de Dennard''', du nom de l'ingénieur qui a réussi à démontrer ces équations à partir des lois de la physique des semi-conducteurs. Une réduction de la finesse de gravure impacte plusieurs paramètres : le nombre de transistors d'une puce électronique, sa tension d'alimentation, sa fréquence, et quelques autres paramètres qu'on détaillera plus bas comme la capacité d'un transistor ou ses courants de fuite.
{| class="wikitable"
|-
! Paramètre !! Coefficient multiplicateur (tous les deux ans) || Variation en pourcentage
|-
| Finesse de gravure || 0.7 || - 30%
|-
| Aire occupée par un transistor || 0.5 || - 50%
|-
| Nombre de transistors par unité de surface || 2 || + 100%
|-
| Tension d'alimentation || 0.7 || - 30%
|-
| Capacité d'un transistor || 0.7 || - 30%
|-
| Fréquence || (1/0.7) = 1.4 || + 40%
|}
===La fin des lois de Dennard===
Les lois de Dennard ont cessé de s'appliquer aux alentours de 2005/2006. Les dimensions d'un transistors sont toujours réduites de 30% tous les deux ans, la loi de Moore est encore valable, mais pour ce qui est de la fréquence et de la tension d'alimentation, c'est autre chose. Les raisons à cela sont multiples, et il faut revenir au fonctionnement d'un transistor MOS pour comprendre pourquoi.
Un MOSFET est composé d'une grille métallique, d'une couche de semi-conducteur, et d'un isolant entre les deux. L'isolant empêche la fuite des charges de la grille vers le semi-conducteur. Mais avec la miniaturisation, la couche d'isolant ne fait guère plus de quelques dizaines atomes d'épaisseur et laisse passer un peu de courant : on parle de '''courants de fuite'''. Plus cette couche d'isolant est petite, plus le courant de fuite sera fort. En clair, une diminution de la finesse de gravure a tendance à augmenter les courants de fuite.
Les courants de fuite dépendent d'une tension appelée '''tension de seuil'''. Il s'agit de la tension minimale pour avoir un courant passant entre la source et le drain. Sous cette tension, le transistor se comporte comme un interrupteur fermé, peut importe ce qu'on met sur la grille. Au-dessus de cette tension, le courant se met à passer entre source et drain, il se comporte comme un interrupteur ouvert. Le courant est d'autant plus fort que la tension sur la grille dépasse la tension de seuil. On ne peut pas faire fonctionner un transistor si la tension d'alimentation (entre source et drain) est inférieure à la tension de seuil. C'est pour cela que ces dernières années, la tension d'alimentation des processeurs est restée plus ou moins stable, à une valeur proche de la tension de seuil (1 volt, environ). Et l'incapacité à réduire cette tension a eu des conséquences fâcheuses.
Nous verrons plus bas que la consommation d'énergie d'un processeur dépend de sa fréquence et de sa tension. Les lois de Dennard nous disent que Si la seconde baisse, on peut augmenter la première sans changer drastiquement la consommation énergétique. Mais si la tension d'alimentation stagne, alors la fréquence doit faire de même. Vu que les concepteurs de processeurs ne pouvaient pas diminuer la fréquence pour garder une consommation soutenable, et ont donc préféré augmenter le nombre de cœurs. L'augmentation de consommation énergétique ne découle que de l'augmentation du nombre de transistors et des diminutions de capacité. Et la diminution de 30 % tous les deux ans de la capacité ne compense plus le doublement du nombre de transistors : la consommation énergétique augmente ainsi de 40 % tous les deux ans. Bilan : la performance par watt stagne. Et ce n'est pas près de s'arranger tant que les tensions de seuil restent ce qu'elles sont.
L'explication est convaincante, mais nous détaillerons celle-ci plus bas, avec les vraies équations qui donnent la consommation d'énergie d'un processeur. Nous en profiterons aussi pour voir quelles sont les technologies utilisées pour réduire cette consommation d'énergie, les deux étant intrinsèquement liés.
==La consommation d'un circuit électronique CMOS==
Tout ordinateur consomme une certaine quantité de courant pour fonctionner, et donc une certaine quantité d'énergie. On peut dire la même chose de tout circuit électronique, que ce soit une mémoire, un processeur, ou quoique ce soit d'autre d'électronique. Il se trouve que cette énergie finit par être dissipée sous forme de chaleur : plus un composant consomme d'énergie, plus il chauffe. La chaleur dissipée est mesurée par ce qu'on appelle l''''enveloppe thermique''', ou TDP (''Thermal Design Power''), mesurée en Watts.
Pour donner un exemple, le TDP d'un processeur tourne autour des 50 watts, parfois plus sur les modèles plus anciens. De telles valeurs signifient que les processeurs actuels chauffent beaucoup. Pourtant, ce n'est pas la faute des fabricants de processeurs qui essaient de diminuer la consommation d'énergie de nos CPU au maximum. Malgré cela, les processeurs voient leur consommation énergétique augmenter toujours plus : l'époque où l'on refroidissait les processeurs avec un simple radiateur est révolue. Place aux gros ventilateurs super puissants, placés sur un radiateur.
===La consommation d'un circuit électronique CMOS===
Pour comprendre pourquoi, on doit parler de ce qui fait qu'un circuit électronique consomme de l'énergie. Une partie de la consommation d'énergie d'un circuit vient du fait qu'il consomme de l'énergie en changeant d'état. On appelle cette perte la '''consommation dynamique'''. A l’opposé, la '''consommation statique''' vient du fait que les circuits ne sont pas des dispositifs parfaits et qu'ils laissent fuiter un peu de courant.
Pour commencer, rappelons qu'un transistor MOS est composé de deux morceaux de conducteurs (l'armature de la grille et la liaison drain-source) séparés par un isolant. L'isolant empêche la fuite des charges de la grille vers la liaison source-drain. Charger ou décharger un condensateur demande de l'énergie. Les lois de la physique nous disent que la quantité d'énergie que peut stocker un condensateur est égale au produit ci-dessous, avec C la capacité du condensateur et U la tension d'alimentation. Il s'agit de l'énergie qu'il faut fournir quand on charge ou décharger un condensateur/MOSFET.
: <math>E = \frac{1}{2} C U^2</math>
L'énergie est dissipée quand les transistors changent d'état, il dissipe une quantité de chaleur proportionnelle au carré de la tension d'alimentation. Or, la fréquence définit le nombre maximal de changements d'état qu'un transistor peut subir en une seconde : pour une fréquence de 50 hertz, un transistor peut changer d'état 50 fois par seconde maximum. Après, il s'agit d'une borne maximale : il se peut qu'un transistor ne change pas d'état à chaque cycle. Sur les architectures modernes, la probabilité de transition 0 ⇔ 1 étant d'environ 10%-30%. Et si les bits gardent la même valeur, alors il n'y a pas de dissipation de puissance. Mais on peut faire l'approximation suivante : le nombre de changement d'état par seconde d'un transistor est proportionnel à la fréquence. L'énergie dissipée en une seconde (la puissance P) par un transistor est approximée par l'équation suivante :
: <math>P_\text{dynamique} \approx \frac{1}{2} C U^2 \times f</math>
On peut alors multiplier par le nombre de transistors d'une puce électronique, ce qui donne :
: <math>P_\text{dynamique} \approx \frac{1}{2} C U^2 \times f \times n</math>
Cette équation nous donne la '''consommation dynamique''', à savoir celle liée à l'activité du processeur/circuit. De plus, l'équation précédente permet de comprendre comment la consommation dynamique a évolué grâce à la loi de Moore. Pour cela, regardons comment chaque variable a évolué et faisons les comptes :
* le nombre de transistors fait *2 tous les deux ans ;
* la capacité est réduite de 30% tous les deux ans ;
* la tension d'alimentation est réduite de 30% tous les deux ans ;
* la fréquence augmente de 40% tous les deux ans.
L'augmentation du nombre de transistors est parfaitement compensé par la baisse de la tension d'alimentation : multiplication par deux d'un côté, divisé par 2 (30% au carré) de l'autre. Idem avec la capacité et la fréquence : la capacité est multipliée par 0.7 tous les deux ans, la fréquence est multipliée par 1.4 de l'autre, et <math>1.4 \approx 1/0.7</math>. En clair, la consommation dynamique d'un processeur ne change pas dans le temps, du moins tant que les lois de Dennard sont valides. Ce qui n'est plus le cas depuis 2005.
Tout ce qui est dit plus haut part du principe que le transistor MOS est un dispositif parfait. Dans les faits, ce n'est pas le cas. En effet, la couche d'isolant entre la grille et le semi-conducteur est très petite. Avec la miniaturisation, la couche d'isolant ne fait guère plus de quelques dizaines atomes d'épaisseur et laisse passer un peu de courant : on parle de ''courants de fuite''. La consommation d'énergie qui résulte de ce courant de fuite est la '''consommation statique'''. Elle s'appelle statique car elle a lieu même si les transistors ne changent pas d'état.
La diminution de la finesse de gravure a tendance à augmenter les courants de fuite. Elle diminue la consommation dynamique, mais augmente la consommation statique. Vu que les processeurs anciens avaient une consommation statique ridiculement basse et une consommation dynamique normale, doubler la première et diviser par deux la seconde donnait un sacré gain au total.
Quantifier la consommation statique est assez compliqué, et les équations deviennent généralement très complexes. Mais une simplification nous dit que les courants de fuite dépendent de la tension de seuil. Une équation très simplifiée est la suivante :
: <math>I_\text{Fuite} \propto e^{{- V_{Th} \over K}}</math>, avec <math>V_{Th}</math> la tension de seuil, et K une constante.
La consommation statique est le produit des courants de fuite et de la tension d'alimentation, avec d'autres facteurs de proportionnalités qui ne nous intéressent pas ici.
: <math>P_\text{Statique} \propto U \times e^{{- V_{Th} \over K}}</math>, avec U la tension d'alimentation.
===La loi de Kommey===
Avant 2005, la réduction de la finesse de gravure permettait de diminuer la consommation d'énergie, tout en augmentant la puissance de calcul. Mais depuis, la consommation statique a fini par rattraper la consommation dynamique. Et cela a une conséquence assez importante. L'efficacité énergétique des processeurs n'a pas cessé d'augmenter au cours du temps : pour le même travail, les processeurs chauffent moins. Globalement, le nombre de Watts nécessaires pour effectuer une instruction a diminué de manière exponentielle avec les progrès de la miniaturisation.
[[File:Computing efficiency, OWID.svg|centre|vignette|upright=2|Watts par millions d'instructions, au cours du temps.]]
[[File:Koomeys law graph, made by Koomey.jpg|vignette|Loi de Kommey]]
Il est aussi intéressant d'étudier la performance par watts, à savoir le nombre de Millions d'instructions par secondes pour chaque watt/kilowatt dépensé pour faire le calcul. Avant l'année 2005, la quantité de calcul que peut effectuer le processeur en dépensant un watt double tous les 1,57 ans. Cette observation porte le nom de '''loi de Kommey'''. Mais depuis 2005, on est passé à une puissance de calcul par watt qui double tous les 2,6 ans. Il y a donc eu un ralentissement dans l'efficacité énergétique des processeurs.
===Le ''dark silicon'' et le mur du TDP===
Malgré les nombreuses améliorations de la performance par watt dans le temps, les processeurs actuels chauffent beaucoup et sont contraints par les limites thermiques. Et ces limites thermiques se marient assez mal avec le grand nombre de transistors présents sur les processeurs modernes. Dans un futur proche, il est possible que les contraintes thermiques limitent le nombre de transistors actifs à un instant donné. Pour le dire autrement, il serait impossible d'utiliser plus de 50% des transistors d'un CPU en même temps, ou encore seulement 30%, etc. Ce genre de scénarios ont reçu le nom d'"utilization wall" dans la communauté académique.
Il y aurait donc un certain pourcentage du processeur qui ne pourrait pas être activée en raison des performances thermiques, portion qui porte le nom de '''''dark silicon'''''. Mais le ''dark silicon'' n'est pas du circuit inutile. Il faut bien comprendre que ce ''dark silicon'' n'est pas une portion précise de la puce. Par exemple, imaginons que 50% d'un processeur soit du ''dark silicon'' : cela ne veut pas dire que 50% du CPU ne sert à rien, mais que les deux moitié du processeur se passent le flambeau régulièrement, ils sont utilisés à tour de rôle.
En soi, le fait que tous les transistors ne soient pas actifs en même temps n'est pas un problème. Les processeurs modernes n'utilisent pas tous leurs circuits en même temps, et certains restent en pause tant qu'on ne les utilise pas. Par exemple, sur un processeur multicœurs, il arrive que certains cœurs ne soient pas utilisés, et restent en veille, voire soient totalement éteints. Par exemple, sur un processeur octo-coeurs, si seuls 4 cœurs sur les 8 sont utilisés, alors 50% du processeur est techniquement en veille.
L'existence du ''dark silicon'' implique cependant qu'il faut construire les processeurs en tenant compte de sa présence, du fait que les contrainte thermiques empêchent d'utiliser une portion significative des transistors à un instant t. Pour cela, l'idée en vogue actuellement est celle des '''architectures hétérogènes''', qui regroupent des processeurs très différents les uns des autres sur la même puce, avec chacun leur spécialisation.
Le premier cas existe déjà à l'heure actuelle. Il s'agit de processeurs qui regroupent deux types de cœurs : des cœurs optimisés pour la performance, et des cœurs optimisés pour la performance. Un exemple est celui des processeurs Intel de 12ème génération et plus, qui mélangent des ''P-core'' et des ''E-core'', les premiers étant des coeurs très performants mais gourmands en énergie, les autres étant économes en énergie et moins performants. Les noms complets des coeurs trahissent le tout : ''Efficiency core'' et ''Performance core''. L'utilité des E-core est d'exécuter des programmes peu gourmands, généralement des tâches d'arrière-plan. Les processeurs ARM de type BIG.little faisaient la même chose, mais avec un cœur de chaque type.
Le second cas est celui des processeurs regroupant un ou plusieurs cœurs normaux, généralistes, complétés par plusieurs accélérateurs spécialisés dans des tâches précises. Par exemple, on peut imaginer que le processeur incorpore un circuit spécialisé dans les calculs cryptographiques, un circuit spécialisé dans le traitement d'image, un autre dans le traitement de signal, un autre pour accélérer certains calculs liés à l'IA, etc. De tels circuits permettent des gains de performance dans des tâches très précises, qui ne se mélangent pas. Par exemple, si on lance un vidéo, le circuit de traitement d'image/vidéo sera activé, mais l'accélérateur cryptographique et l'accélération d'IA seront désactivés.
En soi, le fait d'adapter l'architecture des ordinateurs pour répondre à des contraintes thermiques n'est pas nouvelle. Nous verrons plus bas que l'apparition des processeurs multicœurs dans les années 2000 est une réponse à des contraintes technologiques assez strictes concernant la température, par exemple. La fin de la loi de Dennard a grandement réduit l'amélioration de performance par watt des processeurs à un seul cœur, rendant les processeurs multicœurs plus intéressants. Mais expliquer pourquoi demande d'expliquer pas mal de choses sur la performance d'un processeur simple cœur et comment celle-ci évolue avec la loi de Moore.
==L'évolution de la performance des processeurs==
Les processeurs ont gagné en performance avec le temps. Les raisons à cela sont doubles, mais liées aux lois de Dennard. La première raison est la hausse de la fréquence. C'était une source importante avant 2005, avant que les lois de Dennard cessent de fonctionner. L'autre raison, est la loi de Moore. Mettre plus de transistors permet de nombreuses optimisations importantes, comme utiliser des circuits plus rapides, mais plus gourmands en circuits. Voyons les deux raisons l'une après l'autre.
===La fréquence des processeurs a augmenté===
Les processeurs et mémoires ont vu leur fréquence augmenter au fil du temps. Pour donner un ordre de grandeur, le premier microprocesseur avait une fréquence de 740 kilohertz (740 000 hertz), alors que les processeurs actuels montent jusqu'à plusieurs gigahertz : plusieurs milliards de fronts par secondes ! L'augmentation a été exponentielle, mais plus faible que le nombre de transistors. Les nouveaux processeurs ont augmenté en fréquence, grâce à l'amélioration de la finesse de gravure. On est maintenant à environ 3 GHz à mi-2020.
Plus haut, on a dit que la fréquence augmentait de 40% tous les deux ans, tant que la loi de Dennard restait valide, avant 2005. En réalité, cette augmentation de 40% n'est qu'une approximation : la fréquence effective d'un processeur dépend fortement de sa conception (de la longueur du pipeline, notamment). Pour mettre en avant l'influence de la conception du processeur, il est intéressant de calculer une '''fréquence relative''', à savoir la fréquence à finesse de gravure égale. Elle est difficile à calculer, mais on peut l'utiliser pour comparer des processeurs entre eux, à condition de prendre la fréquence d'un processeur de référence.
Quelques observations montrent qu'elle a subi pas mal de variation d'un processeur à l'autre. La raison est que diverses techniques de conception permettent de gagner en fréquence facilement, la plus importante étant le pipeline. Augmenter la fréquence relative était une approche qui a eu son heure de gloire, mais qui a perdu de sa superbe. Avant les années 2000, augmenter la fréquence permettait de gagner en performance assez facilement. De plus, la fréquence était un argument marketing assez saillant, et l'augmenter faisait bien sur le papier. Aussi, les fréquences ont progressivement augmenté, et ont continué dans ce sens, jusqu’à atteindre une limite haute avec le Pentium 4 d'Intel.
[[File:Fréquences relatives processeurs Intel pré-Pentium 4.png|centre|vignette|upright=2|Fréquences relatives processeurs Intel pré-Pentium 4]]
Le Pentium 4 était une véritable bête en termes de fréquence pour l'époque : 1,5 GHz, contre à peine 1Ghz pour les autres processeurs de l'époque. La fréquence avait été doublée par rapport au Pentium 3 et ses 733 MHz. Et cette fréquence était juste la fréquence de base du processeur, certains circuits allaient plus vite, d'autres moins vite. Par exemple, l'unité de calcul intégrée dans le processeur allait deux fois plus vite, avec une fréquence de 3 GHz ! Et à l'inverse, certaines portions du processeur allaient au quart de cette fréquence, à 750 MHz. Bref, le Pentium 4 gérait plusieurs fréquences différentes : certaines portions importantes pour la performance allaient très vite, d'autres allaient à une vitesse intermédiaire pour faciliter leur conception, d'autres allaient encore moins vite, car elles n'avaient pas besoin d'être très rapides.
Le processeur était spécialement conçu pour fonctionner à très haute fréquence, grâce à diverses techniques qu'on abordera dans les chapitres suivants (pipeline très long, autres). Mais tout cela avait un cout : le processeur chauffait beaucoup ! Et c'était un défaut majeur. De plus, les techniques utilisées pour faire fonctionner le Pentium 4 à haute fréquence (le superpipeline) avaient beaucoup de défauts. Défauts compensés partiellement par des techniques innovantes pour l'époque, comme son ''replay system'' qu'on abordera dans un chapitre à part, mais sans grand succès. Les versions ultérieures du Pentium 4 avaient une fréquence plus base, mais avec un processeur complétement repensé, avec un résultat tout aussi performant. Ces versions chauffaient quand même beaucoup et les contraintes thermiques étaient un vrai problème.
En conséquence, la course à la fréquence s'est arrêtée avec ce processeur pour Intel, et l'industrie a suivi. La raison principale est que l'augmentation en fréquence des processeurs modernes est de plus en plus contrainte par la dissipation de chaleur et la consommation d'énergie. Et cette contrainte s'est manifestée en 2005, après la sortie du processeur Pentium 4. Le point d'inflexion en 2005, à partir duquel la fréquence a cessée d'augmenter drastiquement, s'explique en grande partie par cette contrainte. Les premiers processeurs étaient refroidis par un simple radiateur, alors que les processeurs modernes demandent un radiateur, un ventilateur et une pâte thermique de qualité pour dissiper leur chaleur. Pour limiter la catastrophe, les fabricants de processeurs doivent limiter la fréquence de leurs processeurs. Une autre raison est que la fréquence dépend des transistors, mais aussi de la rapidité du courant dans les interconnexions (les fils) qui relient les transistors, celles-ci devenant de plus en plus un facteur limitant pour la fréquence.
===La performance à fréquence égale a augmenté : la loi de Pollack===
Si la miniaturisation permet d'augmenter la fréquence, elle permet aussi d'améliorer la performance à fréquence égale. Rappelons que la performance à fréquence égale se mesure avec deux critères équivalents : l'IPC et le CPI. Le CPI est le nombre de cycles moyen pour exécuter une instruction. L'IPC est son inverse, à savoir le nombre d'instruction exécutable en un seul cycle. Les deu sont l'inverse l'un de l'autre. La '''loi de Pollack''' dit que l'augmentation de l'IPC d'un processeur est approximativement proportionnelle à la racine carrée du nombre de transistors ajoutés : si on double le nombre de transistors, la performance est multipliée par la racine carrée de 2.
On peut expliquer cette loi de Pollack assez simplement. Il faut savoir que les processeurs modernes peuvent exécuter plusieurs instructions en même temps (on parle d’exécution superscalaire), et peuvent même changer l'ordre des instructions pour gagner en performances (on parle d’exécution dans le désordre). Pour cela, les instructions sont préchargées dans une mémoire tampon de taille fixe, interne au processeur, avant d'être exécutée en parallèle dans divers circuits de calcul. Cependant, le processeur doit gérer les situations où une instruction a besoin du résultat d'une autre pour s'exécuter : si cela arrive, on ne peut exécuter les instructions en parallèle. Pour détecter une telle dépendance, chaque instruction doit être comparée à toutes les autres, pour savoir quelle instruction a besoin des résultats d'une autre. Avec N instructions, vu que chacune d'entre elles doit être comparée à toutes les autres, ce qui demande N^2 comparaisons.
En doublant le nombre de transistors, on peut donc doubler le nombre de comparateurs, ce qui signifie que l'on peut multiplier le nombre d'instructions exécutables en parallèle par la racine carrée de deux. En utilisant la loi de Moore, on en déduit qu'on gagne approximativement 40% d'IPC tous les deux ans, à ajouter aux 40 % d'augmentation de fréquence. En clair, la performance d'un processeur augmente de 40% grâce à la loi de Pollack, et de 40% par l'augmentation de fréquence. On a donc une augmentation de 80% tous les deux ans, donc une multiplication par 1,8 tous les deux ans, soit moins que la hausse de transistors.
===L'augmentation du nombre de cœurs===
Après 2005, l'augmentation de la fréquence a stagné et l'augmentation des performances semblait limitée par la seule loi de Pollack. Mais l'industrie a trouvé un moyen de contourner la loi de Pollack. En effet, cette dernière ne vaut que pour un seul processeur. Mais en utilisant plusieurs processeurs, la performance est, en théorie, la somme des performances individuelles de chacun d'entre eux. Et c'est la réaction qu'à eux l'industrie.
C'est pour cela que depuis les années 2000, le nombre de cœurs a augmenté assez rapidement. Les processeurs actuels sont doubles, voire quadruple cœurs : ce sont simplement des circuits imprimés qui contiennent deux, quatre, voire 8 processeurs différents, placés sur la même puce. Chaque cœur correspond à un processeur. En faisant ainsi, doubler le nombre de transistors permet de doubler le nombre de cœurs et donc de doubler la performance, ce qui est mieux qu'une amélioration de 40%.
[[File:CPU clock speed and Core count Graph.png|centre|vignette|upright=2.5|Comparaison entre fréquence et nombre de coeurs des processeurs, depuis ]]
==L'évolution de la performance des mémoires==
Après avoir vu le processeur, voyons comment la loi de Moore a impacté l'évolution des mémoires. Beaucoup de cours d'architecture des ordinateurs se contentent de voir l'impact de la loi de Moore sur le processeur, mais mettent de côté les mémoires. Et il y a une bonne raison à cela. Le fait est que les ordinateurs modernes ont une hiérarchie mémoire très complexe, avec beaucoup de mémoires différentes. Et les technologies utilisées pour ces mémoires sont très diverses, elles n'utilisent pas toutes des transistors MOS, la loi de Moore ne s'applique pas à toutes les mémoires.
Déjà, évacuons le cas des mémoires magnétiques (disques durs, disquettes) et des mémoires optiques (CD/DVD), qui ne sont pas fabriquées avec des transistors MOS et ne suivent donc pas la loi de Moore. Et de plus, elles sont en voie de disparition, elles ne sont plus vraiment utilisées de nos jours. Il ne reste que les '''mémoires à semi-conducteurs''' qui utilisent des transistors MOS, mais pas que. Seules ces dernières sont concernées par la loi de Moore, mais certaines plus que d'autres. Mais toutes les mémoires ont vu leur prix baisser en même temps que leur capacité a augmenté dans le temps, que ce soit à cause de la loi de Moore pour les mémoires à semi-conducteurs, l'amélioration exponentielle des technologies de stockage magnétique pour les disques durs.
[[File:Historical-cost-of-computer-memory-and-storage OWID.svg|centre|vignette|upright=2.0|Historical-cost-of-computer-memory-and-storage OWID]]
L'impact de la loi de Moore dépend de la mémoire considérée et de sa place dans la hiérarchie mémoire. Les mémoires intégrées au processeur, comme le cache ou les registres, sont des mémoires SRAM/ROM fabriquées intégralement avec des transistors MOS, et donc soumises à la loi de Moore. Leurs performances et leur capacité suivent l'évolution du processeur qui les intègre, leur fréquence augmente au même rythme que celle du processeur, etc. Aussi on peut considérer qu'on en a déjà parlé plus haut. Reste à voir les autres niveaux de la hiérarchie mémoire, à savoir la mémoire RAM principale, la mémoire ROM et les mémoires de masse. Dans les grandes lignes, on peut distinguer deux technologies principales : les mémoires DRAM et les mémoires FLASH.
===Les mémoires FLASH ont suivi la loi de Moore===
Les '''mémoires FLASH''' sont utilisées dans les mémoires de masse, comme les clés USB, les disques durs de type SSD, les cartes mémoires, et autres. Le passage à la mémoire FLASH a fait qu'elles sont plus rapides que les anciennes mémoires magnétiques, pour une capacité légèrement inférieure. Les mémoires FLASH sont aussi utilisées comme mémoire ROM principale ! Par exemple, les PC actuels utilisent de la mémoire FLASH pour stocker le ''firmware''/BIOS/UEFI. De même, les systèmes embarqués qui ont besoin d'un ''firmware'' rapide utilisent généralement de la mémoire FLASH, pas de la mémoire ROM proprement dite (on verra dans quelques chapitres que la mémoire FLASH est un sous-type de mémoire ROM, mais laissons cela à plus tard).
Elles sont basées sur des transistors MOS modifiés, appelés transistors à grille flottante. Un transistor à grille flottante peut être vu comme une sorte de mélange entre transistor et condensateur, à savoir qu'il dispose d'une grille améliorée, dont le caractère de condensateur est utilisé pour mémoriser une tension, donc un bit. Un transistor à grille flottante est utilisé pour mémoriser un bit sur les mémoires dites SLC, deux bits sur les mémoires dites MLC, trois bits sur les mémoires TLC, quatre sur les mémoires QLC. Non, ce n'est pas une erreur, c'est quelque chose de permis grâce aux technologies de fabrication d'une mémoire FLASH, nous détaillerons cela dans le chapitre sur les mémoires FLASH.
Vu que les mémoires FLASH sont basées sur des transistors MOS modifiés, vous ne serez pas trop étonnés d’apprendre que la loi de Moore s'applique à la mémoire FLASH. La taille des transistors à grille flottante suit la loi de Moore : elle diminue de 30% tous les deux ans.
[[File:NAND Fscaling 01.png|centre|vignette|upright=2.0|Taille d'une cellule de mémoire FLASH (de type NAND).]]
La conséquence est que l'aire occupée par un transistor à grille flottante est divisée par deux tous les deux ans. Le résultat est que la capacité des mémoires FLASH augmente de 50 à 60% par an, ce qui fait un doublement de leur capacité tous les deux ans.
[[File:NAND Fscaling 02.png|centre|vignette|upright=2.0|Aire d'une cellule de mémoire FLASH (de type NAND).]]
===Les mémoires RAM ne sont pas concernées par la loi de Moore===
[[File:1T-DRAM.png|vignette|Circuit qui mémorise un bit dans une mémoire DRAM moderne.]]
Les '''mémoires DRAM''' sont utilisées pour la mémoire principale de l'ordinateur, la fameuse mémoire RAM. A l'intérieur des mémoires DRAM actuelles, chaque bit est mémorisé en utilisant un transistor MOS et un condensateur (un réservoir à électron). Leur capacité et leur performance dépend aussi bien de la miniaturisation du transistor que de celle du condensateur. Elles sont donc partiellement concernées par la loi de Moore.
Pour ce qui est de la capacité, les DRAM suivent la loi de Moore d'une manière approximative. La raison est que gagner en capacité demande de réduire la taille des cellules mémoire, donc du transistors et du condensateur à l'intérieur. La miniaturisation des transistors suit la loi de Moore, mais la réduction de la taille du condensateur ne la suit pas. Dans le passé, la capacité des DRAM augmentait légèrement plus vite que la loi de Moore, avec un quadruplement tous les trois ans (4 ans pour la loi de Moore), mais tout a considérablement ralentit avec le temps.
[[File:Confronto processore memoria.jpg|centre|vignette|upright=2.0|Évolution du nombre de transistors d'une mémoire électronique au cours du temps. On voit que celle-ci suit de près la loi de Moore.]]
Niveau performances, la loi de Moore ne s'applique tout simplement pas. La raison à cela est que la performance des DRAM est dominée par la performance du condensateur, pas par celle du transistor. Miniaturiser des transistors permet de les rendre plus rapides, mais le condensateur ne suit pas vraiment. Aussi, les performances des mémoires DRAM stagnent.
Rappelons que la performance d'une mémoire RAM/ROM dépend de deux paramètres : son débit binaire, et son temps d'accès. Il est intéressant de comparer comment les deux ont évolué. Pour les mémoires RAM, le débit binaire a augmenté rapidement, alors que le temps d'accès a baissé doucement. Les estimations varient d'une étude à l'autre, mais disent que le temps d'accès des mémoires se réduit d'environ 10% par an, alors que le débit binaire a lui augmenté d'environ 30 à 60% par an. Une règle approximative est que le débit binaire a grandi d'au moins le carré de l'amélioration du temps d'accès.
Pour comprendre pourquoi temps d'accès et débit binaire n'ont pas évolué simultanément, il faut regarder du côté de la fréquence de la mémoire RAM. Les mémoires modernes sont cadencées avec un signal d'horloge, ce qui fait qu'il faut tenir compte de la '''fréquence de la mémoire'''. Le débit et les temps d'accès dépendent fortement de la fréquence de la mémoire. Plus la fréquence est élevée, plus les temps d'accès sont faibles, plus le débit est important.
Le calcul du débit binaire d'une mémoire est simplement le produit entre fréquence et largeur du bus mémoire. Il se trouve que la largeur du bus de données n'a pas beaucoup augmenté avec le temps. Les premières barrettes de mémoire datée des années 80, les barrettes SIMM, avaient un bus de données de 8 bits pour la version 30 broches, 32 bits pour la version 72 broches. Le bus mémoire était déjà très important. Dans les années 2000, la démocratisation des barrettes mémoires DIMM a permis au bus de données d'atteindre 64 bits, valeur à laquelle il est resté actuellement. Il est difficile d'augmenter la largeur du bus, ca cela demanderait d'ajouter des broches sur des barrettes et des connecteurs déjà bien chargées. L'augmentation du débit binaire ne peut venir que de l'augmentation de la fréquence.
Les premières mémoires utilisées dans les PCs étaient asynchrones, à savoir qu'elles n'avaient pas de fréquence ! Elles se passaient de fréquence d'horloge, et le processeur se débrouillait avec. Il s'agissait des premières mémoires DRAM d'Intel, les mémoires EDO, Fast-page RAM et autres, que nous verrons dans quelques chapitres. Elles étaient utilisées entre les années 70 et 90, où elles étaient le type de mémoire dominant. Elles étaient assez rapides pour le processeur, mémoire et processeur avaient des performances comparables.
Dans les années 1990, la SDRAM est apparue. La terme SDRAM regroupe les premières mémoires RAM synchrones, cadencées par un signal d'horloge. La fréquence des SDRAM était de 66, 100, 133 et 150 MHz. Les RAM à 66 MHz sont apparues en premier, suivies par les SDRAM à 100MHz et puis par les 133 MHz, celles de 150 MHz étaient plus rares. Lors de cette période, la relation entre fréquence, temps d'accès et débit binaire était assez claire. Le temps d'accès est proportionnel à la fréquence, à peu de choses près. Le temps d'accès est de quelques cycles d'horloge, bien qu'il dépende des barrettes de mémoire utilisées. Le débit est le produit entre fréquence et largeur du bus. Donc plus la fréquence est grande, meilleures sont les performances globales. Et la fréquence de la mémoire et celle du bus mémoire étaient identiques.
Depuis les années 2000, les mémoires RAM utilisent des techniques de ''Double data rate'' ou de ''Quad data rate'' qui permettent d'atteindre de hautes fréquences en trichant. La triche vient du fait que la fréquence de la mémoire n'est plus égale à la fréquence du bus mémoire depuis les années 2000. Nous verrons cela en détail dans le chapitre sur le bus mémoire. Pour le moment, nous allons nous contenter de dire que l'idée derrière cette différence de fréquence est d'augmenter le débit binaire des mémoires, mais sans changer leur fréquence interne.
{|class="wikitable"
|-
! Année
! Type de mémoire
! Fréquence de la mémoire (haut de gamme)
! Fréquence du bus
! Coefficient multiplicateur entre les deux fréquences
|-
| 1998
| DDR 1
| 100 - 200 MHz
| 200 - 400 MHz
| 2
|-
| 2003
| DDR 2
| 100 - 266 MHz
| 400 - 1066 MHz
| 4
|-
| 2007
| DDR 3
| 100 - 266 MHz
| 800 - 2133 MHz
| 8
|-
| 2014
| DDR 4
| 200 - 400 MHz
| 1600 - 3200 MHz
| 8
|-
| 2020
| DDR 5
| 200 - 450 MHz
| 3200 - 7200 MHz
| 8 à 16
|}
Le débit binaire est proportionnel à la fréquence du bus mémoire, alors que les temps d'accès sont proportionnel à la fréquence de la mémoire. La fréquence de la mémoire n'a pas beaucoup augmentée et reste très faible, les temps d'accès ont donc fait de même. Par contre, le débit binaire est lui très élevé, car dépendant de la fréquence du bus mémoire, qui a beaucoup augmenté. Au final, les mémoires modernes ont donc un gros débit, mais un temps de latence très élevé.
==La comparaison entre l'évolution des processeurs et des mémoires RAM : le ''memory wall''==
La performance du processeur et de la mémoire doivent idéalement être comparables. Rien ne sert d'avoir un processeur puisant si la mémoire ne suit pas. A quoi bon avoir un processeur ultra-puissant s'il passe 80% de son temps à attendre des données en provenance de la mémoire ? Si la performance des mémoires RAM stagne, alors que les processeurs gagnent en performance de manière exponentielle, on fait face à un problème.
Pour nous rendre compte du problème, il faut comparer la performance du processeur avec celle de la mémoire. Et c'est loin d'être facile, car les indicateurs de performance pour le processeur et la mémoire sont fondamentalement différents. La performance d'une mémoire dépend de son débit binaire et de son temps d'accès, la performance du processeur dépend de son CPI et de sa fréquence. Mais on peut comparer la vitesse à laquelle ces indicateurs grandissent. Pour donner un chiffre assez parlant, quelques estimations estiment que le temps d'accès de la mémoire, exprimé en cycles d'horloge du processeur, double tous les 2,6 ans.
Une autre possibilité est comparer la fréquence des processeurs et des mémoires, pour voir si la fréquence de la mémoire a suivi celle du processeur. On s'attendrait à une augmentation de 40% de la fréquence des mémoires tous les deux ans, comme c'est le cas pour les processeurs. Mais la fréquence des mémoires n'a pas grandit au même rythme et a été beaucoup plus faible que pour les processeurs. Faisons un historique rapide.
Lors de l'époque des mémoires asynchrones, mémoire et processeur avaient des performances comparables. Les accès mémoire se faisaient globalement en un cycle d'horloge, éventuellement deux ou trois cycles, rarement plus. Bien qu'asynchrone, on peut considérer qu'elles allaient à la même fréquence que le processeur, mais cela ne servait à rien de parler de fréquence de la mémoire ou de fréquence du bus mémoire.
Pour la période des mémoires SDRAM, le tableau ci-dessous fait une comparaison des fréquences processeur-mémoire. La comparaison avec les processeurs était assez simple, car la fréquence du bus mémoire et la fréquence de la mémoire sont identiques. On voit que la fréquence de la mémoire est déjà loin derrière la fréquence du processeur, elle est 3 à 5 fois plus faible.
{|class="wikitable"
|-
! Année
! Fréquence du processeur (haut de gamme)
! Fréquence de la mémoire (haut de gamme)
|-
| 1993
| Intel Pentium : 60 à 300 MHz
| 66 Mhz
|-
| 1996-1997
|
* Intel Pentium 2 (1996) : 233 MHz à 450 MHz
* AMD K5 (1996) : 75 MHz à 133 MHz
* AMD K6 (1997) : 166 MHz à 300 MHz
| 100 MHz
|-
| 1999
|
* Intel Pentium 3 : 450 MHz à 1,4 GHz
* AMD Athlon : 500 MHz à 1400 MHz
| 133 MHz
|}
Avec l'invention des mémoires DDR, la comparaison est rendue plus compliquée par la dissociation entre fréquence du bus et fréquence de la mémoire. La comparaison est la suivante :
{|class="wikitable"
|-
! Année
! Type de mémoire
! Fréquence de la mémoire (haut de gamme)
! Fréquence du bus
! Fréquence du processeur (haut de gamme)
|-
| 1998
| DDR 1
| 100 - 200 MHz
| 200 - 400 MHz
| 200 - 1000 MHz
|-
| 2003
| DDR 2
| 100 - 266 MHz
| 400 - 1066 MHz
| 700 - 1500 MHz
|-
| 2007
| DDR 3
| 100 - 266 MHz
| 800 - 2133 MHz
| 1500 - 3000 MHz
|-
| 2014
| DDR 4
| 200 - 400 MHz
| 1600 - 3200 MHz
| 1600 - 4200 MHz
|-
| 2020
| DDR 5
| 200 - 450 MHz
| 3200 - 7200 MHz
| 1700 - 4500 MHz
|}
Le constat est assez clair : le processeur est plus rapide que la mémoire, et l'écart se creuse de plus en plus avec le temps. Il s'agit d'un problème assez important, qui dicte l'organisation des ordinateurs modernes. Les mémoires sont actuellement très lentes comparé au processeur. On parle souvent de '''''memory wall''''', pour décrire ce problème, nous utiliserons le terme '''mur de la mémoire'''. Pour cela, diverses solutions existent. Et la plus importante d'entre elle est l'usage d'une hiérarchie mémoire.
===Les solutions contre le mur de la mémoire : hiérarchie mémoire et RAM computationnelle===
Le mur de la mémoire est un problème avec lequel les architectures modernes doivent composer. Le mur de la mémoire a deux origines. La première est que processeur et mémoire sont strictement séparés et que tout traitement doit lire des opérandes en mémoire, pour ensuite écrire des résultats en mémoire RAM. La seconde est que les transferts entre processeurs et mémoire sont assez lents, ce qui fait que l'idéal est de réduire ces transferts le plus possible.
La solution la plus souvent retenue est l'usage de '''mémoires caches''' intégrées au processeur, pour réduire le trafic entre DRAM et CPU. Les mémoires caches ne sont pas des DRAM, ce qui permet de contourner le problème du mur de la mémoire, causé par la technologie de fabrication des DRAM. Les caches sont des mémoires à semi-conducteur fabriquées avec des transistors CMOS, ce qui fait que leurs performances augmentent au même rythme que le processeur. Elles sont intégrées dans le processeur, même s'il a existé des mémoires caches connectées sur la carte mère.
Mais une autre solution consiste à faire l'inverse, à savoir ajouter des capacités de calcul dans la mémoire RAM. L'idée est de faire les calculs dans la mémoire directement : pas besoin de transférer les opérandes du calcul de la mémoire vers le CPU, ni de transférer les résultats du CPU vers la RAM. L'idée est alors de déléguer certains calculs à la DRAM, voire carrément de fusionner le CPU et la DRAM ! On parle alors de '''''in-memory processing''''', que nous traduirons par le terme quelque peu bâtard de '''RAM computationnelle'''.
Cependant, l'implémentation d'une RAM computationnelle pose quelques problèmes d'ordre pratique. Le premier est au niveau de la technologie utilisée pour les transistors. Comme on vient de le voir, les technologies utilisées pour fabriquer la mémoire sont très différentes de celles utilisées pour fabriquer des circuits logiques, les circuits d'un processeur. Les processeurs utilisent des transistors CMOS normaux, les mémoires FLASH des transistors MOS à grille flottante, les DRAM utilisent un mélange de transistors MOS et de condensateurs. Et au niveau des procédés de fabrication, de gravure des puce, de photolithographie, de la technique des semi-conducteurs, les trois procédés sont totalement différents. Aussi, fusionner DRAM et CPU pose des problèmes de fabrication assez complexes.
Notons que ce problème n'a pas lieu avec la mémoire utilisée pour les registres et les caches, car elle est fabriquée avec des transistors. Nous avons vu il y a quelques chapitres comment créer des registres à partir de transistors et de portes logiques, ce qui utilise le même procédé technologique que pour les CPU. Les mémoires caches utilisent des cellules de mémoire SRAM fabriquées uniquement avec des transistors CMOS, comme nous le verrons dans quelques chapitres. Registres et SRAM sont donc fabriqués avec le même procédé technologique que les processeurs, ce qui fait que l'intégration de registres/caches dans le processeur est assez simple. Ce n'est pas le cas pour la fusion DRAM/CPU.
La majorité des architectures à RAM computationnelle ont été des échecs commerciaux. La raison est qu'il s'agit d'architectures un peu particulières, qui sont formellement des architectures dites à parallélisme de données, assez difficiles à exploiter. La tentative la plus connue était le '''projet IRAM de l'université de Berkeley'''. L'idée était de fusionner un processeur et une mémoire sur la même puce, les deux étant intégrés dans le même circuit. Démarré en 1996, il a été abandonnée en 2004.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=La performance d'un ordinateur
| prevText=La performance d'un ordinateur
| next=Les techniques de réduction de la consommation électrique d'un processeur
| nextText=Les techniques de réduction de la consommation électrique d'un processeur
}}{{autocat}}
</noinclude>
4kxw8n4hvt52usd2b8v1t3kr78q757j
744789
744788
2025-06-15T02:00:55Z
Mewtow
31375
/* La comparaison entre l'évolution des processeurs et des mémoires RAM : le memory wall */
744789
wikitext
text/x-wiki
Il va de soi que les nouveaux processeurs sont plus puissants que les anciens, pareil pour les mémoires. La raison à cela vient des optimisations apportées par les concepteurs de processeurs. La plupart de ces optimisations ne sont cependant possibles qu'avec la miniaturisation des transistors, qui leur permet d'aller plus vite. Et cela se voit dans les données empiriques. Il est intéressant de regarder comment les mémoires et processeurs ont évolué dans le temps.
Pour les processeurs, la loi de Moore a des conséquences qui sont assez peu évidentes. Certes, on peut mettre plus de transistors dans un processeur, mais en quoi cela se traduira par de meilleures performances ? Pour comprendre l'influence qu'à eu la loi de Moore sur les processeurs modernes, regardons ce graphique, qui montre les relations entre nombre de transistors, fréquence du processeur, performance d'un seul cœur, nombre de cœurs, et consommation énergétique.
[[File:50 Years Of Microprocessor Trend Data.png|centre|vignette|upright=2|50 years of microprocessor trend data, par Karl Rupp.]]
Globalement, on voit que le nombre de transistors augmente de façon exponentielle : doubler tous les X années donne une courbe exponentielle, d'où l'échelle semi-logarithmique du graphique. Mais pour le reste, quelque chose s'est passé en 2005 : les courbes n'ont pas la même pente avant et après 2005. Que ce soit la fréquence, la performance d'un seul cœur, la consommation électrique, tout. Et le nombre de cœurs explose au même moment. Tout cela fait penser que toutes ces caractéristiques étaient liées entre elles et augmentaient exponentiellement, mais il y a un après 2005. Reste à expliquer pourquoi, ce qui est le sujet de ce chapitre, sans compter qu'on détaillera tout ce qui a trait à la consommation énergétique.
==La miniaturisation des transistors est la cause des tendances technologiques==
Avant toute chose, nous devons faire quelques rappels sur les transistors MOS, sans lesquels les explications qui vont suivre seront compliquées. Un transistor MOSFET a de nombreuses caractéristiques : ses dimensions, mais aussi d'autres paramètres plus intéressants. Par exemple, il est intéressant de regarder la consommation d'énergie d'un transistor, à savoir combien de watts ils utilise pour faire ce qu'on lui demande. Pour cela, il faudra parler rapidement de certaines de ses caractéristiques comme sa capacité électrique. Rien de bien compliqué, rassurez-vous.
===Les caractéristiques d'un transistor : finesse de gravure, capacité, etc===
[[File:Transistor CMOS - 1.png|vignette|Transistor CMOS - 1]]
Un transistor MOS est composé de deux morceaux de conducteurs (l'armature de la grille et la liaison drain-source) séparés par un isolant. Les dimensions d'un transistors sont au nombre de deux : la distance entre source et drain, la distance entre grille et semi-conducteur. Les deux sont regroupées sous le terme de '''finesse de gravure''', bien que cela soit un terme impropre.
Nous avons dit plus haut qu'un transistor MOS est composé de deux (semi-)conducteurs séparés par un isolant. Tout cela ressemble beaucoup à un autre composant électronique appelé le '''condensateur''', qui sert de réservoir à électricité. On peut le charger en électricité, ou le vider pour fournir un courant durant une petite durée de temps. L'intérieur d'un condensateur est formé de deux couches de métal conducteur, séparées par un isolant électrique. Les charge s'accumulent dans les couche de métal quand on charge le condensateur en électricité. L'intérieur d'un transistor MOS est donc similaire à celui d'un condensateur, si ce n'est qu'une couche métallique est remplacée par un morceau de semi-conducteur. Tout cela fait qu'un transistor MOS incorpore un pseudo-condensateur caché entre la grille et la liaison source-drain, qui porte le nom de capacité parasite du transistor.
[[File:Plaatcondensator2.GIF|centre|vignette|upright=2|Condensateur et accumulation des charges électrique sur les plaques métalliques.]]
Tout condensateur possède une caractéristique importante : sa '''capacité électrique'''. Il s'agit simplement de la quantité d'électrons/charges qu'il peut contenir en fonction de la tension. Il faut savoir que la quantité de charge contenue dans un condensateur est proportionnelle à la tension, la capacité est le coefficient de proportionnalité entre les deux. Tout cela est sans doute plus clair avec une équation :
: <math>Q = C U</math>, avec Q la quantité de charges contenues dans le condensateur, U la tension, et C la capacité.
[[File:Capacity.svg|vignette|Charge/décharge d'un condensateur.]]
La capacité d'un transistor MOS a une influence directe sur la fréquence à laquelle il peut fonctionner. Pour changer l'état d'un transistor MOS, il faut soit charger la grille, soit la décharger. Et pour remplir le transistor, il faut fournir une charge égale à celle donnée par l'équation précédente.
Si on met ce processus en équations, on s’aperçoit qu'on se trouve avec des charges ou décharges exponentielles. Mais par simplicité, on considère que le temps de charge/décharge d'un condensateur est proportionnel à sa capacité (pour être précis, proportionnel au produit 5 RC, avec R la résistance des fils). Tout ce qu'il faut retenir est que plus la capacité est faible, plus le transistor est rapide et plus il peut fonctionner à haute fréquence.
===Les lois de Dennard, ce qui se cache derrière la loi de Moore===
La loi de Moore est le résultat d'une tendance technologique bien précise : les dimensions d'un transistors se réduisent avec les progrès de la miniaturisation. Elles sont réduites de 30% tous les deux ans. Pour le dire autrement, elles sont multipliées par 0.7 tous les deux ans.
[[File:Comparison semiconductor process nodes.svg|centre|vignette|upright=2.0|Évolution de la finesse de gravure au cours du temps pour les processeurs.]]
Les processeurs sont des composants qui ont actuellement une forme carrée, les transistors sont tous placés sur un plan et ne sont pas empilés les uns sur les autres. Ils occupent donc une certaine aire sur la surface du processeur. Si la taille des transistors est réduite de 30% tous les 2 ans, l'aire que prend un transistor sur la puce est quand à elle divisée par 30% * 30% <math>\approx</math> 50%. En conséquence, on peut mettre deux fois plus de transistors sur la même puce électronique : on retrouve la loi de Moore.
Cela a aussi des conséquences sur la tension d'alimentation nécessaire pour faire fonctionner le transistor. Sans rentrer dans les détails, la tension est elle aussi proportionnelle aux dimensions du transistor. La raison technique, que vous comprendrez si vous avez eu des cours d'électricité avancés durant votre scolarité, est que le champ électrique ne change pas dans le transistor, et que la tension est le produit du champ électrique par la distance. Là encore, la tension d'alimentation est réduite de 30% tous les deux ans.
[[File:Parallel plate capacitor.svg|vignette|Condensateur plan]]
La miniaturisation a une influence directe sur la capacité électrique du transistor. Pour comprendre pourquoi, il faut savoir que le condensateur formé par la grille, l'isolant et le morceau de semi-conducteur est ce que l'on appelle un ''condensateur plan''. La capacité de ce type de condensateur dépend de la surface de la plaque de métal (la grille), du matériau utilisé comme isolant et de la distance entre la grille et le semi-conducteur. On peut calculer cette capacité comme suit, avec A la surface de la grille, e un paramètre qui dépend de l'isolant utilisé et d la distance entre le semi-conducteur et la grille (l'épaisseur de l'isolant, quoi).
: <math>C = \frac{A}{d} \times e</math>
Le coefficient e (la permittivité électrique) reste le même d'une génération de processeur à l'autre, même si les fabricants ont réussi à le faire baisser un peu grâce à matériaux particuliers. Mais laissons cela de côté : dans les faits, seuls les coefficients S et d vont nous intéresser. Si la finesse de gravure diminue de 30%, la distance d diminue du même ordre, la surface A diminue du carré de 30%, c’est-à-dire qu'elle sera approximativement divisée par 2. La capacité totale sera donc divisée par 30% tous les deux ans.
Réduire la capacité des transistors a un impact indirect très fort sur la fréquence à laquelle on peut faire fonctionner le transistor. En effet, la période de l'horloge correspond ''grosso modo'' au temps qu'il faut pour remplir ou vider l'armature de la grille, et on sait que le temps de charge/décharge d'un condensateur est approximativement proportionnel à sa capacité. La capacité et le temps de charge/décharge est donc réduit de 30% tous les deux ans. La fréquence étant inversement proportionnelle au temps de remplissage du condensateur, elle est donc augmentée de 1/0.7 = <math>\approx</math> 40%.
Tout ce qu'on vient de dire plus a été formalisé sous le nom de '''lois de Dennard''', du nom de l'ingénieur qui a réussi à démontrer ces équations à partir des lois de la physique des semi-conducteurs. Une réduction de la finesse de gravure impacte plusieurs paramètres : le nombre de transistors d'une puce électronique, sa tension d'alimentation, sa fréquence, et quelques autres paramètres qu'on détaillera plus bas comme la capacité d'un transistor ou ses courants de fuite.
{| class="wikitable"
|-
! Paramètre !! Coefficient multiplicateur (tous les deux ans) || Variation en pourcentage
|-
| Finesse de gravure || 0.7 || - 30%
|-
| Aire occupée par un transistor || 0.5 || - 50%
|-
| Nombre de transistors par unité de surface || 2 || + 100%
|-
| Tension d'alimentation || 0.7 || - 30%
|-
| Capacité d'un transistor || 0.7 || - 30%
|-
| Fréquence || (1/0.7) = 1.4 || + 40%
|}
===La fin des lois de Dennard===
Les lois de Dennard ont cessé de s'appliquer aux alentours de 2005/2006. Les dimensions d'un transistors sont toujours réduites de 30% tous les deux ans, la loi de Moore est encore valable, mais pour ce qui est de la fréquence et de la tension d'alimentation, c'est autre chose. Les raisons à cela sont multiples, et il faut revenir au fonctionnement d'un transistor MOS pour comprendre pourquoi.
Un MOSFET est composé d'une grille métallique, d'une couche de semi-conducteur, et d'un isolant entre les deux. L'isolant empêche la fuite des charges de la grille vers le semi-conducteur. Mais avec la miniaturisation, la couche d'isolant ne fait guère plus de quelques dizaines atomes d'épaisseur et laisse passer un peu de courant : on parle de '''courants de fuite'''. Plus cette couche d'isolant est petite, plus le courant de fuite sera fort. En clair, une diminution de la finesse de gravure a tendance à augmenter les courants de fuite.
Les courants de fuite dépendent d'une tension appelée '''tension de seuil'''. Il s'agit de la tension minimale pour avoir un courant passant entre la source et le drain. Sous cette tension, le transistor se comporte comme un interrupteur fermé, peut importe ce qu'on met sur la grille. Au-dessus de cette tension, le courant se met à passer entre source et drain, il se comporte comme un interrupteur ouvert. Le courant est d'autant plus fort que la tension sur la grille dépasse la tension de seuil. On ne peut pas faire fonctionner un transistor si la tension d'alimentation (entre source et drain) est inférieure à la tension de seuil. C'est pour cela que ces dernières années, la tension d'alimentation des processeurs est restée plus ou moins stable, à une valeur proche de la tension de seuil (1 volt, environ). Et l'incapacité à réduire cette tension a eu des conséquences fâcheuses.
Nous verrons plus bas que la consommation d'énergie d'un processeur dépend de sa fréquence et de sa tension. Les lois de Dennard nous disent que Si la seconde baisse, on peut augmenter la première sans changer drastiquement la consommation énergétique. Mais si la tension d'alimentation stagne, alors la fréquence doit faire de même. Vu que les concepteurs de processeurs ne pouvaient pas diminuer la fréquence pour garder une consommation soutenable, et ont donc préféré augmenter le nombre de cœurs. L'augmentation de consommation énergétique ne découle que de l'augmentation du nombre de transistors et des diminutions de capacité. Et la diminution de 30 % tous les deux ans de la capacité ne compense plus le doublement du nombre de transistors : la consommation énergétique augmente ainsi de 40 % tous les deux ans. Bilan : la performance par watt stagne. Et ce n'est pas près de s'arranger tant que les tensions de seuil restent ce qu'elles sont.
L'explication est convaincante, mais nous détaillerons celle-ci plus bas, avec les vraies équations qui donnent la consommation d'énergie d'un processeur. Nous en profiterons aussi pour voir quelles sont les technologies utilisées pour réduire cette consommation d'énergie, les deux étant intrinsèquement liés.
==La consommation d'un circuit électronique CMOS==
Tout ordinateur consomme une certaine quantité de courant pour fonctionner, et donc une certaine quantité d'énergie. On peut dire la même chose de tout circuit électronique, que ce soit une mémoire, un processeur, ou quoique ce soit d'autre d'électronique. Il se trouve que cette énergie finit par être dissipée sous forme de chaleur : plus un composant consomme d'énergie, plus il chauffe. La chaleur dissipée est mesurée par ce qu'on appelle l''''enveloppe thermique''', ou TDP (''Thermal Design Power''), mesurée en Watts.
Pour donner un exemple, le TDP d'un processeur tourne autour des 50 watts, parfois plus sur les modèles plus anciens. De telles valeurs signifient que les processeurs actuels chauffent beaucoup. Pourtant, ce n'est pas la faute des fabricants de processeurs qui essaient de diminuer la consommation d'énergie de nos CPU au maximum. Malgré cela, les processeurs voient leur consommation énergétique augmenter toujours plus : l'époque où l'on refroidissait les processeurs avec un simple radiateur est révolue. Place aux gros ventilateurs super puissants, placés sur un radiateur.
===La consommation d'un circuit électronique CMOS===
Pour comprendre pourquoi, on doit parler de ce qui fait qu'un circuit électronique consomme de l'énergie. Une partie de la consommation d'énergie d'un circuit vient du fait qu'il consomme de l'énergie en changeant d'état. On appelle cette perte la '''consommation dynamique'''. A l’opposé, la '''consommation statique''' vient du fait que les circuits ne sont pas des dispositifs parfaits et qu'ils laissent fuiter un peu de courant.
Pour commencer, rappelons qu'un transistor MOS est composé de deux morceaux de conducteurs (l'armature de la grille et la liaison drain-source) séparés par un isolant. L'isolant empêche la fuite des charges de la grille vers la liaison source-drain. Charger ou décharger un condensateur demande de l'énergie. Les lois de la physique nous disent que la quantité d'énergie que peut stocker un condensateur est égale au produit ci-dessous, avec C la capacité du condensateur et U la tension d'alimentation. Il s'agit de l'énergie qu'il faut fournir quand on charge ou décharger un condensateur/MOSFET.
: <math>E = \frac{1}{2} C U^2</math>
L'énergie est dissipée quand les transistors changent d'état, il dissipe une quantité de chaleur proportionnelle au carré de la tension d'alimentation. Or, la fréquence définit le nombre maximal de changements d'état qu'un transistor peut subir en une seconde : pour une fréquence de 50 hertz, un transistor peut changer d'état 50 fois par seconde maximum. Après, il s'agit d'une borne maximale : il se peut qu'un transistor ne change pas d'état à chaque cycle. Sur les architectures modernes, la probabilité de transition 0 ⇔ 1 étant d'environ 10%-30%. Et si les bits gardent la même valeur, alors il n'y a pas de dissipation de puissance. Mais on peut faire l'approximation suivante : le nombre de changement d'état par seconde d'un transistor est proportionnel à la fréquence. L'énergie dissipée en une seconde (la puissance P) par un transistor est approximée par l'équation suivante :
: <math>P_\text{dynamique} \approx \frac{1}{2} C U^2 \times f</math>
On peut alors multiplier par le nombre de transistors d'une puce électronique, ce qui donne :
: <math>P_\text{dynamique} \approx \frac{1}{2} C U^2 \times f \times n</math>
Cette équation nous donne la '''consommation dynamique''', à savoir celle liée à l'activité du processeur/circuit. De plus, l'équation précédente permet de comprendre comment la consommation dynamique a évolué grâce à la loi de Moore. Pour cela, regardons comment chaque variable a évolué et faisons les comptes :
* le nombre de transistors fait *2 tous les deux ans ;
* la capacité est réduite de 30% tous les deux ans ;
* la tension d'alimentation est réduite de 30% tous les deux ans ;
* la fréquence augmente de 40% tous les deux ans.
L'augmentation du nombre de transistors est parfaitement compensé par la baisse de la tension d'alimentation : multiplication par deux d'un côté, divisé par 2 (30% au carré) de l'autre. Idem avec la capacité et la fréquence : la capacité est multipliée par 0.7 tous les deux ans, la fréquence est multipliée par 1.4 de l'autre, et <math>1.4 \approx 1/0.7</math>. En clair, la consommation dynamique d'un processeur ne change pas dans le temps, du moins tant que les lois de Dennard sont valides. Ce qui n'est plus le cas depuis 2005.
Tout ce qui est dit plus haut part du principe que le transistor MOS est un dispositif parfait. Dans les faits, ce n'est pas le cas. En effet, la couche d'isolant entre la grille et le semi-conducteur est très petite. Avec la miniaturisation, la couche d'isolant ne fait guère plus de quelques dizaines atomes d'épaisseur et laisse passer un peu de courant : on parle de ''courants de fuite''. La consommation d'énergie qui résulte de ce courant de fuite est la '''consommation statique'''. Elle s'appelle statique car elle a lieu même si les transistors ne changent pas d'état.
La diminution de la finesse de gravure a tendance à augmenter les courants de fuite. Elle diminue la consommation dynamique, mais augmente la consommation statique. Vu que les processeurs anciens avaient une consommation statique ridiculement basse et une consommation dynamique normale, doubler la première et diviser par deux la seconde donnait un sacré gain au total.
Quantifier la consommation statique est assez compliqué, et les équations deviennent généralement très complexes. Mais une simplification nous dit que les courants de fuite dépendent de la tension de seuil. Une équation très simplifiée est la suivante :
: <math>I_\text{Fuite} \propto e^{{- V_{Th} \over K}}</math>, avec <math>V_{Th}</math> la tension de seuil, et K une constante.
La consommation statique est le produit des courants de fuite et de la tension d'alimentation, avec d'autres facteurs de proportionnalités qui ne nous intéressent pas ici.
: <math>P_\text{Statique} \propto U \times e^{{- V_{Th} \over K}}</math>, avec U la tension d'alimentation.
===La loi de Kommey===
Avant 2005, la réduction de la finesse de gravure permettait de diminuer la consommation d'énergie, tout en augmentant la puissance de calcul. Mais depuis, la consommation statique a fini par rattraper la consommation dynamique. Et cela a une conséquence assez importante. L'efficacité énergétique des processeurs n'a pas cessé d'augmenter au cours du temps : pour le même travail, les processeurs chauffent moins. Globalement, le nombre de Watts nécessaires pour effectuer une instruction a diminué de manière exponentielle avec les progrès de la miniaturisation.
[[File:Computing efficiency, OWID.svg|centre|vignette|upright=2|Watts par millions d'instructions, au cours du temps.]]
[[File:Koomeys law graph, made by Koomey.jpg|vignette|Loi de Kommey]]
Il est aussi intéressant d'étudier la performance par watts, à savoir le nombre de Millions d'instructions par secondes pour chaque watt/kilowatt dépensé pour faire le calcul. Avant l'année 2005, la quantité de calcul que peut effectuer le processeur en dépensant un watt double tous les 1,57 ans. Cette observation porte le nom de '''loi de Kommey'''. Mais depuis 2005, on est passé à une puissance de calcul par watt qui double tous les 2,6 ans. Il y a donc eu un ralentissement dans l'efficacité énergétique des processeurs.
===Le ''dark silicon'' et le mur du TDP===
Malgré les nombreuses améliorations de la performance par watt dans le temps, les processeurs actuels chauffent beaucoup et sont contraints par les limites thermiques. Et ces limites thermiques se marient assez mal avec le grand nombre de transistors présents sur les processeurs modernes. Dans un futur proche, il est possible que les contraintes thermiques limitent le nombre de transistors actifs à un instant donné. Pour le dire autrement, il serait impossible d'utiliser plus de 50% des transistors d'un CPU en même temps, ou encore seulement 30%, etc. Ce genre de scénarios ont reçu le nom d'"utilization wall" dans la communauté académique.
Il y aurait donc un certain pourcentage du processeur qui ne pourrait pas être activée en raison des performances thermiques, portion qui porte le nom de '''''dark silicon'''''. Mais le ''dark silicon'' n'est pas du circuit inutile. Il faut bien comprendre que ce ''dark silicon'' n'est pas une portion précise de la puce. Par exemple, imaginons que 50% d'un processeur soit du ''dark silicon'' : cela ne veut pas dire que 50% du CPU ne sert à rien, mais que les deux moitié du processeur se passent le flambeau régulièrement, ils sont utilisés à tour de rôle.
En soi, le fait que tous les transistors ne soient pas actifs en même temps n'est pas un problème. Les processeurs modernes n'utilisent pas tous leurs circuits en même temps, et certains restent en pause tant qu'on ne les utilise pas. Par exemple, sur un processeur multicœurs, il arrive que certains cœurs ne soient pas utilisés, et restent en veille, voire soient totalement éteints. Par exemple, sur un processeur octo-coeurs, si seuls 4 cœurs sur les 8 sont utilisés, alors 50% du processeur est techniquement en veille.
L'existence du ''dark silicon'' implique cependant qu'il faut construire les processeurs en tenant compte de sa présence, du fait que les contrainte thermiques empêchent d'utiliser une portion significative des transistors à un instant t. Pour cela, l'idée en vogue actuellement est celle des '''architectures hétérogènes''', qui regroupent des processeurs très différents les uns des autres sur la même puce, avec chacun leur spécialisation.
Le premier cas existe déjà à l'heure actuelle. Il s'agit de processeurs qui regroupent deux types de cœurs : des cœurs optimisés pour la performance, et des cœurs optimisés pour la performance. Un exemple est celui des processeurs Intel de 12ème génération et plus, qui mélangent des ''P-core'' et des ''E-core'', les premiers étant des coeurs très performants mais gourmands en énergie, les autres étant économes en énergie et moins performants. Les noms complets des coeurs trahissent le tout : ''Efficiency core'' et ''Performance core''. L'utilité des E-core est d'exécuter des programmes peu gourmands, généralement des tâches d'arrière-plan. Les processeurs ARM de type BIG.little faisaient la même chose, mais avec un cœur de chaque type.
Le second cas est celui des processeurs regroupant un ou plusieurs cœurs normaux, généralistes, complétés par plusieurs accélérateurs spécialisés dans des tâches précises. Par exemple, on peut imaginer que le processeur incorpore un circuit spécialisé dans les calculs cryptographiques, un circuit spécialisé dans le traitement d'image, un autre dans le traitement de signal, un autre pour accélérer certains calculs liés à l'IA, etc. De tels circuits permettent des gains de performance dans des tâches très précises, qui ne se mélangent pas. Par exemple, si on lance un vidéo, le circuit de traitement d'image/vidéo sera activé, mais l'accélérateur cryptographique et l'accélération d'IA seront désactivés.
En soi, le fait d'adapter l'architecture des ordinateurs pour répondre à des contraintes thermiques n'est pas nouvelle. Nous verrons plus bas que l'apparition des processeurs multicœurs dans les années 2000 est une réponse à des contraintes technologiques assez strictes concernant la température, par exemple. La fin de la loi de Dennard a grandement réduit l'amélioration de performance par watt des processeurs à un seul cœur, rendant les processeurs multicœurs plus intéressants. Mais expliquer pourquoi demande d'expliquer pas mal de choses sur la performance d'un processeur simple cœur et comment celle-ci évolue avec la loi de Moore.
==L'évolution de la performance des processeurs==
Les processeurs ont gagné en performance avec le temps. Les raisons à cela sont doubles, mais liées aux lois de Dennard. La première raison est la hausse de la fréquence. C'était une source importante avant 2005, avant que les lois de Dennard cessent de fonctionner. L'autre raison, est la loi de Moore. Mettre plus de transistors permet de nombreuses optimisations importantes, comme utiliser des circuits plus rapides, mais plus gourmands en circuits. Voyons les deux raisons l'une après l'autre.
===La fréquence des processeurs a augmenté===
Les processeurs et mémoires ont vu leur fréquence augmenter au fil du temps. Pour donner un ordre de grandeur, le premier microprocesseur avait une fréquence de 740 kilohertz (740 000 hertz), alors que les processeurs actuels montent jusqu'à plusieurs gigahertz : plusieurs milliards de fronts par secondes ! L'augmentation a été exponentielle, mais plus faible que le nombre de transistors. Les nouveaux processeurs ont augmenté en fréquence, grâce à l'amélioration de la finesse de gravure. On est maintenant à environ 3 GHz à mi-2020.
Plus haut, on a dit que la fréquence augmentait de 40% tous les deux ans, tant que la loi de Dennard restait valide, avant 2005. En réalité, cette augmentation de 40% n'est qu'une approximation : la fréquence effective d'un processeur dépend fortement de sa conception (de la longueur du pipeline, notamment). Pour mettre en avant l'influence de la conception du processeur, il est intéressant de calculer une '''fréquence relative''', à savoir la fréquence à finesse de gravure égale. Elle est difficile à calculer, mais on peut l'utiliser pour comparer des processeurs entre eux, à condition de prendre la fréquence d'un processeur de référence.
Quelques observations montrent qu'elle a subi pas mal de variation d'un processeur à l'autre. La raison est que diverses techniques de conception permettent de gagner en fréquence facilement, la plus importante étant le pipeline. Augmenter la fréquence relative était une approche qui a eu son heure de gloire, mais qui a perdu de sa superbe. Avant les années 2000, augmenter la fréquence permettait de gagner en performance assez facilement. De plus, la fréquence était un argument marketing assez saillant, et l'augmenter faisait bien sur le papier. Aussi, les fréquences ont progressivement augmenté, et ont continué dans ce sens, jusqu’à atteindre une limite haute avec le Pentium 4 d'Intel.
[[File:Fréquences relatives processeurs Intel pré-Pentium 4.png|centre|vignette|upright=2|Fréquences relatives processeurs Intel pré-Pentium 4]]
Le Pentium 4 était une véritable bête en termes de fréquence pour l'époque : 1,5 GHz, contre à peine 1Ghz pour les autres processeurs de l'époque. La fréquence avait été doublée par rapport au Pentium 3 et ses 733 MHz. Et cette fréquence était juste la fréquence de base du processeur, certains circuits allaient plus vite, d'autres moins vite. Par exemple, l'unité de calcul intégrée dans le processeur allait deux fois plus vite, avec une fréquence de 3 GHz ! Et à l'inverse, certaines portions du processeur allaient au quart de cette fréquence, à 750 MHz. Bref, le Pentium 4 gérait plusieurs fréquences différentes : certaines portions importantes pour la performance allaient très vite, d'autres allaient à une vitesse intermédiaire pour faciliter leur conception, d'autres allaient encore moins vite, car elles n'avaient pas besoin d'être très rapides.
Le processeur était spécialement conçu pour fonctionner à très haute fréquence, grâce à diverses techniques qu'on abordera dans les chapitres suivants (pipeline très long, autres). Mais tout cela avait un cout : le processeur chauffait beaucoup ! Et c'était un défaut majeur. De plus, les techniques utilisées pour faire fonctionner le Pentium 4 à haute fréquence (le superpipeline) avaient beaucoup de défauts. Défauts compensés partiellement par des techniques innovantes pour l'époque, comme son ''replay system'' qu'on abordera dans un chapitre à part, mais sans grand succès. Les versions ultérieures du Pentium 4 avaient une fréquence plus base, mais avec un processeur complétement repensé, avec un résultat tout aussi performant. Ces versions chauffaient quand même beaucoup et les contraintes thermiques étaient un vrai problème.
En conséquence, la course à la fréquence s'est arrêtée avec ce processeur pour Intel, et l'industrie a suivi. La raison principale est que l'augmentation en fréquence des processeurs modernes est de plus en plus contrainte par la dissipation de chaleur et la consommation d'énergie. Et cette contrainte s'est manifestée en 2005, après la sortie du processeur Pentium 4. Le point d'inflexion en 2005, à partir duquel la fréquence a cessée d'augmenter drastiquement, s'explique en grande partie par cette contrainte. Les premiers processeurs étaient refroidis par un simple radiateur, alors que les processeurs modernes demandent un radiateur, un ventilateur et une pâte thermique de qualité pour dissiper leur chaleur. Pour limiter la catastrophe, les fabricants de processeurs doivent limiter la fréquence de leurs processeurs. Une autre raison est que la fréquence dépend des transistors, mais aussi de la rapidité du courant dans les interconnexions (les fils) qui relient les transistors, celles-ci devenant de plus en plus un facteur limitant pour la fréquence.
===La performance à fréquence égale a augmenté : la loi de Pollack===
Si la miniaturisation permet d'augmenter la fréquence, elle permet aussi d'améliorer la performance à fréquence égale. Rappelons que la performance à fréquence égale se mesure avec deux critères équivalents : l'IPC et le CPI. Le CPI est le nombre de cycles moyen pour exécuter une instruction. L'IPC est son inverse, à savoir le nombre d'instruction exécutable en un seul cycle. Les deu sont l'inverse l'un de l'autre. La '''loi de Pollack''' dit que l'augmentation de l'IPC d'un processeur est approximativement proportionnelle à la racine carrée du nombre de transistors ajoutés : si on double le nombre de transistors, la performance est multipliée par la racine carrée de 2.
On peut expliquer cette loi de Pollack assez simplement. Il faut savoir que les processeurs modernes peuvent exécuter plusieurs instructions en même temps (on parle d’exécution superscalaire), et peuvent même changer l'ordre des instructions pour gagner en performances (on parle d’exécution dans le désordre). Pour cela, les instructions sont préchargées dans une mémoire tampon de taille fixe, interne au processeur, avant d'être exécutée en parallèle dans divers circuits de calcul. Cependant, le processeur doit gérer les situations où une instruction a besoin du résultat d'une autre pour s'exécuter : si cela arrive, on ne peut exécuter les instructions en parallèle. Pour détecter une telle dépendance, chaque instruction doit être comparée à toutes les autres, pour savoir quelle instruction a besoin des résultats d'une autre. Avec N instructions, vu que chacune d'entre elles doit être comparée à toutes les autres, ce qui demande N^2 comparaisons.
En doublant le nombre de transistors, on peut donc doubler le nombre de comparateurs, ce qui signifie que l'on peut multiplier le nombre d'instructions exécutables en parallèle par la racine carrée de deux. En utilisant la loi de Moore, on en déduit qu'on gagne approximativement 40% d'IPC tous les deux ans, à ajouter aux 40 % d'augmentation de fréquence. En clair, la performance d'un processeur augmente de 40% grâce à la loi de Pollack, et de 40% par l'augmentation de fréquence. On a donc une augmentation de 80% tous les deux ans, donc une multiplication par 1,8 tous les deux ans, soit moins que la hausse de transistors.
===L'augmentation du nombre de cœurs===
Après 2005, l'augmentation de la fréquence a stagné et l'augmentation des performances semblait limitée par la seule loi de Pollack. Mais l'industrie a trouvé un moyen de contourner la loi de Pollack. En effet, cette dernière ne vaut que pour un seul processeur. Mais en utilisant plusieurs processeurs, la performance est, en théorie, la somme des performances individuelles de chacun d'entre eux. Et c'est la réaction qu'à eux l'industrie.
C'est pour cela que depuis les années 2000, le nombre de cœurs a augmenté assez rapidement. Les processeurs actuels sont doubles, voire quadruple cœurs : ce sont simplement des circuits imprimés qui contiennent deux, quatre, voire 8 processeurs différents, placés sur la même puce. Chaque cœur correspond à un processeur. En faisant ainsi, doubler le nombre de transistors permet de doubler le nombre de cœurs et donc de doubler la performance, ce qui est mieux qu'une amélioration de 40%.
[[File:CPU clock speed and Core count Graph.png|centre|vignette|upright=2.5|Comparaison entre fréquence et nombre de coeurs des processeurs, depuis ]]
==L'évolution de la performance des mémoires==
Après avoir vu le processeur, voyons comment la loi de Moore a impacté l'évolution des mémoires. Beaucoup de cours d'architecture des ordinateurs se contentent de voir l'impact de la loi de Moore sur le processeur, mais mettent de côté les mémoires. Et il y a une bonne raison à cela. Le fait est que les ordinateurs modernes ont une hiérarchie mémoire très complexe, avec beaucoup de mémoires différentes. Et les technologies utilisées pour ces mémoires sont très diverses, elles n'utilisent pas toutes des transistors MOS, la loi de Moore ne s'applique pas à toutes les mémoires.
Déjà, évacuons le cas des mémoires magnétiques (disques durs, disquettes) et des mémoires optiques (CD/DVD), qui ne sont pas fabriquées avec des transistors MOS et ne suivent donc pas la loi de Moore. Et de plus, elles sont en voie de disparition, elles ne sont plus vraiment utilisées de nos jours. Il ne reste que les '''mémoires à semi-conducteurs''' qui utilisent des transistors MOS, mais pas que. Seules ces dernières sont concernées par la loi de Moore, mais certaines plus que d'autres. Mais toutes les mémoires ont vu leur prix baisser en même temps que leur capacité a augmenté dans le temps, que ce soit à cause de la loi de Moore pour les mémoires à semi-conducteurs, l'amélioration exponentielle des technologies de stockage magnétique pour les disques durs.
[[File:Historical-cost-of-computer-memory-and-storage OWID.svg|centre|vignette|upright=2.0|Historical-cost-of-computer-memory-and-storage OWID]]
L'impact de la loi de Moore dépend de la mémoire considérée et de sa place dans la hiérarchie mémoire. Les mémoires intégrées au processeur, comme le cache ou les registres, sont des mémoires SRAM/ROM fabriquées intégralement avec des transistors MOS, et donc soumises à la loi de Moore. Leurs performances et leur capacité suivent l'évolution du processeur qui les intègre, leur fréquence augmente au même rythme que celle du processeur, etc. Aussi on peut considérer qu'on en a déjà parlé plus haut. Reste à voir les autres niveaux de la hiérarchie mémoire, à savoir la mémoire RAM principale, la mémoire ROM et les mémoires de masse. Dans les grandes lignes, on peut distinguer deux technologies principales : les mémoires DRAM et les mémoires FLASH.
===Les mémoires FLASH ont suivi la loi de Moore===
Les '''mémoires FLASH''' sont utilisées dans les mémoires de masse, comme les clés USB, les disques durs de type SSD, les cartes mémoires, et autres. Le passage à la mémoire FLASH a fait qu'elles sont plus rapides que les anciennes mémoires magnétiques, pour une capacité légèrement inférieure. Les mémoires FLASH sont aussi utilisées comme mémoire ROM principale ! Par exemple, les PC actuels utilisent de la mémoire FLASH pour stocker le ''firmware''/BIOS/UEFI. De même, les systèmes embarqués qui ont besoin d'un ''firmware'' rapide utilisent généralement de la mémoire FLASH, pas de la mémoire ROM proprement dite (on verra dans quelques chapitres que la mémoire FLASH est un sous-type de mémoire ROM, mais laissons cela à plus tard).
Elles sont basées sur des transistors MOS modifiés, appelés transistors à grille flottante. Un transistor à grille flottante peut être vu comme une sorte de mélange entre transistor et condensateur, à savoir qu'il dispose d'une grille améliorée, dont le caractère de condensateur est utilisé pour mémoriser une tension, donc un bit. Un transistor à grille flottante est utilisé pour mémoriser un bit sur les mémoires dites SLC, deux bits sur les mémoires dites MLC, trois bits sur les mémoires TLC, quatre sur les mémoires QLC. Non, ce n'est pas une erreur, c'est quelque chose de permis grâce aux technologies de fabrication d'une mémoire FLASH, nous détaillerons cela dans le chapitre sur les mémoires FLASH.
Vu que les mémoires FLASH sont basées sur des transistors MOS modifiés, vous ne serez pas trop étonnés d’apprendre que la loi de Moore s'applique à la mémoire FLASH. La taille des transistors à grille flottante suit la loi de Moore : elle diminue de 30% tous les deux ans.
[[File:NAND Fscaling 01.png|centre|vignette|upright=2.0|Taille d'une cellule de mémoire FLASH (de type NAND).]]
La conséquence est que l'aire occupée par un transistor à grille flottante est divisée par deux tous les deux ans. Le résultat est que la capacité des mémoires FLASH augmente de 50 à 60% par an, ce qui fait un doublement de leur capacité tous les deux ans.
[[File:NAND Fscaling 02.png|centre|vignette|upright=2.0|Aire d'une cellule de mémoire FLASH (de type NAND).]]
===Les mémoires RAM ne sont pas concernées par la loi de Moore===
[[File:1T-DRAM.png|vignette|Circuit qui mémorise un bit dans une mémoire DRAM moderne.]]
Les '''mémoires DRAM''' sont utilisées pour la mémoire principale de l'ordinateur, la fameuse mémoire RAM. A l'intérieur des mémoires DRAM actuelles, chaque bit est mémorisé en utilisant un transistor MOS et un condensateur (un réservoir à électron). Leur capacité et leur performance dépend aussi bien de la miniaturisation du transistor que de celle du condensateur. Elles sont donc partiellement concernées par la loi de Moore.
Pour ce qui est de la capacité, les DRAM suivent la loi de Moore d'une manière approximative. La raison est que gagner en capacité demande de réduire la taille des cellules mémoire, donc du transistors et du condensateur à l'intérieur. La miniaturisation des transistors suit la loi de Moore, mais la réduction de la taille du condensateur ne la suit pas. Dans le passé, la capacité des DRAM augmentait légèrement plus vite que la loi de Moore, avec un quadruplement tous les trois ans (4 ans pour la loi de Moore), mais tout a considérablement ralentit avec le temps.
[[File:Confronto processore memoria.jpg|centre|vignette|upright=2.0|Évolution du nombre de transistors d'une mémoire électronique au cours du temps. On voit que celle-ci suit de près la loi de Moore.]]
Niveau performances, la loi de Moore ne s'applique tout simplement pas. La raison à cela est que la performance des DRAM est dominée par la performance du condensateur, pas par celle du transistor. Miniaturiser des transistors permet de les rendre plus rapides, mais le condensateur ne suit pas vraiment. Aussi, les performances des mémoires DRAM stagnent.
Rappelons que la performance d'une mémoire RAM/ROM dépend de deux paramètres : son débit binaire, et son temps d'accès. Il est intéressant de comparer comment les deux ont évolué. Pour les mémoires RAM, le débit binaire a augmenté rapidement, alors que le temps d'accès a baissé doucement. Les estimations varient d'une étude à l'autre, mais disent que le temps d'accès des mémoires se réduit d'environ 10% par an, alors que le débit binaire a lui augmenté d'environ 30 à 60% par an. Une règle approximative est que le débit binaire a grandi d'au moins le carré de l'amélioration du temps d'accès.
Pour comprendre pourquoi temps d'accès et débit binaire n'ont pas évolué simultanément, il faut regarder du côté de la fréquence de la mémoire RAM. Les mémoires modernes sont cadencées avec un signal d'horloge, ce qui fait qu'il faut tenir compte de la '''fréquence de la mémoire'''. Le débit et les temps d'accès dépendent fortement de la fréquence de la mémoire. Plus la fréquence est élevée, plus les temps d'accès sont faibles, plus le débit est important.
Le calcul du débit binaire d'une mémoire est simplement le produit entre fréquence et largeur du bus mémoire. Il se trouve que la largeur du bus de données n'a pas beaucoup augmenté avec le temps. Les premières barrettes de mémoire datée des années 80, les barrettes SIMM, avaient un bus de données de 8 bits pour la version 30 broches, 32 bits pour la version 72 broches. Le bus mémoire était déjà très important. Dans les années 2000, la démocratisation des barrettes mémoires DIMM a permis au bus de données d'atteindre 64 bits, valeur à laquelle il est resté actuellement. Il est difficile d'augmenter la largeur du bus, ca cela demanderait d'ajouter des broches sur des barrettes et des connecteurs déjà bien chargées. L'augmentation du débit binaire ne peut venir que de l'augmentation de la fréquence.
Les premières mémoires utilisées dans les PCs étaient asynchrones, à savoir qu'elles n'avaient pas de fréquence ! Elles se passaient de fréquence d'horloge, et le processeur se débrouillait avec. Il s'agissait des premières mémoires DRAM d'Intel, les mémoires EDO, Fast-page RAM et autres, que nous verrons dans quelques chapitres. Elles étaient utilisées entre les années 70 et 90, où elles étaient le type de mémoire dominant. Elles étaient assez rapides pour le processeur, mémoire et processeur avaient des performances comparables.
Dans les années 1990, la SDRAM est apparue. La terme SDRAM regroupe les premières mémoires RAM synchrones, cadencées par un signal d'horloge. La fréquence des SDRAM était de 66, 100, 133 et 150 MHz. Les RAM à 66 MHz sont apparues en premier, suivies par les SDRAM à 100MHz et puis par les 133 MHz, celles de 150 MHz étaient plus rares. Lors de cette période, la relation entre fréquence, temps d'accès et débit binaire était assez claire. Le temps d'accès est proportionnel à la fréquence, à peu de choses près. Le temps d'accès est de quelques cycles d'horloge, bien qu'il dépende des barrettes de mémoire utilisées. Le débit est le produit entre fréquence et largeur du bus. Donc plus la fréquence est grande, meilleures sont les performances globales. Et la fréquence de la mémoire et celle du bus mémoire étaient identiques.
Depuis les années 2000, les mémoires RAM utilisent des techniques de ''Double data rate'' ou de ''Quad data rate'' qui permettent d'atteindre de hautes fréquences en trichant. La triche vient du fait que la fréquence de la mémoire n'est plus égale à la fréquence du bus mémoire depuis les années 2000. Nous verrons cela en détail dans le chapitre sur le bus mémoire. Pour le moment, nous allons nous contenter de dire que l'idée derrière cette différence de fréquence est d'augmenter le débit binaire des mémoires, mais sans changer leur fréquence interne.
{|class="wikitable"
|-
! Année
! Type de mémoire
! Fréquence de la mémoire (haut de gamme)
! Fréquence du bus
! Coefficient multiplicateur entre les deux fréquences
|-
| 1998
| DDR 1
| 100 - 200 MHz
| 200 - 400 MHz
| 2
|-
| 2003
| DDR 2
| 100 - 266 MHz
| 400 - 1066 MHz
| 4
|-
| 2007
| DDR 3
| 100 - 266 MHz
| 800 - 2133 MHz
| 8
|-
| 2014
| DDR 4
| 200 - 400 MHz
| 1600 - 3200 MHz
| 8
|-
| 2020
| DDR 5
| 200 - 450 MHz
| 3200 - 7200 MHz
| 8 à 16
|}
Le débit binaire est proportionnel à la fréquence du bus mémoire, alors que les temps d'accès sont proportionnel à la fréquence de la mémoire. La fréquence de la mémoire n'a pas beaucoup augmentée et reste très faible, les temps d'accès ont donc fait de même. Par contre, le débit binaire est lui très élevé, car dépendant de la fréquence du bus mémoire, qui a beaucoup augmenté. Au final, les mémoires modernes ont donc un gros débit, mais un temps de latence très élevé.
==La comparaison des performances des processeurs et mémoires RAM : le ''memory wall''==
La performance du processeur et de la mémoire doivent idéalement être comparables. Rien ne sert d'avoir un processeur puisant si la mémoire ne suit pas. A quoi bon avoir un processeur ultra-puissant s'il passe 80% de son temps à attendre des données en provenance de la mémoire ? Si la performance des mémoires RAM stagne, alors que les processeurs gagnent en performance de manière exponentielle, on fait face à un problème.
Pour nous rendre compte du problème, il faut comparer la performance du processeur avec celle de la mémoire. Et c'est loin d'être facile, car les indicateurs de performance pour le processeur et la mémoire sont fondamentalement différents. La performance d'une mémoire dépend de son débit binaire et de son temps d'accès, la performance du processeur dépend de son CPI et de sa fréquence. Mais on peut comparer la vitesse à laquelle ces indicateurs grandissent. Pour donner un chiffre assez parlant, quelques estimations estiment que le temps d'accès de la mémoire, exprimé en cycles d'horloge du processeur, double tous les 2,6 ans.
===L'évolution dans le temps du ratio des fréquences processeur/mémoire===
Une autre possibilité est comparer la fréquence des processeurs et des mémoires, pour voir si la fréquence de la mémoire a suivi celle du processeur. On s'attendrait à une augmentation de 40% de la fréquence des mémoires tous les deux ans, comme c'est le cas pour les processeurs. Mais la fréquence des mémoires n'a pas grandit au même rythme et a été beaucoup plus faible que pour les processeurs. Faisons un historique rapide.
Lors de l'époque des mémoires asynchrones, mémoire et processeur avaient des performances comparables. Les accès mémoire se faisaient globalement en un cycle d'horloge, éventuellement deux ou trois cycles, rarement plus. Bien qu'asynchrone, on peut considérer qu'elles allaient à la même fréquence que le processeur, mais cela ne servait à rien de parler de fréquence de la mémoire ou de fréquence du bus mémoire.
Pour la période des mémoires SDRAM, le tableau ci-dessous fait une comparaison des fréquences processeur-mémoire. La comparaison avec les processeurs était assez simple, car la fréquence du bus mémoire et la fréquence de la mémoire sont identiques. On voit que la fréquence de la mémoire est déjà loin derrière la fréquence du processeur, elle est 3 à 5 fois plus faible.
{|class="wikitable"
|-
! Année
! Fréquence du processeur (haut de gamme)
! Fréquence de la mémoire (haut de gamme)
|-
| 1993
| Intel Pentium : 60 à 300 MHz
| 66 Mhz
|-
| 1996-1997
|
* Intel Pentium 2 (1996) : 233 MHz à 450 MHz
* AMD K5 (1996) : 75 MHz à 133 MHz
* AMD K6 (1997) : 166 MHz à 300 MHz
| 100 MHz
|-
| 1999
|
* Intel Pentium 3 : 450 MHz à 1,4 GHz
* AMD Athlon : 500 MHz à 1400 MHz
| 133 MHz
|}
Avec l'invention des mémoires DDR, la comparaison est rendue plus compliquée par la dissociation entre fréquence du bus et fréquence de la mémoire. La comparaison est la suivante :
{|class="wikitable"
|-
! Année
! Type de mémoire
! Fréquence de la mémoire (haut de gamme)
! Fréquence du bus
! Fréquence du processeur (haut de gamme)
|-
| 1998
| DDR 1
| 100 - 200 MHz
| 200 - 400 MHz
| 200 - 1000 MHz
|-
| 2003
| DDR 2
| 100 - 266 MHz
| 400 - 1066 MHz
| 700 - 1500 MHz
|-
| 2007
| DDR 3
| 100 - 266 MHz
| 800 - 2133 MHz
| 1500 - 3000 MHz
|-
| 2014
| DDR 4
| 200 - 400 MHz
| 1600 - 3200 MHz
| 1600 - 4200 MHz
|-
| 2020
| DDR 5
| 200 - 450 MHz
| 3200 - 7200 MHz
| 1700 - 4500 MHz
|}
Le constat est assez clair : le processeur est plus rapide que la mémoire, et l'écart se creuse de plus en plus avec le temps. Il s'agit d'un problème assez important, qui dicte l'organisation des ordinateurs modernes. Les mémoires sont actuellement très lentes comparé au processeur. On parle souvent de '''''memory wall''''', pour décrire ce problème, nous utiliserons le terme '''mur de la mémoire'''. Pour cela, diverses solutions existent. Et la plus importante d'entre elle est l'usage d'une hiérarchie mémoire.
===Les solutions contre le mur de la mémoire : hiérarchie mémoire et RAM computationnelle===
Le mur de la mémoire est un problème avec lequel les architectures modernes doivent composer. Le mur de la mémoire a deux origines. La première est que processeur et mémoire sont strictement séparés et que tout traitement doit lire des opérandes en mémoire, pour ensuite écrire des résultats en mémoire RAM. La seconde est que les transferts entre processeurs et mémoire sont assez lents, ce qui fait que l'idéal est de réduire ces transferts le plus possible.
La solution la plus souvent retenue est l'usage de '''mémoires caches''' intégrées au processeur, pour réduire le trafic entre DRAM et CPU. Les mémoires caches ne sont pas des DRAM, ce qui permet de contourner le problème du mur de la mémoire, causé par la technologie de fabrication des DRAM. Les caches sont des mémoires à semi-conducteur fabriquées avec des transistors CMOS, ce qui fait que leurs performances augmentent au même rythme que le processeur. Elles sont intégrées dans le processeur, même s'il a existé des mémoires caches connectées sur la carte mère.
Mais une autre solution consiste à faire l'inverse, à savoir ajouter des capacités de calcul dans la mémoire RAM. L'idée est de faire les calculs dans la mémoire directement : pas besoin de transférer les opérandes du calcul de la mémoire vers le CPU, ni de transférer les résultats du CPU vers la RAM. L'idée est alors de déléguer certains calculs à la DRAM, voire carrément de fusionner le CPU et la DRAM ! On parle alors de '''''in-memory processing''''', que nous traduirons par le terme quelque peu bâtard de '''RAM computationnelle'''.
Cependant, l'implémentation d'une RAM computationnelle pose quelques problèmes d'ordre pratique. Le premier est au niveau de la technologie utilisée pour les transistors. Comme on vient de le voir, les technologies utilisées pour fabriquer la mémoire sont très différentes de celles utilisées pour fabriquer des circuits logiques, les circuits d'un processeur. Les processeurs utilisent des transistors CMOS normaux, les mémoires FLASH des transistors MOS à grille flottante, les DRAM utilisent un mélange de transistors MOS et de condensateurs. Et au niveau des procédés de fabrication, de gravure des puce, de photolithographie, de la technique des semi-conducteurs, les trois procédés sont totalement différents. Aussi, fusionner DRAM et CPU pose des problèmes de fabrication assez complexes.
Notons que ce problème n'a pas lieu avec la mémoire utilisée pour les registres et les caches, car elle est fabriquée avec des transistors. Nous avons vu il y a quelques chapitres comment créer des registres à partir de transistors et de portes logiques, ce qui utilise le même procédé technologique que pour les CPU. Les mémoires caches utilisent des cellules de mémoire SRAM fabriquées uniquement avec des transistors CMOS, comme nous le verrons dans quelques chapitres. Registres et SRAM sont donc fabriqués avec le même procédé technologique que les processeurs, ce qui fait que l'intégration de registres/caches dans le processeur est assez simple. Ce n'est pas le cas pour la fusion DRAM/CPU.
La majorité des architectures à RAM computationnelle ont été des échecs commerciaux. La raison est qu'il s'agit d'architectures un peu particulières, qui sont formellement des architectures dites à parallélisme de données, assez difficiles à exploiter. La tentative la plus connue était le '''projet IRAM de l'université de Berkeley'''. L'idée était de fusionner un processeur et une mémoire sur la même puce, les deux étant intégrés dans le même circuit. Démarré en 1996, il a été abandonnée en 2004.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=La performance d'un ordinateur
| prevText=La performance d'un ordinateur
| next=Les techniques de réduction de la consommation électrique d'un processeur
| nextText=Les techniques de réduction de la consommation électrique d'un processeur
}}{{autocat}}
</noinclude>
fznxf66bqr615oy9zdwql6rafvnbnvl
744790
744789
2025-06-15T02:01:04Z
Mewtow
31375
/* La comparaison des performances des processeurs et mémoires RAM : le memory wall */
744790
wikitext
text/x-wiki
Il va de soi que les nouveaux processeurs sont plus puissants que les anciens, pareil pour les mémoires. La raison à cela vient des optimisations apportées par les concepteurs de processeurs. La plupart de ces optimisations ne sont cependant possibles qu'avec la miniaturisation des transistors, qui leur permet d'aller plus vite. Et cela se voit dans les données empiriques. Il est intéressant de regarder comment les mémoires et processeurs ont évolué dans le temps.
Pour les processeurs, la loi de Moore a des conséquences qui sont assez peu évidentes. Certes, on peut mettre plus de transistors dans un processeur, mais en quoi cela se traduira par de meilleures performances ? Pour comprendre l'influence qu'à eu la loi de Moore sur les processeurs modernes, regardons ce graphique, qui montre les relations entre nombre de transistors, fréquence du processeur, performance d'un seul cœur, nombre de cœurs, et consommation énergétique.
[[File:50 Years Of Microprocessor Trend Data.png|centre|vignette|upright=2|50 years of microprocessor trend data, par Karl Rupp.]]
Globalement, on voit que le nombre de transistors augmente de façon exponentielle : doubler tous les X années donne une courbe exponentielle, d'où l'échelle semi-logarithmique du graphique. Mais pour le reste, quelque chose s'est passé en 2005 : les courbes n'ont pas la même pente avant et après 2005. Que ce soit la fréquence, la performance d'un seul cœur, la consommation électrique, tout. Et le nombre de cœurs explose au même moment. Tout cela fait penser que toutes ces caractéristiques étaient liées entre elles et augmentaient exponentiellement, mais il y a un après 2005. Reste à expliquer pourquoi, ce qui est le sujet de ce chapitre, sans compter qu'on détaillera tout ce qui a trait à la consommation énergétique.
==La miniaturisation des transistors est la cause des tendances technologiques==
Avant toute chose, nous devons faire quelques rappels sur les transistors MOS, sans lesquels les explications qui vont suivre seront compliquées. Un transistor MOSFET a de nombreuses caractéristiques : ses dimensions, mais aussi d'autres paramètres plus intéressants. Par exemple, il est intéressant de regarder la consommation d'énergie d'un transistor, à savoir combien de watts ils utilise pour faire ce qu'on lui demande. Pour cela, il faudra parler rapidement de certaines de ses caractéristiques comme sa capacité électrique. Rien de bien compliqué, rassurez-vous.
===Les caractéristiques d'un transistor : finesse de gravure, capacité, etc===
[[File:Transistor CMOS - 1.png|vignette|Transistor CMOS - 1]]
Un transistor MOS est composé de deux morceaux de conducteurs (l'armature de la grille et la liaison drain-source) séparés par un isolant. Les dimensions d'un transistors sont au nombre de deux : la distance entre source et drain, la distance entre grille et semi-conducteur. Les deux sont regroupées sous le terme de '''finesse de gravure''', bien que cela soit un terme impropre.
Nous avons dit plus haut qu'un transistor MOS est composé de deux (semi-)conducteurs séparés par un isolant. Tout cela ressemble beaucoup à un autre composant électronique appelé le '''condensateur''', qui sert de réservoir à électricité. On peut le charger en électricité, ou le vider pour fournir un courant durant une petite durée de temps. L'intérieur d'un condensateur est formé de deux couches de métal conducteur, séparées par un isolant électrique. Les charge s'accumulent dans les couche de métal quand on charge le condensateur en électricité. L'intérieur d'un transistor MOS est donc similaire à celui d'un condensateur, si ce n'est qu'une couche métallique est remplacée par un morceau de semi-conducteur. Tout cela fait qu'un transistor MOS incorpore un pseudo-condensateur caché entre la grille et la liaison source-drain, qui porte le nom de capacité parasite du transistor.
[[File:Plaatcondensator2.GIF|centre|vignette|upright=2|Condensateur et accumulation des charges électrique sur les plaques métalliques.]]
Tout condensateur possède une caractéristique importante : sa '''capacité électrique'''. Il s'agit simplement de la quantité d'électrons/charges qu'il peut contenir en fonction de la tension. Il faut savoir que la quantité de charge contenue dans un condensateur est proportionnelle à la tension, la capacité est le coefficient de proportionnalité entre les deux. Tout cela est sans doute plus clair avec une équation :
: <math>Q = C U</math>, avec Q la quantité de charges contenues dans le condensateur, U la tension, et C la capacité.
[[File:Capacity.svg|vignette|Charge/décharge d'un condensateur.]]
La capacité d'un transistor MOS a une influence directe sur la fréquence à laquelle il peut fonctionner. Pour changer l'état d'un transistor MOS, il faut soit charger la grille, soit la décharger. Et pour remplir le transistor, il faut fournir une charge égale à celle donnée par l'équation précédente.
Si on met ce processus en équations, on s’aperçoit qu'on se trouve avec des charges ou décharges exponentielles. Mais par simplicité, on considère que le temps de charge/décharge d'un condensateur est proportionnel à sa capacité (pour être précis, proportionnel au produit 5 RC, avec R la résistance des fils). Tout ce qu'il faut retenir est que plus la capacité est faible, plus le transistor est rapide et plus il peut fonctionner à haute fréquence.
===Les lois de Dennard, ce qui se cache derrière la loi de Moore===
La loi de Moore est le résultat d'une tendance technologique bien précise : les dimensions d'un transistors se réduisent avec les progrès de la miniaturisation. Elles sont réduites de 30% tous les deux ans. Pour le dire autrement, elles sont multipliées par 0.7 tous les deux ans.
[[File:Comparison semiconductor process nodes.svg|centre|vignette|upright=2.0|Évolution de la finesse de gravure au cours du temps pour les processeurs.]]
Les processeurs sont des composants qui ont actuellement une forme carrée, les transistors sont tous placés sur un plan et ne sont pas empilés les uns sur les autres. Ils occupent donc une certaine aire sur la surface du processeur. Si la taille des transistors est réduite de 30% tous les 2 ans, l'aire que prend un transistor sur la puce est quand à elle divisée par 30% * 30% <math>\approx</math> 50%. En conséquence, on peut mettre deux fois plus de transistors sur la même puce électronique : on retrouve la loi de Moore.
Cela a aussi des conséquences sur la tension d'alimentation nécessaire pour faire fonctionner le transistor. Sans rentrer dans les détails, la tension est elle aussi proportionnelle aux dimensions du transistor. La raison technique, que vous comprendrez si vous avez eu des cours d'électricité avancés durant votre scolarité, est que le champ électrique ne change pas dans le transistor, et que la tension est le produit du champ électrique par la distance. Là encore, la tension d'alimentation est réduite de 30% tous les deux ans.
[[File:Parallel plate capacitor.svg|vignette|Condensateur plan]]
La miniaturisation a une influence directe sur la capacité électrique du transistor. Pour comprendre pourquoi, il faut savoir que le condensateur formé par la grille, l'isolant et le morceau de semi-conducteur est ce que l'on appelle un ''condensateur plan''. La capacité de ce type de condensateur dépend de la surface de la plaque de métal (la grille), du matériau utilisé comme isolant et de la distance entre la grille et le semi-conducteur. On peut calculer cette capacité comme suit, avec A la surface de la grille, e un paramètre qui dépend de l'isolant utilisé et d la distance entre le semi-conducteur et la grille (l'épaisseur de l'isolant, quoi).
: <math>C = \frac{A}{d} \times e</math>
Le coefficient e (la permittivité électrique) reste le même d'une génération de processeur à l'autre, même si les fabricants ont réussi à le faire baisser un peu grâce à matériaux particuliers. Mais laissons cela de côté : dans les faits, seuls les coefficients S et d vont nous intéresser. Si la finesse de gravure diminue de 30%, la distance d diminue du même ordre, la surface A diminue du carré de 30%, c’est-à-dire qu'elle sera approximativement divisée par 2. La capacité totale sera donc divisée par 30% tous les deux ans.
Réduire la capacité des transistors a un impact indirect très fort sur la fréquence à laquelle on peut faire fonctionner le transistor. En effet, la période de l'horloge correspond ''grosso modo'' au temps qu'il faut pour remplir ou vider l'armature de la grille, et on sait que le temps de charge/décharge d'un condensateur est approximativement proportionnel à sa capacité. La capacité et le temps de charge/décharge est donc réduit de 30% tous les deux ans. La fréquence étant inversement proportionnelle au temps de remplissage du condensateur, elle est donc augmentée de 1/0.7 = <math>\approx</math> 40%.
Tout ce qu'on vient de dire plus a été formalisé sous le nom de '''lois de Dennard''', du nom de l'ingénieur qui a réussi à démontrer ces équations à partir des lois de la physique des semi-conducteurs. Une réduction de la finesse de gravure impacte plusieurs paramètres : le nombre de transistors d'une puce électronique, sa tension d'alimentation, sa fréquence, et quelques autres paramètres qu'on détaillera plus bas comme la capacité d'un transistor ou ses courants de fuite.
{| class="wikitable"
|-
! Paramètre !! Coefficient multiplicateur (tous les deux ans) || Variation en pourcentage
|-
| Finesse de gravure || 0.7 || - 30%
|-
| Aire occupée par un transistor || 0.5 || - 50%
|-
| Nombre de transistors par unité de surface || 2 || + 100%
|-
| Tension d'alimentation || 0.7 || - 30%
|-
| Capacité d'un transistor || 0.7 || - 30%
|-
| Fréquence || (1/0.7) = 1.4 || + 40%
|}
===La fin des lois de Dennard===
Les lois de Dennard ont cessé de s'appliquer aux alentours de 2005/2006. Les dimensions d'un transistors sont toujours réduites de 30% tous les deux ans, la loi de Moore est encore valable, mais pour ce qui est de la fréquence et de la tension d'alimentation, c'est autre chose. Les raisons à cela sont multiples, et il faut revenir au fonctionnement d'un transistor MOS pour comprendre pourquoi.
Un MOSFET est composé d'une grille métallique, d'une couche de semi-conducteur, et d'un isolant entre les deux. L'isolant empêche la fuite des charges de la grille vers le semi-conducteur. Mais avec la miniaturisation, la couche d'isolant ne fait guère plus de quelques dizaines atomes d'épaisseur et laisse passer un peu de courant : on parle de '''courants de fuite'''. Plus cette couche d'isolant est petite, plus le courant de fuite sera fort. En clair, une diminution de la finesse de gravure a tendance à augmenter les courants de fuite.
Les courants de fuite dépendent d'une tension appelée '''tension de seuil'''. Il s'agit de la tension minimale pour avoir un courant passant entre la source et le drain. Sous cette tension, le transistor se comporte comme un interrupteur fermé, peut importe ce qu'on met sur la grille. Au-dessus de cette tension, le courant se met à passer entre source et drain, il se comporte comme un interrupteur ouvert. Le courant est d'autant plus fort que la tension sur la grille dépasse la tension de seuil. On ne peut pas faire fonctionner un transistor si la tension d'alimentation (entre source et drain) est inférieure à la tension de seuil. C'est pour cela que ces dernières années, la tension d'alimentation des processeurs est restée plus ou moins stable, à une valeur proche de la tension de seuil (1 volt, environ). Et l'incapacité à réduire cette tension a eu des conséquences fâcheuses.
Nous verrons plus bas que la consommation d'énergie d'un processeur dépend de sa fréquence et de sa tension. Les lois de Dennard nous disent que Si la seconde baisse, on peut augmenter la première sans changer drastiquement la consommation énergétique. Mais si la tension d'alimentation stagne, alors la fréquence doit faire de même. Vu que les concepteurs de processeurs ne pouvaient pas diminuer la fréquence pour garder une consommation soutenable, et ont donc préféré augmenter le nombre de cœurs. L'augmentation de consommation énergétique ne découle que de l'augmentation du nombre de transistors et des diminutions de capacité. Et la diminution de 30 % tous les deux ans de la capacité ne compense plus le doublement du nombre de transistors : la consommation énergétique augmente ainsi de 40 % tous les deux ans. Bilan : la performance par watt stagne. Et ce n'est pas près de s'arranger tant que les tensions de seuil restent ce qu'elles sont.
L'explication est convaincante, mais nous détaillerons celle-ci plus bas, avec les vraies équations qui donnent la consommation d'énergie d'un processeur. Nous en profiterons aussi pour voir quelles sont les technologies utilisées pour réduire cette consommation d'énergie, les deux étant intrinsèquement liés.
==La consommation d'un circuit électronique CMOS==
Tout ordinateur consomme une certaine quantité de courant pour fonctionner, et donc une certaine quantité d'énergie. On peut dire la même chose de tout circuit électronique, que ce soit une mémoire, un processeur, ou quoique ce soit d'autre d'électronique. Il se trouve que cette énergie finit par être dissipée sous forme de chaleur : plus un composant consomme d'énergie, plus il chauffe. La chaleur dissipée est mesurée par ce qu'on appelle l''''enveloppe thermique''', ou TDP (''Thermal Design Power''), mesurée en Watts.
Pour donner un exemple, le TDP d'un processeur tourne autour des 50 watts, parfois plus sur les modèles plus anciens. De telles valeurs signifient que les processeurs actuels chauffent beaucoup. Pourtant, ce n'est pas la faute des fabricants de processeurs qui essaient de diminuer la consommation d'énergie de nos CPU au maximum. Malgré cela, les processeurs voient leur consommation énergétique augmenter toujours plus : l'époque où l'on refroidissait les processeurs avec un simple radiateur est révolue. Place aux gros ventilateurs super puissants, placés sur un radiateur.
===La consommation d'un circuit électronique CMOS===
Pour comprendre pourquoi, on doit parler de ce qui fait qu'un circuit électronique consomme de l'énergie. Une partie de la consommation d'énergie d'un circuit vient du fait qu'il consomme de l'énergie en changeant d'état. On appelle cette perte la '''consommation dynamique'''. A l’opposé, la '''consommation statique''' vient du fait que les circuits ne sont pas des dispositifs parfaits et qu'ils laissent fuiter un peu de courant.
Pour commencer, rappelons qu'un transistor MOS est composé de deux morceaux de conducteurs (l'armature de la grille et la liaison drain-source) séparés par un isolant. L'isolant empêche la fuite des charges de la grille vers la liaison source-drain. Charger ou décharger un condensateur demande de l'énergie. Les lois de la physique nous disent que la quantité d'énergie que peut stocker un condensateur est égale au produit ci-dessous, avec C la capacité du condensateur et U la tension d'alimentation. Il s'agit de l'énergie qu'il faut fournir quand on charge ou décharger un condensateur/MOSFET.
: <math>E = \frac{1}{2} C U^2</math>
L'énergie est dissipée quand les transistors changent d'état, il dissipe une quantité de chaleur proportionnelle au carré de la tension d'alimentation. Or, la fréquence définit le nombre maximal de changements d'état qu'un transistor peut subir en une seconde : pour une fréquence de 50 hertz, un transistor peut changer d'état 50 fois par seconde maximum. Après, il s'agit d'une borne maximale : il se peut qu'un transistor ne change pas d'état à chaque cycle. Sur les architectures modernes, la probabilité de transition 0 ⇔ 1 étant d'environ 10%-30%. Et si les bits gardent la même valeur, alors il n'y a pas de dissipation de puissance. Mais on peut faire l'approximation suivante : le nombre de changement d'état par seconde d'un transistor est proportionnel à la fréquence. L'énergie dissipée en une seconde (la puissance P) par un transistor est approximée par l'équation suivante :
: <math>P_\text{dynamique} \approx \frac{1}{2} C U^2 \times f</math>
On peut alors multiplier par le nombre de transistors d'une puce électronique, ce qui donne :
: <math>P_\text{dynamique} \approx \frac{1}{2} C U^2 \times f \times n</math>
Cette équation nous donne la '''consommation dynamique''', à savoir celle liée à l'activité du processeur/circuit. De plus, l'équation précédente permet de comprendre comment la consommation dynamique a évolué grâce à la loi de Moore. Pour cela, regardons comment chaque variable a évolué et faisons les comptes :
* le nombre de transistors fait *2 tous les deux ans ;
* la capacité est réduite de 30% tous les deux ans ;
* la tension d'alimentation est réduite de 30% tous les deux ans ;
* la fréquence augmente de 40% tous les deux ans.
L'augmentation du nombre de transistors est parfaitement compensé par la baisse de la tension d'alimentation : multiplication par deux d'un côté, divisé par 2 (30% au carré) de l'autre. Idem avec la capacité et la fréquence : la capacité est multipliée par 0.7 tous les deux ans, la fréquence est multipliée par 1.4 de l'autre, et <math>1.4 \approx 1/0.7</math>. En clair, la consommation dynamique d'un processeur ne change pas dans le temps, du moins tant que les lois de Dennard sont valides. Ce qui n'est plus le cas depuis 2005.
Tout ce qui est dit plus haut part du principe que le transistor MOS est un dispositif parfait. Dans les faits, ce n'est pas le cas. En effet, la couche d'isolant entre la grille et le semi-conducteur est très petite. Avec la miniaturisation, la couche d'isolant ne fait guère plus de quelques dizaines atomes d'épaisseur et laisse passer un peu de courant : on parle de ''courants de fuite''. La consommation d'énergie qui résulte de ce courant de fuite est la '''consommation statique'''. Elle s'appelle statique car elle a lieu même si les transistors ne changent pas d'état.
La diminution de la finesse de gravure a tendance à augmenter les courants de fuite. Elle diminue la consommation dynamique, mais augmente la consommation statique. Vu que les processeurs anciens avaient une consommation statique ridiculement basse et une consommation dynamique normale, doubler la première et diviser par deux la seconde donnait un sacré gain au total.
Quantifier la consommation statique est assez compliqué, et les équations deviennent généralement très complexes. Mais une simplification nous dit que les courants de fuite dépendent de la tension de seuil. Une équation très simplifiée est la suivante :
: <math>I_\text{Fuite} \propto e^{{- V_{Th} \over K}}</math>, avec <math>V_{Th}</math> la tension de seuil, et K une constante.
La consommation statique est le produit des courants de fuite et de la tension d'alimentation, avec d'autres facteurs de proportionnalités qui ne nous intéressent pas ici.
: <math>P_\text{Statique} \propto U \times e^{{- V_{Th} \over K}}</math>, avec U la tension d'alimentation.
===La loi de Kommey===
Avant 2005, la réduction de la finesse de gravure permettait de diminuer la consommation d'énergie, tout en augmentant la puissance de calcul. Mais depuis, la consommation statique a fini par rattraper la consommation dynamique. Et cela a une conséquence assez importante. L'efficacité énergétique des processeurs n'a pas cessé d'augmenter au cours du temps : pour le même travail, les processeurs chauffent moins. Globalement, le nombre de Watts nécessaires pour effectuer une instruction a diminué de manière exponentielle avec les progrès de la miniaturisation.
[[File:Computing efficiency, OWID.svg|centre|vignette|upright=2|Watts par millions d'instructions, au cours du temps.]]
[[File:Koomeys law graph, made by Koomey.jpg|vignette|Loi de Kommey]]
Il est aussi intéressant d'étudier la performance par watts, à savoir le nombre de Millions d'instructions par secondes pour chaque watt/kilowatt dépensé pour faire le calcul. Avant l'année 2005, la quantité de calcul que peut effectuer le processeur en dépensant un watt double tous les 1,57 ans. Cette observation porte le nom de '''loi de Kommey'''. Mais depuis 2005, on est passé à une puissance de calcul par watt qui double tous les 2,6 ans. Il y a donc eu un ralentissement dans l'efficacité énergétique des processeurs.
===Le ''dark silicon'' et le mur du TDP===
Malgré les nombreuses améliorations de la performance par watt dans le temps, les processeurs actuels chauffent beaucoup et sont contraints par les limites thermiques. Et ces limites thermiques se marient assez mal avec le grand nombre de transistors présents sur les processeurs modernes. Dans un futur proche, il est possible que les contraintes thermiques limitent le nombre de transistors actifs à un instant donné. Pour le dire autrement, il serait impossible d'utiliser plus de 50% des transistors d'un CPU en même temps, ou encore seulement 30%, etc. Ce genre de scénarios ont reçu le nom d'"utilization wall" dans la communauté académique.
Il y aurait donc un certain pourcentage du processeur qui ne pourrait pas être activée en raison des performances thermiques, portion qui porte le nom de '''''dark silicon'''''. Mais le ''dark silicon'' n'est pas du circuit inutile. Il faut bien comprendre que ce ''dark silicon'' n'est pas une portion précise de la puce. Par exemple, imaginons que 50% d'un processeur soit du ''dark silicon'' : cela ne veut pas dire que 50% du CPU ne sert à rien, mais que les deux moitié du processeur se passent le flambeau régulièrement, ils sont utilisés à tour de rôle.
En soi, le fait que tous les transistors ne soient pas actifs en même temps n'est pas un problème. Les processeurs modernes n'utilisent pas tous leurs circuits en même temps, et certains restent en pause tant qu'on ne les utilise pas. Par exemple, sur un processeur multicœurs, il arrive que certains cœurs ne soient pas utilisés, et restent en veille, voire soient totalement éteints. Par exemple, sur un processeur octo-coeurs, si seuls 4 cœurs sur les 8 sont utilisés, alors 50% du processeur est techniquement en veille.
L'existence du ''dark silicon'' implique cependant qu'il faut construire les processeurs en tenant compte de sa présence, du fait que les contrainte thermiques empêchent d'utiliser une portion significative des transistors à un instant t. Pour cela, l'idée en vogue actuellement est celle des '''architectures hétérogènes''', qui regroupent des processeurs très différents les uns des autres sur la même puce, avec chacun leur spécialisation.
Le premier cas existe déjà à l'heure actuelle. Il s'agit de processeurs qui regroupent deux types de cœurs : des cœurs optimisés pour la performance, et des cœurs optimisés pour la performance. Un exemple est celui des processeurs Intel de 12ème génération et plus, qui mélangent des ''P-core'' et des ''E-core'', les premiers étant des coeurs très performants mais gourmands en énergie, les autres étant économes en énergie et moins performants. Les noms complets des coeurs trahissent le tout : ''Efficiency core'' et ''Performance core''. L'utilité des E-core est d'exécuter des programmes peu gourmands, généralement des tâches d'arrière-plan. Les processeurs ARM de type BIG.little faisaient la même chose, mais avec un cœur de chaque type.
Le second cas est celui des processeurs regroupant un ou plusieurs cœurs normaux, généralistes, complétés par plusieurs accélérateurs spécialisés dans des tâches précises. Par exemple, on peut imaginer que le processeur incorpore un circuit spécialisé dans les calculs cryptographiques, un circuit spécialisé dans le traitement d'image, un autre dans le traitement de signal, un autre pour accélérer certains calculs liés à l'IA, etc. De tels circuits permettent des gains de performance dans des tâches très précises, qui ne se mélangent pas. Par exemple, si on lance un vidéo, le circuit de traitement d'image/vidéo sera activé, mais l'accélérateur cryptographique et l'accélération d'IA seront désactivés.
En soi, le fait d'adapter l'architecture des ordinateurs pour répondre à des contraintes thermiques n'est pas nouvelle. Nous verrons plus bas que l'apparition des processeurs multicœurs dans les années 2000 est une réponse à des contraintes technologiques assez strictes concernant la température, par exemple. La fin de la loi de Dennard a grandement réduit l'amélioration de performance par watt des processeurs à un seul cœur, rendant les processeurs multicœurs plus intéressants. Mais expliquer pourquoi demande d'expliquer pas mal de choses sur la performance d'un processeur simple cœur et comment celle-ci évolue avec la loi de Moore.
==L'évolution de la performance des processeurs==
Les processeurs ont gagné en performance avec le temps. Les raisons à cela sont doubles, mais liées aux lois de Dennard. La première raison est la hausse de la fréquence. C'était une source importante avant 2005, avant que les lois de Dennard cessent de fonctionner. L'autre raison, est la loi de Moore. Mettre plus de transistors permet de nombreuses optimisations importantes, comme utiliser des circuits plus rapides, mais plus gourmands en circuits. Voyons les deux raisons l'une après l'autre.
===La fréquence des processeurs a augmenté===
Les processeurs et mémoires ont vu leur fréquence augmenter au fil du temps. Pour donner un ordre de grandeur, le premier microprocesseur avait une fréquence de 740 kilohertz (740 000 hertz), alors que les processeurs actuels montent jusqu'à plusieurs gigahertz : plusieurs milliards de fronts par secondes ! L'augmentation a été exponentielle, mais plus faible que le nombre de transistors. Les nouveaux processeurs ont augmenté en fréquence, grâce à l'amélioration de la finesse de gravure. On est maintenant à environ 3 GHz à mi-2020.
Plus haut, on a dit que la fréquence augmentait de 40% tous les deux ans, tant que la loi de Dennard restait valide, avant 2005. En réalité, cette augmentation de 40% n'est qu'une approximation : la fréquence effective d'un processeur dépend fortement de sa conception (de la longueur du pipeline, notamment). Pour mettre en avant l'influence de la conception du processeur, il est intéressant de calculer une '''fréquence relative''', à savoir la fréquence à finesse de gravure égale. Elle est difficile à calculer, mais on peut l'utiliser pour comparer des processeurs entre eux, à condition de prendre la fréquence d'un processeur de référence.
Quelques observations montrent qu'elle a subi pas mal de variation d'un processeur à l'autre. La raison est que diverses techniques de conception permettent de gagner en fréquence facilement, la plus importante étant le pipeline. Augmenter la fréquence relative était une approche qui a eu son heure de gloire, mais qui a perdu de sa superbe. Avant les années 2000, augmenter la fréquence permettait de gagner en performance assez facilement. De plus, la fréquence était un argument marketing assez saillant, et l'augmenter faisait bien sur le papier. Aussi, les fréquences ont progressivement augmenté, et ont continué dans ce sens, jusqu’à atteindre une limite haute avec le Pentium 4 d'Intel.
[[File:Fréquences relatives processeurs Intel pré-Pentium 4.png|centre|vignette|upright=2|Fréquences relatives processeurs Intel pré-Pentium 4]]
Le Pentium 4 était une véritable bête en termes de fréquence pour l'époque : 1,5 GHz, contre à peine 1Ghz pour les autres processeurs de l'époque. La fréquence avait été doublée par rapport au Pentium 3 et ses 733 MHz. Et cette fréquence était juste la fréquence de base du processeur, certains circuits allaient plus vite, d'autres moins vite. Par exemple, l'unité de calcul intégrée dans le processeur allait deux fois plus vite, avec une fréquence de 3 GHz ! Et à l'inverse, certaines portions du processeur allaient au quart de cette fréquence, à 750 MHz. Bref, le Pentium 4 gérait plusieurs fréquences différentes : certaines portions importantes pour la performance allaient très vite, d'autres allaient à une vitesse intermédiaire pour faciliter leur conception, d'autres allaient encore moins vite, car elles n'avaient pas besoin d'être très rapides.
Le processeur était spécialement conçu pour fonctionner à très haute fréquence, grâce à diverses techniques qu'on abordera dans les chapitres suivants (pipeline très long, autres). Mais tout cela avait un cout : le processeur chauffait beaucoup ! Et c'était un défaut majeur. De plus, les techniques utilisées pour faire fonctionner le Pentium 4 à haute fréquence (le superpipeline) avaient beaucoup de défauts. Défauts compensés partiellement par des techniques innovantes pour l'époque, comme son ''replay system'' qu'on abordera dans un chapitre à part, mais sans grand succès. Les versions ultérieures du Pentium 4 avaient une fréquence plus base, mais avec un processeur complétement repensé, avec un résultat tout aussi performant. Ces versions chauffaient quand même beaucoup et les contraintes thermiques étaient un vrai problème.
En conséquence, la course à la fréquence s'est arrêtée avec ce processeur pour Intel, et l'industrie a suivi. La raison principale est que l'augmentation en fréquence des processeurs modernes est de plus en plus contrainte par la dissipation de chaleur et la consommation d'énergie. Et cette contrainte s'est manifestée en 2005, après la sortie du processeur Pentium 4. Le point d'inflexion en 2005, à partir duquel la fréquence a cessée d'augmenter drastiquement, s'explique en grande partie par cette contrainte. Les premiers processeurs étaient refroidis par un simple radiateur, alors que les processeurs modernes demandent un radiateur, un ventilateur et une pâte thermique de qualité pour dissiper leur chaleur. Pour limiter la catastrophe, les fabricants de processeurs doivent limiter la fréquence de leurs processeurs. Une autre raison est que la fréquence dépend des transistors, mais aussi de la rapidité du courant dans les interconnexions (les fils) qui relient les transistors, celles-ci devenant de plus en plus un facteur limitant pour la fréquence.
===La performance à fréquence égale a augmenté : la loi de Pollack===
Si la miniaturisation permet d'augmenter la fréquence, elle permet aussi d'améliorer la performance à fréquence égale. Rappelons que la performance à fréquence égale se mesure avec deux critères équivalents : l'IPC et le CPI. Le CPI est le nombre de cycles moyen pour exécuter une instruction. L'IPC est son inverse, à savoir le nombre d'instruction exécutable en un seul cycle. Les deu sont l'inverse l'un de l'autre. La '''loi de Pollack''' dit que l'augmentation de l'IPC d'un processeur est approximativement proportionnelle à la racine carrée du nombre de transistors ajoutés : si on double le nombre de transistors, la performance est multipliée par la racine carrée de 2.
On peut expliquer cette loi de Pollack assez simplement. Il faut savoir que les processeurs modernes peuvent exécuter plusieurs instructions en même temps (on parle d’exécution superscalaire), et peuvent même changer l'ordre des instructions pour gagner en performances (on parle d’exécution dans le désordre). Pour cela, les instructions sont préchargées dans une mémoire tampon de taille fixe, interne au processeur, avant d'être exécutée en parallèle dans divers circuits de calcul. Cependant, le processeur doit gérer les situations où une instruction a besoin du résultat d'une autre pour s'exécuter : si cela arrive, on ne peut exécuter les instructions en parallèle. Pour détecter une telle dépendance, chaque instruction doit être comparée à toutes les autres, pour savoir quelle instruction a besoin des résultats d'une autre. Avec N instructions, vu que chacune d'entre elles doit être comparée à toutes les autres, ce qui demande N^2 comparaisons.
En doublant le nombre de transistors, on peut donc doubler le nombre de comparateurs, ce qui signifie que l'on peut multiplier le nombre d'instructions exécutables en parallèle par la racine carrée de deux. En utilisant la loi de Moore, on en déduit qu'on gagne approximativement 40% d'IPC tous les deux ans, à ajouter aux 40 % d'augmentation de fréquence. En clair, la performance d'un processeur augmente de 40% grâce à la loi de Pollack, et de 40% par l'augmentation de fréquence. On a donc une augmentation de 80% tous les deux ans, donc une multiplication par 1,8 tous les deux ans, soit moins que la hausse de transistors.
===L'augmentation du nombre de cœurs===
Après 2005, l'augmentation de la fréquence a stagné et l'augmentation des performances semblait limitée par la seule loi de Pollack. Mais l'industrie a trouvé un moyen de contourner la loi de Pollack. En effet, cette dernière ne vaut que pour un seul processeur. Mais en utilisant plusieurs processeurs, la performance est, en théorie, la somme des performances individuelles de chacun d'entre eux. Et c'est la réaction qu'à eux l'industrie.
C'est pour cela que depuis les années 2000, le nombre de cœurs a augmenté assez rapidement. Les processeurs actuels sont doubles, voire quadruple cœurs : ce sont simplement des circuits imprimés qui contiennent deux, quatre, voire 8 processeurs différents, placés sur la même puce. Chaque cœur correspond à un processeur. En faisant ainsi, doubler le nombre de transistors permet de doubler le nombre de cœurs et donc de doubler la performance, ce qui est mieux qu'une amélioration de 40%.
[[File:CPU clock speed and Core count Graph.png|centre|vignette|upright=2.5|Comparaison entre fréquence et nombre de coeurs des processeurs, depuis ]]
==L'évolution de la performance des mémoires==
Après avoir vu le processeur, voyons comment la loi de Moore a impacté l'évolution des mémoires. Beaucoup de cours d'architecture des ordinateurs se contentent de voir l'impact de la loi de Moore sur le processeur, mais mettent de côté les mémoires. Et il y a une bonne raison à cela. Le fait est que les ordinateurs modernes ont une hiérarchie mémoire très complexe, avec beaucoup de mémoires différentes. Et les technologies utilisées pour ces mémoires sont très diverses, elles n'utilisent pas toutes des transistors MOS, la loi de Moore ne s'applique pas à toutes les mémoires.
Déjà, évacuons le cas des mémoires magnétiques (disques durs, disquettes) et des mémoires optiques (CD/DVD), qui ne sont pas fabriquées avec des transistors MOS et ne suivent donc pas la loi de Moore. Et de plus, elles sont en voie de disparition, elles ne sont plus vraiment utilisées de nos jours. Il ne reste que les '''mémoires à semi-conducteurs''' qui utilisent des transistors MOS, mais pas que. Seules ces dernières sont concernées par la loi de Moore, mais certaines plus que d'autres. Mais toutes les mémoires ont vu leur prix baisser en même temps que leur capacité a augmenté dans le temps, que ce soit à cause de la loi de Moore pour les mémoires à semi-conducteurs, l'amélioration exponentielle des technologies de stockage magnétique pour les disques durs.
[[File:Historical-cost-of-computer-memory-and-storage OWID.svg|centre|vignette|upright=2.0|Historical-cost-of-computer-memory-and-storage OWID]]
L'impact de la loi de Moore dépend de la mémoire considérée et de sa place dans la hiérarchie mémoire. Les mémoires intégrées au processeur, comme le cache ou les registres, sont des mémoires SRAM/ROM fabriquées intégralement avec des transistors MOS, et donc soumises à la loi de Moore. Leurs performances et leur capacité suivent l'évolution du processeur qui les intègre, leur fréquence augmente au même rythme que celle du processeur, etc. Aussi on peut considérer qu'on en a déjà parlé plus haut. Reste à voir les autres niveaux de la hiérarchie mémoire, à savoir la mémoire RAM principale, la mémoire ROM et les mémoires de masse. Dans les grandes lignes, on peut distinguer deux technologies principales : les mémoires DRAM et les mémoires FLASH.
===Les mémoires FLASH ont suivi la loi de Moore===
Les '''mémoires FLASH''' sont utilisées dans les mémoires de masse, comme les clés USB, les disques durs de type SSD, les cartes mémoires, et autres. Le passage à la mémoire FLASH a fait qu'elles sont plus rapides que les anciennes mémoires magnétiques, pour une capacité légèrement inférieure. Les mémoires FLASH sont aussi utilisées comme mémoire ROM principale ! Par exemple, les PC actuels utilisent de la mémoire FLASH pour stocker le ''firmware''/BIOS/UEFI. De même, les systèmes embarqués qui ont besoin d'un ''firmware'' rapide utilisent généralement de la mémoire FLASH, pas de la mémoire ROM proprement dite (on verra dans quelques chapitres que la mémoire FLASH est un sous-type de mémoire ROM, mais laissons cela à plus tard).
Elles sont basées sur des transistors MOS modifiés, appelés transistors à grille flottante. Un transistor à grille flottante peut être vu comme une sorte de mélange entre transistor et condensateur, à savoir qu'il dispose d'une grille améliorée, dont le caractère de condensateur est utilisé pour mémoriser une tension, donc un bit. Un transistor à grille flottante est utilisé pour mémoriser un bit sur les mémoires dites SLC, deux bits sur les mémoires dites MLC, trois bits sur les mémoires TLC, quatre sur les mémoires QLC. Non, ce n'est pas une erreur, c'est quelque chose de permis grâce aux technologies de fabrication d'une mémoire FLASH, nous détaillerons cela dans le chapitre sur les mémoires FLASH.
Vu que les mémoires FLASH sont basées sur des transistors MOS modifiés, vous ne serez pas trop étonnés d’apprendre que la loi de Moore s'applique à la mémoire FLASH. La taille des transistors à grille flottante suit la loi de Moore : elle diminue de 30% tous les deux ans.
[[File:NAND Fscaling 01.png|centre|vignette|upright=2.0|Taille d'une cellule de mémoire FLASH (de type NAND).]]
La conséquence est que l'aire occupée par un transistor à grille flottante est divisée par deux tous les deux ans. Le résultat est que la capacité des mémoires FLASH augmente de 50 à 60% par an, ce qui fait un doublement de leur capacité tous les deux ans.
[[File:NAND Fscaling 02.png|centre|vignette|upright=2.0|Aire d'une cellule de mémoire FLASH (de type NAND).]]
===Les mémoires RAM ne sont pas concernées par la loi de Moore===
[[File:1T-DRAM.png|vignette|Circuit qui mémorise un bit dans une mémoire DRAM moderne.]]
Les '''mémoires DRAM''' sont utilisées pour la mémoire principale de l'ordinateur, la fameuse mémoire RAM. A l'intérieur des mémoires DRAM actuelles, chaque bit est mémorisé en utilisant un transistor MOS et un condensateur (un réservoir à électron). Leur capacité et leur performance dépend aussi bien de la miniaturisation du transistor que de celle du condensateur. Elles sont donc partiellement concernées par la loi de Moore.
Pour ce qui est de la capacité, les DRAM suivent la loi de Moore d'une manière approximative. La raison est que gagner en capacité demande de réduire la taille des cellules mémoire, donc du transistors et du condensateur à l'intérieur. La miniaturisation des transistors suit la loi de Moore, mais la réduction de la taille du condensateur ne la suit pas. Dans le passé, la capacité des DRAM augmentait légèrement plus vite que la loi de Moore, avec un quadruplement tous les trois ans (4 ans pour la loi de Moore), mais tout a considérablement ralentit avec le temps.
[[File:Confronto processore memoria.jpg|centre|vignette|upright=2.0|Évolution du nombre de transistors d'une mémoire électronique au cours du temps. On voit que celle-ci suit de près la loi de Moore.]]
Niveau performances, la loi de Moore ne s'applique tout simplement pas. La raison à cela est que la performance des DRAM est dominée par la performance du condensateur, pas par celle du transistor. Miniaturiser des transistors permet de les rendre plus rapides, mais le condensateur ne suit pas vraiment. Aussi, les performances des mémoires DRAM stagnent.
Rappelons que la performance d'une mémoire RAM/ROM dépend de deux paramètres : son débit binaire, et son temps d'accès. Il est intéressant de comparer comment les deux ont évolué. Pour les mémoires RAM, le débit binaire a augmenté rapidement, alors que le temps d'accès a baissé doucement. Les estimations varient d'une étude à l'autre, mais disent que le temps d'accès des mémoires se réduit d'environ 10% par an, alors que le débit binaire a lui augmenté d'environ 30 à 60% par an. Une règle approximative est que le débit binaire a grandi d'au moins le carré de l'amélioration du temps d'accès.
Pour comprendre pourquoi temps d'accès et débit binaire n'ont pas évolué simultanément, il faut regarder du côté de la fréquence de la mémoire RAM. Les mémoires modernes sont cadencées avec un signal d'horloge, ce qui fait qu'il faut tenir compte de la '''fréquence de la mémoire'''. Le débit et les temps d'accès dépendent fortement de la fréquence de la mémoire. Plus la fréquence est élevée, plus les temps d'accès sont faibles, plus le débit est important.
Le calcul du débit binaire d'une mémoire est simplement le produit entre fréquence et largeur du bus mémoire. Il se trouve que la largeur du bus de données n'a pas beaucoup augmenté avec le temps. Les premières barrettes de mémoire datée des années 80, les barrettes SIMM, avaient un bus de données de 8 bits pour la version 30 broches, 32 bits pour la version 72 broches. Le bus mémoire était déjà très important. Dans les années 2000, la démocratisation des barrettes mémoires DIMM a permis au bus de données d'atteindre 64 bits, valeur à laquelle il est resté actuellement. Il est difficile d'augmenter la largeur du bus, ca cela demanderait d'ajouter des broches sur des barrettes et des connecteurs déjà bien chargées. L'augmentation du débit binaire ne peut venir que de l'augmentation de la fréquence.
Les premières mémoires utilisées dans les PCs étaient asynchrones, à savoir qu'elles n'avaient pas de fréquence ! Elles se passaient de fréquence d'horloge, et le processeur se débrouillait avec. Il s'agissait des premières mémoires DRAM d'Intel, les mémoires EDO, Fast-page RAM et autres, que nous verrons dans quelques chapitres. Elles étaient utilisées entre les années 70 et 90, où elles étaient le type de mémoire dominant. Elles étaient assez rapides pour le processeur, mémoire et processeur avaient des performances comparables.
Dans les années 1990, la SDRAM est apparue. La terme SDRAM regroupe les premières mémoires RAM synchrones, cadencées par un signal d'horloge. La fréquence des SDRAM était de 66, 100, 133 et 150 MHz. Les RAM à 66 MHz sont apparues en premier, suivies par les SDRAM à 100MHz et puis par les 133 MHz, celles de 150 MHz étaient plus rares. Lors de cette période, la relation entre fréquence, temps d'accès et débit binaire était assez claire. Le temps d'accès est proportionnel à la fréquence, à peu de choses près. Le temps d'accès est de quelques cycles d'horloge, bien qu'il dépende des barrettes de mémoire utilisées. Le débit est le produit entre fréquence et largeur du bus. Donc plus la fréquence est grande, meilleures sont les performances globales. Et la fréquence de la mémoire et celle du bus mémoire étaient identiques.
Depuis les années 2000, les mémoires RAM utilisent des techniques de ''Double data rate'' ou de ''Quad data rate'' qui permettent d'atteindre de hautes fréquences en trichant. La triche vient du fait que la fréquence de la mémoire n'est plus égale à la fréquence du bus mémoire depuis les années 2000. Nous verrons cela en détail dans le chapitre sur le bus mémoire. Pour le moment, nous allons nous contenter de dire que l'idée derrière cette différence de fréquence est d'augmenter le débit binaire des mémoires, mais sans changer leur fréquence interne.
{|class="wikitable"
|-
! Année
! Type de mémoire
! Fréquence de la mémoire (haut de gamme)
! Fréquence du bus
! Coefficient multiplicateur entre les deux fréquences
|-
| 1998
| DDR 1
| 100 - 200 MHz
| 200 - 400 MHz
| 2
|-
| 2003
| DDR 2
| 100 - 266 MHz
| 400 - 1066 MHz
| 4
|-
| 2007
| DDR 3
| 100 - 266 MHz
| 800 - 2133 MHz
| 8
|-
| 2014
| DDR 4
| 200 - 400 MHz
| 1600 - 3200 MHz
| 8
|-
| 2020
| DDR 5
| 200 - 450 MHz
| 3200 - 7200 MHz
| 8 à 16
|}
Le débit binaire est proportionnel à la fréquence du bus mémoire, alors que les temps d'accès sont proportionnel à la fréquence de la mémoire. La fréquence de la mémoire n'a pas beaucoup augmentée et reste très faible, les temps d'accès ont donc fait de même. Par contre, le débit binaire est lui très élevé, car dépendant de la fréquence du bus mémoire, qui a beaucoup augmenté. Au final, les mémoires modernes ont donc un gros débit, mais un temps de latence très élevé.
==La comparaison des performances des CPU et mémoires RAM : le ''memory wall''==
La performance du processeur et de la mémoire doivent idéalement être comparables. Rien ne sert d'avoir un processeur puisant si la mémoire ne suit pas. A quoi bon avoir un processeur ultra-puissant s'il passe 80% de son temps à attendre des données en provenance de la mémoire ? Si la performance des mémoires RAM stagne, alors que les processeurs gagnent en performance de manière exponentielle, on fait face à un problème.
Pour nous rendre compte du problème, il faut comparer la performance du processeur avec celle de la mémoire. Et c'est loin d'être facile, car les indicateurs de performance pour le processeur et la mémoire sont fondamentalement différents. La performance d'une mémoire dépend de son débit binaire et de son temps d'accès, la performance du processeur dépend de son CPI et de sa fréquence. Mais on peut comparer la vitesse à laquelle ces indicateurs grandissent. Pour donner un chiffre assez parlant, quelques estimations estiment que le temps d'accès de la mémoire, exprimé en cycles d'horloge du processeur, double tous les 2,6 ans.
===L'évolution dans le temps du ratio des fréquences processeur/mémoire===
Une autre possibilité est comparer la fréquence des processeurs et des mémoires, pour voir si la fréquence de la mémoire a suivi celle du processeur. On s'attendrait à une augmentation de 40% de la fréquence des mémoires tous les deux ans, comme c'est le cas pour les processeurs. Mais la fréquence des mémoires n'a pas grandit au même rythme et a été beaucoup plus faible que pour les processeurs. Faisons un historique rapide.
Lors de l'époque des mémoires asynchrones, mémoire et processeur avaient des performances comparables. Les accès mémoire se faisaient globalement en un cycle d'horloge, éventuellement deux ou trois cycles, rarement plus. Bien qu'asynchrone, on peut considérer qu'elles allaient à la même fréquence que le processeur, mais cela ne servait à rien de parler de fréquence de la mémoire ou de fréquence du bus mémoire.
Pour la période des mémoires SDRAM, le tableau ci-dessous fait une comparaison des fréquences processeur-mémoire. La comparaison avec les processeurs était assez simple, car la fréquence du bus mémoire et la fréquence de la mémoire sont identiques. On voit que la fréquence de la mémoire est déjà loin derrière la fréquence du processeur, elle est 3 à 5 fois plus faible.
{|class="wikitable"
|-
! Année
! Fréquence du processeur (haut de gamme)
! Fréquence de la mémoire (haut de gamme)
|-
| 1993
| Intel Pentium : 60 à 300 MHz
| 66 Mhz
|-
| 1996-1997
|
* Intel Pentium 2 (1996) : 233 MHz à 450 MHz
* AMD K5 (1996) : 75 MHz à 133 MHz
* AMD K6 (1997) : 166 MHz à 300 MHz
| 100 MHz
|-
| 1999
|
* Intel Pentium 3 : 450 MHz à 1,4 GHz
* AMD Athlon : 500 MHz à 1400 MHz
| 133 MHz
|}
Avec l'invention des mémoires DDR, la comparaison est rendue plus compliquée par la dissociation entre fréquence du bus et fréquence de la mémoire. La comparaison est la suivante :
{|class="wikitable"
|-
! Année
! Type de mémoire
! Fréquence de la mémoire (haut de gamme)
! Fréquence du bus
! Fréquence du processeur (haut de gamme)
|-
| 1998
| DDR 1
| 100 - 200 MHz
| 200 - 400 MHz
| 200 - 1000 MHz
|-
| 2003
| DDR 2
| 100 - 266 MHz
| 400 - 1066 MHz
| 700 - 1500 MHz
|-
| 2007
| DDR 3
| 100 - 266 MHz
| 800 - 2133 MHz
| 1500 - 3000 MHz
|-
| 2014
| DDR 4
| 200 - 400 MHz
| 1600 - 3200 MHz
| 1600 - 4200 MHz
|-
| 2020
| DDR 5
| 200 - 450 MHz
| 3200 - 7200 MHz
| 1700 - 4500 MHz
|}
Le constat est assez clair : le processeur est plus rapide que la mémoire, et l'écart se creuse de plus en plus avec le temps. Il s'agit d'un problème assez important, qui dicte l'organisation des ordinateurs modernes. Les mémoires sont actuellement très lentes comparé au processeur. On parle souvent de '''''memory wall''''', pour décrire ce problème, nous utiliserons le terme '''mur de la mémoire'''. Pour cela, diverses solutions existent. Et la plus importante d'entre elle est l'usage d'une hiérarchie mémoire.
===Les solutions contre le mur de la mémoire : hiérarchie mémoire et RAM computationnelle===
Le mur de la mémoire est un problème avec lequel les architectures modernes doivent composer. Le mur de la mémoire a deux origines. La première est que processeur et mémoire sont strictement séparés et que tout traitement doit lire des opérandes en mémoire, pour ensuite écrire des résultats en mémoire RAM. La seconde est que les transferts entre processeurs et mémoire sont assez lents, ce qui fait que l'idéal est de réduire ces transferts le plus possible.
La solution la plus souvent retenue est l'usage de '''mémoires caches''' intégrées au processeur, pour réduire le trafic entre DRAM et CPU. Les mémoires caches ne sont pas des DRAM, ce qui permet de contourner le problème du mur de la mémoire, causé par la technologie de fabrication des DRAM. Les caches sont des mémoires à semi-conducteur fabriquées avec des transistors CMOS, ce qui fait que leurs performances augmentent au même rythme que le processeur. Elles sont intégrées dans le processeur, même s'il a existé des mémoires caches connectées sur la carte mère.
Mais une autre solution consiste à faire l'inverse, à savoir ajouter des capacités de calcul dans la mémoire RAM. L'idée est de faire les calculs dans la mémoire directement : pas besoin de transférer les opérandes du calcul de la mémoire vers le CPU, ni de transférer les résultats du CPU vers la RAM. L'idée est alors de déléguer certains calculs à la DRAM, voire carrément de fusionner le CPU et la DRAM ! On parle alors de '''''in-memory processing''''', que nous traduirons par le terme quelque peu bâtard de '''RAM computationnelle'''.
Cependant, l'implémentation d'une RAM computationnelle pose quelques problèmes d'ordre pratique. Le premier est au niveau de la technologie utilisée pour les transistors. Comme on vient de le voir, les technologies utilisées pour fabriquer la mémoire sont très différentes de celles utilisées pour fabriquer des circuits logiques, les circuits d'un processeur. Les processeurs utilisent des transistors CMOS normaux, les mémoires FLASH des transistors MOS à grille flottante, les DRAM utilisent un mélange de transistors MOS et de condensateurs. Et au niveau des procédés de fabrication, de gravure des puce, de photolithographie, de la technique des semi-conducteurs, les trois procédés sont totalement différents. Aussi, fusionner DRAM et CPU pose des problèmes de fabrication assez complexes.
Notons que ce problème n'a pas lieu avec la mémoire utilisée pour les registres et les caches, car elle est fabriquée avec des transistors. Nous avons vu il y a quelques chapitres comment créer des registres à partir de transistors et de portes logiques, ce qui utilise le même procédé technologique que pour les CPU. Les mémoires caches utilisent des cellules de mémoire SRAM fabriquées uniquement avec des transistors CMOS, comme nous le verrons dans quelques chapitres. Registres et SRAM sont donc fabriqués avec le même procédé technologique que les processeurs, ce qui fait que l'intégration de registres/caches dans le processeur est assez simple. Ce n'est pas le cas pour la fusion DRAM/CPU.
La majorité des architectures à RAM computationnelle ont été des échecs commerciaux. La raison est qu'il s'agit d'architectures un peu particulières, qui sont formellement des architectures dites à parallélisme de données, assez difficiles à exploiter. La tentative la plus connue était le '''projet IRAM de l'université de Berkeley'''. L'idée était de fusionner un processeur et une mémoire sur la même puce, les deux étant intégrés dans le même circuit. Démarré en 1996, il a été abandonnée en 2004.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=La performance d'un ordinateur
| prevText=La performance d'un ordinateur
| next=Les techniques de réduction de la consommation électrique d'un processeur
| nextText=Les techniques de réduction de la consommation électrique d'un processeur
}}{{autocat}}
</noinclude>
4h3h1iw2rq56wei7zokz9zbb0tvmmt5
Fonctionnement d'un ordinateur/Les circuits de masquage
0
70434
744780
741175
2025-06-15T01:41:50Z
Mewtow
31375
/* Les opérations bit à bit à une opérande */
744780
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les '''opérations bit à bit''', un ensemble d'opérations qui appliquent une opération binaire sur un ou deux nombres. La plus simple d'entre elle est l'opération NON, aussi appelée opération de complémentation, qui inverse tous les bits d'un nombre. Il s'agit de l'opération la plus simple et nous en avions déjà parlé dans les chapitres précédents. Mais il existe des opérations bit à bit un chouia plus complexes, comme celles qui font un ET/OU/XOR entre deux nombres. Pour être plus précis, elles font un ET/OU/XOR entre les deux bits de même poids. L'exemple du OU bit à bit est illustré ci-dessous, les exemples du ET et du XOR sont similaires.
[[File:Binary num bitwise OR.png|centre|vignette|upright=2|Opération OU bit à bit.]]
De telles opérations sont appelées bit à bit car elles combinent les bits de même poids de deux opérandes. Par contre, il n'y a pas de calculs entre bits de poids différents, les colonnes sont traitées indépendamment. Elles sont très utilisées en programmation, et tout ordinateur digne de ce nom contient un circuit capable d'effectuer ces opérations. Dans ce chapitre, nous allons voir divers circuits capables d'effectuer des opérations bit à bit, et voir comment les combiner.
Les opérations bit à bit classiques peuvent prendre une ou deux opérandes. La plupart en prenant deux comme les opérations ET/OU/XOR, l'opération NON en prend une seule. Les opérations bit à bit sur deux opérandes sont au nombre de 16, ce qui correspond au nombre de portes logiques à deux entrées possibles. Mais ce chiffre de 16 inclut les opérations bit à bit sur une opérande unique, qui sont au nombre de 4. Les opérations bit à bit sur une seule opérande sont plus simples à voir, nous verrons les opérations bit à bit à deux opérandes plus tard.
==Les opérations bit à bit à une opérande==
Les opérations bit à bit n'ayant qu'une seule opérande sont au nombre de quatre :
* Mettre à zéro l'opérande (porte FALSE).
* Mettre à 11111...11111 l'opérande (porte TRUE).
* Inverser les bits de l'opérande (porte NON).
* Recopier l'opérande (porte OUI).
Dans ce qui va suivre, nous allons créer un circuit qui prend en entrée une opérande, un nombre, et applique une des quatre opérations précédente. On peut choisir l'opération voulue grâce à plusieurs bits de commande, idéalement deux. Le circuit est composé à partir de circuits plus simples, au maximum trois : un circuit qui inverse le bit d'entrée à la demande, un autre qui le met à 1, un autre qui le met à 0. Ces trois circuits ont une entrée de commande qui détermine s'il faut faire l'opération, ou si le circuit doit se comporter comme une simple porte OUI, qui recopie sont entrée sur sa sortie et ne fait donc aucune opération. Le circuit recopie le bit d'entrée si cette entrée est à 0, mais il inverse/set/reset le bit d'entrée si elle est à 1.
Pour comprendre comment concevoir ces circuits, il faut rappeler les relations suivantes, qui donnent le résultat d'un ET/OU/XOR entre un bit d'opérande noté a et un bit qui vaut 0 ou 1.
{|class="wikitable"
|-
!
! Opération
! Interprétation du résultat
|-
! rowspan="2" | Porte ET
| <math>a.0=0</math>
| Mise à zéro du bit d'entrée
|-
| <math>a.1=a</math>
| Recopie du bit d'entrée
|-
! rowspan="2" | Porte OU
| <math>a+1=1</math>
| Mise à 1 du bit d'entrée
|-
| <math>a+0=a</math>
| Recopie du bit d'entrée
|-
! rowspan="2" | Porte XOR
| <math>a \oplus 0=a</math>
| Recopie du bit d'entrée
|-
| <math>a \oplus 1=\overline{a}</math>
| Inversion du bit d'entrée
|}
Pour résumer ce qui va suivre :
* Le circuit de mise à 1 commandable est une porte simple OU.
* Le circuit d’inversion commandable est une simple porte XOR.
* Le circuit de Reset, qui permet de mettre à zéro un bit si besoin, est une porte ET un peu modifiée.
===Le circuit de mise à la valeur maximale===
Dans cette section, nous allons voir un circuit qui prend en entrée un nombre et met sa sortie à la valeur maximale si une condition est respectée. Pour le dire autrement, le circuit va soit recopier l'entrée telle quelle sur sa sortie, soit la mettre à 11111...111. Le choix entre les deux situations est réalisé par une entrée ''Set'' de 1 bit : un 1 sur cette entrée met la sortie à la valeur maximale, un 0 signifie que l'entrée est recopiée en sortie.
La porte OU est toute indiquée pour cela. La mise à 1 d'un bit d'entrée demande de faire un OU de celui-ci avec un 1, alors que recopier un bit d'entrée demande de faire un OU de celui-ci avec un 0.
[[File:Circuit de mise à 1111111...11.png|centre|vignette|upright=2|Circuit de mise à 1111111...11]]
Ce circuit est utilisé pour gérer les débordements d'entier dans les circuits de calculs qui utilise l'arithmétique saturée (voir le chapitre sur le codage des entiers pour plus d'explications). Les circuits de calculs sont souvent suivis par ce circuit de mise à 111111...111, pour gérer le cas où le calcul déborde, afin de mettre la sortie à la valeur maximale. Évidemment, le circuit de calcul doit non seulement faire le calcul, mais aussi détecter les débordements d'entiers, afin de fournir le bit pour l'entrée ''Set''. Mais nous verrons cela dans le chapitre sur les circuits de calcul entier.
===Le circuit de mise à zéro===
Le circuit de ''Reset'' prend entrée le bit d'entrée, puis un bit de commande qui indique s'il faut mettre à zéro le bit d'entrée ou non. Le bit de commande en question est appelé le bit Reset. Si le signal Reset est à 1, alors on met à zéro le bit d'entrée, mais on le laisse intact sinon.
Le tableau ci-dessus nous dit que la porte ET est adaptée : elle recopie le bit d'entrée si le bit de commande vaut 1, et elle le met à 0 si le bit de commande vaut 0. Cependant, rappelons que l'on souhaite que le le circuit fasse un ''Reset'' si le bit de commande est à 1, pas 0, et la porte ET fait l'inverse. Pour corriger cela, on doit ajouter une porte NON. Le tout donne le circuit ci-dessous.
[[File:Circuit de mise à zéro d'un bit.png|centre|vignette|upright=2|Circuit de mise à zéro d'un bit]]
Un circuit qui met à zéro un nombre est composé de plusieurs circuits ci-dessus, à la différence que la porte NON est potentiellement partagée. Par contre, chaque bit est bien relié à une porte ET.
[[File:Circuit de mise à zéro.png|centre|vignette|upright=2|Circuit de mise à zéro]]
===L'inverseur commandable===
Dans cette section, nous allons voir un '''inverseur commandable''', un circuit qui, comme son nom l'indique, inverse les bits d'un nombre passé en entrée. Ce circuit inverse un nombre quand le bit de commande, souvent nommé ''Invert'', vaut 1.
La porte XOR est toute indiquée pour, ce qui fait que le circuit d'inversion commandable est composé d'une couche de portes XOR, chaque porte ayant une entrée connectée au bit de commande.
[[File:Inverseur commandable.png|centre|vignette|upright=2|Inverseur commandable par un bit.]]
===Le circuit qui combine les trois précédents===
Voyons maintenant un circuit qui combine les trois circuits précédents. L'implémentation naïve met les trois circuits les uns à la suite des autres, ce qui donne pour chaque bit d'opérande trois portes logiques ET/OU/XOR en série. Le problème est qu'il faut préciser trois bits de commandes, alors qu'on peut en théorie se débrouiller avec seulement 2 bits. Il faut alors ajouter un circuit combinatoire pour calculer les trois bits de commande à partir des deux bits initiaux.
[[File:Porte logique universelle de 1 bit, faite avec trois portes.png|centre|vignette|upright=2|Porte logique universelle de 1 bit, faite avec trois portes]]
Mais il y a moyen de se passer d'une porte logique ! L'idée est que mettre à 0 et mettre à 1 sont deux opérations inverses l'une de l'autre. Mettre à 1 revient à mettre à 0, puis à inverser le résultat. Et inversement, mettre à 0 revient à mettre à 1 avant d'inverser le tout. Il suffit donc de mettre le circuit d'inversion commandable à la fin du circuit, juste après un circuit de mise à 0 ou de mise à 1, au choix. En faisant comme cela, il ne reste que deux portes logiques, donc deux entrées. En choisissant bien les valeurs sur l'entrée de commande, on peut connecter les entrées de commande directement sur les opérandes des deux portes, sans passer par un circuit combinatoire.
[[File:Porte logique universelle de 1 bit, faite avec deux portes.png|centre|vignette|upright=2|Porte logique universelle de 1 bit, faite avec deux portes]]
==Les opérations bit à bit à deux opérandes==
Les opérations bit à bit à deux opérandes effectuent un ET, un OU, ou un XOR entre deux opérandes. Ici, le ET/OU/XOR se fait entre deux bits de même poids dans une opérande. Les circuits qui effectuent ces opérations sont assez simples, ils sont composés de portes logiques placées les unes à côté des autres. Il n'y a pas de possibilité de combiner des portes comme c'était le cas dans la section précédente.
===Les opérations de masquage===
Il est intéressant de donner quelques exemples d'utilisation des opérations bit à bit ET/OU/XOR. L'utilité des opérations bit est bit est en effet loin d'être évidente. L'exemple que nous allons prendre est celui des '''opérations de masquage''', très connue des programmeurs bas niveau. Leur but est de modifier certains bits d'un opérande, mais de laisser certains intouchés. Les bits modifiés peuvent être forcés à 1, forcés à 0, ou inversés.
Pour cela, on combine l'opérande avec un second opérande, qui est appelée le '''masque'''. Les bits à modifier sont indiqués par le masque : chaque bit du masque indique s'il faut modifier ou laisser intact le bit correspondant dans l'opérande. Le résultat dépend de l'opération entre masque et opérande, les trois opérations utilisées étant un ET, un OU ou un XOR.
Faire un ET entre l'opérande et le masque va mettre certains bits de l’opérande à 0. Les bits mis à 0 sont ceux où le bit du masque correspondant est à 0, tandis que les autres sont recopiés tels quels.
La même chose a lieu avec l'opération OU, sauf que cette fois-ci, certains bits de l'opérande sont mis à 1. Les bits mis à 1 sont ceux pour lesquels le bit du masque correspondant est un 1.
[[File:Masques 1.png|centre|vignette|upright=2.5|Masques 1]]
Dans le cas d'un XOR, les bits sont inversés. Les bits inversés sont ceux pour lesquels le bit du masque correspondant est un 1.
[[File:Masquage des n bits de poids faible.png|centre|vignette|upright=2|Masquage des n bits de poids faible]]
Pour donner un exemple d'utilisation, parlons des droits d'accès à un fichier. Ceux-ci sont regroupés dans une suite de bits : un des bits indique s'il est accessible en écriture, un autre pour les accès en lecture, un autre s'il est exécutable, etc. Bref, modifier les droits en écriture de ce fichier demande de modifier le bit associé à 1 ou à 0, sans toucher aux autres. Cela peut se faire facilement en utilisant une instruction bit à bit avec un masque bien choisie.
Un autre cas typique est celui où un développeur compacte plusieurs données dans un seul entier. Par exemple, prenons le cas d'une date, exprimée sous la forme jour/mois/année. Un développeur normal stockera cette date dans trois entiers : un pour le jour, un pour le mois, et un pour la date. Mais un programmeur plus pointilleux sera capable d'utiliser un seul entier pour stocker le jour, le mois et l'année. Pour cela, il raisonnera comme suit :
* un mois comporte maximum 31 jours : on doit donc encoder tous les nombres compris entre 1 et 31, ce qui peut se faire en 5 bits ;
* une année comporte 12 mois, ce qui tient dans 4 bits ;
* et enfin, en supposant que l'on doive gérer les années depuis la naissance de Jésus jusqu'à l'année 2047, 11 bits peuvent suffire.
Dans ces conditions, notre développeur décidera d'utiliser un entier de 32 bits pour le stockage des dates :
* les 5 bits de poids forts serviront à stocker le jour ;
* les 4 bits suivants stockeront le mois ;
* et les bits qui restent stockeront l'année.
Le développeur qui souhaite modifier le jour ou le mois d'une date devra modifier une partie des bits, tout en laissant les autres intacts. Encore une fois, cela peut se faire facilement en utilisant une instruction bit à bit avec un masque bien choisi.
===Les opérations pour tester un bit===
Une opération assez courante teste si un bit précis vaut 0 ou 1 dans une opérande. Elle est implémentée, là encore, avec un masque. L'opération se fait en deux temps : on sélectionne le bit voulu avec un masque, on teste la valeur du résultat. Pour sélectionner le bit voulu, il suffit de mettre tous les autres bits à 0, grâce au masque adéquat. Le résultat de l'opération met tous les autres bits à 0. Il reste alors à comparer le résultat obtenu avec 0. Si le résultat vaut 0, c'est que le bit sélectionné valait 0. Sinon, le bit testé valait 1.
[[File:Masques 2.png|centre|vignette|upright=2.5|Masques pour tester un bit.]]
Tester la valeur d'un bit peut se faire avec un circuit assez simple, lui-même composé de trois sous-circuits. Le premier circuit génère le masque, le second fait un ET entre le masque et l'opérande, le troisième compare le résultat avec 0.
[[File:Circuit qui sélectionne un bit et teste sa valeur.png|centre|vignette|upright=2|Circuit qui sélectionne un bit et teste sa valeur]]
Le circuit de comparaison avec zéro est une simple porte OU à plusieurs entrées. Si l'entrée vaut 0, le OU fournira un 0 en sortie. Mais si le bit testé va 1, le résultat après application du masque contiendra un 1. Ce qui fait qu'une entrée du OU sera à 1, ce qui implique une sortie à 1. La difficulté est de créer le circuit de génération du masque, ce qu'on ne peut pas faire à ce point du cours. Par contre, nous sauront le faire au chapitre suivant.
===Les portes logiques universelles à deux entrées===
Dans cette section, nous allons voir comment créer un circuit capable d'effectuer plusieurs opérations logiques, le choix de l'opération étant le fait d'une entrée de commande. Par exemple, imaginons un circuit capable de faire à la fois un ET, un OU, un XOR et un NXOR. Le circuit contiendra une entrée de commande de 2 bits, et la valeur sur cette entrée permet de sélectionner quelle opération faire : 00 pour un ET, 01 pour un OU, 11 pour un XOR, 01 pour le NXOR Nous allons créer un tel circuit, sauf qu'il est capable de faire toutes les opérations entre deux bits et regroupe donc les 16 portes logiques existantes. Nous allons aussi voir la même chose, mais pour les portes logiques de 1 bit.
Sachez qu'avec un simple multiplexeur, on peut créer un circuit qui effectue toutes les opérations bit à bit possible avec deux bits. Et cela a déjà été utilisé sur de vrais ordinateurs. Pour deux bits, divers théorèmes de l’algèbre de Boole nous disent que ces opérations sont au nombre de 16, ce qui inclus les traditionnels ET, OU, XOR, NAND, NOR et NXOR. Voici la liste complète de ces opérations, avec leur table de vérité ci-dessous (le nom des opérations n'est pas indiqué) :
* Les opérateurs nommés 0 et 1, qui renvoient systématiquement 0 ou 1 quel que soit l'entrée ;
* L'opérateur OUI qui recopie l'entrée a ou b, et l'opérateur NON qui l'inverse : <math>a</math>, <math>b</math>, <math>\overline{a}</math>, <math>\overline{b}</math> ;
* L’opérateur ET, avec éventuellement une négation des opérandes : <math>a . b</math>, <math>\overline{a} . b</math>, <math>a . \overline{b}</math>, <math>\overline{a . b}</math> ;
* La même chose avec l’opérateur OU : <math>a + b</math>, <math>\overline{a} + b</math>, <math>a + \overline{b}</math>, <math>\overline{a + b}</math> ;
* Et enfin les opérateurs XOR et NXOR : <math>a \oplus b</math>, <math>\overline{a \oplus b}</math>.
{|class="wikitable"
|-
!a
!b
!
!<math>0</math>
!<math>a . b</math>
!<math>a . \overline{b}</math>
!<math>a</math>
!<math>\overline{a} . b</math>
!<math>b</math>
!<math>a \oplus b</math>
!<math>a + b</math>
!<math>\overline{a . b}</math>
!<math>\overline{a \oplus b}</math>
!<math>\overline{b}</math>
!<math>a + \overline{b}</math>
!<math>\overline{a}</math>
!<math>\overline{a} + b</math>
!<math>\overline{a + b}</math>
!<math>1</math>
|-
|0 || 0 || - ||0 || 0 ||0 ||0 ||0 ||0 ||0 ||0 ||1 ||1 ||1 ||1 ||1 ||1 ||1 ||1
|-
|0 ||1 || - ||0 ||0 ||0 ||0 ||1 ||1 ||1 ||1 ||0 ||0 ||0 ||0 ||1 ||1 ||1 ||1
|-
|1 ||0 || - ||0 ||0 ||1 ||1 ||0 ||0 ||1 ||1 ||0 ||0 ||1 ||1 ||0 ||0 ||1 ||1 |1
|-
|1 ||1 || - ||0 ||1 ||0 ||1 ||0 ||1 ||0 ||1 ||0 ||1 ||0 ||1 ||0 ||1 ||0 ||1
|}
Le circuit à concevoir prend deux bits, que nous noterons a et b, et fournit sur sa sortie : soit a ET b, soit a OU b, soit a XOR b, etc. Pour sélectionner l'opération, une entrée du circuit indique quelle est l'opération à effectuer, chaque opération étant codée par un nombre. On pourrait penser que concevoir ce circuit serait assez complexe, mais il n'en est rien grâce à une astuce particulièrement intelligente. Regardez le tableau ci-dessus : vous voyez que chaque colonne forme une suite de bits, qui peut être interprétée comme un nombre. Il suffit d'attribuer ce nombre à l'opération de la colonne ! En faisant ainsi, le nombre attribué à chaque opération contient tous les résultats de celle-ci. Il suffit de sélectionner le bon bit parmi ce nombre pour obtenir le résultat. Et on peut faire cela avec un simple multiplexeur, comme indiqué dans le schéma ci-dessous !
[[File:Unité de calcul bit à bit de 2 bits, capable d'effectuer toute opération bit à bit.png|centre|vignette|Unité de calcul bit à bit de 2 bits, capable d'effectuer toute opération bit à bit.]]
Il faut noter que le raisonnement peut se généraliser avec 3, 4, 5 bits, voire plus ! Par exemple, il est possible d'implémenter toutes les opérations bit à bit possibles entre trois bits en utilisant un multiplexeur 8 vers 3.
Maintenant que nous sommes armés des portes logiques universelles, nous pouvons implémenter un circuit généraliste, qui peut effectuer la même opération logique sur tous les bits. Ce circuit est appelé une '''unité de calcul logique'''. Elle prend en entrée deux opérandes, ainsi qu'une entrée de commande sur laquelle on précise quelle opération il faut faire. Elle est simplement composée d'autant de portes universelles 2 bits qu'il n'y a de bits dans les deux opérandes. Par exemple, si on veut un circuit qui manipule des opérandes 8 bits, il faut prendre 8 portes universelles deux bits. Toutes les entrées de commande des portes sont reliées à la même entrée de commande.
[[File:Unité de calcul bit à bit de 4 bits, capable d'effectuer toute opération bit à bit.png|centre|vignette|upright=2|Unité de calcul bit à bit de 4 bits, capable d'effectuer toute opération bit à bit]]
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits combinatoires
| prevText=Les circuits combinatoires
| next=Les circuits de sélection
| nextText=Les circuits de sélection
}}
{{autocat}}
8rf4usj3yyiblt2ecimg1d887nndg51
Les cartes graphiques/Sommaire
0
70681
744769
742148
2025-06-15T01:23:16Z
Mewtow
31375
/* Annexe */
744769
wikitext
text/x-wiki
===Les cartes d'affichage et d'accélération 2D===
* [[Les cartes graphiques/Les cartes d'affichage|Les cartes d'affichage]]
* [[Les cartes graphiques/Le Video Display Controler|Le Video Display Controler]]
* [[Les cartes graphiques/Les systèmes à framebuffer|Les systèmes à framebuffer]]
* [[Les cartes graphiques/Les cartes accélératrices 2D|Les cartes accélératrices 2D]]
* [[Les cartes graphiques/Le mode texte et le rendu en tiles|Le mode texte et le rendu en tiles]]
* [[Les cartes graphiques/Les accélérateurs de scanline|Les accélérateurs de scanline]]
* [[Les cartes graphiques/Les Video Display Controler atypiques|Les Video Display Controler atypiques]]
===Les cartes accélératrices 3D===
* [[Les cartes graphiques/Le rendu d'une scène 3D : shaders et T&L|Le rendu d'une scène 3D : shaders et T&L]]
* [[Les cartes graphiques/Les cartes graphiques : architecture de base|Les cartes graphiques : architecture de base]]
* [[Les cartes graphiques/Les cartes accélératrices 3D|Les cartes accélératrices 3D]]
===Les processeurs de ''shader''===
* [[Les cartes graphiques/Les processeurs de shaders|Les processeurs de shaders]]
* [[Les cartes graphiques/La répartition du travail sur les unités de shaders|La répartition du travail sur les unités de shaders]]
* [[Les cartes graphiques/La microarchitecture des processeurs de shaders|La microarchitecture des processeurs de shaders]]
===La mémoire d'une carte graphique===
* [[Les cartes graphiques/La mémoire unifiée et la mémoire vidéo dédiée|La mémoire unifiée et la mémoire vidéo dédiée]]
* [[Les cartes graphiques/La hiérarchie mémoire d'un GPU|La hiérarchie mémoire d'un GPU]]
===Le pipeline fixe, non-programmable===
* [[Les cartes graphiques/Le processeur de commandes|Le processeur de commandes]]
* [[Les cartes graphiques/Le pipeline géométrique d'avant DirectX 10|Le pipeline géométrique d'avant DirectX 10]]
* [[Les cartes graphiques/Le pipeline géométrique après DirectX 10|Le pipeline géométrique après DirectX 10]]
* [[Les cartes graphiques/Le rasterizeur|Le rasterizeur]]
* [[Les cartes graphiques/Les unités de texture|Les unités de texture]]
* [[Les cartes graphiques/Les Render Output Target|Les Render Output Target]]
===Annexe===
* [[Les cartes graphiques/Le support matériel du lancer de rayons|Le support matériel du lancer de rayons]]
* [[Les cartes graphiques/L'antialiasing|L'antialiasing]]
* [[Les cartes graphiques/Le multi-GPU|Le multi-GPU]]
==En savoir plus==
* [https://fgiesen.wordpress.com/2011/07/09/a-trip-through-the-graphics-pipeline-2011-index/ A trip through the Graphics Pipeline 2011: Index]
{{autocat}}
ifd57ub8hnuhz8jfaa0swk4zfemhs8b
Fonctionnement d'un ordinateur/Les transistors et portes logiques
0
71050
744783
742721
2025-06-15T01:51:57Z
Mewtow
31375
/* La pass transistor logic utilise des multiplexeurs 2 vers 1 */
744783
wikitext
text/x-wiki
Dans le chapitre précédent, nous avons abordé les portes logiques. Dans ce chapitre, nous allons voir qu'elles sont fabriquées avec des composants électroniques que l'on appelle des '''transistors'''. Ces derniers sont reliés entre eux pour former des circuits plus ou moins compliqués. Pour donner un exemple, sachez que les derniers modèles de processeurs peuvent utiliser près d'un milliard de transistors.
==Les transistors MOS==
[[File:Transistor basic flow.svg|vignette|Un transistor est un morceau de conducteur, dont la conductivité est contrôlée par sa troisième broche/borne.]]
Les transistors possèdent trois '''broches''', des pattes métalliques sur lesquelles on connecte des fils électriques. On peut appliquer une tension électrique sur ces broches, qui peut représenter soit 0 soit 1. Sur ces trois broches, il y en a deux entre lesquelles circule un courant, et une troisième qui commande le courant. Le transistor s'utilise le plus souvent comme un interrupteur commandé par sa troisième broche. Le courant qui traverse les deux premières broches passe ou ne passe pas selon ce qu'on met sur la troisième.
Il existe plusieurs types de transistors, mais les deux principaux sont les transistors bipolaires et les transistors MOS. De nos jours, les transistors utilisés dans les ordinateurs sont tous des '''transistors MOS'''. Les raisons à cela sont multiples, mais les plus importantes sont les suivantes. Premièrement, les transistors bipolaires sont plus difficiles à fabriquer et sont donc plus chers. Deuxièmement, ils consomment bien plus de courant que les transistors MOS. Et enfin, les transistors bipolaires sont plus gros, ce qui n'aide pas à miniaturiser les puces électroniques. Tout cela fait que les transistors bipolaires sont aujourd'hui tombés en désuétude et ne sont utilisés que dans une minorité de circuits.
===Les types de transistors MOS : PMOS et NMOS===
Sur un transistor MOS, chaque broche a un nom, nom qui est indiqué sur le schéma ci-dessous.On distingue ainsi le '''drain''', la '''source''' et la '''grille''' On l'utilise le plus souvent comme un interrupteur commandé par sa grille. Appliquez la tension adéquate et la liaison entre la source et le drain se comportera comme un interrupteur fermé. Mettez la grille à une autre valeur et cette liaison se comportera comme un interrupteur ouvert.
Il existe deux types de transistors CMOS, qui diffèrent entre autres par le bit qu'il faut mettre sur la grille pour les ouvrir/fermer :
* les transistors NMOS qui s'ouvrent lorsqu'on envoie un zéro sur la grille et se ferment si la grille est à un ;
* et les PMOS qui se ferment lorsque la grille est à zéro, et s'ouvrent si la grille est à un.
[[File:Td7bfig2.png|centre|vignette|upright=2|Illustration du fonctionnement des transistors NMOS et PMOS.]]
Voici les symboles de chaque transistor.
{|
|[[File:Transistor CMOS.png|vignette|upright=0.5|Transistor CMOS]]
|[[File:IGFET N-Ch Enh Labelled simplified.svg|vignette|upright=0.5|Transistor MOS à canal N (NMOS).]]
|[[File:IGFET P-Ch Enh Labelled simplified.svg|vignette|upright=0.5|Transistor MOS à canal P (PMOS).]]
|}
===L'anatomie d'un transistor MOS===
À l'intérieur du transistor, on trouve simplement une plaque en métal reliée à la grille appelée l'armature, un bout de semi-conducteur entre la source et le drain, et un morceau d'isolant entre les deux. Pour rappel, un semi-conducteur est un matériau qui se comporte soit comme un isolant, soit comme un conducteur, selon les conditions auxquelles on le soumet. Dans un transistor, son rôle est de laisser passer le courant, ou de ne pas le transmettre, quand il faut. C'est grâce à ce semi-conducteur que le transistor peut fonctionner en interrupteur : interrupteur fermé quand le semi-conducteur conduit, ouvert quand il bloque le courant. La commande de la résistance du semi-conducteur (le fait qu'il laisse passer ou non le courant) est réalisée par la grille, comme nous allons le voir ci-dessous.
[[File:Transistor CMOS - 1.png|centre|vignette|upright=2|Transistor CMOS]]
Suivant la tension que l'on place sur la grille, celle-ci va se remplir avec des charges négatives ou positives. Cela va entrainer une modification de la répartition des charges dans le semi-conducteur, ce qui modulera la résistance du conducteur. Prenons par exemple le cas d'un transistor NMOS et étudions ce qui se passe selon la tension placée sur la grille. Si on met un zéro, la grille sera vide de charges et le semi-conducteur se comportera comme un isolant : le courant ne passera pas. En clair, le transistor sera équivalent à un interrupteur ouvert. Si on met un 1 sur la grille, celle-ci va se remplir de charges. Le semi-conducteur va réagir et se mettre à conduire le courant. En clair, le transistor se comporte comme un interrupteur fermé.
{|
|[[File:Transistor CMOS - 3.png|vignette|upright=1.5|Transistor NMOS fermé.]]
|[[File:Transistor CMOS - 4.png|vignette|upright=1.5|Transistor NMOS ouvert.]]
|}
===La tension de seuil d'un transistor===
Le fonctionnement d'un transistor est légèrement plus complexe que ce qui a été dit auparavant. Mais pour rester assez simple, disons que son fonctionnement exact dépend de trois paramètres : la tension d'alimentation, le courant entre drain et source, et un nouveau paramètre appelé la tension de seuil.
Appliquons une tension sur la grille d'un transistor NMOS. Si la tension de grille reste sous un certain seuil, le transistor se comporte comme un interrupteur fermé. Le seuil de tension est appelé, très simplement, la '''tension de seuil'''. Au-delà de la tension de seuil, le transistor se comporte comme un interrupteur ouvert, il laisse passer le courant. La valeur exacte du courant dépend de la tension entre drain et source, soit la tension d'alimentation. Elle aussi dépend de la différence entre tension de grille et de seuil, à savoir <math>U_G - U_\text{seuil}</math>.
Le paragraphe qui va suivre est optionnel, mais détaille un peu plus le fonctionnement d'un transistor MOS. Tout ce qu'il faut comprendre est que la tension de seuil est une tension minimale pour ouvrir le transistor. Le plus important à retenir est que l'on ne peut pas baisser la tension d'alimentation sous la tension de seuil, ce qui est un léger problème en termes de consommation énergétique. Ce détail reviendra plus tard dans ce cours, quand nous parlerons de la consommation d'énergie des circuits électroniques.
Dans les cas que nous allons voir dans ce cours, la tension d'alimentation est plus grande que <math>U_G - U_\text{seuil}</math>. Le courant est alors maximal, il est proportionnel à <math>U_G - U_\text{seuil}</math>. Le transistor ne fonctionne alors pas comme un amplificateur, le courant reste le même. Si la tension d'alimentation est plus petite que <math>U_G - U_\text{seuil}</math>, le transistor est en régime linéaire : le courant de sortie est proportionnel à <math>U_G - U_\text{seuil}</math>, ainsi qu'à la tension d'alimentation. Le transistor fonctionne alors comme un amplificateur de courant, dont l'intensité de l'amplification est commandée par la tension.
[[File:MOSFET enhancement-mode n-channel en.svg|centre|vignette|upright=2.5|Relations entre tensions et courant d'un MOSFET à dopage N.]]
==La technologie CMOS==
Les portes logiques que nous venons de voir sont actuellement fabriquées en utilisant des transistors. Il existe de nombreuses manières pour concevoir des circuits à base de transistors, qui portent les noms de DTL, RTL, TLL, CMOS et bien d'autres. Les techniques anciennes concevaient des portes logiques en utilisant des diodes, des transistors bipolaires et des résistances. Mais elles sont aujourd'hui tombées en désuétudes dans les circuits de haute performance. De nos jours, on n'utilise que des logiques MOS (''Metal Oxyde Silicium''), qui utilisent des transistors MOS vus plus haut dans ce chapitre, parfois couplés à des résistances. On distingue :
* La '''logique NMOS''', qui utilise des transistors NMOS associés à des résistances.
* La '''logique PMOS''', qui utilise des transistors PMOS associés à des résistances.
* La '''logique CMOS''', qui utilise des transistors PMOS et NMOS, sans résistances.
Dans cette section, nous allons montrer comment fabriquer des portes logiques en utilisant la '''technologie CMOS'''. Avec celle-ci, chaque porte logique est fabriquée à la fois avec des transistors NMOS et des transistors PMOS. On peut la voir comme un mélange entre la technologie PMOS et NMOS. Tout circuit CMOS est divisé en deux parties : une intégralement composée de transistors PMOS et une autre de transistors NMOS. Chacune relie la sortie du circuit soit à la masse, soit à la tension d'alimentation.
[[File:Principe de la conception de circuit en technologie CMOS.png|centre|vignette|upright=2|Principe de conception d'une porte logique/d'un circuit en technologie CMOS.]]
La première partie relie la tension d'alimentation à la sortie, mais uniquement quand la sortie doit être à 1. Si la sortie doit être à 1, des transistors PMOS vont se fermer et connecter tension et sortie. Dans le cas contraire, des transistors s'ouvrent et cela déconnecte la liaison entre sortie et tension d'alimentation. L'autre partie du circuit fonctionne de la même manière que la partie de PMOS, sauf qu'elle relie la sortie à la masse et qu'elle se ferme quand la sortie doit être mise à 0
[[File:Fonctionnement d'un circuit en logique CMOS.png|centre|vignette|upright=2|Fonctionnement d'un circuit en logique CMOS.]]
Dans ce qui va suivre, nous allons étudier la porte NON, la porte NAND et la porte NOR. La porte de base de la technologie CMOS est la porte NON, les portes NAND et NOR ne sont que des versions altérées de la porte NON qui ajoutent des entrées et quelques transistors. Les autres portes, comme la porte ET et la porte OU, sont construites à partir de ces portes. Nous parlerons aussi de la porte XOR, qui est un peu particulière.
===La porte NON===
Cette porte est fabriquée avec seulement deux transistors, comme indiqué ci-dessous.
[[File:Porte NON fabriquée avec des transistors CMOS. 01.jpg|centre|vignette|upright=1|Porte NON fabriquée avec des transistors CMOS.]]
Si on met un 1 en entrée de ce circuit, le transistor du haut va fonctionner comme un interrupteur ouvert, et celui du bas comme un interrupteur fermé : la sortie est reliée au zéro volt, et vaut donc 0. Inversement, si on met un 0 en entrée de ce petit montage électronique, le transistor du bas va fonctionner comme un interrupteur ouvert, et celui du haut comme un interrupteur fermé : la sortie est reliée à la tension d'alimentation, et vaut donc 1.
[[File:Porte NON fabriquée avec des transistors CMOS - fonctionnement.png|centre|vignette|upright=2|Porte NON fabriquée avec des transistors CMOS - fonctionnement.]]
===Les portes NAND et NOR===
Passons maintenant aux portes logiques à plusieurs entrées. Pour celles-ci, on va devoir utiliser plus de transistors que pour la porte NON, ce qui demande de les organiser un minium. Une porte logique à deux entrées demande d'utiliser au moins deux transistors par entrée : un transistor PMOS et un NMOS par entrée. Rappelons qu'un transistor est associé à une entrée : l'entrée est directement envoyée sur la grille du transistor et commande son ouverture/fermeture. Pour les portes logiques à 3, 4, 5 entrées, la logiques est la même : au minimum deux transistors par entrée, un PMOS et un NMOS.
Nous allons d'abord voir le cas d'une porte NOR/NAND en CMOS. Avec elles, les transistors sont organisées de deux manières, appelées '''transistors en série''' (l'un après l'autre, sur le même fil) et '''transistors en parallèle''' (sur des fils différents). Le tout est illustré ci-dessous. Avec des transistors en série, plusieurs transistors NMOS ou deux PMOS se suivent sur le même fil, mais on ne peut pas mélanger NMOS et PMOS sur le même fil.
[[File:Transistors CMOS en série et en parallèle.png|centre|vignette|upright=2|Transistors CMOS en série et en parallèle]]
====Les portes NAND/NOR à deux entrées====
Voyons d'abord le cas des portes NAND/NOR à deux entrées. Elles utilisent deux transistors NMOS et deux PMOS.
Avec des transistors en série, deux transistors NMOS ou deux PMOS se suivent sur le même fil, mais on ne peut pas mélanger NMOS et PMOS sur le même fil. Avec des transistors en parallèle, c'est l'exact inverse. L'idée est de relier la tension d'alimentation à la sortie à travers deux PMOS transistors distincts, chacun sur son propre fil, sa propre connexion indépendante des autres. Pour la masse (0 volt), il faut utiliser deux transistors NMOS pour la relier à la sortie, avec là encore chaque transistor NMOS ayant sa propre connexion indépendante des autres. En clair, chaque entrée commande un transistor qui peut à lui seul fermer le circuit.
On rappelle deux choses : chaque transistor est associée à une entrée sur sa grille, un transistor se ferme si l'entrée vaut 0 pour des transistors PMOS et 1 pour des NMOS. Avec ces deux détails, on peut expliquer comment fonctionnent des transistors en série et en parallèle. Pour résumer, les transistors en série ferment la connexion quand toutes les entrées sont à 1 (NMOS) ou 0 (PMOS). Avec les transistors en parallèle, il faut qu'une seule entrée soit à 1 (NMOS) ou 0 (PMOS) pour que la connexion se fasse.
Une porte NOR met sa sortie à 1 si toutes les entrées sont à 0, à 0 si une seule entrée vaut 1. Pour reformuler, il faut connecter la sortie à la tension d'alimentation si toutes les entrées sont à 0, ce qui demande d'utiliser des transistors PMOS en série. Pour gérer le cas d'une seule entrée à 1, il faut utiliser deux transistors en parallèle entre la masse et la sortie. Le circuit obtenu est donc celui obtenu dans le premier schéma. Le même raisonnement pour une porte NAND donne le second schéma.
{|
|[[File:Porte NOR fabriquée avec des transistors. 02.png|centre|vignette|upright=1|Porte NOR fabriquée avec des transistors.]]
|[[File:Porte NAND fabriquée avec des transistors. 04.png|centre|vignette|upright=1|Porte NAND fabriquée avec des transistors.]]
|}
Leur fonctionnement s'explique assez bien si on regarde ce qu'il se passe en fonction des entrées. Suivant la valeur de chaque entrée, les transistors vont se fermer ou s'ouvrir, ce qui va connecter la sortie soit à la tension d'alimentation, soit à la masse.
Voici ce que cela donne pour une porte NAND :
[[File:Porte NAND fabriquée avec des transistors - Fonctionnement.png|centre|vignette|upright=2|Porte NAND fabriquée avec des transistors.]]
Voici ce que cela donne pour une porte NOR :
[[File:Porte NOR fabriquée avec des transistors - Fonctionnement.png|centre|vignette|upright=2|Porte NOR fabriquée avec des transistors.]]
====Les portes NAND/NOR/ET/OU à plusieurs entrées====
Les portes NOR/NAND à plusieurs entrées sont construites à partir de portes NAND/NOR à deux entrées auxquelles on rajoute des transistors. Il y a autant de transistors en série que d'entrée, pareil pour les transistors en parallèle. Leur fonctionnement est similaire à leurs cousines à deux entrées. Les portes ET et OU à plusieurs entrées sont construites à partie de NAND/NOR suivies d'une porte NON.
{|
|[[File:NAND plusieurs entrées.png|vignette|NAND plusieurs entrées]]
|[[File:NOR plusieurs entrées.png|vignette|NOR plusieurs entrées]]
|}
En théorie, on pourrait créer des portes avec un nombre arbitraire d'entrées avec cette méthode. Cependant, au-delà d'un certain nombre de transistors en série/parallèle, les performances s'effondrent rapidement. Le circuit devient alors trop lent, sans compter que des problèmes purement électriques surviennent. En pratique, difficile de dépasser la dizaine d'entrées. Dans ce cas, les portes sont construites en assemblant plusieurs portes NAND/NOR ensemble. Et faire ainsi marche nettement mieux pour fabriquer des portes ET/OU que pour des portes NAND/NOR.
====Les portes ET/OU sont fabriquées à partir de NAND/NOR en CMOS====
En logique CMOS, les portes logiques ET et OU sont construites en prenant une porte NAND/NOR et en mettant une porte NON sur sa sortie. Il est théoriquement possible d'utiliser uniquement des transistors en série et en parallèle, mais cette solution utilise plus de transistors.
{|
|[[File:CMOS AND Layout.svg|vignette|Porte ET en CMOS]]
|[[File:CMOS OR.svg|vignette|Porte OU en CMOS]]
|}
Pour ce qui est des portes ET/OU avec beaucoup d'entrées, il est fréquent qu'elles soit construites en combinant plusieurs portes ET/OU moins complexes. Par exemple, une porte ET à 32 entrées sera construite à partir de portes à seulement 4 ou 5 entrées. Il existe cependant une alternative qui se marie nettement mieux avec la logique CMOS. Rappelons qu'en logique CMOS, les portes NAND et NOR sont les portes à plusieurs entrées les plus simples à fabriquer. L'idée est alors de combiner des portes NAND/NOR pour créer une porte ET/OU.
Voici la comparaison entre les deux solutions pour une porte ET :
{|
|[[File:12 input AND gate via cascade of AND gates.svg|vignette|ET plusieurs entrées]]
|[[File:12-input AND gate from NAND and NOR.svg|vignette|ET plusieurs entrées]]
|}
Voici la comparaison entre les deux solutions pour une porte OU :
{|
|[[File:12-input OR gate via cascade of OR gates.svg|vignette|OU plusieurs entrées]]
|[[File:12-input OR gate via NOR and NAND gates.svg|vignette|OU plusieurs entrées]]
|}
D'autres portes mélangent transistors en série et en parallèle d'une manière différente. Les portes ET-OU-NON et OU-ET-NON en sont un bon exemple.
===Une méthode générale===
Il existe une méthode générale pour créer des portes logiques à deux entrées. Avec elle, il faut repartir du montage avec deux transistors NMOS/PMOS en série. En théorie, il permet de relier la sortie à la tension d'alimentation/zéro volt si toutes les entrées sont à 0 (PMOS) ou 1 (NMOS). L'idée est de regarder ce qui se passe si on fait précéder l'entrée d'un transistor par une porte NON. Pour deux transistors, cela fait 4 possibilités, 8 au total si on fait la différence entre PMOS et NMOS. Voici les valeurs d'entrées qui ferment le montage à transistor en série, suivant l’endroit où on place la porte NON.
[[File:Transistors CMOS en série.png|centre|vignette|upright=2|Transistors CMOS en série]]
Mine de rien, avec ces 8 montages de base, on peut créer n'importe quelle porte logique à deux entrées. Il faut juste se souvenir que d'après les règles du CMOS, les deux transistors PMOS se placent entre la tension d'alimentation et la sortie, et servent à mettre la sortie à 1. Pour les deux transistors NMOS, ils sont reliés à la masse et mettent la sortie à 0. Pour mieux comprendre, prenons l'exemple d'une porte XOR.
Appliquons la méthode que je viens d'expliquer avec une porte XOR. Le résultat est sous-optimal, mieux vaut fabriquer une porte XOR en combinant d'autres portes logiques, mais c'est pour l'exemple. L'idée est très simple : on prend la table de vérité de la porte logique, et on associe deux transistors en série pour chaque ligne. Regardons d'abord la table de vérité ligne par ligne :
{|class="wikitable"
|-
!Entrée 1!!Entrée 2!!Sortie
|-
||0||0||0
|-
||0||1||1
|-
||1||0||1
|-
||1||1||0
|}
La première ligne a ses deux entrées à 0 et sort un 0. La sortie est à 0, ce qui signifie qu'il faut regarder sur la ligne des transistors NMOS, qui connectent la sortie à la masse. Le montage qui se ferme quand les deux entrées sont à 0 est celui tout en bas à droite du tableau précédent, à savoir deux transistors NMOS avec deux portes NON.
Les deux lignes du milieu ont une entrée à 0 et une à 1, et leur sortie à 1. La sortie à 1 signifie qu'il faut regarder sur la ligne des transistors PMOS, qui connectent la tension d'alimentation à la sortie. Les deux montages avec deux entrées différentes sont les deux situés au milieu, avec deux transistors PMOS et une porte logique.
La dernière ligne a ses deux entrées à 1 et sort un 0. La sortie est à 0, ce qui signifie qu'il faut regarder sur la ligne des transistors NMOS, qui connectent la sortie à la masse. Le montage qui se ferme quand les deux entrées sont à 1 est celui tout en bas à gauche du tableau précédent, à savoir deux transistors NMOS seuls.
En combinant ces quatre montages, on trouve le circuit suivant. Notons qu'il n'y a que deux portes NON marquées en vert et bleu : on a juste besoin d'inverser la première entrée et la seconde, pas besoin de portes en plus. Les portes NOn sont en quelque sorte partagées entre les transistors PMOS et NMOS.
[[File:Cmos xor.svg|centre|vignette|upright=1|class=transparent|Porte XOR en logique CMOS.]]
Si les deux entrées sont à 1, alors les deux transistors en bas à gauche vont se fermer et connecter la sortie au 0 volt, les trois autres groupes ayant au moins un transistor ouvert. Si les deux entrées sont à 0, alors les deux transistors en bas à droite vont se fermer et connecter la sortie au 0 volt, les autres quadrants ayant au moins un transistor ouvert. Et pareil quand les deux bits sont différents : un des deux quadrants aura ses deux transistors fermés, alors que les autres auront au moins un transistor ouvert, ce qui connecte la sortie à la tension d'alimentation.
On peut construire la porte NXOR sur la même logique. Et toutes les portes logiques peuvent se construire avec cette méthode. Le nombre de transistors est alors le même : on utilise 12 transistors au total : 4 paires de transistors en série, 4 transistors en plus pour les portes NON. Que ce soit pour la porte XOR ou NXOR, on économise beaucoup de transistors comparés à la solution naïve, qui consiste à utiliser plusieurs portes NON/ET/OU. Si on ne peut pas faire mieux dans le cas de la porte XOR/NXOR, sachez cependant que les autres portes construites avec cette méthode utilisent plus de transistors que nécessaire. De nombreuses simplifications sont possibles, comme on le verra plus bas.
Dans les faits, la méthode n'est pas utilisée pour les portes XOR. A la place, les portes XOR sont construites à base d'autres portes logiques plus simples, comme des portes NAND/NOR/ET/OU. Le résultat est que l'on a un circuit à 10 transistors, contre 12 avec la méthode précédente.
[[File:CMOS10TrXOR.svg|centre|vignette|Porte XOR en CMOS en 10 transistors.]]
===Les circuits plus complexes (''full adder'', ...)===
Il est possible de fusionner plusieurs portes ET-OU-NON en un seul circuit à transistors CMOS, ce qui permet des simplifications assez impressionnantes. Pour donner un exemple, le schéma suivant compare l'implémentation d'un circuit qui fait un ET entre les deux premières entrées, puis un NOR entre le résultat du ET et la troisième entrée. L'implémentation à droite du schéma avec une porte ET et une porte NOR prend 10 transistors. L'implémentation la plus simple, à gauche du schéma, prend seulement 6 transistors.
[[File:AOI21 complex vs standard gates.svg|centre|vignette|upright=1.5|Porte ET-OU-NON à trois entrées (de type 2-1) à gauche, contre la combinaison de plusieurs portes à droite.]]
Une conséquence est que des circuits assez complexes gagnent à être fabriqués directement avec des transistors. Prenons l'exemple de l'additionneur complet. Une implémentation naïve, avec 5 portes logiques, utilise beaucoup de transistors. Deux portes XOR, deux portes OU et une porte ET, cela dépasse la trentaine de transistors. Faisons le compte : 10 transistors par porte XOR, 6 pour les trois autres portes, cela fait 38 transistors. Les additionneurs des processeurs modernes sont optimisés directement au niveau des transistors, pour leur permettre d'économiser des transistors. Par exemple, l'implémentation suivante en utilise seulement 24 !
[[File:Inverting full adder CMOS 24T.svg|centre|vignette|upright=1.5|Additionneur complet fabriqué avec 24 transistors.]]
Et c'est sans compter que l'additionneur complet naïf n'est pas forcément le top du top en termes de performances. Là encore, une implémentation avec des transistors peut être optimisée pour être plus rapide, notamment au niveau du calcul de la retenue, ou au contraire d'économiser des transistors. Tout dépend de l'objectif visé, certains circuit optimisant à fond pour la vitesse, d'autres pour le nombre de transistors, d'autres font un compromis entre les deux. Les circuits de ce genre sont très nombreux, trop pour qu'on puisse les citer.
==La ''pass transistor logic''==
La '''''pass transistor logic''''' est une forme particulière de technologie CMOS, une version non-conventionnelle. Avec le CMOS normal, la porte de base est la porte NON. En modifiant celle-ci, on arrive à fabriquer des portes NAND, NOR, puis les autres portes logiques. Les transistors sont conçus de manière à connecter la sortie, soit la tension d'alimentation, soit la masse. Avec la ''pass transistor logic'', le montage de base est un circuit interrupteur, qui connecte l'entrée directement sur la sortie. Le circuit interrupteur n'est autre que les portes à transmission vues il y a quelques chapitres.
La ''pass transistor logic'' a été utilisée dans des processeurs commerciaux, comme dans l'ARM1, le premier processeur ARM. Sur l'ARM1, les concepteurs ont décidé d'implémenter certains circuits avec des multiplexeurs. La raison n'est pas une question de performance ou d'économie de transistors, juste que c'était plus pratique à fabriquer, sachant que le processeur était le premier CPU ARM de l'entreprise.
Dans la suite du cours, nous verrons quelques circuits qui utilisent cette technologie, mais ils seront rares. Nous l'utiliserons quand nous parlerons des additionneurs, ou les multiplexeurs, guère plus. Mais il est sympathique de savoir que cette technologie existe.
===La porte à transmission===
Le circuit de base est une '''porte à transmission''', à savoir une porte logique qui agit comme une sorte d'interrupteur, qui s'ouvre ou se ferme suivant ce qu'on met sur l'entrée de commande. Le circuit peut soit connecter l'entrée et la sortie, soit déconnecter la sortie de l'entrée. Le choix entre les deux dépend de l’entrée de commande.
Intuitivement, on se dit qu'une transistor fonctionne déjà comme un interrupteur, mais une porte à transmission est construit avec deux transistors. La raison la plus intuitive est que la logique CMOS fait que tout transistor PMOS doit être associé à un transistor NMOS et réciproquement. Mais une autre raison, plus importante, est que les transistors NMOS et PMOS ne sont pas des interrupteurs parfaits. Les NMOS laissent passer les 0, mais laissent mal passer les 1 : la tension en sortie, pour un 1, est atténuée. Et c'est l'inverse pour les PMOS, qui laissent bien passer les 1 mais fournissent une tension de sortie peut adéquate pour les 0. Donc, deux transistors permettent d'obtenir une tension de sortie convenable. Le montage de base est le suivant :
[[File:CMOS transmission gate.PNG|centre|vignette|upright=1|CMOS Transmission gate]]
Vous remarquerez que le circuit est fondamentalement différent des circuits précédents. Les précédents connectaient la sortie soit à la tension d'alimentation, soit à la masse. Ici, la sortie est connectée sur l'entrée, rien de plus. Il n'y a pas d'alimentation électrique ni de contact à la masse. Retenez ce point, il sera important par la suite.
Les deux entrées A et /A sont l'inverse l'une de l'autre, ce qui fait qu'il faut en théorie rajouter une porte NON CMOS normale, pour obtenir le circuit complet. Mais dans les faits, on arrive souvent à s'en passer. Ce qui fait que la porte à transmission est définie comme étant le circuit à deux transistors précédents.
===Les multiplexeurs 2 vers 1 en ''pass transistor logic''===
Dans les chapitres précédents, nous avions vu que les portes à transmission sont assez peu utilisées. Nous ne nous en sommes servies que dans de rares cas, mais l'un d'entre eux va nous intéresser : les multiplexeurs et les démultiplexeurs. Pour rappel, il est assez simple de fabriquer un multiplexeur 2 vers 1 en utilisant des portes à transmission. L'idée est de relier chaque entrée à la sortie par l'intermédiaire d'une porte à transmission. Quand l'une sera ouverte, l'autre sera fermée. Le résultat n'utilise que deux portes à transmission et une porte NON. Voici le circuit qui en découle :
[[File:Multiplexeur fabriqué avec des portes à transmission et-ou des tampons trois-états.png|centre|vignette|upright=1.5|Multiplexeur fabriqué avec des portes à transmission]]
En utilisant les portes à transmission CMOS vues plus haut, on obtient le circuit suivant :
[[File:Multiplexeur fabriqué avec des portes à transmission.png|centre|vignette|upright=1.5|Multiplexeur fabriqué avec des portes à transmission CMOS.]]
===La ''pass transistor logic'' utilise des multiplexeurs 2 vers 1===
La ''pass transistor logic'' implémente les portes logiques d'une manière assez étonnante : les portes logiques sont basées sur un multiplexeur 2 vers 1 amélioré ! Un multiplexeur 2 vers 1 peut être utilisé pour implémenter de nombreux circuits différents. Par exemple, il peut être utilisé pour implémenter des portes logiques, tout dépend de ce qu'on met sur ses entrées.
L'idée est d'émuler une porte logique à deux entrées avec un multiplexeur 2 vers 1. Et intuitivement, vous vous dites que les deux entrées de la porte logique correspondent aux deux entrées de donnée du multiplexeur. Mais non, c'est une erreur ! En réalité, un bit d'entrée est envoyé sur l'entrée de commande, et l'autre bit sur une entrée de donnée du multiplexeur. Suivant ce qu'on met sur la seconde entrée du multiplexeur, on obtient une porte ET, OU, XOR, etc. Il y a quatre choix possibles : soit on envoie un 0, soit un 1, soit l'inverse du bit d'entrée, soit envoyer deux fois le bit d'entrée.
[[File:Portes logiques faites à partir de multiplexeurs.png|centre|vignette|upright=2|Portes logiques faites à partir de multiplexeurs]]
Ils peuvent aussi être utilisés pour implémenter une bascule D, (pour rappel : une petite mémoire de 1 bit), comme on l'a vu dans les chapitres sur les bascules. Il suffit pour cela de boucler la sortie d'un multiplexeur sur une entrée, en ajoutant deux portes NON dans la boucle pour régénérer le signal électrique.
[[File:Multiplexer-based latch using transmission gates.svg|centre|vignette|upright=2|Implémentation alternative d'une bascule D.]]
===La porte XOR en ''pass transistor logic''===
Il est facile d'implémenter une porte XOR avec un multiplexeur 2 vers 1. Pour rappel, une porte XOR est une sorte d'inverseur commandable, à savoir un circuit prend un bit d'entrée A, et l'inverse ou non suivant la valeur d'un bit de commande B. Un tel circuit commandable n'est autre qu'une porte logique XOR, qui XOR A et B. Et cela nous dit comment implémenter une porte XOR avec un multiplexeur : il suffit de prendre un multiplexeur qui choisit sa sortie parmi deux entrées : A et <math>\overline{A}</math> ! Pour deux bits A et B, l'un est envoyé sur l'entrée de commande, l'autre bit est envoyée sur les deux entrées (le bit sur une entrée, son inverse sur l'autre). Le circuit obtenu, sans les portes NON, est celui-ci :
[[File:XOR gate implemented with pass gates.svg|centre|vignette|upright=1|Porte XOR implémentée avec une porte à transmission.]]
La version précédente est une porte XOR où les signaux d'entrée sont doublés : on a le bit d'entrée original, et son inverse. C'est quelque chose de fréquent dans les circuits en ''pass transistor logic'', où les signaux/bits sont doublés. Mais il est possible de créer des versions normales, sans duplication des bits d'entrée. La solution la plus simple de rajouter deux portes NON, pour inverser les deux entrées. Le circuit passe donc de 4 à 8 transistors, ce qui reste peu. Mais on peut ruser, ce qui donne le circuit ci-dessous. Comme vous pouvez les voir, il mélange porte à transmission et portes NON CMOS normales.
[[File:CmosXORGate.svg|centre|vignette|upright=1|XOR en ''pass transistor logic'']]
Dans les deux cas, l'économie en transistors est drastique comparé au CMOS normal. Plus haut, nous avons illustré plusieurs versions possibles d'une porte XOR en CMOS normal, toutes de 12 transistors. Ici, on va de 6 transistors maximum, à seulement 4 ou 5 pour les versions plus simples. Le gain est clairement significatif, suffisamment pour que les circuits avec beaucoup de portes XOR gagnent à être implémentés avec la ''pass transistor logic''.
Quelques processeurs implémentaient leurs portes XOR en ''pass transistor logic'', alors que les autres portes étaient en CMOS normal. Un exemple est le mythique processeur Z80.
===Les avantages et défauts de la ''pass transistor logic''===
Une porte logique en logique CMOS connecte directement sa sortie sur la tension d'alimentation ou la masse. Mais dans une porte logique en ''pass transistor logic'', il n'y a ni tension d'alimentation, ni masse (O Volts). La sortie d'une porte à transmission est alimentée par la tension d'entrée. Et vu que les transistors ne sont pas parfaits, on a toujours une petite perte de tension en sortie d'une porte à transmission.
Le résultat est que si on enchaine les portes à transmission, la tension de sortie a tendance à diminuer, et ce d'autant plus vite qu'on a enchainé de portes à transmission. Il faut souvent rajouter des portes OUI pour restaurer les tensions adéquates, à divers endroits du circuit. La ''pass transistor logic'' mélange donc porte OUI/NON CMOS normales avec des portes à transmission. Afin de faire des économies de circuit, on utilise parfois une seule porte NON CMOS comme amplificateur, ce qui fait que de nombreux signaux sont inversés dans les circuits, sans que cela ne change grand chose si le circuit est bien conçu.
Par contre, ce défaut entraine aussi des avantages. Notamment, la consommation d'énergie est fortement diminuée. Seules les portes amplificatrices, les portes NON CMOS, sont alimentées en tension/courant. Le reste des circuits n'est pas alimenté, car il n'y a pas de connexion à la tension d'alimentation et la masse. De même, la ''pass transistor logic'' utilise généralement moins de transistors pour implémenter une porte logique, et un circuit électronique en général. L'exemple avec la porte XOR est assez parlant : on passe de 12 à 6 transistors par porte XOR. Des circuits riches en portes XOR, comme les circuits additionneurs, gagnent beaucoup à utiliser des portes à transmission.
==Les technologies PMOS et NMOS==
Dans ce qui va suivre, nous allons voir la technologie NMOS et POMS. Pour simplifier, la technologie NMOS est équivalente aux circuits CMOS, sauf que les transistors PMOS sont remplacés par une résistance. Pareil avec la technologie PMOS, sauf que c'est les transistors NMOS qui sont remplacés par une résistance. Les deux technologies étaient utilisées avant l'invention de la technologie CMOS, quand on ne savait pas comment faire pour avoir à la fois des transistors PMOS et NMOS sur la même puce électronique, mais sont aujourd'hui révolues. Nous en parlons ici, car nous évoquerons quelques circuits en PMOS/NMOS dans le chapitre sur les cellules mémoire, mais vous pouvez considérer que cette section est facultative.
===Le fonctionnement des logiques NMOS et PMOS===
Avec la technologie NMOS, les portes logiques sont fabriqués avec des transistors NMOS intercalés avec une résistance.
[[File:Circuit en logique NMOS.png|centre|vignette|upright=2|Circuit en logique NMOS.]]
Leur fonctionnement est assez facile à expliquer. Quand la sortie doit être à 1, tous les transistors sont ouverts. La sortie est connectée à la tension d'alimentation et déconnectée de la masse, ce qui fait qu'elle est mise à 1. La résistance est là pour éviter que le courant qui arrive dans la sortie soit trop fort. Quand au moins un transistor NMOS qui se ferme, il connecte l'alimentation à la masse, les choses changent. Les lois compliquées de l'électricité nous disent alors que la sortie est connectée à la masse, elle est donc mise à 0.
[[File:Fonctionnement d'un circuit en technologie NMOS.png|centre|vignette|upright=2|Fonctionnement d'un circuit en technologie NMOS.]]
Les circuits PMOS sont construits d'une manière assez similaire aux circuits CMOS, si ce n'est que les transistors NMOS sont remplacés par une résistance qui relie ici la masse à la sortie. Rien d'étonnant à cela, les deux types de transistors, PMOS et NMOS, ayant un fonctionnement inverse.
===Les portes logiques en NMOS et PMOS===
Que ce soit en logique PMOS ou NMOS, les portes de base sont les portes NON, NAND et NOR. Les autres portes sont fabriquées en combinant des portes de base. Voici les circuits obtenus en NMOS et PMOS:
{|class="wikitable flexible"
|-
! colspan="5 | NMOS
|-
| class="transparent" | [[File:NMOS NOT.svg|class=transparent|100px|Porte NON NMOS.]]
| class="transparent" | [[File:NMOS NAND.svg|class=transparent|100px|NMOS-NAND]]
| class="transparent" | [[File:NMOS NOR.png|100px|NMOS-NOR]]
| class="transparent" | [[File:NMOS AND gate.png|100px|NMOS AND]]
| class="transparent" | [[File:NMOS OR gate.png|100px|NMOS OR]]
|-
! colspan="5 | PMOS
|-
| class="transparent" | [[File:PMOS NOT.png|100px|PMOS NOT]]
| class="transparent" | [[File:PMOS NAND corr.png|100px|PMOS NAND]]
| class="transparent" | [[File:PMOS NOR corr.png|100px|PMOS NOR]]
| class="transparent" |
| class="transparent" | [[File:PMOS OR gate.png|100px|PMOS OR]]
|}
====Les portes logiques de base en NMOS====
Le circuit d'une porte NON en technologie NMOS est illustré ci-dessous. Le principe de ce circuit est similaire au CMOS, avec quelques petites différences. Si on envoie un 0 sur la grille du transistor, il s'ouvre et connecte la sortie à la tension d'alimentation à travers la résistance. À l'inverse, quand on met un 1 sur la grille, le transistor se ferme et la sortie est reliée à la masse, donc mise à 0. Le résultat est bien un circuit inverseur.
{|class="wikitable flexible"
|[[File:NMOS NOT.svg|class=transparent|Porte NON NMOS.]]
|[[File:Not.PNG|class=transparent|Porte NON NMOS : fonctionnement.]]
|}
La porte NOR est similaire à la porte NON, si ce n'est qu'il y a maintenant deux transistors en parallèle. Si l'une des grilles est mise à 1, son transistor se fermera et la sortie sera mise à 0. Par contre, quand les deux entrées sont à 0, les transistors sont tous les deux ouverts, et la sortie est mise à 1. Le comportement obtenu est bien celui d'une porte NOR.
{|class="wikitable flexible"
|[[File:NMOS NOR.png|NMOS-NOR-gate]]
|[[File:Funktionsprinzip eines NOR-Gatters.png|class=transparent|Fonctionnement d'une porte NOR NMOS.]]
|}
La porte NAND fonctionne sur un principe similaire au précédent, si ce n'est qu'il faut que les deux grilles soient à zéro pour obtenir une sortie à 1. Pour mettre la sortie à 0 quand seulement les deux transistors sont ouverts, il suffit de les mettre en série, comme dans le schéma ci-dessous. Le circuit obtenu est bien une porte NAND.
{|class="wikitable flexible"
|[[File:NMOS NAND.svg|class=transparent|centre|NMOS-NAND-gate]]
|[[File:Funktionsprinzip eines NAND-Gatters.png|class=transparent|centre|Funktionsprinzip eines NAND-Gatters]]
|}
===Les avantages et inconvénients des technologies CMOS, PMOS et NMOS===
La technologie PMOS et NMOS ne sont pas totalement équivalentes, niveau performances. Ces technologies se distinguent sur plusieurs points : la vitesse des transistors et leur consommation énergétique.
La vitesse des circuits NMOS/PMOS/CMOS dépend des transistors eux-mêmes. Les transistors PMOS sont plus lents que les transistors NMOS, ce qui fait que les circuits NMOS sont plus rapides que les circuits PMOS. Les circuits CMOS ont une vitesse intermédiaire, car ils contiennent à la fois des transistors NMOS et PMOS.
Pour la consommation électrique, les résistances sont plus goumandes que les transistors. En PMOS et NMOS, la résistance est traversée par du courant en permanence, peu importe l'état des transistors. Et résistance traversée par du courant signifie consommation d'énergie, dissipée sous forme de chaleur par la résistance. Il s'agit d'une perte sèche d'énergie, une consommation d'énergie inutile. En CMOS, l'absence de résistance fait que la consommation d'énergie est liée aux transistors, et celle-ci est beaucoup plus faible que pour une résistance.
Les transistors PMOS sont plus simples à fabriquer que les NMOS, ils sont plus simples à sortir d'usine. Les premiers processeurs étaient fabriqués en logique PMOS, plus simple à fabriquer. Puis, une fois la fabrication des circuits NMOS maitrisée, les processeurs sont tous passés en logique NMOS du fait de sa rapidité. La logique CMOS a mis du temps à remplacer les logiques PMOS et NMOS, car il a fallu maitriser les techniques pour mettre à la fois des transistors NMOS et PMOS sur la même puce. Les premières puces électroniques étaient fabriquées en PMOS ou en NMOS, parce qu'on n’avait pas le choix. Mais une fois la technologie CMOS maitrisée, elle s'est imposée en raison de deux gros avantages : une meilleure fiabilité (une meilleure tolérance au bruit électrique), et une consommation électrique plus faible.
==La logique dynamique MOS==
La '''logique dynamique''' permet de créer des portes logiques ou des bascules d'une manière assez intéressante. Et aussi étonnant que cela puisse paraître, le signal d’horloge est alors utilisé pour fabriquer des circuits combinatoires !
===Un transistor MOS peut servir de condensateur===
Les technologies CMOS conventionnelles mettent la sortie d'une porte logique à 0/1 en la connectant à la tension d'alimentation ou à la masse. La logique ''pass transistor'' transfère la tension et le courant de l'entrée vers la sortie. Dans les deux cas, la sortie est connectée directement ou indirectement à la tension d'alimentation quand on veut lui faire sortie un 1. Avec la logique dynamique, ce n'est pas le cas. La sortie est maintenue à 0 ou à 1 en utilisant un réservoir d'électron qui remplace la tension d'alimentation.
En électronique, il existe un composant qui sert de réservoir à électricité : il s'agit du '''condensateur'''. On peut le charger en électricité, ou le vider pour fournir un courant durant une petite durée de temps. Par convention, un condensateur stocke un 1 s'il est rempli, un 0 s'il est vide. L'intérieur d'un condensateur est formé de deux couches de métal conducteur, séparées par un isolant électrique. Les deux plaques de conducteur sont appelées les armatures du condensateur. C'est sur celles-ci que les charges électriques s'accumulent lors de la charge/décharge d'un condensateur. L'isolant empêche la fuite des charges d'une armature à l'autre, ce qui permet au condensateur de fonctionner comme un réservoir, et non comme un simple fil.
Il est possible de fabriquer un pseudo-condensateur avec un transistor MOS. En effet, tout transistor MOS a un pseudo-condensateur caché entre la grille et la liaison source-drain. Pour comprendre ce qui se passe dans ce transistor de mémorisation, il faut savoir ce qu'il y a dans un transistor CMOS. À l'intérieur, on trouve une plaque en métal appelée l'armature, un bout de semi-conducteur entre la source et le drain, et un morceau d'isolant entre les deux. L'ensemble forme donc un condensateur, certes imparfait, qui porte le nom de capacité parasite du transistor. Suivant la tension qu'on envoie sur la grille, l'armature va se remplir d’électrons ou se vider, ce qui permet de stocker un bit : une grille pleine compte pour un 1, une grille vide compte pour un 0.
[[File:Transistor CMOS - 1.png|centre|vignette|upright=2|Anatomie d'un transistor CMOS]]
L'utilisation de transistors MOS comme condensateur n'est pas spécifique à la logique dynamique. Certains mémoires RAM le font, comme nous le verrons dans le chapitre sur les cellules mémoires. Aussi, il est intéressant d'en parler maintenant, histoire de préparer le terrain. D'ailleurs, les mémoires RAM sont remplies de logique dynamique.
===L'utilisation des pseudo-condensateurs en logique dynamique===
Un circuit conçu en logique dynamique contient un transistor est utilisé comme condensateur. Il s’insère entre la tension d'alimentation et la sortie du circuit. Son rôle est simple : lorsqu'on utilise la sortie, le condensateur se vide, ce qui place la sortie à 1. le reste du temps, le condensateur est relié à la tension d'alimentation et se charge. Un circuit en logique dynamique effectue son travail en deux phases : une phase d'inactivité où il remplit ses condensateurs, et une phase où sa sortie fonctionne. Les deux phases sont appelées la '''phase de précharge''' et la '''phase d'évaluation'''. La succession de ces deux phases est réalisée par le signal d'horloge : la première pahse a lieu quand le signal d'horloge est à 1, l'autre quand il est à 0.
====Une porte NAND en logique dynamique CMOS====
Voici un exemple de porte NAND en logique dynamique MOS. La porte est alors réalisée avec des transistors NMOS et PMOS, le circuit ressemble à ce qu'on a en logique NMOS. En bas, on trouve les transistors NMOS pour relier la sortie au 0 volt. Mais au-dessus, on trouve un transistor CMOS qui remplace la résistance. Le fonctionnement du circuit est simple. Quand l'entrée ''clock'' est à 1, le condensateur se charge, les deux transistors NMOS sont déconnectés de la masse et le circuit est inactif. Puis, quand ''clock'' passe à 0, Le transistor PMOS se comporte en circuit ouvert, ce qui déconnecte la tension d'alimentation. Et son pseudo-condensateur se vide, ce qui fournit une tension d'alimentation de remplacement temporaire. Le transistor NMOS du bas se ferme, ce qui fait que les deux transistors A et B décident de si la sortie est connectée au 0 volt ou non. Si c'est le cas, le pseudo-condensateur se vide dans le 0 volt et la sortie est à 0. Sinon, le pseudo-condensateur se vide dans la sortie, ce qui la met à 1.
[[File:Dlnand.svg|centre|vignette|Porte NAND en logique CMOS.]]
====Une bascule D en logique dynamique CMOS====
Il est possible de créer une bascule D en utilisant la logique dynamique. L'idée est de prendre une bascule D normale, mais d'ajouter un fonctionnement en deux étapes en ajoutant des transistors/interrupteurs. Pour rappel, une bascule D normale est composée de deux inverseurs reliés l'un à l'autre en formant une boucle, avec un multiplexeur pour permettre les écritures dans la boucle.
[[File:Implémentation conceptuelle d'une bascule D.png|centre|vignette|upright=2|Implémentation conceptuelle d'une bascule D]]
[[File:Animation du fonctionnement d'une bascule.gif|vignette|upright=2|Animation du fonctionnement de la bascule précédente.]]
Le circuit final ajoute deux transistors entre les inverseurs tête-bêche. Les transistors en question sont reliés à l'horloge, l'un étant ouvert quand l'autre est fermé. Grâce à eux, le bit mémorisé circule d'un inverseur à l'autre : il est dans le premier inverseur quand le signal d'horloge est à 1, dans l'autre inverseur quand il est à 0 (en fait son inverse, comme vous l'aurez compris). Le tout est illustré ci-contre. Cette implémentation a été utilisée autrefois, notamment dans le processeur Intel 8086.
[[File:Bascule D en logique Dynamique, avec entrée Enable.png|centre|vignette|upright=2|Bascule D en logique Dynamique, avec entrée Enable]]
Il existe une variante très utilisée, qui permet de remplacer le multiplexeur par un circuit légèrement plus simple. Avec elle, on a deux entrées pour commander la bascule, et non une seule entrée Enable. L'entrée Enable autorise les écriture, l'entrée Hold ferme la boucle qui relie la sortie du second inverseur au premier. Chaque entrée est associé à un transistor/interrupteur. Le transistor sur lequel on envoie l'entrée Enable se ferme uniquement lors des écritures et reste fermé sinon. A l'inverse, le transistor relié au signal Hold est fermé en permanence, sauf lors des écritures. En clair, les deux signaux sont l'inverse l'un de l'autre. Il permet de fermer le circuit, de bien relier les deux inverseurs en tête-bêche, sauf lors des écritures. On envoie donc l'inverse de l'entrée Enable sur ce transistor.
[[File:Bascule D en logique dynamique.png|centre|vignette|upright=2|Bascule D en logique dynamique]]
Une manière de comprendre le circuit précédent est de le comparer à celui avec le multiplexeur. Le multiplexeur est composé d'une porte NON et de deux transistors. Il se trouve que les deux transistors en question sont placés au même endroit que les transistors connectés aux signaux Hold et Enable. En prenant retirant la porte NON du multiplexeur, on se retrouve avec le circuit. Au lieu de prendre un Signal Enable qui commande les deux transistors, ce qui demande d'ajouter une porte NON vu que les deux transistors doivent faire l'inverse l'un de l'autre, on se contente d'envoyer deux signaux séparés pour commander chaque transistor indépendamment.
===Avantages et inconvénients===
Les circuits en logique dynamique sont opposés aux circuits en logique statique, ces derniers étant les circuits CMOS, PMOS, NMOS ou TTL vu jusqu'à présent. Les circuits dynamiques et statiques ont des différences notables, ainsi que des avantages et inconvénients divers. Si on devait résumer :
* la logique dynamique utilise généralement un peu plus de transistors qu'un circuit CMOS normal ;
* la logique dynamique est souvent très rapide par rapport à la concurrence, car elle n'utilise que des transistors NMOS, plus rapides ;
* la consommation d'énergie est généralement supérieure comparé au CMOS.
Un désavantage de la logique dynamique est qu'elle utilise plus de transistors. On économise certes des transistors MOS, mais il faut rajouter les transistors pour déconnecter les transistors NMOS de la masse (0 volt). Le second surcompense le premier.
Un autre désavantage est que le signal d'horloge ne doit pas tomber en-dessous d'une fréquence minimale. Avec une logique statique, on a une fréquence maximale, mais pas de fréquence minimale. Avec un circuit statique peut réduire la fréquence d'un circuit pour économiser de l'énergie, pour améliorer sa stabilité, et de nombreux processeurs modernes ne s'en privent pas. On peut même stopper le signal d'horloge et figer le circuit, ce qui permet de le mettre en veille, d'en stopper le fonctionnement, etc. Impossible avec la logique dynamique, qui demande de ne pas tomber sous la fréquence minimale. Cela a un impact sur la consommation d'énergie, sans compter que cela se marie assez mal avec certaines applications. Un processeur moderne ne peut pas être totalement fabriqué en logique dynamique, car il a besoin d'être mis en veille et qu'il a besoin de varier sa fréquence en fonction des besoins.
Le dernier désavantage implique l'arbre d'horloge, le système d'interconnexion qui distribue le signal d'horloge à toutes les bascules d'un circuit. L'arbre d'horloge est beaucoup plus compliqué avec la logique dynamique qu'avec la logique statique. Avec la logique statique, seules les bascules doivent recevoir le signal d'horloge, avec éventuellement quelques rares circuits annexes. Mais avec la logique dynamique, toutes les portes logiques doivent recevoir le signal d'horloge, ce qui rend la distribution de l'hrologe beaucoup plus compliquée. C'est un point qui fait que la logique dynamique est assez peu utilisée, et souvent limitée à quelques portions bien précise d'un processeur.
==La logique TTL : un apercu rapide==
Tous ce que nous avons vu depuis le début de ce chapitre porte sur les transistors MOS et les technologies associées. Mais les transistors MOS n'ont pas été les premiers inventés. Ils ont été précédés par les '''transistors bipolaires'''. Nous ne parlerons pas en détail du fonctionnement d'un transistor bipolaire, car celui-ci est extraordinairement compliqué. Cependant, nous devons parler rapidement de la logique TTL, qui permet de fabriquer des portes logiques avec ces transistors bipolaires. Là encore, rassurez-vous, nous n'allons pas voir comment fabriquer des portes logiques en logique TTL, cela serait trop compliqué, sans compter que le but n'est pas de faire un cours d'électronique. Mais nous devons fait quelques remarques et donner quelques explications superficielles.
La raison à cela est double. La première raison est que certains circuits présents dans les mémoires RAM sont fabriqués avec des transistors bipolaires. C'est notamment le cas des amplificateurs de lecture ou d'autres circuits de ce genre. De tels circuits ne peuvent pas être implémentés facilement avec des transistors CMOS et nous expliquerons rapidement pourquoi dans ce qui suit. La seconde raison est que ce cours parlera occasionnellement de circuits anciens et qu'il faut quelques bases sur le TTL pour en parler.
Dans la suite du cours, nous verrons occasionnellement quelques circuits anciens, pour la raison suivante : ils sont très simples, très pédagogiques, et permettent d'expliquer simplement certains concepts du cours. Rien de mieux que d'étudier des circuits réels pour donner un peu de chair à des explications abstraites. Par exemple, pour expliquer comment fabriquer une unité logique de calcul bit à bit, je pourrais utiliser l'exemple du Motorola MC14500B, un processeur 1 bit qui est justement une unité logique sous stéroïdes. Ou encore, dans le chapitre sur les circuits additionneurs, je parlerais du circuit additionneur présent dans l'Intel 8008 et dans l'Intel 4004, les deux premiers microprocesseurs commerciaux. Malheureusement, malgré leurs aspects pédagogiques indéniables, ces circuits ont le défaut d'être des circuits TTL. Ce qui est intuitif : les circuits les plus simples ont été inventés en premier et utilisent du TTL plus ancien. Beaucoup de ces circuits ont été inventés avant même que le CMOS ou même les transistors MOS existent. D'où le fait que nous devons faire quelques explications mineures sur le TTL.
===Les transistors bipolaires===
Les '''transistors bipolaires''' ressemblent beaucoup aux transistors MOS. Les transistors bipolaires ont trois broches, appelées le collecteur, la base et l'émetteur. Notez que ces trois termes sont différents de ceux utilisés pour les transistors MOS, où on parle de la grille, du drain et de la source.
Là encore, comme pour les transistors PMOS et NMOS, il existe deux types de transistors bipolaires : les NPN et les PNP. Là encore, il est possible de fabriquer une puce en utilisant seulement des NPN, seulement des PNP, ou en mixant les deux. Mais les ressemblances s'arrêtent là. La différence entre PNP et NPN tient dans la manière dont les courants entrent ou sortent du transistor. La flèche des symboles ci-dessous indique si le courant rentre ou sort par l'émetteur : il rentre pour un PNP, sort pour un NPN. Dans la suite du cours, nous n'utiliserons que des transistors NPN, les plus couramment utilisés.
{|
|[[File:BJT PNP symbol.svg|vignette|BJT PNP]]
|[[File:BJT NPN symbol.svg|vignette|BJT NPN]]
|}
Plus haut nous avons dit que les transistors CMOS sont des interrupteurs. La réalité est que tout transistor peut être utilisé de deux manières : soit comme interrupteur, soit comme amplificateur de tension/courant. Pour simplifier, le transistor bipolaire NPN prend en entrée un courant sur sa base et fournit un courant amplifié sur l'émetteur. Pour s'en servir comme amplificateur, il faut fournir une source de courant sur le collecteur. Le fonctionnement exact est cependant plus compliqué.
[[File:Transistor bipolaire, explication simplifiée de son fonctionnement.png|centre|vignette|upright=1.5|Transistor bipolaire, explication simplifiée de son fonctionnement]]
Les transistors bipolaires sont de bons amplificateurs, mais de piètres interrupteurs. A l'inverse, les transistors CMOS sont généralement de bons interrupteurs, mais de moyens amplificateurs. Pour des circuits numériques, la fonction d'interrupteur est clairement plus adaptée, car elle-même binaire (un transistor est fermé ou ouvert : deux choix possibles). Aussi, les circuits modernes privilégient des transistors CMOS aux transistors bipolaires. A l'inverse, la fonction d'amplification est adaptée aux circuits analogiques.
C'est pour ça que nous rencontrerons les transistors bipolaires soit dans des portions de l'ordinateur qui sont au contact de circuits analogiques. Pensez par exemple aux cartes sons ou au vieux écrans cathodiques, qui gèrent des signaux analogiques (le son pour la carte son, les signaux vidéo analogique pour les vieux écrans). On les croisera aussi dans les mémoires DRAM, dont la conception est un mix entre circuits analogiques et numériques. Nous les croiserons aussi dans de vieux circuits antérieurs aux transistors MOS. Les anciens circuits faisaient avec les transistors bipolaires car ils n'avaient pas le choix, mais ils ont été partiellement remplacés dès l'apparition des transistors CMOS.
===Les portes logiques complexes en TTL===
Le détail le plus important qui nous concernera dans la suite du cours est le suivant : on peut créer des portes logiques exceptionnellement complexes en TTL. Pour comprendre pourquoi, sachez qu'il existe des transistors bipolaires qui possèdent plusieurs émetteurs. Ils sont très utilisés pour fabriquer des portes logiques à plusieurs entrées. Les émetteurs correspondent alors à des entrées de la porte logique. Ainsi, une porte logique à plusieurs entrées se fait non pas en ajoutant des transistors, comme c'est le cas avec les transistors MOS, mais en ajoutant un émetteur sur un transistor. Cela permet à une porte NAND à trois entrées de n'utiliser que deux transistors bipolaires, au lieu de quatre transistors MOS.
[[File:Multiemitter Transistor.svg|centre|vignette|upright=1|Transistor bipolaire avec plusieurs émetteurs.]]
De plus, là où les logiques PMOS/NMOS/CMOS permettent de fabriquer les portes de base que nous avons précédemment, elles ne peuvent pas faire plus. Au pire, on peut implémenter des portes ET/OU/NAND/NOR à plusieurs entrées, mais pas plus. En TTL, on peut parfaitement créer des portes de type ET/OU/NON ou OU/ET/NON, avec seulement quatre transistors. Par exemple, une '''porte ET/OU/NON''' de type 2-2 entrées (pour rappel, qui effectue un ET par paire d’entrée puis fait un NOR entre le résultat des deux ET) est bien implémenté en une seule porte logique, pas en enchainant deux ou trois portes à la suite.
[[File:TTL AND-OR-INVERT 1961.png|centre|vignette|upright=2|TTL AND-OR-INVERT 1961]]
===Les désavantages et avantages des circuits TTL===
Pour résumer, le TTL à l'avantage de pouvoir fabriquer des portes logiques avec peu de transistors comparé au CMOS, surtout pour les portes logiques complexes. Et autant vous dire que les concepteurs de puce électroniques ne se gênaient pas pour utiliser ces portes complexes, capables de fusionner 3 à 5 portes en une seule : les économies de transistors étaient conséquentes.
Et pourtant, les circuits TTL étaient beaucoup plus gros que leurs équivalents CMOS. La raison est qu'un transistor bipolaire prend beaucoup de place : il est environ 10 fois plus gros qu'un transistor MOS. Autant dire que les économies réalisées avec des portes logiques complexes ne faisaient que compenser la taille énorme des transistors bipolaires. Et encore, cette compensation n'était que partielle, ce qui fait que les circuits PMOS/NMOS/CMOS se miniaturisent beaucoup plus facilement. Un avantage pour le transistor MOS !
De plus, les schémas précédents montrent que les portes logiques en TTL utilisent une résistance, elle aussi difficile à miniaturiser. Et cette résistance est parcourue en permanence par un courant, ce qui fait qu'elle consomme de l'énergie et chauffe. C'est la même chose en logique NMOS et PMOS, ce qui explique leur forte consommation d'énergie. Les circuits TTL ont donc le même problème.
[[File:TTL Input voltage.svg|vignette|upright=0.5|TTL voltage.]]
Un autre défaut est lié à la une tension d'alimentation. Les circuits TTL utilisent une tension d'alimentation de 5 volts, alors que les circuits CMOS ont une tension d'alimentation beaucoup plus variable. Les circuits CMOS vont de 3 volts à 18 volts pour les circuits commerciaux, avec des tensions de 1 à 3 volts pour les circuits optimisés. Les circuits CMOS sont généralement bien optimisés et utilisent une tension d'alimentation plus basse que les circuits TTL, ce qui fait qu'ils consomment moins d'énergie et de courant.
De plus, rappelons que coder un zéro demande que la tension soit sous un seuil, alors que coder un 1 demande qu'elle dépasse un autre seuil, avec une petite marge de sécurité entre les deux. Les seuils en question sont indiqués dans le diagramme ci-dessous. Il s'agit des seuils VIH et VIL. On voit que sur les circuits TTL, la marge de sécurité est plus faible qu'avec les circuits CMOS. De plus, les marges sont bien équilibrées en CMOS, à savoir que la marge de sécurité est en plein milieu entre la tension max et le zéro volt. Avec le TTL normal, la marge de sécurité est très proche du zéro volt. Un 1 est codé par une tension entre 2 et 5 volts en TTL ! Une version améliorée du TTL, le LVTTL, corrige ce défaut. Elle baisse la tension d'alimentation à 3,3 Volts, mais elle demande des efforts de fabrication conséquents.
[[File:Niveaux logiques CMOS-TTL-LVTTL.png|centre|vignette|upright=2|Niveaux logiques CMOS-TTL-LVTTL]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de conversion analogique-numérique
| prevText=Les circuits de conversion analogique-numérique
| next=Les circuits intégrés
| nextText=Les circuits intégrés
}}
{{autocat}}
</noinclude>
gyzbpgggi6ss49k2o0gfv79sw3ikyts
744784
744783
2025-06-15T01:52:18Z
Mewtow
31375
/* La pass transistor logic utilise des multiplexeurs 2 vers 1 */
744784
wikitext
text/x-wiki
Dans le chapitre précédent, nous avons abordé les portes logiques. Dans ce chapitre, nous allons voir qu'elles sont fabriquées avec des composants électroniques que l'on appelle des '''transistors'''. Ces derniers sont reliés entre eux pour former des circuits plus ou moins compliqués. Pour donner un exemple, sachez que les derniers modèles de processeurs peuvent utiliser près d'un milliard de transistors.
==Les transistors MOS==
[[File:Transistor basic flow.svg|vignette|Un transistor est un morceau de conducteur, dont la conductivité est contrôlée par sa troisième broche/borne.]]
Les transistors possèdent trois '''broches''', des pattes métalliques sur lesquelles on connecte des fils électriques. On peut appliquer une tension électrique sur ces broches, qui peut représenter soit 0 soit 1. Sur ces trois broches, il y en a deux entre lesquelles circule un courant, et une troisième qui commande le courant. Le transistor s'utilise le plus souvent comme un interrupteur commandé par sa troisième broche. Le courant qui traverse les deux premières broches passe ou ne passe pas selon ce qu'on met sur la troisième.
Il existe plusieurs types de transistors, mais les deux principaux sont les transistors bipolaires et les transistors MOS. De nos jours, les transistors utilisés dans les ordinateurs sont tous des '''transistors MOS'''. Les raisons à cela sont multiples, mais les plus importantes sont les suivantes. Premièrement, les transistors bipolaires sont plus difficiles à fabriquer et sont donc plus chers. Deuxièmement, ils consomment bien plus de courant que les transistors MOS. Et enfin, les transistors bipolaires sont plus gros, ce qui n'aide pas à miniaturiser les puces électroniques. Tout cela fait que les transistors bipolaires sont aujourd'hui tombés en désuétude et ne sont utilisés que dans une minorité de circuits.
===Les types de transistors MOS : PMOS et NMOS===
Sur un transistor MOS, chaque broche a un nom, nom qui est indiqué sur le schéma ci-dessous.On distingue ainsi le '''drain''', la '''source''' et la '''grille''' On l'utilise le plus souvent comme un interrupteur commandé par sa grille. Appliquez la tension adéquate et la liaison entre la source et le drain se comportera comme un interrupteur fermé. Mettez la grille à une autre valeur et cette liaison se comportera comme un interrupteur ouvert.
Il existe deux types de transistors CMOS, qui diffèrent entre autres par le bit qu'il faut mettre sur la grille pour les ouvrir/fermer :
* les transistors NMOS qui s'ouvrent lorsqu'on envoie un zéro sur la grille et se ferment si la grille est à un ;
* et les PMOS qui se ferment lorsque la grille est à zéro, et s'ouvrent si la grille est à un.
[[File:Td7bfig2.png|centre|vignette|upright=2|Illustration du fonctionnement des transistors NMOS et PMOS.]]
Voici les symboles de chaque transistor.
{|
|[[File:Transistor CMOS.png|vignette|upright=0.5|Transistor CMOS]]
|[[File:IGFET N-Ch Enh Labelled simplified.svg|vignette|upright=0.5|Transistor MOS à canal N (NMOS).]]
|[[File:IGFET P-Ch Enh Labelled simplified.svg|vignette|upright=0.5|Transistor MOS à canal P (PMOS).]]
|}
===L'anatomie d'un transistor MOS===
À l'intérieur du transistor, on trouve simplement une plaque en métal reliée à la grille appelée l'armature, un bout de semi-conducteur entre la source et le drain, et un morceau d'isolant entre les deux. Pour rappel, un semi-conducteur est un matériau qui se comporte soit comme un isolant, soit comme un conducteur, selon les conditions auxquelles on le soumet. Dans un transistor, son rôle est de laisser passer le courant, ou de ne pas le transmettre, quand il faut. C'est grâce à ce semi-conducteur que le transistor peut fonctionner en interrupteur : interrupteur fermé quand le semi-conducteur conduit, ouvert quand il bloque le courant. La commande de la résistance du semi-conducteur (le fait qu'il laisse passer ou non le courant) est réalisée par la grille, comme nous allons le voir ci-dessous.
[[File:Transistor CMOS - 1.png|centre|vignette|upright=2|Transistor CMOS]]
Suivant la tension que l'on place sur la grille, celle-ci va se remplir avec des charges négatives ou positives. Cela va entrainer une modification de la répartition des charges dans le semi-conducteur, ce qui modulera la résistance du conducteur. Prenons par exemple le cas d'un transistor NMOS et étudions ce qui se passe selon la tension placée sur la grille. Si on met un zéro, la grille sera vide de charges et le semi-conducteur se comportera comme un isolant : le courant ne passera pas. En clair, le transistor sera équivalent à un interrupteur ouvert. Si on met un 1 sur la grille, celle-ci va se remplir de charges. Le semi-conducteur va réagir et se mettre à conduire le courant. En clair, le transistor se comporte comme un interrupteur fermé.
{|
|[[File:Transistor CMOS - 3.png|vignette|upright=1.5|Transistor NMOS fermé.]]
|[[File:Transistor CMOS - 4.png|vignette|upright=1.5|Transistor NMOS ouvert.]]
|}
===La tension de seuil d'un transistor===
Le fonctionnement d'un transistor est légèrement plus complexe que ce qui a été dit auparavant. Mais pour rester assez simple, disons que son fonctionnement exact dépend de trois paramètres : la tension d'alimentation, le courant entre drain et source, et un nouveau paramètre appelé la tension de seuil.
Appliquons une tension sur la grille d'un transistor NMOS. Si la tension de grille reste sous un certain seuil, le transistor se comporte comme un interrupteur fermé. Le seuil de tension est appelé, très simplement, la '''tension de seuil'''. Au-delà de la tension de seuil, le transistor se comporte comme un interrupteur ouvert, il laisse passer le courant. La valeur exacte du courant dépend de la tension entre drain et source, soit la tension d'alimentation. Elle aussi dépend de la différence entre tension de grille et de seuil, à savoir <math>U_G - U_\text{seuil}</math>.
Le paragraphe qui va suivre est optionnel, mais détaille un peu plus le fonctionnement d'un transistor MOS. Tout ce qu'il faut comprendre est que la tension de seuil est une tension minimale pour ouvrir le transistor. Le plus important à retenir est que l'on ne peut pas baisser la tension d'alimentation sous la tension de seuil, ce qui est un léger problème en termes de consommation énergétique. Ce détail reviendra plus tard dans ce cours, quand nous parlerons de la consommation d'énergie des circuits électroniques.
Dans les cas que nous allons voir dans ce cours, la tension d'alimentation est plus grande que <math>U_G - U_\text{seuil}</math>. Le courant est alors maximal, il est proportionnel à <math>U_G - U_\text{seuil}</math>. Le transistor ne fonctionne alors pas comme un amplificateur, le courant reste le même. Si la tension d'alimentation est plus petite que <math>U_G - U_\text{seuil}</math>, le transistor est en régime linéaire : le courant de sortie est proportionnel à <math>U_G - U_\text{seuil}</math>, ainsi qu'à la tension d'alimentation. Le transistor fonctionne alors comme un amplificateur de courant, dont l'intensité de l'amplification est commandée par la tension.
[[File:MOSFET enhancement-mode n-channel en.svg|centre|vignette|upright=2.5|Relations entre tensions et courant d'un MOSFET à dopage N.]]
==La technologie CMOS==
Les portes logiques que nous venons de voir sont actuellement fabriquées en utilisant des transistors. Il existe de nombreuses manières pour concevoir des circuits à base de transistors, qui portent les noms de DTL, RTL, TLL, CMOS et bien d'autres. Les techniques anciennes concevaient des portes logiques en utilisant des diodes, des transistors bipolaires et des résistances. Mais elles sont aujourd'hui tombées en désuétudes dans les circuits de haute performance. De nos jours, on n'utilise que des logiques MOS (''Metal Oxyde Silicium''), qui utilisent des transistors MOS vus plus haut dans ce chapitre, parfois couplés à des résistances. On distingue :
* La '''logique NMOS''', qui utilise des transistors NMOS associés à des résistances.
* La '''logique PMOS''', qui utilise des transistors PMOS associés à des résistances.
* La '''logique CMOS''', qui utilise des transistors PMOS et NMOS, sans résistances.
Dans cette section, nous allons montrer comment fabriquer des portes logiques en utilisant la '''technologie CMOS'''. Avec celle-ci, chaque porte logique est fabriquée à la fois avec des transistors NMOS et des transistors PMOS. On peut la voir comme un mélange entre la technologie PMOS et NMOS. Tout circuit CMOS est divisé en deux parties : une intégralement composée de transistors PMOS et une autre de transistors NMOS. Chacune relie la sortie du circuit soit à la masse, soit à la tension d'alimentation.
[[File:Principe de la conception de circuit en technologie CMOS.png|centre|vignette|upright=2|Principe de conception d'une porte logique/d'un circuit en technologie CMOS.]]
La première partie relie la tension d'alimentation à la sortie, mais uniquement quand la sortie doit être à 1. Si la sortie doit être à 1, des transistors PMOS vont se fermer et connecter tension et sortie. Dans le cas contraire, des transistors s'ouvrent et cela déconnecte la liaison entre sortie et tension d'alimentation. L'autre partie du circuit fonctionne de la même manière que la partie de PMOS, sauf qu'elle relie la sortie à la masse et qu'elle se ferme quand la sortie doit être mise à 0
[[File:Fonctionnement d'un circuit en logique CMOS.png|centre|vignette|upright=2|Fonctionnement d'un circuit en logique CMOS.]]
Dans ce qui va suivre, nous allons étudier la porte NON, la porte NAND et la porte NOR. La porte de base de la technologie CMOS est la porte NON, les portes NAND et NOR ne sont que des versions altérées de la porte NON qui ajoutent des entrées et quelques transistors. Les autres portes, comme la porte ET et la porte OU, sont construites à partir de ces portes. Nous parlerons aussi de la porte XOR, qui est un peu particulière.
===La porte NON===
Cette porte est fabriquée avec seulement deux transistors, comme indiqué ci-dessous.
[[File:Porte NON fabriquée avec des transistors CMOS. 01.jpg|centre|vignette|upright=1|Porte NON fabriquée avec des transistors CMOS.]]
Si on met un 1 en entrée de ce circuit, le transistor du haut va fonctionner comme un interrupteur ouvert, et celui du bas comme un interrupteur fermé : la sortie est reliée au zéro volt, et vaut donc 0. Inversement, si on met un 0 en entrée de ce petit montage électronique, le transistor du bas va fonctionner comme un interrupteur ouvert, et celui du haut comme un interrupteur fermé : la sortie est reliée à la tension d'alimentation, et vaut donc 1.
[[File:Porte NON fabriquée avec des transistors CMOS - fonctionnement.png|centre|vignette|upright=2|Porte NON fabriquée avec des transistors CMOS - fonctionnement.]]
===Les portes NAND et NOR===
Passons maintenant aux portes logiques à plusieurs entrées. Pour celles-ci, on va devoir utiliser plus de transistors que pour la porte NON, ce qui demande de les organiser un minium. Une porte logique à deux entrées demande d'utiliser au moins deux transistors par entrée : un transistor PMOS et un NMOS par entrée. Rappelons qu'un transistor est associé à une entrée : l'entrée est directement envoyée sur la grille du transistor et commande son ouverture/fermeture. Pour les portes logiques à 3, 4, 5 entrées, la logiques est la même : au minimum deux transistors par entrée, un PMOS et un NMOS.
Nous allons d'abord voir le cas d'une porte NOR/NAND en CMOS. Avec elles, les transistors sont organisées de deux manières, appelées '''transistors en série''' (l'un après l'autre, sur le même fil) et '''transistors en parallèle''' (sur des fils différents). Le tout est illustré ci-dessous. Avec des transistors en série, plusieurs transistors NMOS ou deux PMOS se suivent sur le même fil, mais on ne peut pas mélanger NMOS et PMOS sur le même fil.
[[File:Transistors CMOS en série et en parallèle.png|centre|vignette|upright=2|Transistors CMOS en série et en parallèle]]
====Les portes NAND/NOR à deux entrées====
Voyons d'abord le cas des portes NAND/NOR à deux entrées. Elles utilisent deux transistors NMOS et deux PMOS.
Avec des transistors en série, deux transistors NMOS ou deux PMOS se suivent sur le même fil, mais on ne peut pas mélanger NMOS et PMOS sur le même fil. Avec des transistors en parallèle, c'est l'exact inverse. L'idée est de relier la tension d'alimentation à la sortie à travers deux PMOS transistors distincts, chacun sur son propre fil, sa propre connexion indépendante des autres. Pour la masse (0 volt), il faut utiliser deux transistors NMOS pour la relier à la sortie, avec là encore chaque transistor NMOS ayant sa propre connexion indépendante des autres. En clair, chaque entrée commande un transistor qui peut à lui seul fermer le circuit.
On rappelle deux choses : chaque transistor est associée à une entrée sur sa grille, un transistor se ferme si l'entrée vaut 0 pour des transistors PMOS et 1 pour des NMOS. Avec ces deux détails, on peut expliquer comment fonctionnent des transistors en série et en parallèle. Pour résumer, les transistors en série ferment la connexion quand toutes les entrées sont à 1 (NMOS) ou 0 (PMOS). Avec les transistors en parallèle, il faut qu'une seule entrée soit à 1 (NMOS) ou 0 (PMOS) pour que la connexion se fasse.
Une porte NOR met sa sortie à 1 si toutes les entrées sont à 0, à 0 si une seule entrée vaut 1. Pour reformuler, il faut connecter la sortie à la tension d'alimentation si toutes les entrées sont à 0, ce qui demande d'utiliser des transistors PMOS en série. Pour gérer le cas d'une seule entrée à 1, il faut utiliser deux transistors en parallèle entre la masse et la sortie. Le circuit obtenu est donc celui obtenu dans le premier schéma. Le même raisonnement pour une porte NAND donne le second schéma.
{|
|[[File:Porte NOR fabriquée avec des transistors. 02.png|centre|vignette|upright=1|Porte NOR fabriquée avec des transistors.]]
|[[File:Porte NAND fabriquée avec des transistors. 04.png|centre|vignette|upright=1|Porte NAND fabriquée avec des transistors.]]
|}
Leur fonctionnement s'explique assez bien si on regarde ce qu'il se passe en fonction des entrées. Suivant la valeur de chaque entrée, les transistors vont se fermer ou s'ouvrir, ce qui va connecter la sortie soit à la tension d'alimentation, soit à la masse.
Voici ce que cela donne pour une porte NAND :
[[File:Porte NAND fabriquée avec des transistors - Fonctionnement.png|centre|vignette|upright=2|Porte NAND fabriquée avec des transistors.]]
Voici ce que cela donne pour une porte NOR :
[[File:Porte NOR fabriquée avec des transistors - Fonctionnement.png|centre|vignette|upright=2|Porte NOR fabriquée avec des transistors.]]
====Les portes NAND/NOR/ET/OU à plusieurs entrées====
Les portes NOR/NAND à plusieurs entrées sont construites à partir de portes NAND/NOR à deux entrées auxquelles on rajoute des transistors. Il y a autant de transistors en série que d'entrée, pareil pour les transistors en parallèle. Leur fonctionnement est similaire à leurs cousines à deux entrées. Les portes ET et OU à plusieurs entrées sont construites à partie de NAND/NOR suivies d'une porte NON.
{|
|[[File:NAND plusieurs entrées.png|vignette|NAND plusieurs entrées]]
|[[File:NOR plusieurs entrées.png|vignette|NOR plusieurs entrées]]
|}
En théorie, on pourrait créer des portes avec un nombre arbitraire d'entrées avec cette méthode. Cependant, au-delà d'un certain nombre de transistors en série/parallèle, les performances s'effondrent rapidement. Le circuit devient alors trop lent, sans compter que des problèmes purement électriques surviennent. En pratique, difficile de dépasser la dizaine d'entrées. Dans ce cas, les portes sont construites en assemblant plusieurs portes NAND/NOR ensemble. Et faire ainsi marche nettement mieux pour fabriquer des portes ET/OU que pour des portes NAND/NOR.
====Les portes ET/OU sont fabriquées à partir de NAND/NOR en CMOS====
En logique CMOS, les portes logiques ET et OU sont construites en prenant une porte NAND/NOR et en mettant une porte NON sur sa sortie. Il est théoriquement possible d'utiliser uniquement des transistors en série et en parallèle, mais cette solution utilise plus de transistors.
{|
|[[File:CMOS AND Layout.svg|vignette|Porte ET en CMOS]]
|[[File:CMOS OR.svg|vignette|Porte OU en CMOS]]
|}
Pour ce qui est des portes ET/OU avec beaucoup d'entrées, il est fréquent qu'elles soit construites en combinant plusieurs portes ET/OU moins complexes. Par exemple, une porte ET à 32 entrées sera construite à partir de portes à seulement 4 ou 5 entrées. Il existe cependant une alternative qui se marie nettement mieux avec la logique CMOS. Rappelons qu'en logique CMOS, les portes NAND et NOR sont les portes à plusieurs entrées les plus simples à fabriquer. L'idée est alors de combiner des portes NAND/NOR pour créer une porte ET/OU.
Voici la comparaison entre les deux solutions pour une porte ET :
{|
|[[File:12 input AND gate via cascade of AND gates.svg|vignette|ET plusieurs entrées]]
|[[File:12-input AND gate from NAND and NOR.svg|vignette|ET plusieurs entrées]]
|}
Voici la comparaison entre les deux solutions pour une porte OU :
{|
|[[File:12-input OR gate via cascade of OR gates.svg|vignette|OU plusieurs entrées]]
|[[File:12-input OR gate via NOR and NAND gates.svg|vignette|OU plusieurs entrées]]
|}
D'autres portes mélangent transistors en série et en parallèle d'une manière différente. Les portes ET-OU-NON et OU-ET-NON en sont un bon exemple.
===Une méthode générale===
Il existe une méthode générale pour créer des portes logiques à deux entrées. Avec elle, il faut repartir du montage avec deux transistors NMOS/PMOS en série. En théorie, il permet de relier la sortie à la tension d'alimentation/zéro volt si toutes les entrées sont à 0 (PMOS) ou 1 (NMOS). L'idée est de regarder ce qui se passe si on fait précéder l'entrée d'un transistor par une porte NON. Pour deux transistors, cela fait 4 possibilités, 8 au total si on fait la différence entre PMOS et NMOS. Voici les valeurs d'entrées qui ferment le montage à transistor en série, suivant l’endroit où on place la porte NON.
[[File:Transistors CMOS en série.png|centre|vignette|upright=2|Transistors CMOS en série]]
Mine de rien, avec ces 8 montages de base, on peut créer n'importe quelle porte logique à deux entrées. Il faut juste se souvenir que d'après les règles du CMOS, les deux transistors PMOS se placent entre la tension d'alimentation et la sortie, et servent à mettre la sortie à 1. Pour les deux transistors NMOS, ils sont reliés à la masse et mettent la sortie à 0. Pour mieux comprendre, prenons l'exemple d'une porte XOR.
Appliquons la méthode que je viens d'expliquer avec une porte XOR. Le résultat est sous-optimal, mieux vaut fabriquer une porte XOR en combinant d'autres portes logiques, mais c'est pour l'exemple. L'idée est très simple : on prend la table de vérité de la porte logique, et on associe deux transistors en série pour chaque ligne. Regardons d'abord la table de vérité ligne par ligne :
{|class="wikitable"
|-
!Entrée 1!!Entrée 2!!Sortie
|-
||0||0||0
|-
||0||1||1
|-
||1||0||1
|-
||1||1||0
|}
La première ligne a ses deux entrées à 0 et sort un 0. La sortie est à 0, ce qui signifie qu'il faut regarder sur la ligne des transistors NMOS, qui connectent la sortie à la masse. Le montage qui se ferme quand les deux entrées sont à 0 est celui tout en bas à droite du tableau précédent, à savoir deux transistors NMOS avec deux portes NON.
Les deux lignes du milieu ont une entrée à 0 et une à 1, et leur sortie à 1. La sortie à 1 signifie qu'il faut regarder sur la ligne des transistors PMOS, qui connectent la tension d'alimentation à la sortie. Les deux montages avec deux entrées différentes sont les deux situés au milieu, avec deux transistors PMOS et une porte logique.
La dernière ligne a ses deux entrées à 1 et sort un 0. La sortie est à 0, ce qui signifie qu'il faut regarder sur la ligne des transistors NMOS, qui connectent la sortie à la masse. Le montage qui se ferme quand les deux entrées sont à 1 est celui tout en bas à gauche du tableau précédent, à savoir deux transistors NMOS seuls.
En combinant ces quatre montages, on trouve le circuit suivant. Notons qu'il n'y a que deux portes NON marquées en vert et bleu : on a juste besoin d'inverser la première entrée et la seconde, pas besoin de portes en plus. Les portes NOn sont en quelque sorte partagées entre les transistors PMOS et NMOS.
[[File:Cmos xor.svg|centre|vignette|upright=1|class=transparent|Porte XOR en logique CMOS.]]
Si les deux entrées sont à 1, alors les deux transistors en bas à gauche vont se fermer et connecter la sortie au 0 volt, les trois autres groupes ayant au moins un transistor ouvert. Si les deux entrées sont à 0, alors les deux transistors en bas à droite vont se fermer et connecter la sortie au 0 volt, les autres quadrants ayant au moins un transistor ouvert. Et pareil quand les deux bits sont différents : un des deux quadrants aura ses deux transistors fermés, alors que les autres auront au moins un transistor ouvert, ce qui connecte la sortie à la tension d'alimentation.
On peut construire la porte NXOR sur la même logique. Et toutes les portes logiques peuvent se construire avec cette méthode. Le nombre de transistors est alors le même : on utilise 12 transistors au total : 4 paires de transistors en série, 4 transistors en plus pour les portes NON. Que ce soit pour la porte XOR ou NXOR, on économise beaucoup de transistors comparés à la solution naïve, qui consiste à utiliser plusieurs portes NON/ET/OU. Si on ne peut pas faire mieux dans le cas de la porte XOR/NXOR, sachez cependant que les autres portes construites avec cette méthode utilisent plus de transistors que nécessaire. De nombreuses simplifications sont possibles, comme on le verra plus bas.
Dans les faits, la méthode n'est pas utilisée pour les portes XOR. A la place, les portes XOR sont construites à base d'autres portes logiques plus simples, comme des portes NAND/NOR/ET/OU. Le résultat est que l'on a un circuit à 10 transistors, contre 12 avec la méthode précédente.
[[File:CMOS10TrXOR.svg|centre|vignette|Porte XOR en CMOS en 10 transistors.]]
===Les circuits plus complexes (''full adder'', ...)===
Il est possible de fusionner plusieurs portes ET-OU-NON en un seul circuit à transistors CMOS, ce qui permet des simplifications assez impressionnantes. Pour donner un exemple, le schéma suivant compare l'implémentation d'un circuit qui fait un ET entre les deux premières entrées, puis un NOR entre le résultat du ET et la troisième entrée. L'implémentation à droite du schéma avec une porte ET et une porte NOR prend 10 transistors. L'implémentation la plus simple, à gauche du schéma, prend seulement 6 transistors.
[[File:AOI21 complex vs standard gates.svg|centre|vignette|upright=1.5|Porte ET-OU-NON à trois entrées (de type 2-1) à gauche, contre la combinaison de plusieurs portes à droite.]]
Une conséquence est que des circuits assez complexes gagnent à être fabriqués directement avec des transistors. Prenons l'exemple de l'additionneur complet. Une implémentation naïve, avec 5 portes logiques, utilise beaucoup de transistors. Deux portes XOR, deux portes OU et une porte ET, cela dépasse la trentaine de transistors. Faisons le compte : 10 transistors par porte XOR, 6 pour les trois autres portes, cela fait 38 transistors. Les additionneurs des processeurs modernes sont optimisés directement au niveau des transistors, pour leur permettre d'économiser des transistors. Par exemple, l'implémentation suivante en utilise seulement 24 !
[[File:Inverting full adder CMOS 24T.svg|centre|vignette|upright=1.5|Additionneur complet fabriqué avec 24 transistors.]]
Et c'est sans compter que l'additionneur complet naïf n'est pas forcément le top du top en termes de performances. Là encore, une implémentation avec des transistors peut être optimisée pour être plus rapide, notamment au niveau du calcul de la retenue, ou au contraire d'économiser des transistors. Tout dépend de l'objectif visé, certains circuit optimisant à fond pour la vitesse, d'autres pour le nombre de transistors, d'autres font un compromis entre les deux. Les circuits de ce genre sont très nombreux, trop pour qu'on puisse les citer.
==La ''pass transistor logic''==
La '''''pass transistor logic''''' est une forme particulière de technologie CMOS, une version non-conventionnelle. Avec le CMOS normal, la porte de base est la porte NON. En modifiant celle-ci, on arrive à fabriquer des portes NAND, NOR, puis les autres portes logiques. Les transistors sont conçus de manière à connecter la sortie, soit la tension d'alimentation, soit la masse. Avec la ''pass transistor logic'', le montage de base est un circuit interrupteur, qui connecte l'entrée directement sur la sortie. Le circuit interrupteur n'est autre que les portes à transmission vues il y a quelques chapitres.
La ''pass transistor logic'' a été utilisée dans des processeurs commerciaux, comme dans l'ARM1, le premier processeur ARM. Sur l'ARM1, les concepteurs ont décidé d'implémenter certains circuits avec des multiplexeurs. La raison n'est pas une question de performance ou d'économie de transistors, juste que c'était plus pratique à fabriquer, sachant que le processeur était le premier CPU ARM de l'entreprise.
Dans la suite du cours, nous verrons quelques circuits qui utilisent cette technologie, mais ils seront rares. Nous l'utiliserons quand nous parlerons des additionneurs, ou les multiplexeurs, guère plus. Mais il est sympathique de savoir que cette technologie existe.
===La porte à transmission===
Le circuit de base est une '''porte à transmission''', à savoir une porte logique qui agit comme une sorte d'interrupteur, qui s'ouvre ou se ferme suivant ce qu'on met sur l'entrée de commande. Le circuit peut soit connecter l'entrée et la sortie, soit déconnecter la sortie de l'entrée. Le choix entre les deux dépend de l’entrée de commande.
Intuitivement, on se dit qu'une transistor fonctionne déjà comme un interrupteur, mais une porte à transmission est construit avec deux transistors. La raison la plus intuitive est que la logique CMOS fait que tout transistor PMOS doit être associé à un transistor NMOS et réciproquement. Mais une autre raison, plus importante, est que les transistors NMOS et PMOS ne sont pas des interrupteurs parfaits. Les NMOS laissent passer les 0, mais laissent mal passer les 1 : la tension en sortie, pour un 1, est atténuée. Et c'est l'inverse pour les PMOS, qui laissent bien passer les 1 mais fournissent une tension de sortie peut adéquate pour les 0. Donc, deux transistors permettent d'obtenir une tension de sortie convenable. Le montage de base est le suivant :
[[File:CMOS transmission gate.PNG|centre|vignette|upright=1|CMOS Transmission gate]]
Vous remarquerez que le circuit est fondamentalement différent des circuits précédents. Les précédents connectaient la sortie soit à la tension d'alimentation, soit à la masse. Ici, la sortie est connectée sur l'entrée, rien de plus. Il n'y a pas d'alimentation électrique ni de contact à la masse. Retenez ce point, il sera important par la suite.
Les deux entrées A et /A sont l'inverse l'une de l'autre, ce qui fait qu'il faut en théorie rajouter une porte NON CMOS normale, pour obtenir le circuit complet. Mais dans les faits, on arrive souvent à s'en passer. Ce qui fait que la porte à transmission est définie comme étant le circuit à deux transistors précédents.
===Les multiplexeurs 2 vers 1 en ''pass transistor logic''===
Dans les chapitres précédents, nous avions vu que les portes à transmission sont assez peu utilisées. Nous ne nous en sommes servies que dans de rares cas, mais l'un d'entre eux va nous intéresser : les multiplexeurs et les démultiplexeurs. Pour rappel, il est assez simple de fabriquer un multiplexeur 2 vers 1 en utilisant des portes à transmission. L'idée est de relier chaque entrée à la sortie par l'intermédiaire d'une porte à transmission. Quand l'une sera ouverte, l'autre sera fermée. Le résultat n'utilise que deux portes à transmission et une porte NON. Voici le circuit qui en découle :
[[File:Multiplexeur fabriqué avec des portes à transmission et-ou des tampons trois-états.png|centre|vignette|upright=1.5|Multiplexeur fabriqué avec des portes à transmission]]
En utilisant les portes à transmission CMOS vues plus haut, on obtient le circuit suivant :
[[File:Multiplexeur fabriqué avec des portes à transmission.png|centre|vignette|upright=1.5|Multiplexeur fabriqué avec des portes à transmission CMOS.]]
===La ''pass transistor logic'' utilise des multiplexeurs 2 vers 1===
La ''pass transistor logic'' implémente les portes logiques d'une manière assez étonnante : les portes logiques sont basées sur un multiplexeur 2 vers 1 amélioré ! L'idée est d'émuler une porte logique à deux entrées avec un multiplexeur 2 vers 1. Et intuitivement, vous vous dites que les deux entrées de la porte logique correspondent aux deux entrées de donnée du multiplexeur. Mais non, c'est une erreur ! En réalité, un bit d'entrée est envoyé sur l'entrée de commande, et l'autre bit sur une entrée de donnée du multiplexeur. Suivant ce qu'on met sur la seconde entrée du multiplexeur, on obtient une porte ET, OU, XOR, etc. Il y a quatre choix possibles : soit on envoie un 0, soit un 1, soit l'inverse du bit d'entrée, soit envoyer deux fois le bit d'entrée.
[[File:Portes logiques faites à partir de multiplexeurs.png|centre|vignette|upright=2|Portes logiques faites à partir de multiplexeurs]]
Ils peuvent aussi être utilisés pour implémenter une bascule D, (pour rappel : une petite mémoire de 1 bit), comme on l'a vu dans les chapitres sur les bascules. Il suffit pour cela de boucler la sortie d'un multiplexeur sur une entrée, en ajoutant deux portes NON dans la boucle pour régénérer le signal électrique.
[[File:Multiplexer-based latch using transmission gates.svg|centre|vignette|upright=2|Implémentation alternative d'une bascule D.]]
===La porte XOR en ''pass transistor logic''===
Il est facile d'implémenter une porte XOR avec un multiplexeur 2 vers 1. Pour rappel, une porte XOR est une sorte d'inverseur commandable, à savoir un circuit prend un bit d'entrée A, et l'inverse ou non suivant la valeur d'un bit de commande B. Un tel circuit commandable n'est autre qu'une porte logique XOR, qui XOR A et B. Et cela nous dit comment implémenter une porte XOR avec un multiplexeur : il suffit de prendre un multiplexeur qui choisit sa sortie parmi deux entrées : A et <math>\overline{A}</math> ! Pour deux bits A et B, l'un est envoyé sur l'entrée de commande, l'autre bit est envoyée sur les deux entrées (le bit sur une entrée, son inverse sur l'autre). Le circuit obtenu, sans les portes NON, est celui-ci :
[[File:XOR gate implemented with pass gates.svg|centre|vignette|upright=1|Porte XOR implémentée avec une porte à transmission.]]
La version précédente est une porte XOR où les signaux d'entrée sont doublés : on a le bit d'entrée original, et son inverse. C'est quelque chose de fréquent dans les circuits en ''pass transistor logic'', où les signaux/bits sont doublés. Mais il est possible de créer des versions normales, sans duplication des bits d'entrée. La solution la plus simple de rajouter deux portes NON, pour inverser les deux entrées. Le circuit passe donc de 4 à 8 transistors, ce qui reste peu. Mais on peut ruser, ce qui donne le circuit ci-dessous. Comme vous pouvez les voir, il mélange porte à transmission et portes NON CMOS normales.
[[File:CmosXORGate.svg|centre|vignette|upright=1|XOR en ''pass transistor logic'']]
Dans les deux cas, l'économie en transistors est drastique comparé au CMOS normal. Plus haut, nous avons illustré plusieurs versions possibles d'une porte XOR en CMOS normal, toutes de 12 transistors. Ici, on va de 6 transistors maximum, à seulement 4 ou 5 pour les versions plus simples. Le gain est clairement significatif, suffisamment pour que les circuits avec beaucoup de portes XOR gagnent à être implémentés avec la ''pass transistor logic''.
Quelques processeurs implémentaient leurs portes XOR en ''pass transistor logic'', alors que les autres portes étaient en CMOS normal. Un exemple est le mythique processeur Z80.
===Les avantages et défauts de la ''pass transistor logic''===
Une porte logique en logique CMOS connecte directement sa sortie sur la tension d'alimentation ou la masse. Mais dans une porte logique en ''pass transistor logic'', il n'y a ni tension d'alimentation, ni masse (O Volts). La sortie d'une porte à transmission est alimentée par la tension d'entrée. Et vu que les transistors ne sont pas parfaits, on a toujours une petite perte de tension en sortie d'une porte à transmission.
Le résultat est que si on enchaine les portes à transmission, la tension de sortie a tendance à diminuer, et ce d'autant plus vite qu'on a enchainé de portes à transmission. Il faut souvent rajouter des portes OUI pour restaurer les tensions adéquates, à divers endroits du circuit. La ''pass transistor logic'' mélange donc porte OUI/NON CMOS normales avec des portes à transmission. Afin de faire des économies de circuit, on utilise parfois une seule porte NON CMOS comme amplificateur, ce qui fait que de nombreux signaux sont inversés dans les circuits, sans que cela ne change grand chose si le circuit est bien conçu.
Par contre, ce défaut entraine aussi des avantages. Notamment, la consommation d'énergie est fortement diminuée. Seules les portes amplificatrices, les portes NON CMOS, sont alimentées en tension/courant. Le reste des circuits n'est pas alimenté, car il n'y a pas de connexion à la tension d'alimentation et la masse. De même, la ''pass transistor logic'' utilise généralement moins de transistors pour implémenter une porte logique, et un circuit électronique en général. L'exemple avec la porte XOR est assez parlant : on passe de 12 à 6 transistors par porte XOR. Des circuits riches en portes XOR, comme les circuits additionneurs, gagnent beaucoup à utiliser des portes à transmission.
==Les technologies PMOS et NMOS==
Dans ce qui va suivre, nous allons voir la technologie NMOS et POMS. Pour simplifier, la technologie NMOS est équivalente aux circuits CMOS, sauf que les transistors PMOS sont remplacés par une résistance. Pareil avec la technologie PMOS, sauf que c'est les transistors NMOS qui sont remplacés par une résistance. Les deux technologies étaient utilisées avant l'invention de la technologie CMOS, quand on ne savait pas comment faire pour avoir à la fois des transistors PMOS et NMOS sur la même puce électronique, mais sont aujourd'hui révolues. Nous en parlons ici, car nous évoquerons quelques circuits en PMOS/NMOS dans le chapitre sur les cellules mémoire, mais vous pouvez considérer que cette section est facultative.
===Le fonctionnement des logiques NMOS et PMOS===
Avec la technologie NMOS, les portes logiques sont fabriqués avec des transistors NMOS intercalés avec une résistance.
[[File:Circuit en logique NMOS.png|centre|vignette|upright=2|Circuit en logique NMOS.]]
Leur fonctionnement est assez facile à expliquer. Quand la sortie doit être à 1, tous les transistors sont ouverts. La sortie est connectée à la tension d'alimentation et déconnectée de la masse, ce qui fait qu'elle est mise à 1. La résistance est là pour éviter que le courant qui arrive dans la sortie soit trop fort. Quand au moins un transistor NMOS qui se ferme, il connecte l'alimentation à la masse, les choses changent. Les lois compliquées de l'électricité nous disent alors que la sortie est connectée à la masse, elle est donc mise à 0.
[[File:Fonctionnement d'un circuit en technologie NMOS.png|centre|vignette|upright=2|Fonctionnement d'un circuit en technologie NMOS.]]
Les circuits PMOS sont construits d'une manière assez similaire aux circuits CMOS, si ce n'est que les transistors NMOS sont remplacés par une résistance qui relie ici la masse à la sortie. Rien d'étonnant à cela, les deux types de transistors, PMOS et NMOS, ayant un fonctionnement inverse.
===Les portes logiques en NMOS et PMOS===
Que ce soit en logique PMOS ou NMOS, les portes de base sont les portes NON, NAND et NOR. Les autres portes sont fabriquées en combinant des portes de base. Voici les circuits obtenus en NMOS et PMOS:
{|class="wikitable flexible"
|-
! colspan="5 | NMOS
|-
| class="transparent" | [[File:NMOS NOT.svg|class=transparent|100px|Porte NON NMOS.]]
| class="transparent" | [[File:NMOS NAND.svg|class=transparent|100px|NMOS-NAND]]
| class="transparent" | [[File:NMOS NOR.png|100px|NMOS-NOR]]
| class="transparent" | [[File:NMOS AND gate.png|100px|NMOS AND]]
| class="transparent" | [[File:NMOS OR gate.png|100px|NMOS OR]]
|-
! colspan="5 | PMOS
|-
| class="transparent" | [[File:PMOS NOT.png|100px|PMOS NOT]]
| class="transparent" | [[File:PMOS NAND corr.png|100px|PMOS NAND]]
| class="transparent" | [[File:PMOS NOR corr.png|100px|PMOS NOR]]
| class="transparent" |
| class="transparent" | [[File:PMOS OR gate.png|100px|PMOS OR]]
|}
====Les portes logiques de base en NMOS====
Le circuit d'une porte NON en technologie NMOS est illustré ci-dessous. Le principe de ce circuit est similaire au CMOS, avec quelques petites différences. Si on envoie un 0 sur la grille du transistor, il s'ouvre et connecte la sortie à la tension d'alimentation à travers la résistance. À l'inverse, quand on met un 1 sur la grille, le transistor se ferme et la sortie est reliée à la masse, donc mise à 0. Le résultat est bien un circuit inverseur.
{|class="wikitable flexible"
|[[File:NMOS NOT.svg|class=transparent|Porte NON NMOS.]]
|[[File:Not.PNG|class=transparent|Porte NON NMOS : fonctionnement.]]
|}
La porte NOR est similaire à la porte NON, si ce n'est qu'il y a maintenant deux transistors en parallèle. Si l'une des grilles est mise à 1, son transistor se fermera et la sortie sera mise à 0. Par contre, quand les deux entrées sont à 0, les transistors sont tous les deux ouverts, et la sortie est mise à 1. Le comportement obtenu est bien celui d'une porte NOR.
{|class="wikitable flexible"
|[[File:NMOS NOR.png|NMOS-NOR-gate]]
|[[File:Funktionsprinzip eines NOR-Gatters.png|class=transparent|Fonctionnement d'une porte NOR NMOS.]]
|}
La porte NAND fonctionne sur un principe similaire au précédent, si ce n'est qu'il faut que les deux grilles soient à zéro pour obtenir une sortie à 1. Pour mettre la sortie à 0 quand seulement les deux transistors sont ouverts, il suffit de les mettre en série, comme dans le schéma ci-dessous. Le circuit obtenu est bien une porte NAND.
{|class="wikitable flexible"
|[[File:NMOS NAND.svg|class=transparent|centre|NMOS-NAND-gate]]
|[[File:Funktionsprinzip eines NAND-Gatters.png|class=transparent|centre|Funktionsprinzip eines NAND-Gatters]]
|}
===Les avantages et inconvénients des technologies CMOS, PMOS et NMOS===
La technologie PMOS et NMOS ne sont pas totalement équivalentes, niveau performances. Ces technologies se distinguent sur plusieurs points : la vitesse des transistors et leur consommation énergétique.
La vitesse des circuits NMOS/PMOS/CMOS dépend des transistors eux-mêmes. Les transistors PMOS sont plus lents que les transistors NMOS, ce qui fait que les circuits NMOS sont plus rapides que les circuits PMOS. Les circuits CMOS ont une vitesse intermédiaire, car ils contiennent à la fois des transistors NMOS et PMOS.
Pour la consommation électrique, les résistances sont plus goumandes que les transistors. En PMOS et NMOS, la résistance est traversée par du courant en permanence, peu importe l'état des transistors. Et résistance traversée par du courant signifie consommation d'énergie, dissipée sous forme de chaleur par la résistance. Il s'agit d'une perte sèche d'énergie, une consommation d'énergie inutile. En CMOS, l'absence de résistance fait que la consommation d'énergie est liée aux transistors, et celle-ci est beaucoup plus faible que pour une résistance.
Les transistors PMOS sont plus simples à fabriquer que les NMOS, ils sont plus simples à sortir d'usine. Les premiers processeurs étaient fabriqués en logique PMOS, plus simple à fabriquer. Puis, une fois la fabrication des circuits NMOS maitrisée, les processeurs sont tous passés en logique NMOS du fait de sa rapidité. La logique CMOS a mis du temps à remplacer les logiques PMOS et NMOS, car il a fallu maitriser les techniques pour mettre à la fois des transistors NMOS et PMOS sur la même puce. Les premières puces électroniques étaient fabriquées en PMOS ou en NMOS, parce qu'on n’avait pas le choix. Mais une fois la technologie CMOS maitrisée, elle s'est imposée en raison de deux gros avantages : une meilleure fiabilité (une meilleure tolérance au bruit électrique), et une consommation électrique plus faible.
==La logique dynamique MOS==
La '''logique dynamique''' permet de créer des portes logiques ou des bascules d'une manière assez intéressante. Et aussi étonnant que cela puisse paraître, le signal d’horloge est alors utilisé pour fabriquer des circuits combinatoires !
===Un transistor MOS peut servir de condensateur===
Les technologies CMOS conventionnelles mettent la sortie d'une porte logique à 0/1 en la connectant à la tension d'alimentation ou à la masse. La logique ''pass transistor'' transfère la tension et le courant de l'entrée vers la sortie. Dans les deux cas, la sortie est connectée directement ou indirectement à la tension d'alimentation quand on veut lui faire sortie un 1. Avec la logique dynamique, ce n'est pas le cas. La sortie est maintenue à 0 ou à 1 en utilisant un réservoir d'électron qui remplace la tension d'alimentation.
En électronique, il existe un composant qui sert de réservoir à électricité : il s'agit du '''condensateur'''. On peut le charger en électricité, ou le vider pour fournir un courant durant une petite durée de temps. Par convention, un condensateur stocke un 1 s'il est rempli, un 0 s'il est vide. L'intérieur d'un condensateur est formé de deux couches de métal conducteur, séparées par un isolant électrique. Les deux plaques de conducteur sont appelées les armatures du condensateur. C'est sur celles-ci que les charges électriques s'accumulent lors de la charge/décharge d'un condensateur. L'isolant empêche la fuite des charges d'une armature à l'autre, ce qui permet au condensateur de fonctionner comme un réservoir, et non comme un simple fil.
Il est possible de fabriquer un pseudo-condensateur avec un transistor MOS. En effet, tout transistor MOS a un pseudo-condensateur caché entre la grille et la liaison source-drain. Pour comprendre ce qui se passe dans ce transistor de mémorisation, il faut savoir ce qu'il y a dans un transistor CMOS. À l'intérieur, on trouve une plaque en métal appelée l'armature, un bout de semi-conducteur entre la source et le drain, et un morceau d'isolant entre les deux. L'ensemble forme donc un condensateur, certes imparfait, qui porte le nom de capacité parasite du transistor. Suivant la tension qu'on envoie sur la grille, l'armature va se remplir d’électrons ou se vider, ce qui permet de stocker un bit : une grille pleine compte pour un 1, une grille vide compte pour un 0.
[[File:Transistor CMOS - 1.png|centre|vignette|upright=2|Anatomie d'un transistor CMOS]]
L'utilisation de transistors MOS comme condensateur n'est pas spécifique à la logique dynamique. Certains mémoires RAM le font, comme nous le verrons dans le chapitre sur les cellules mémoires. Aussi, il est intéressant d'en parler maintenant, histoire de préparer le terrain. D'ailleurs, les mémoires RAM sont remplies de logique dynamique.
===L'utilisation des pseudo-condensateurs en logique dynamique===
Un circuit conçu en logique dynamique contient un transistor est utilisé comme condensateur. Il s’insère entre la tension d'alimentation et la sortie du circuit. Son rôle est simple : lorsqu'on utilise la sortie, le condensateur se vide, ce qui place la sortie à 1. le reste du temps, le condensateur est relié à la tension d'alimentation et se charge. Un circuit en logique dynamique effectue son travail en deux phases : une phase d'inactivité où il remplit ses condensateurs, et une phase où sa sortie fonctionne. Les deux phases sont appelées la '''phase de précharge''' et la '''phase d'évaluation'''. La succession de ces deux phases est réalisée par le signal d'horloge : la première pahse a lieu quand le signal d'horloge est à 1, l'autre quand il est à 0.
====Une porte NAND en logique dynamique CMOS====
Voici un exemple de porte NAND en logique dynamique MOS. La porte est alors réalisée avec des transistors NMOS et PMOS, le circuit ressemble à ce qu'on a en logique NMOS. En bas, on trouve les transistors NMOS pour relier la sortie au 0 volt. Mais au-dessus, on trouve un transistor CMOS qui remplace la résistance. Le fonctionnement du circuit est simple. Quand l'entrée ''clock'' est à 1, le condensateur se charge, les deux transistors NMOS sont déconnectés de la masse et le circuit est inactif. Puis, quand ''clock'' passe à 0, Le transistor PMOS se comporte en circuit ouvert, ce qui déconnecte la tension d'alimentation. Et son pseudo-condensateur se vide, ce qui fournit une tension d'alimentation de remplacement temporaire. Le transistor NMOS du bas se ferme, ce qui fait que les deux transistors A et B décident de si la sortie est connectée au 0 volt ou non. Si c'est le cas, le pseudo-condensateur se vide dans le 0 volt et la sortie est à 0. Sinon, le pseudo-condensateur se vide dans la sortie, ce qui la met à 1.
[[File:Dlnand.svg|centre|vignette|Porte NAND en logique CMOS.]]
====Une bascule D en logique dynamique CMOS====
Il est possible de créer une bascule D en utilisant la logique dynamique. L'idée est de prendre une bascule D normale, mais d'ajouter un fonctionnement en deux étapes en ajoutant des transistors/interrupteurs. Pour rappel, une bascule D normale est composée de deux inverseurs reliés l'un à l'autre en formant une boucle, avec un multiplexeur pour permettre les écritures dans la boucle.
[[File:Implémentation conceptuelle d'une bascule D.png|centre|vignette|upright=2|Implémentation conceptuelle d'une bascule D]]
[[File:Animation du fonctionnement d'une bascule.gif|vignette|upright=2|Animation du fonctionnement de la bascule précédente.]]
Le circuit final ajoute deux transistors entre les inverseurs tête-bêche. Les transistors en question sont reliés à l'horloge, l'un étant ouvert quand l'autre est fermé. Grâce à eux, le bit mémorisé circule d'un inverseur à l'autre : il est dans le premier inverseur quand le signal d'horloge est à 1, dans l'autre inverseur quand il est à 0 (en fait son inverse, comme vous l'aurez compris). Le tout est illustré ci-contre. Cette implémentation a été utilisée autrefois, notamment dans le processeur Intel 8086.
[[File:Bascule D en logique Dynamique, avec entrée Enable.png|centre|vignette|upright=2|Bascule D en logique Dynamique, avec entrée Enable]]
Il existe une variante très utilisée, qui permet de remplacer le multiplexeur par un circuit légèrement plus simple. Avec elle, on a deux entrées pour commander la bascule, et non une seule entrée Enable. L'entrée Enable autorise les écriture, l'entrée Hold ferme la boucle qui relie la sortie du second inverseur au premier. Chaque entrée est associé à un transistor/interrupteur. Le transistor sur lequel on envoie l'entrée Enable se ferme uniquement lors des écritures et reste fermé sinon. A l'inverse, le transistor relié au signal Hold est fermé en permanence, sauf lors des écritures. En clair, les deux signaux sont l'inverse l'un de l'autre. Il permet de fermer le circuit, de bien relier les deux inverseurs en tête-bêche, sauf lors des écritures. On envoie donc l'inverse de l'entrée Enable sur ce transistor.
[[File:Bascule D en logique dynamique.png|centre|vignette|upright=2|Bascule D en logique dynamique]]
Une manière de comprendre le circuit précédent est de le comparer à celui avec le multiplexeur. Le multiplexeur est composé d'une porte NON et de deux transistors. Il se trouve que les deux transistors en question sont placés au même endroit que les transistors connectés aux signaux Hold et Enable. En prenant retirant la porte NON du multiplexeur, on se retrouve avec le circuit. Au lieu de prendre un Signal Enable qui commande les deux transistors, ce qui demande d'ajouter une porte NON vu que les deux transistors doivent faire l'inverse l'un de l'autre, on se contente d'envoyer deux signaux séparés pour commander chaque transistor indépendamment.
===Avantages et inconvénients===
Les circuits en logique dynamique sont opposés aux circuits en logique statique, ces derniers étant les circuits CMOS, PMOS, NMOS ou TTL vu jusqu'à présent. Les circuits dynamiques et statiques ont des différences notables, ainsi que des avantages et inconvénients divers. Si on devait résumer :
* la logique dynamique utilise généralement un peu plus de transistors qu'un circuit CMOS normal ;
* la logique dynamique est souvent très rapide par rapport à la concurrence, car elle n'utilise que des transistors NMOS, plus rapides ;
* la consommation d'énergie est généralement supérieure comparé au CMOS.
Un désavantage de la logique dynamique est qu'elle utilise plus de transistors. On économise certes des transistors MOS, mais il faut rajouter les transistors pour déconnecter les transistors NMOS de la masse (0 volt). Le second surcompense le premier.
Un autre désavantage est que le signal d'horloge ne doit pas tomber en-dessous d'une fréquence minimale. Avec une logique statique, on a une fréquence maximale, mais pas de fréquence minimale. Avec un circuit statique peut réduire la fréquence d'un circuit pour économiser de l'énergie, pour améliorer sa stabilité, et de nombreux processeurs modernes ne s'en privent pas. On peut même stopper le signal d'horloge et figer le circuit, ce qui permet de le mettre en veille, d'en stopper le fonctionnement, etc. Impossible avec la logique dynamique, qui demande de ne pas tomber sous la fréquence minimale. Cela a un impact sur la consommation d'énergie, sans compter que cela se marie assez mal avec certaines applications. Un processeur moderne ne peut pas être totalement fabriqué en logique dynamique, car il a besoin d'être mis en veille et qu'il a besoin de varier sa fréquence en fonction des besoins.
Le dernier désavantage implique l'arbre d'horloge, le système d'interconnexion qui distribue le signal d'horloge à toutes les bascules d'un circuit. L'arbre d'horloge est beaucoup plus compliqué avec la logique dynamique qu'avec la logique statique. Avec la logique statique, seules les bascules doivent recevoir le signal d'horloge, avec éventuellement quelques rares circuits annexes. Mais avec la logique dynamique, toutes les portes logiques doivent recevoir le signal d'horloge, ce qui rend la distribution de l'hrologe beaucoup plus compliquée. C'est un point qui fait que la logique dynamique est assez peu utilisée, et souvent limitée à quelques portions bien précise d'un processeur.
==La logique TTL : un apercu rapide==
Tous ce que nous avons vu depuis le début de ce chapitre porte sur les transistors MOS et les technologies associées. Mais les transistors MOS n'ont pas été les premiers inventés. Ils ont été précédés par les '''transistors bipolaires'''. Nous ne parlerons pas en détail du fonctionnement d'un transistor bipolaire, car celui-ci est extraordinairement compliqué. Cependant, nous devons parler rapidement de la logique TTL, qui permet de fabriquer des portes logiques avec ces transistors bipolaires. Là encore, rassurez-vous, nous n'allons pas voir comment fabriquer des portes logiques en logique TTL, cela serait trop compliqué, sans compter que le but n'est pas de faire un cours d'électronique. Mais nous devons fait quelques remarques et donner quelques explications superficielles.
La raison à cela est double. La première raison est que certains circuits présents dans les mémoires RAM sont fabriqués avec des transistors bipolaires. C'est notamment le cas des amplificateurs de lecture ou d'autres circuits de ce genre. De tels circuits ne peuvent pas être implémentés facilement avec des transistors CMOS et nous expliquerons rapidement pourquoi dans ce qui suit. La seconde raison est que ce cours parlera occasionnellement de circuits anciens et qu'il faut quelques bases sur le TTL pour en parler.
Dans la suite du cours, nous verrons occasionnellement quelques circuits anciens, pour la raison suivante : ils sont très simples, très pédagogiques, et permettent d'expliquer simplement certains concepts du cours. Rien de mieux que d'étudier des circuits réels pour donner un peu de chair à des explications abstraites. Par exemple, pour expliquer comment fabriquer une unité logique de calcul bit à bit, je pourrais utiliser l'exemple du Motorola MC14500B, un processeur 1 bit qui est justement une unité logique sous stéroïdes. Ou encore, dans le chapitre sur les circuits additionneurs, je parlerais du circuit additionneur présent dans l'Intel 8008 et dans l'Intel 4004, les deux premiers microprocesseurs commerciaux. Malheureusement, malgré leurs aspects pédagogiques indéniables, ces circuits ont le défaut d'être des circuits TTL. Ce qui est intuitif : les circuits les plus simples ont été inventés en premier et utilisent du TTL plus ancien. Beaucoup de ces circuits ont été inventés avant même que le CMOS ou même les transistors MOS existent. D'où le fait que nous devons faire quelques explications mineures sur le TTL.
===Les transistors bipolaires===
Les '''transistors bipolaires''' ressemblent beaucoup aux transistors MOS. Les transistors bipolaires ont trois broches, appelées le collecteur, la base et l'émetteur. Notez que ces trois termes sont différents de ceux utilisés pour les transistors MOS, où on parle de la grille, du drain et de la source.
Là encore, comme pour les transistors PMOS et NMOS, il existe deux types de transistors bipolaires : les NPN et les PNP. Là encore, il est possible de fabriquer une puce en utilisant seulement des NPN, seulement des PNP, ou en mixant les deux. Mais les ressemblances s'arrêtent là. La différence entre PNP et NPN tient dans la manière dont les courants entrent ou sortent du transistor. La flèche des symboles ci-dessous indique si le courant rentre ou sort par l'émetteur : il rentre pour un PNP, sort pour un NPN. Dans la suite du cours, nous n'utiliserons que des transistors NPN, les plus couramment utilisés.
{|
|[[File:BJT PNP symbol.svg|vignette|BJT PNP]]
|[[File:BJT NPN symbol.svg|vignette|BJT NPN]]
|}
Plus haut nous avons dit que les transistors CMOS sont des interrupteurs. La réalité est que tout transistor peut être utilisé de deux manières : soit comme interrupteur, soit comme amplificateur de tension/courant. Pour simplifier, le transistor bipolaire NPN prend en entrée un courant sur sa base et fournit un courant amplifié sur l'émetteur. Pour s'en servir comme amplificateur, il faut fournir une source de courant sur le collecteur. Le fonctionnement exact est cependant plus compliqué.
[[File:Transistor bipolaire, explication simplifiée de son fonctionnement.png|centre|vignette|upright=1.5|Transistor bipolaire, explication simplifiée de son fonctionnement]]
Les transistors bipolaires sont de bons amplificateurs, mais de piètres interrupteurs. A l'inverse, les transistors CMOS sont généralement de bons interrupteurs, mais de moyens amplificateurs. Pour des circuits numériques, la fonction d'interrupteur est clairement plus adaptée, car elle-même binaire (un transistor est fermé ou ouvert : deux choix possibles). Aussi, les circuits modernes privilégient des transistors CMOS aux transistors bipolaires. A l'inverse, la fonction d'amplification est adaptée aux circuits analogiques.
C'est pour ça que nous rencontrerons les transistors bipolaires soit dans des portions de l'ordinateur qui sont au contact de circuits analogiques. Pensez par exemple aux cartes sons ou au vieux écrans cathodiques, qui gèrent des signaux analogiques (le son pour la carte son, les signaux vidéo analogique pour les vieux écrans). On les croisera aussi dans les mémoires DRAM, dont la conception est un mix entre circuits analogiques et numériques. Nous les croiserons aussi dans de vieux circuits antérieurs aux transistors MOS. Les anciens circuits faisaient avec les transistors bipolaires car ils n'avaient pas le choix, mais ils ont été partiellement remplacés dès l'apparition des transistors CMOS.
===Les portes logiques complexes en TTL===
Le détail le plus important qui nous concernera dans la suite du cours est le suivant : on peut créer des portes logiques exceptionnellement complexes en TTL. Pour comprendre pourquoi, sachez qu'il existe des transistors bipolaires qui possèdent plusieurs émetteurs. Ils sont très utilisés pour fabriquer des portes logiques à plusieurs entrées. Les émetteurs correspondent alors à des entrées de la porte logique. Ainsi, une porte logique à plusieurs entrées se fait non pas en ajoutant des transistors, comme c'est le cas avec les transistors MOS, mais en ajoutant un émetteur sur un transistor. Cela permet à une porte NAND à trois entrées de n'utiliser que deux transistors bipolaires, au lieu de quatre transistors MOS.
[[File:Multiemitter Transistor.svg|centre|vignette|upright=1|Transistor bipolaire avec plusieurs émetteurs.]]
De plus, là où les logiques PMOS/NMOS/CMOS permettent de fabriquer les portes de base que nous avons précédemment, elles ne peuvent pas faire plus. Au pire, on peut implémenter des portes ET/OU/NAND/NOR à plusieurs entrées, mais pas plus. En TTL, on peut parfaitement créer des portes de type ET/OU/NON ou OU/ET/NON, avec seulement quatre transistors. Par exemple, une '''porte ET/OU/NON''' de type 2-2 entrées (pour rappel, qui effectue un ET par paire d’entrée puis fait un NOR entre le résultat des deux ET) est bien implémenté en une seule porte logique, pas en enchainant deux ou trois portes à la suite.
[[File:TTL AND-OR-INVERT 1961.png|centre|vignette|upright=2|TTL AND-OR-INVERT 1961]]
===Les désavantages et avantages des circuits TTL===
Pour résumer, le TTL à l'avantage de pouvoir fabriquer des portes logiques avec peu de transistors comparé au CMOS, surtout pour les portes logiques complexes. Et autant vous dire que les concepteurs de puce électroniques ne se gênaient pas pour utiliser ces portes complexes, capables de fusionner 3 à 5 portes en une seule : les économies de transistors étaient conséquentes.
Et pourtant, les circuits TTL étaient beaucoup plus gros que leurs équivalents CMOS. La raison est qu'un transistor bipolaire prend beaucoup de place : il est environ 10 fois plus gros qu'un transistor MOS. Autant dire que les économies réalisées avec des portes logiques complexes ne faisaient que compenser la taille énorme des transistors bipolaires. Et encore, cette compensation n'était que partielle, ce qui fait que les circuits PMOS/NMOS/CMOS se miniaturisent beaucoup plus facilement. Un avantage pour le transistor MOS !
De plus, les schémas précédents montrent que les portes logiques en TTL utilisent une résistance, elle aussi difficile à miniaturiser. Et cette résistance est parcourue en permanence par un courant, ce qui fait qu'elle consomme de l'énergie et chauffe. C'est la même chose en logique NMOS et PMOS, ce qui explique leur forte consommation d'énergie. Les circuits TTL ont donc le même problème.
[[File:TTL Input voltage.svg|vignette|upright=0.5|TTL voltage.]]
Un autre défaut est lié à la une tension d'alimentation. Les circuits TTL utilisent une tension d'alimentation de 5 volts, alors que les circuits CMOS ont une tension d'alimentation beaucoup plus variable. Les circuits CMOS vont de 3 volts à 18 volts pour les circuits commerciaux, avec des tensions de 1 à 3 volts pour les circuits optimisés. Les circuits CMOS sont généralement bien optimisés et utilisent une tension d'alimentation plus basse que les circuits TTL, ce qui fait qu'ils consomment moins d'énergie et de courant.
De plus, rappelons que coder un zéro demande que la tension soit sous un seuil, alors que coder un 1 demande qu'elle dépasse un autre seuil, avec une petite marge de sécurité entre les deux. Les seuils en question sont indiqués dans le diagramme ci-dessous. Il s'agit des seuils VIH et VIL. On voit que sur les circuits TTL, la marge de sécurité est plus faible qu'avec les circuits CMOS. De plus, les marges sont bien équilibrées en CMOS, à savoir que la marge de sécurité est en plein milieu entre la tension max et le zéro volt. Avec le TTL normal, la marge de sécurité est très proche du zéro volt. Un 1 est codé par une tension entre 2 et 5 volts en TTL ! Une version améliorée du TTL, le LVTTL, corrige ce défaut. Elle baisse la tension d'alimentation à 3,3 Volts, mais elle demande des efforts de fabrication conséquents.
[[File:Niveaux logiques CMOS-TTL-LVTTL.png|centre|vignette|upright=2|Niveaux logiques CMOS-TTL-LVTTL]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de conversion analogique-numérique
| prevText=Les circuits de conversion analogique-numérique
| next=Les circuits intégrés
| nextText=Les circuits intégrés
}}
{{autocat}}
</noinclude>
sal49clgssxzkw6gonb2vvv7soj7bur
744785
744784
2025-06-15T01:52:53Z
Mewtow
31375
/* La pass transistor logic utilise des multiplexeurs 2 vers 1 */
744785
wikitext
text/x-wiki
Dans le chapitre précédent, nous avons abordé les portes logiques. Dans ce chapitre, nous allons voir qu'elles sont fabriquées avec des composants électroniques que l'on appelle des '''transistors'''. Ces derniers sont reliés entre eux pour former des circuits plus ou moins compliqués. Pour donner un exemple, sachez que les derniers modèles de processeurs peuvent utiliser près d'un milliard de transistors.
==Les transistors MOS==
[[File:Transistor basic flow.svg|vignette|Un transistor est un morceau de conducteur, dont la conductivité est contrôlée par sa troisième broche/borne.]]
Les transistors possèdent trois '''broches''', des pattes métalliques sur lesquelles on connecte des fils électriques. On peut appliquer une tension électrique sur ces broches, qui peut représenter soit 0 soit 1. Sur ces trois broches, il y en a deux entre lesquelles circule un courant, et une troisième qui commande le courant. Le transistor s'utilise le plus souvent comme un interrupteur commandé par sa troisième broche. Le courant qui traverse les deux premières broches passe ou ne passe pas selon ce qu'on met sur la troisième.
Il existe plusieurs types de transistors, mais les deux principaux sont les transistors bipolaires et les transistors MOS. De nos jours, les transistors utilisés dans les ordinateurs sont tous des '''transistors MOS'''. Les raisons à cela sont multiples, mais les plus importantes sont les suivantes. Premièrement, les transistors bipolaires sont plus difficiles à fabriquer et sont donc plus chers. Deuxièmement, ils consomment bien plus de courant que les transistors MOS. Et enfin, les transistors bipolaires sont plus gros, ce qui n'aide pas à miniaturiser les puces électroniques. Tout cela fait que les transistors bipolaires sont aujourd'hui tombés en désuétude et ne sont utilisés que dans une minorité de circuits.
===Les types de transistors MOS : PMOS et NMOS===
Sur un transistor MOS, chaque broche a un nom, nom qui est indiqué sur le schéma ci-dessous.On distingue ainsi le '''drain''', la '''source''' et la '''grille''' On l'utilise le plus souvent comme un interrupteur commandé par sa grille. Appliquez la tension adéquate et la liaison entre la source et le drain se comportera comme un interrupteur fermé. Mettez la grille à une autre valeur et cette liaison se comportera comme un interrupteur ouvert.
Il existe deux types de transistors CMOS, qui diffèrent entre autres par le bit qu'il faut mettre sur la grille pour les ouvrir/fermer :
* les transistors NMOS qui s'ouvrent lorsqu'on envoie un zéro sur la grille et se ferment si la grille est à un ;
* et les PMOS qui se ferment lorsque la grille est à zéro, et s'ouvrent si la grille est à un.
[[File:Td7bfig2.png|centre|vignette|upright=2|Illustration du fonctionnement des transistors NMOS et PMOS.]]
Voici les symboles de chaque transistor.
{|
|[[File:Transistor CMOS.png|vignette|upright=0.5|Transistor CMOS]]
|[[File:IGFET N-Ch Enh Labelled simplified.svg|vignette|upright=0.5|Transistor MOS à canal N (NMOS).]]
|[[File:IGFET P-Ch Enh Labelled simplified.svg|vignette|upright=0.5|Transistor MOS à canal P (PMOS).]]
|}
===L'anatomie d'un transistor MOS===
À l'intérieur du transistor, on trouve simplement une plaque en métal reliée à la grille appelée l'armature, un bout de semi-conducteur entre la source et le drain, et un morceau d'isolant entre les deux. Pour rappel, un semi-conducteur est un matériau qui se comporte soit comme un isolant, soit comme un conducteur, selon les conditions auxquelles on le soumet. Dans un transistor, son rôle est de laisser passer le courant, ou de ne pas le transmettre, quand il faut. C'est grâce à ce semi-conducteur que le transistor peut fonctionner en interrupteur : interrupteur fermé quand le semi-conducteur conduit, ouvert quand il bloque le courant. La commande de la résistance du semi-conducteur (le fait qu'il laisse passer ou non le courant) est réalisée par la grille, comme nous allons le voir ci-dessous.
[[File:Transistor CMOS - 1.png|centre|vignette|upright=2|Transistor CMOS]]
Suivant la tension que l'on place sur la grille, celle-ci va se remplir avec des charges négatives ou positives. Cela va entrainer une modification de la répartition des charges dans le semi-conducteur, ce qui modulera la résistance du conducteur. Prenons par exemple le cas d'un transistor NMOS et étudions ce qui se passe selon la tension placée sur la grille. Si on met un zéro, la grille sera vide de charges et le semi-conducteur se comportera comme un isolant : le courant ne passera pas. En clair, le transistor sera équivalent à un interrupteur ouvert. Si on met un 1 sur la grille, celle-ci va se remplir de charges. Le semi-conducteur va réagir et se mettre à conduire le courant. En clair, le transistor se comporte comme un interrupteur fermé.
{|
|[[File:Transistor CMOS - 3.png|vignette|upright=1.5|Transistor NMOS fermé.]]
|[[File:Transistor CMOS - 4.png|vignette|upright=1.5|Transistor NMOS ouvert.]]
|}
===La tension de seuil d'un transistor===
Le fonctionnement d'un transistor est légèrement plus complexe que ce qui a été dit auparavant. Mais pour rester assez simple, disons que son fonctionnement exact dépend de trois paramètres : la tension d'alimentation, le courant entre drain et source, et un nouveau paramètre appelé la tension de seuil.
Appliquons une tension sur la grille d'un transistor NMOS. Si la tension de grille reste sous un certain seuil, le transistor se comporte comme un interrupteur fermé. Le seuil de tension est appelé, très simplement, la '''tension de seuil'''. Au-delà de la tension de seuil, le transistor se comporte comme un interrupteur ouvert, il laisse passer le courant. La valeur exacte du courant dépend de la tension entre drain et source, soit la tension d'alimentation. Elle aussi dépend de la différence entre tension de grille et de seuil, à savoir <math>U_G - U_\text{seuil}</math>.
Le paragraphe qui va suivre est optionnel, mais détaille un peu plus le fonctionnement d'un transistor MOS. Tout ce qu'il faut comprendre est que la tension de seuil est une tension minimale pour ouvrir le transistor. Le plus important à retenir est que l'on ne peut pas baisser la tension d'alimentation sous la tension de seuil, ce qui est un léger problème en termes de consommation énergétique. Ce détail reviendra plus tard dans ce cours, quand nous parlerons de la consommation d'énergie des circuits électroniques.
Dans les cas que nous allons voir dans ce cours, la tension d'alimentation est plus grande que <math>U_G - U_\text{seuil}</math>. Le courant est alors maximal, il est proportionnel à <math>U_G - U_\text{seuil}</math>. Le transistor ne fonctionne alors pas comme un amplificateur, le courant reste le même. Si la tension d'alimentation est plus petite que <math>U_G - U_\text{seuil}</math>, le transistor est en régime linéaire : le courant de sortie est proportionnel à <math>U_G - U_\text{seuil}</math>, ainsi qu'à la tension d'alimentation. Le transistor fonctionne alors comme un amplificateur de courant, dont l'intensité de l'amplification est commandée par la tension.
[[File:MOSFET enhancement-mode n-channel en.svg|centre|vignette|upright=2.5|Relations entre tensions et courant d'un MOSFET à dopage N.]]
==La technologie CMOS==
Les portes logiques que nous venons de voir sont actuellement fabriquées en utilisant des transistors. Il existe de nombreuses manières pour concevoir des circuits à base de transistors, qui portent les noms de DTL, RTL, TLL, CMOS et bien d'autres. Les techniques anciennes concevaient des portes logiques en utilisant des diodes, des transistors bipolaires et des résistances. Mais elles sont aujourd'hui tombées en désuétudes dans les circuits de haute performance. De nos jours, on n'utilise que des logiques MOS (''Metal Oxyde Silicium''), qui utilisent des transistors MOS vus plus haut dans ce chapitre, parfois couplés à des résistances. On distingue :
* La '''logique NMOS''', qui utilise des transistors NMOS associés à des résistances.
* La '''logique PMOS''', qui utilise des transistors PMOS associés à des résistances.
* La '''logique CMOS''', qui utilise des transistors PMOS et NMOS, sans résistances.
Dans cette section, nous allons montrer comment fabriquer des portes logiques en utilisant la '''technologie CMOS'''. Avec celle-ci, chaque porte logique est fabriquée à la fois avec des transistors NMOS et des transistors PMOS. On peut la voir comme un mélange entre la technologie PMOS et NMOS. Tout circuit CMOS est divisé en deux parties : une intégralement composée de transistors PMOS et une autre de transistors NMOS. Chacune relie la sortie du circuit soit à la masse, soit à la tension d'alimentation.
[[File:Principe de la conception de circuit en technologie CMOS.png|centre|vignette|upright=2|Principe de conception d'une porte logique/d'un circuit en technologie CMOS.]]
La première partie relie la tension d'alimentation à la sortie, mais uniquement quand la sortie doit être à 1. Si la sortie doit être à 1, des transistors PMOS vont se fermer et connecter tension et sortie. Dans le cas contraire, des transistors s'ouvrent et cela déconnecte la liaison entre sortie et tension d'alimentation. L'autre partie du circuit fonctionne de la même manière que la partie de PMOS, sauf qu'elle relie la sortie à la masse et qu'elle se ferme quand la sortie doit être mise à 0
[[File:Fonctionnement d'un circuit en logique CMOS.png|centre|vignette|upright=2|Fonctionnement d'un circuit en logique CMOS.]]
Dans ce qui va suivre, nous allons étudier la porte NON, la porte NAND et la porte NOR. La porte de base de la technologie CMOS est la porte NON, les portes NAND et NOR ne sont que des versions altérées de la porte NON qui ajoutent des entrées et quelques transistors. Les autres portes, comme la porte ET et la porte OU, sont construites à partir de ces portes. Nous parlerons aussi de la porte XOR, qui est un peu particulière.
===La porte NON===
Cette porte est fabriquée avec seulement deux transistors, comme indiqué ci-dessous.
[[File:Porte NON fabriquée avec des transistors CMOS. 01.jpg|centre|vignette|upright=1|Porte NON fabriquée avec des transistors CMOS.]]
Si on met un 1 en entrée de ce circuit, le transistor du haut va fonctionner comme un interrupteur ouvert, et celui du bas comme un interrupteur fermé : la sortie est reliée au zéro volt, et vaut donc 0. Inversement, si on met un 0 en entrée de ce petit montage électronique, le transistor du bas va fonctionner comme un interrupteur ouvert, et celui du haut comme un interrupteur fermé : la sortie est reliée à la tension d'alimentation, et vaut donc 1.
[[File:Porte NON fabriquée avec des transistors CMOS - fonctionnement.png|centre|vignette|upright=2|Porte NON fabriquée avec des transistors CMOS - fonctionnement.]]
===Les portes NAND et NOR===
Passons maintenant aux portes logiques à plusieurs entrées. Pour celles-ci, on va devoir utiliser plus de transistors que pour la porte NON, ce qui demande de les organiser un minium. Une porte logique à deux entrées demande d'utiliser au moins deux transistors par entrée : un transistor PMOS et un NMOS par entrée. Rappelons qu'un transistor est associé à une entrée : l'entrée est directement envoyée sur la grille du transistor et commande son ouverture/fermeture. Pour les portes logiques à 3, 4, 5 entrées, la logiques est la même : au minimum deux transistors par entrée, un PMOS et un NMOS.
Nous allons d'abord voir le cas d'une porte NOR/NAND en CMOS. Avec elles, les transistors sont organisées de deux manières, appelées '''transistors en série''' (l'un après l'autre, sur le même fil) et '''transistors en parallèle''' (sur des fils différents). Le tout est illustré ci-dessous. Avec des transistors en série, plusieurs transistors NMOS ou deux PMOS se suivent sur le même fil, mais on ne peut pas mélanger NMOS et PMOS sur le même fil.
[[File:Transistors CMOS en série et en parallèle.png|centre|vignette|upright=2|Transistors CMOS en série et en parallèle]]
====Les portes NAND/NOR à deux entrées====
Voyons d'abord le cas des portes NAND/NOR à deux entrées. Elles utilisent deux transistors NMOS et deux PMOS.
Avec des transistors en série, deux transistors NMOS ou deux PMOS se suivent sur le même fil, mais on ne peut pas mélanger NMOS et PMOS sur le même fil. Avec des transistors en parallèle, c'est l'exact inverse. L'idée est de relier la tension d'alimentation à la sortie à travers deux PMOS transistors distincts, chacun sur son propre fil, sa propre connexion indépendante des autres. Pour la masse (0 volt), il faut utiliser deux transistors NMOS pour la relier à la sortie, avec là encore chaque transistor NMOS ayant sa propre connexion indépendante des autres. En clair, chaque entrée commande un transistor qui peut à lui seul fermer le circuit.
On rappelle deux choses : chaque transistor est associée à une entrée sur sa grille, un transistor se ferme si l'entrée vaut 0 pour des transistors PMOS et 1 pour des NMOS. Avec ces deux détails, on peut expliquer comment fonctionnent des transistors en série et en parallèle. Pour résumer, les transistors en série ferment la connexion quand toutes les entrées sont à 1 (NMOS) ou 0 (PMOS). Avec les transistors en parallèle, il faut qu'une seule entrée soit à 1 (NMOS) ou 0 (PMOS) pour que la connexion se fasse.
Une porte NOR met sa sortie à 1 si toutes les entrées sont à 0, à 0 si une seule entrée vaut 1. Pour reformuler, il faut connecter la sortie à la tension d'alimentation si toutes les entrées sont à 0, ce qui demande d'utiliser des transistors PMOS en série. Pour gérer le cas d'une seule entrée à 1, il faut utiliser deux transistors en parallèle entre la masse et la sortie. Le circuit obtenu est donc celui obtenu dans le premier schéma. Le même raisonnement pour une porte NAND donne le second schéma.
{|
|[[File:Porte NOR fabriquée avec des transistors. 02.png|centre|vignette|upright=1|Porte NOR fabriquée avec des transistors.]]
|[[File:Porte NAND fabriquée avec des transistors. 04.png|centre|vignette|upright=1|Porte NAND fabriquée avec des transistors.]]
|}
Leur fonctionnement s'explique assez bien si on regarde ce qu'il se passe en fonction des entrées. Suivant la valeur de chaque entrée, les transistors vont se fermer ou s'ouvrir, ce qui va connecter la sortie soit à la tension d'alimentation, soit à la masse.
Voici ce que cela donne pour une porte NAND :
[[File:Porte NAND fabriquée avec des transistors - Fonctionnement.png|centre|vignette|upright=2|Porte NAND fabriquée avec des transistors.]]
Voici ce que cela donne pour une porte NOR :
[[File:Porte NOR fabriquée avec des transistors - Fonctionnement.png|centre|vignette|upright=2|Porte NOR fabriquée avec des transistors.]]
====Les portes NAND/NOR/ET/OU à plusieurs entrées====
Les portes NOR/NAND à plusieurs entrées sont construites à partir de portes NAND/NOR à deux entrées auxquelles on rajoute des transistors. Il y a autant de transistors en série que d'entrée, pareil pour les transistors en parallèle. Leur fonctionnement est similaire à leurs cousines à deux entrées. Les portes ET et OU à plusieurs entrées sont construites à partie de NAND/NOR suivies d'une porte NON.
{|
|[[File:NAND plusieurs entrées.png|vignette|NAND plusieurs entrées]]
|[[File:NOR plusieurs entrées.png|vignette|NOR plusieurs entrées]]
|}
En théorie, on pourrait créer des portes avec un nombre arbitraire d'entrées avec cette méthode. Cependant, au-delà d'un certain nombre de transistors en série/parallèle, les performances s'effondrent rapidement. Le circuit devient alors trop lent, sans compter que des problèmes purement électriques surviennent. En pratique, difficile de dépasser la dizaine d'entrées. Dans ce cas, les portes sont construites en assemblant plusieurs portes NAND/NOR ensemble. Et faire ainsi marche nettement mieux pour fabriquer des portes ET/OU que pour des portes NAND/NOR.
====Les portes ET/OU sont fabriquées à partir de NAND/NOR en CMOS====
En logique CMOS, les portes logiques ET et OU sont construites en prenant une porte NAND/NOR et en mettant une porte NON sur sa sortie. Il est théoriquement possible d'utiliser uniquement des transistors en série et en parallèle, mais cette solution utilise plus de transistors.
{|
|[[File:CMOS AND Layout.svg|vignette|Porte ET en CMOS]]
|[[File:CMOS OR.svg|vignette|Porte OU en CMOS]]
|}
Pour ce qui est des portes ET/OU avec beaucoup d'entrées, il est fréquent qu'elles soit construites en combinant plusieurs portes ET/OU moins complexes. Par exemple, une porte ET à 32 entrées sera construite à partir de portes à seulement 4 ou 5 entrées. Il existe cependant une alternative qui se marie nettement mieux avec la logique CMOS. Rappelons qu'en logique CMOS, les portes NAND et NOR sont les portes à plusieurs entrées les plus simples à fabriquer. L'idée est alors de combiner des portes NAND/NOR pour créer une porte ET/OU.
Voici la comparaison entre les deux solutions pour une porte ET :
{|
|[[File:12 input AND gate via cascade of AND gates.svg|vignette|ET plusieurs entrées]]
|[[File:12-input AND gate from NAND and NOR.svg|vignette|ET plusieurs entrées]]
|}
Voici la comparaison entre les deux solutions pour une porte OU :
{|
|[[File:12-input OR gate via cascade of OR gates.svg|vignette|OU plusieurs entrées]]
|[[File:12-input OR gate via NOR and NAND gates.svg|vignette|OU plusieurs entrées]]
|}
D'autres portes mélangent transistors en série et en parallèle d'une manière différente. Les portes ET-OU-NON et OU-ET-NON en sont un bon exemple.
===Une méthode générale===
Il existe une méthode générale pour créer des portes logiques à deux entrées. Avec elle, il faut repartir du montage avec deux transistors NMOS/PMOS en série. En théorie, il permet de relier la sortie à la tension d'alimentation/zéro volt si toutes les entrées sont à 0 (PMOS) ou 1 (NMOS). L'idée est de regarder ce qui se passe si on fait précéder l'entrée d'un transistor par une porte NON. Pour deux transistors, cela fait 4 possibilités, 8 au total si on fait la différence entre PMOS et NMOS. Voici les valeurs d'entrées qui ferment le montage à transistor en série, suivant l’endroit où on place la porte NON.
[[File:Transistors CMOS en série.png|centre|vignette|upright=2|Transistors CMOS en série]]
Mine de rien, avec ces 8 montages de base, on peut créer n'importe quelle porte logique à deux entrées. Il faut juste se souvenir que d'après les règles du CMOS, les deux transistors PMOS se placent entre la tension d'alimentation et la sortie, et servent à mettre la sortie à 1. Pour les deux transistors NMOS, ils sont reliés à la masse et mettent la sortie à 0. Pour mieux comprendre, prenons l'exemple d'une porte XOR.
Appliquons la méthode que je viens d'expliquer avec une porte XOR. Le résultat est sous-optimal, mieux vaut fabriquer une porte XOR en combinant d'autres portes logiques, mais c'est pour l'exemple. L'idée est très simple : on prend la table de vérité de la porte logique, et on associe deux transistors en série pour chaque ligne. Regardons d'abord la table de vérité ligne par ligne :
{|class="wikitable"
|-
!Entrée 1!!Entrée 2!!Sortie
|-
||0||0||0
|-
||0||1||1
|-
||1||0||1
|-
||1||1||0
|}
La première ligne a ses deux entrées à 0 et sort un 0. La sortie est à 0, ce qui signifie qu'il faut regarder sur la ligne des transistors NMOS, qui connectent la sortie à la masse. Le montage qui se ferme quand les deux entrées sont à 0 est celui tout en bas à droite du tableau précédent, à savoir deux transistors NMOS avec deux portes NON.
Les deux lignes du milieu ont une entrée à 0 et une à 1, et leur sortie à 1. La sortie à 1 signifie qu'il faut regarder sur la ligne des transistors PMOS, qui connectent la tension d'alimentation à la sortie. Les deux montages avec deux entrées différentes sont les deux situés au milieu, avec deux transistors PMOS et une porte logique.
La dernière ligne a ses deux entrées à 1 et sort un 0. La sortie est à 0, ce qui signifie qu'il faut regarder sur la ligne des transistors NMOS, qui connectent la sortie à la masse. Le montage qui se ferme quand les deux entrées sont à 1 est celui tout en bas à gauche du tableau précédent, à savoir deux transistors NMOS seuls.
En combinant ces quatre montages, on trouve le circuit suivant. Notons qu'il n'y a que deux portes NON marquées en vert et bleu : on a juste besoin d'inverser la première entrée et la seconde, pas besoin de portes en plus. Les portes NOn sont en quelque sorte partagées entre les transistors PMOS et NMOS.
[[File:Cmos xor.svg|centre|vignette|upright=1|class=transparent|Porte XOR en logique CMOS.]]
Si les deux entrées sont à 1, alors les deux transistors en bas à gauche vont se fermer et connecter la sortie au 0 volt, les trois autres groupes ayant au moins un transistor ouvert. Si les deux entrées sont à 0, alors les deux transistors en bas à droite vont se fermer et connecter la sortie au 0 volt, les autres quadrants ayant au moins un transistor ouvert. Et pareil quand les deux bits sont différents : un des deux quadrants aura ses deux transistors fermés, alors que les autres auront au moins un transistor ouvert, ce qui connecte la sortie à la tension d'alimentation.
On peut construire la porte NXOR sur la même logique. Et toutes les portes logiques peuvent se construire avec cette méthode. Le nombre de transistors est alors le même : on utilise 12 transistors au total : 4 paires de transistors en série, 4 transistors en plus pour les portes NON. Que ce soit pour la porte XOR ou NXOR, on économise beaucoup de transistors comparés à la solution naïve, qui consiste à utiliser plusieurs portes NON/ET/OU. Si on ne peut pas faire mieux dans le cas de la porte XOR/NXOR, sachez cependant que les autres portes construites avec cette méthode utilisent plus de transistors que nécessaire. De nombreuses simplifications sont possibles, comme on le verra plus bas.
Dans les faits, la méthode n'est pas utilisée pour les portes XOR. A la place, les portes XOR sont construites à base d'autres portes logiques plus simples, comme des portes NAND/NOR/ET/OU. Le résultat est que l'on a un circuit à 10 transistors, contre 12 avec la méthode précédente.
[[File:CMOS10TrXOR.svg|centre|vignette|Porte XOR en CMOS en 10 transistors.]]
===Les circuits plus complexes (''full adder'', ...)===
Il est possible de fusionner plusieurs portes ET-OU-NON en un seul circuit à transistors CMOS, ce qui permet des simplifications assez impressionnantes. Pour donner un exemple, le schéma suivant compare l'implémentation d'un circuit qui fait un ET entre les deux premières entrées, puis un NOR entre le résultat du ET et la troisième entrée. L'implémentation à droite du schéma avec une porte ET et une porte NOR prend 10 transistors. L'implémentation la plus simple, à gauche du schéma, prend seulement 6 transistors.
[[File:AOI21 complex vs standard gates.svg|centre|vignette|upright=1.5|Porte ET-OU-NON à trois entrées (de type 2-1) à gauche, contre la combinaison de plusieurs portes à droite.]]
Une conséquence est que des circuits assez complexes gagnent à être fabriqués directement avec des transistors. Prenons l'exemple de l'additionneur complet. Une implémentation naïve, avec 5 portes logiques, utilise beaucoup de transistors. Deux portes XOR, deux portes OU et une porte ET, cela dépasse la trentaine de transistors. Faisons le compte : 10 transistors par porte XOR, 6 pour les trois autres portes, cela fait 38 transistors. Les additionneurs des processeurs modernes sont optimisés directement au niveau des transistors, pour leur permettre d'économiser des transistors. Par exemple, l'implémentation suivante en utilise seulement 24 !
[[File:Inverting full adder CMOS 24T.svg|centre|vignette|upright=1.5|Additionneur complet fabriqué avec 24 transistors.]]
Et c'est sans compter que l'additionneur complet naïf n'est pas forcément le top du top en termes de performances. Là encore, une implémentation avec des transistors peut être optimisée pour être plus rapide, notamment au niveau du calcul de la retenue, ou au contraire d'économiser des transistors. Tout dépend de l'objectif visé, certains circuit optimisant à fond pour la vitesse, d'autres pour le nombre de transistors, d'autres font un compromis entre les deux. Les circuits de ce genre sont très nombreux, trop pour qu'on puisse les citer.
==La ''pass transistor logic''==
La '''''pass transistor logic''''' est une forme particulière de technologie CMOS, une version non-conventionnelle. Avec le CMOS normal, la porte de base est la porte NON. En modifiant celle-ci, on arrive à fabriquer des portes NAND, NOR, puis les autres portes logiques. Les transistors sont conçus de manière à connecter la sortie, soit la tension d'alimentation, soit la masse. Avec la ''pass transistor logic'', le montage de base est un circuit interrupteur, qui connecte l'entrée directement sur la sortie. Le circuit interrupteur n'est autre que les portes à transmission vues il y a quelques chapitres.
La ''pass transistor logic'' a été utilisée dans des processeurs commerciaux, comme dans l'ARM1, le premier processeur ARM. Sur l'ARM1, les concepteurs ont décidé d'implémenter certains circuits avec des multiplexeurs. La raison n'est pas une question de performance ou d'économie de transistors, juste que c'était plus pratique à fabriquer, sachant que le processeur était le premier CPU ARM de l'entreprise.
Dans la suite du cours, nous verrons quelques circuits qui utilisent cette technologie, mais ils seront rares. Nous l'utiliserons quand nous parlerons des additionneurs, ou les multiplexeurs, guère plus. Mais il est sympathique de savoir que cette technologie existe.
===La porte à transmission===
Le circuit de base est une '''porte à transmission''', à savoir une porte logique qui agit comme une sorte d'interrupteur, qui s'ouvre ou se ferme suivant ce qu'on met sur l'entrée de commande. Le circuit peut soit connecter l'entrée et la sortie, soit déconnecter la sortie de l'entrée. Le choix entre les deux dépend de l’entrée de commande.
Intuitivement, on se dit qu'une transistor fonctionne déjà comme un interrupteur, mais une porte à transmission est construit avec deux transistors. La raison la plus intuitive est que la logique CMOS fait que tout transistor PMOS doit être associé à un transistor NMOS et réciproquement. Mais une autre raison, plus importante, est que les transistors NMOS et PMOS ne sont pas des interrupteurs parfaits. Les NMOS laissent passer les 0, mais laissent mal passer les 1 : la tension en sortie, pour un 1, est atténuée. Et c'est l'inverse pour les PMOS, qui laissent bien passer les 1 mais fournissent une tension de sortie peut adéquate pour les 0. Donc, deux transistors permettent d'obtenir une tension de sortie convenable. Le montage de base est le suivant :
[[File:CMOS transmission gate.PNG|centre|vignette|upright=1|CMOS Transmission gate]]
Vous remarquerez que le circuit est fondamentalement différent des circuits précédents. Les précédents connectaient la sortie soit à la tension d'alimentation, soit à la masse. Ici, la sortie est connectée sur l'entrée, rien de plus. Il n'y a pas d'alimentation électrique ni de contact à la masse. Retenez ce point, il sera important par la suite.
Les deux entrées A et /A sont l'inverse l'une de l'autre, ce qui fait qu'il faut en théorie rajouter une porte NON CMOS normale, pour obtenir le circuit complet. Mais dans les faits, on arrive souvent à s'en passer. Ce qui fait que la porte à transmission est définie comme étant le circuit à deux transistors précédents.
===Les multiplexeurs 2 vers 1 en ''pass transistor logic''===
Dans les chapitres précédents, nous avions vu que les portes à transmission sont assez peu utilisées. Nous ne nous en sommes servies que dans de rares cas, mais l'un d'entre eux va nous intéresser : les multiplexeurs et les démultiplexeurs. Pour rappel, il est assez simple de fabriquer un multiplexeur 2 vers 1 en utilisant des portes à transmission. L'idée est de relier chaque entrée à la sortie par l'intermédiaire d'une porte à transmission. Quand l'une sera ouverte, l'autre sera fermée. Le résultat n'utilise que deux portes à transmission et une porte NON. Voici le circuit qui en découle :
[[File:Multiplexeur fabriqué avec des portes à transmission et-ou des tampons trois-états.png|centre|vignette|upright=1.5|Multiplexeur fabriqué avec des portes à transmission]]
En utilisant les portes à transmission CMOS vues plus haut, on obtient le circuit suivant :
[[File:Multiplexeur fabriqué avec des portes à transmission.png|centre|vignette|upright=1.5|Multiplexeur fabriqué avec des portes à transmission CMOS.]]
===La ''pass transistor logic'' utilise des multiplexeurs 2 vers 1===
La ''pass transistor logic'' implémente les portes logiques d'une manière assez étonnante : les portes logiques sont basées sur un multiplexeur 2 vers 1 amélioré ! L'idée est d'émuler une porte logique à deux entrées avec un multiplexeur 2 vers 1. Et intuitivement, vous vous dites que les deux entrées de la porte logique correspondent aux deux entrées de donnée du multiplexeur. Mais non, c'est une erreur ! En réalité, un bit d'entrée est envoyé sur l'entrée de commande, et l'autre bit sur une entrée de donnée du multiplexeur. Suivant ce qu'on met sur la seconde entrée du multiplexeur, on obtient une porte ET, OU, XOR, etc. Il y a quatre choix possibles : soit on envoie un 0, soit un 1, soit l'inverse du bit d'entrée, soit envoyer deux fois le bit d'entrée.
[[File:Portes logiques faites à partir de multiplexeurs.png|centre|vignette|upright=2|Portes logiques faites à partir de multiplexeurs]]
Les multiplexeurs 2 vers 1 peuvent aussi être utilisés pour implémenter une bascule D, (pour rappel : une petite mémoire de 1 bit), comme on l'a vu dans les chapitres sur les bascules. Il suffit pour cela de boucler la sortie d'un multiplexeur sur une entrée, en ajoutant deux portes NON dans la boucle pour régénérer le signal électrique.
[[File:Multiplexer-based latch using transmission gates.svg|centre|vignette|upright=2|Implémentation alternative d'une bascule D.]]
===La porte XOR en ''pass transistor logic''===
Il est facile d'implémenter une porte XOR avec un multiplexeur 2 vers 1. Pour rappel, une porte XOR est une sorte d'inverseur commandable, à savoir un circuit prend un bit d'entrée A, et l'inverse ou non suivant la valeur d'un bit de commande B. Un tel circuit commandable n'est autre qu'une porte logique XOR, qui XOR A et B. Et cela nous dit comment implémenter une porte XOR avec un multiplexeur : il suffit de prendre un multiplexeur qui choisit sa sortie parmi deux entrées : A et <math>\overline{A}</math> ! Pour deux bits A et B, l'un est envoyé sur l'entrée de commande, l'autre bit est envoyée sur les deux entrées (le bit sur une entrée, son inverse sur l'autre). Le circuit obtenu, sans les portes NON, est celui-ci :
[[File:XOR gate implemented with pass gates.svg|centre|vignette|upright=1|Porte XOR implémentée avec une porte à transmission.]]
La version précédente est une porte XOR où les signaux d'entrée sont doublés : on a le bit d'entrée original, et son inverse. C'est quelque chose de fréquent dans les circuits en ''pass transistor logic'', où les signaux/bits sont doublés. Mais il est possible de créer des versions normales, sans duplication des bits d'entrée. La solution la plus simple de rajouter deux portes NON, pour inverser les deux entrées. Le circuit passe donc de 4 à 8 transistors, ce qui reste peu. Mais on peut ruser, ce qui donne le circuit ci-dessous. Comme vous pouvez les voir, il mélange porte à transmission et portes NON CMOS normales.
[[File:CmosXORGate.svg|centre|vignette|upright=1|XOR en ''pass transistor logic'']]
Dans les deux cas, l'économie en transistors est drastique comparé au CMOS normal. Plus haut, nous avons illustré plusieurs versions possibles d'une porte XOR en CMOS normal, toutes de 12 transistors. Ici, on va de 6 transistors maximum, à seulement 4 ou 5 pour les versions plus simples. Le gain est clairement significatif, suffisamment pour que les circuits avec beaucoup de portes XOR gagnent à être implémentés avec la ''pass transistor logic''.
Quelques processeurs implémentaient leurs portes XOR en ''pass transistor logic'', alors que les autres portes étaient en CMOS normal. Un exemple est le mythique processeur Z80.
===Les avantages et défauts de la ''pass transistor logic''===
Une porte logique en logique CMOS connecte directement sa sortie sur la tension d'alimentation ou la masse. Mais dans une porte logique en ''pass transistor logic'', il n'y a ni tension d'alimentation, ni masse (O Volts). La sortie d'une porte à transmission est alimentée par la tension d'entrée. Et vu que les transistors ne sont pas parfaits, on a toujours une petite perte de tension en sortie d'une porte à transmission.
Le résultat est que si on enchaine les portes à transmission, la tension de sortie a tendance à diminuer, et ce d'autant plus vite qu'on a enchainé de portes à transmission. Il faut souvent rajouter des portes OUI pour restaurer les tensions adéquates, à divers endroits du circuit. La ''pass transistor logic'' mélange donc porte OUI/NON CMOS normales avec des portes à transmission. Afin de faire des économies de circuit, on utilise parfois une seule porte NON CMOS comme amplificateur, ce qui fait que de nombreux signaux sont inversés dans les circuits, sans que cela ne change grand chose si le circuit est bien conçu.
Par contre, ce défaut entraine aussi des avantages. Notamment, la consommation d'énergie est fortement diminuée. Seules les portes amplificatrices, les portes NON CMOS, sont alimentées en tension/courant. Le reste des circuits n'est pas alimenté, car il n'y a pas de connexion à la tension d'alimentation et la masse. De même, la ''pass transistor logic'' utilise généralement moins de transistors pour implémenter une porte logique, et un circuit électronique en général. L'exemple avec la porte XOR est assez parlant : on passe de 12 à 6 transistors par porte XOR. Des circuits riches en portes XOR, comme les circuits additionneurs, gagnent beaucoup à utiliser des portes à transmission.
==Les technologies PMOS et NMOS==
Dans ce qui va suivre, nous allons voir la technologie NMOS et POMS. Pour simplifier, la technologie NMOS est équivalente aux circuits CMOS, sauf que les transistors PMOS sont remplacés par une résistance. Pareil avec la technologie PMOS, sauf que c'est les transistors NMOS qui sont remplacés par une résistance. Les deux technologies étaient utilisées avant l'invention de la technologie CMOS, quand on ne savait pas comment faire pour avoir à la fois des transistors PMOS et NMOS sur la même puce électronique, mais sont aujourd'hui révolues. Nous en parlons ici, car nous évoquerons quelques circuits en PMOS/NMOS dans le chapitre sur les cellules mémoire, mais vous pouvez considérer que cette section est facultative.
===Le fonctionnement des logiques NMOS et PMOS===
Avec la technologie NMOS, les portes logiques sont fabriqués avec des transistors NMOS intercalés avec une résistance.
[[File:Circuit en logique NMOS.png|centre|vignette|upright=2|Circuit en logique NMOS.]]
Leur fonctionnement est assez facile à expliquer. Quand la sortie doit être à 1, tous les transistors sont ouverts. La sortie est connectée à la tension d'alimentation et déconnectée de la masse, ce qui fait qu'elle est mise à 1. La résistance est là pour éviter que le courant qui arrive dans la sortie soit trop fort. Quand au moins un transistor NMOS qui se ferme, il connecte l'alimentation à la masse, les choses changent. Les lois compliquées de l'électricité nous disent alors que la sortie est connectée à la masse, elle est donc mise à 0.
[[File:Fonctionnement d'un circuit en technologie NMOS.png|centre|vignette|upright=2|Fonctionnement d'un circuit en technologie NMOS.]]
Les circuits PMOS sont construits d'une manière assez similaire aux circuits CMOS, si ce n'est que les transistors NMOS sont remplacés par une résistance qui relie ici la masse à la sortie. Rien d'étonnant à cela, les deux types de transistors, PMOS et NMOS, ayant un fonctionnement inverse.
===Les portes logiques en NMOS et PMOS===
Que ce soit en logique PMOS ou NMOS, les portes de base sont les portes NON, NAND et NOR. Les autres portes sont fabriquées en combinant des portes de base. Voici les circuits obtenus en NMOS et PMOS:
{|class="wikitable flexible"
|-
! colspan="5 | NMOS
|-
| class="transparent" | [[File:NMOS NOT.svg|class=transparent|100px|Porte NON NMOS.]]
| class="transparent" | [[File:NMOS NAND.svg|class=transparent|100px|NMOS-NAND]]
| class="transparent" | [[File:NMOS NOR.png|100px|NMOS-NOR]]
| class="transparent" | [[File:NMOS AND gate.png|100px|NMOS AND]]
| class="transparent" | [[File:NMOS OR gate.png|100px|NMOS OR]]
|-
! colspan="5 | PMOS
|-
| class="transparent" | [[File:PMOS NOT.png|100px|PMOS NOT]]
| class="transparent" | [[File:PMOS NAND corr.png|100px|PMOS NAND]]
| class="transparent" | [[File:PMOS NOR corr.png|100px|PMOS NOR]]
| class="transparent" |
| class="transparent" | [[File:PMOS OR gate.png|100px|PMOS OR]]
|}
====Les portes logiques de base en NMOS====
Le circuit d'une porte NON en technologie NMOS est illustré ci-dessous. Le principe de ce circuit est similaire au CMOS, avec quelques petites différences. Si on envoie un 0 sur la grille du transistor, il s'ouvre et connecte la sortie à la tension d'alimentation à travers la résistance. À l'inverse, quand on met un 1 sur la grille, le transistor se ferme et la sortie est reliée à la masse, donc mise à 0. Le résultat est bien un circuit inverseur.
{|class="wikitable flexible"
|[[File:NMOS NOT.svg|class=transparent|Porte NON NMOS.]]
|[[File:Not.PNG|class=transparent|Porte NON NMOS : fonctionnement.]]
|}
La porte NOR est similaire à la porte NON, si ce n'est qu'il y a maintenant deux transistors en parallèle. Si l'une des grilles est mise à 1, son transistor se fermera et la sortie sera mise à 0. Par contre, quand les deux entrées sont à 0, les transistors sont tous les deux ouverts, et la sortie est mise à 1. Le comportement obtenu est bien celui d'une porte NOR.
{|class="wikitable flexible"
|[[File:NMOS NOR.png|NMOS-NOR-gate]]
|[[File:Funktionsprinzip eines NOR-Gatters.png|class=transparent|Fonctionnement d'une porte NOR NMOS.]]
|}
La porte NAND fonctionne sur un principe similaire au précédent, si ce n'est qu'il faut que les deux grilles soient à zéro pour obtenir une sortie à 1. Pour mettre la sortie à 0 quand seulement les deux transistors sont ouverts, il suffit de les mettre en série, comme dans le schéma ci-dessous. Le circuit obtenu est bien une porte NAND.
{|class="wikitable flexible"
|[[File:NMOS NAND.svg|class=transparent|centre|NMOS-NAND-gate]]
|[[File:Funktionsprinzip eines NAND-Gatters.png|class=transparent|centre|Funktionsprinzip eines NAND-Gatters]]
|}
===Les avantages et inconvénients des technologies CMOS, PMOS et NMOS===
La technologie PMOS et NMOS ne sont pas totalement équivalentes, niveau performances. Ces technologies se distinguent sur plusieurs points : la vitesse des transistors et leur consommation énergétique.
La vitesse des circuits NMOS/PMOS/CMOS dépend des transistors eux-mêmes. Les transistors PMOS sont plus lents que les transistors NMOS, ce qui fait que les circuits NMOS sont plus rapides que les circuits PMOS. Les circuits CMOS ont une vitesse intermédiaire, car ils contiennent à la fois des transistors NMOS et PMOS.
Pour la consommation électrique, les résistances sont plus goumandes que les transistors. En PMOS et NMOS, la résistance est traversée par du courant en permanence, peu importe l'état des transistors. Et résistance traversée par du courant signifie consommation d'énergie, dissipée sous forme de chaleur par la résistance. Il s'agit d'une perte sèche d'énergie, une consommation d'énergie inutile. En CMOS, l'absence de résistance fait que la consommation d'énergie est liée aux transistors, et celle-ci est beaucoup plus faible que pour une résistance.
Les transistors PMOS sont plus simples à fabriquer que les NMOS, ils sont plus simples à sortir d'usine. Les premiers processeurs étaient fabriqués en logique PMOS, plus simple à fabriquer. Puis, une fois la fabrication des circuits NMOS maitrisée, les processeurs sont tous passés en logique NMOS du fait de sa rapidité. La logique CMOS a mis du temps à remplacer les logiques PMOS et NMOS, car il a fallu maitriser les techniques pour mettre à la fois des transistors NMOS et PMOS sur la même puce. Les premières puces électroniques étaient fabriquées en PMOS ou en NMOS, parce qu'on n’avait pas le choix. Mais une fois la technologie CMOS maitrisée, elle s'est imposée en raison de deux gros avantages : une meilleure fiabilité (une meilleure tolérance au bruit électrique), et une consommation électrique plus faible.
==La logique dynamique MOS==
La '''logique dynamique''' permet de créer des portes logiques ou des bascules d'une manière assez intéressante. Et aussi étonnant que cela puisse paraître, le signal d’horloge est alors utilisé pour fabriquer des circuits combinatoires !
===Un transistor MOS peut servir de condensateur===
Les technologies CMOS conventionnelles mettent la sortie d'une porte logique à 0/1 en la connectant à la tension d'alimentation ou à la masse. La logique ''pass transistor'' transfère la tension et le courant de l'entrée vers la sortie. Dans les deux cas, la sortie est connectée directement ou indirectement à la tension d'alimentation quand on veut lui faire sortie un 1. Avec la logique dynamique, ce n'est pas le cas. La sortie est maintenue à 0 ou à 1 en utilisant un réservoir d'électron qui remplace la tension d'alimentation.
En électronique, il existe un composant qui sert de réservoir à électricité : il s'agit du '''condensateur'''. On peut le charger en électricité, ou le vider pour fournir un courant durant une petite durée de temps. Par convention, un condensateur stocke un 1 s'il est rempli, un 0 s'il est vide. L'intérieur d'un condensateur est formé de deux couches de métal conducteur, séparées par un isolant électrique. Les deux plaques de conducteur sont appelées les armatures du condensateur. C'est sur celles-ci que les charges électriques s'accumulent lors de la charge/décharge d'un condensateur. L'isolant empêche la fuite des charges d'une armature à l'autre, ce qui permet au condensateur de fonctionner comme un réservoir, et non comme un simple fil.
Il est possible de fabriquer un pseudo-condensateur avec un transistor MOS. En effet, tout transistor MOS a un pseudo-condensateur caché entre la grille et la liaison source-drain. Pour comprendre ce qui se passe dans ce transistor de mémorisation, il faut savoir ce qu'il y a dans un transistor CMOS. À l'intérieur, on trouve une plaque en métal appelée l'armature, un bout de semi-conducteur entre la source et le drain, et un morceau d'isolant entre les deux. L'ensemble forme donc un condensateur, certes imparfait, qui porte le nom de capacité parasite du transistor. Suivant la tension qu'on envoie sur la grille, l'armature va se remplir d’électrons ou se vider, ce qui permet de stocker un bit : une grille pleine compte pour un 1, une grille vide compte pour un 0.
[[File:Transistor CMOS - 1.png|centre|vignette|upright=2|Anatomie d'un transistor CMOS]]
L'utilisation de transistors MOS comme condensateur n'est pas spécifique à la logique dynamique. Certains mémoires RAM le font, comme nous le verrons dans le chapitre sur les cellules mémoires. Aussi, il est intéressant d'en parler maintenant, histoire de préparer le terrain. D'ailleurs, les mémoires RAM sont remplies de logique dynamique.
===L'utilisation des pseudo-condensateurs en logique dynamique===
Un circuit conçu en logique dynamique contient un transistor est utilisé comme condensateur. Il s’insère entre la tension d'alimentation et la sortie du circuit. Son rôle est simple : lorsqu'on utilise la sortie, le condensateur se vide, ce qui place la sortie à 1. le reste du temps, le condensateur est relié à la tension d'alimentation et se charge. Un circuit en logique dynamique effectue son travail en deux phases : une phase d'inactivité où il remplit ses condensateurs, et une phase où sa sortie fonctionne. Les deux phases sont appelées la '''phase de précharge''' et la '''phase d'évaluation'''. La succession de ces deux phases est réalisée par le signal d'horloge : la première pahse a lieu quand le signal d'horloge est à 1, l'autre quand il est à 0.
====Une porte NAND en logique dynamique CMOS====
Voici un exemple de porte NAND en logique dynamique MOS. La porte est alors réalisée avec des transistors NMOS et PMOS, le circuit ressemble à ce qu'on a en logique NMOS. En bas, on trouve les transistors NMOS pour relier la sortie au 0 volt. Mais au-dessus, on trouve un transistor CMOS qui remplace la résistance. Le fonctionnement du circuit est simple. Quand l'entrée ''clock'' est à 1, le condensateur se charge, les deux transistors NMOS sont déconnectés de la masse et le circuit est inactif. Puis, quand ''clock'' passe à 0, Le transistor PMOS se comporte en circuit ouvert, ce qui déconnecte la tension d'alimentation. Et son pseudo-condensateur se vide, ce qui fournit une tension d'alimentation de remplacement temporaire. Le transistor NMOS du bas se ferme, ce qui fait que les deux transistors A et B décident de si la sortie est connectée au 0 volt ou non. Si c'est le cas, le pseudo-condensateur se vide dans le 0 volt et la sortie est à 0. Sinon, le pseudo-condensateur se vide dans la sortie, ce qui la met à 1.
[[File:Dlnand.svg|centre|vignette|Porte NAND en logique CMOS.]]
====Une bascule D en logique dynamique CMOS====
Il est possible de créer une bascule D en utilisant la logique dynamique. L'idée est de prendre une bascule D normale, mais d'ajouter un fonctionnement en deux étapes en ajoutant des transistors/interrupteurs. Pour rappel, une bascule D normale est composée de deux inverseurs reliés l'un à l'autre en formant une boucle, avec un multiplexeur pour permettre les écritures dans la boucle.
[[File:Implémentation conceptuelle d'une bascule D.png|centre|vignette|upright=2|Implémentation conceptuelle d'une bascule D]]
[[File:Animation du fonctionnement d'une bascule.gif|vignette|upright=2|Animation du fonctionnement de la bascule précédente.]]
Le circuit final ajoute deux transistors entre les inverseurs tête-bêche. Les transistors en question sont reliés à l'horloge, l'un étant ouvert quand l'autre est fermé. Grâce à eux, le bit mémorisé circule d'un inverseur à l'autre : il est dans le premier inverseur quand le signal d'horloge est à 1, dans l'autre inverseur quand il est à 0 (en fait son inverse, comme vous l'aurez compris). Le tout est illustré ci-contre. Cette implémentation a été utilisée autrefois, notamment dans le processeur Intel 8086.
[[File:Bascule D en logique Dynamique, avec entrée Enable.png|centre|vignette|upright=2|Bascule D en logique Dynamique, avec entrée Enable]]
Il existe une variante très utilisée, qui permet de remplacer le multiplexeur par un circuit légèrement plus simple. Avec elle, on a deux entrées pour commander la bascule, et non une seule entrée Enable. L'entrée Enable autorise les écriture, l'entrée Hold ferme la boucle qui relie la sortie du second inverseur au premier. Chaque entrée est associé à un transistor/interrupteur. Le transistor sur lequel on envoie l'entrée Enable se ferme uniquement lors des écritures et reste fermé sinon. A l'inverse, le transistor relié au signal Hold est fermé en permanence, sauf lors des écritures. En clair, les deux signaux sont l'inverse l'un de l'autre. Il permet de fermer le circuit, de bien relier les deux inverseurs en tête-bêche, sauf lors des écritures. On envoie donc l'inverse de l'entrée Enable sur ce transistor.
[[File:Bascule D en logique dynamique.png|centre|vignette|upright=2|Bascule D en logique dynamique]]
Une manière de comprendre le circuit précédent est de le comparer à celui avec le multiplexeur. Le multiplexeur est composé d'une porte NON et de deux transistors. Il se trouve que les deux transistors en question sont placés au même endroit que les transistors connectés aux signaux Hold et Enable. En prenant retirant la porte NON du multiplexeur, on se retrouve avec le circuit. Au lieu de prendre un Signal Enable qui commande les deux transistors, ce qui demande d'ajouter une porte NON vu que les deux transistors doivent faire l'inverse l'un de l'autre, on se contente d'envoyer deux signaux séparés pour commander chaque transistor indépendamment.
===Avantages et inconvénients===
Les circuits en logique dynamique sont opposés aux circuits en logique statique, ces derniers étant les circuits CMOS, PMOS, NMOS ou TTL vu jusqu'à présent. Les circuits dynamiques et statiques ont des différences notables, ainsi que des avantages et inconvénients divers. Si on devait résumer :
* la logique dynamique utilise généralement un peu plus de transistors qu'un circuit CMOS normal ;
* la logique dynamique est souvent très rapide par rapport à la concurrence, car elle n'utilise que des transistors NMOS, plus rapides ;
* la consommation d'énergie est généralement supérieure comparé au CMOS.
Un désavantage de la logique dynamique est qu'elle utilise plus de transistors. On économise certes des transistors MOS, mais il faut rajouter les transistors pour déconnecter les transistors NMOS de la masse (0 volt). Le second surcompense le premier.
Un autre désavantage est que le signal d'horloge ne doit pas tomber en-dessous d'une fréquence minimale. Avec une logique statique, on a une fréquence maximale, mais pas de fréquence minimale. Avec un circuit statique peut réduire la fréquence d'un circuit pour économiser de l'énergie, pour améliorer sa stabilité, et de nombreux processeurs modernes ne s'en privent pas. On peut même stopper le signal d'horloge et figer le circuit, ce qui permet de le mettre en veille, d'en stopper le fonctionnement, etc. Impossible avec la logique dynamique, qui demande de ne pas tomber sous la fréquence minimale. Cela a un impact sur la consommation d'énergie, sans compter que cela se marie assez mal avec certaines applications. Un processeur moderne ne peut pas être totalement fabriqué en logique dynamique, car il a besoin d'être mis en veille et qu'il a besoin de varier sa fréquence en fonction des besoins.
Le dernier désavantage implique l'arbre d'horloge, le système d'interconnexion qui distribue le signal d'horloge à toutes les bascules d'un circuit. L'arbre d'horloge est beaucoup plus compliqué avec la logique dynamique qu'avec la logique statique. Avec la logique statique, seules les bascules doivent recevoir le signal d'horloge, avec éventuellement quelques rares circuits annexes. Mais avec la logique dynamique, toutes les portes logiques doivent recevoir le signal d'horloge, ce qui rend la distribution de l'hrologe beaucoup plus compliquée. C'est un point qui fait que la logique dynamique est assez peu utilisée, et souvent limitée à quelques portions bien précise d'un processeur.
==La logique TTL : un apercu rapide==
Tous ce que nous avons vu depuis le début de ce chapitre porte sur les transistors MOS et les technologies associées. Mais les transistors MOS n'ont pas été les premiers inventés. Ils ont été précédés par les '''transistors bipolaires'''. Nous ne parlerons pas en détail du fonctionnement d'un transistor bipolaire, car celui-ci est extraordinairement compliqué. Cependant, nous devons parler rapidement de la logique TTL, qui permet de fabriquer des portes logiques avec ces transistors bipolaires. Là encore, rassurez-vous, nous n'allons pas voir comment fabriquer des portes logiques en logique TTL, cela serait trop compliqué, sans compter que le but n'est pas de faire un cours d'électronique. Mais nous devons fait quelques remarques et donner quelques explications superficielles.
La raison à cela est double. La première raison est que certains circuits présents dans les mémoires RAM sont fabriqués avec des transistors bipolaires. C'est notamment le cas des amplificateurs de lecture ou d'autres circuits de ce genre. De tels circuits ne peuvent pas être implémentés facilement avec des transistors CMOS et nous expliquerons rapidement pourquoi dans ce qui suit. La seconde raison est que ce cours parlera occasionnellement de circuits anciens et qu'il faut quelques bases sur le TTL pour en parler.
Dans la suite du cours, nous verrons occasionnellement quelques circuits anciens, pour la raison suivante : ils sont très simples, très pédagogiques, et permettent d'expliquer simplement certains concepts du cours. Rien de mieux que d'étudier des circuits réels pour donner un peu de chair à des explications abstraites. Par exemple, pour expliquer comment fabriquer une unité logique de calcul bit à bit, je pourrais utiliser l'exemple du Motorola MC14500B, un processeur 1 bit qui est justement une unité logique sous stéroïdes. Ou encore, dans le chapitre sur les circuits additionneurs, je parlerais du circuit additionneur présent dans l'Intel 8008 et dans l'Intel 4004, les deux premiers microprocesseurs commerciaux. Malheureusement, malgré leurs aspects pédagogiques indéniables, ces circuits ont le défaut d'être des circuits TTL. Ce qui est intuitif : les circuits les plus simples ont été inventés en premier et utilisent du TTL plus ancien. Beaucoup de ces circuits ont été inventés avant même que le CMOS ou même les transistors MOS existent. D'où le fait que nous devons faire quelques explications mineures sur le TTL.
===Les transistors bipolaires===
Les '''transistors bipolaires''' ressemblent beaucoup aux transistors MOS. Les transistors bipolaires ont trois broches, appelées le collecteur, la base et l'émetteur. Notez que ces trois termes sont différents de ceux utilisés pour les transistors MOS, où on parle de la grille, du drain et de la source.
Là encore, comme pour les transistors PMOS et NMOS, il existe deux types de transistors bipolaires : les NPN et les PNP. Là encore, il est possible de fabriquer une puce en utilisant seulement des NPN, seulement des PNP, ou en mixant les deux. Mais les ressemblances s'arrêtent là. La différence entre PNP et NPN tient dans la manière dont les courants entrent ou sortent du transistor. La flèche des symboles ci-dessous indique si le courant rentre ou sort par l'émetteur : il rentre pour un PNP, sort pour un NPN. Dans la suite du cours, nous n'utiliserons que des transistors NPN, les plus couramment utilisés.
{|
|[[File:BJT PNP symbol.svg|vignette|BJT PNP]]
|[[File:BJT NPN symbol.svg|vignette|BJT NPN]]
|}
Plus haut nous avons dit que les transistors CMOS sont des interrupteurs. La réalité est que tout transistor peut être utilisé de deux manières : soit comme interrupteur, soit comme amplificateur de tension/courant. Pour simplifier, le transistor bipolaire NPN prend en entrée un courant sur sa base et fournit un courant amplifié sur l'émetteur. Pour s'en servir comme amplificateur, il faut fournir une source de courant sur le collecteur. Le fonctionnement exact est cependant plus compliqué.
[[File:Transistor bipolaire, explication simplifiée de son fonctionnement.png|centre|vignette|upright=1.5|Transistor bipolaire, explication simplifiée de son fonctionnement]]
Les transistors bipolaires sont de bons amplificateurs, mais de piètres interrupteurs. A l'inverse, les transistors CMOS sont généralement de bons interrupteurs, mais de moyens amplificateurs. Pour des circuits numériques, la fonction d'interrupteur est clairement plus adaptée, car elle-même binaire (un transistor est fermé ou ouvert : deux choix possibles). Aussi, les circuits modernes privilégient des transistors CMOS aux transistors bipolaires. A l'inverse, la fonction d'amplification est adaptée aux circuits analogiques.
C'est pour ça que nous rencontrerons les transistors bipolaires soit dans des portions de l'ordinateur qui sont au contact de circuits analogiques. Pensez par exemple aux cartes sons ou au vieux écrans cathodiques, qui gèrent des signaux analogiques (le son pour la carte son, les signaux vidéo analogique pour les vieux écrans). On les croisera aussi dans les mémoires DRAM, dont la conception est un mix entre circuits analogiques et numériques. Nous les croiserons aussi dans de vieux circuits antérieurs aux transistors MOS. Les anciens circuits faisaient avec les transistors bipolaires car ils n'avaient pas le choix, mais ils ont été partiellement remplacés dès l'apparition des transistors CMOS.
===Les portes logiques complexes en TTL===
Le détail le plus important qui nous concernera dans la suite du cours est le suivant : on peut créer des portes logiques exceptionnellement complexes en TTL. Pour comprendre pourquoi, sachez qu'il existe des transistors bipolaires qui possèdent plusieurs émetteurs. Ils sont très utilisés pour fabriquer des portes logiques à plusieurs entrées. Les émetteurs correspondent alors à des entrées de la porte logique. Ainsi, une porte logique à plusieurs entrées se fait non pas en ajoutant des transistors, comme c'est le cas avec les transistors MOS, mais en ajoutant un émetteur sur un transistor. Cela permet à une porte NAND à trois entrées de n'utiliser que deux transistors bipolaires, au lieu de quatre transistors MOS.
[[File:Multiemitter Transistor.svg|centre|vignette|upright=1|Transistor bipolaire avec plusieurs émetteurs.]]
De plus, là où les logiques PMOS/NMOS/CMOS permettent de fabriquer les portes de base que nous avons précédemment, elles ne peuvent pas faire plus. Au pire, on peut implémenter des portes ET/OU/NAND/NOR à plusieurs entrées, mais pas plus. En TTL, on peut parfaitement créer des portes de type ET/OU/NON ou OU/ET/NON, avec seulement quatre transistors. Par exemple, une '''porte ET/OU/NON''' de type 2-2 entrées (pour rappel, qui effectue un ET par paire d’entrée puis fait un NOR entre le résultat des deux ET) est bien implémenté en une seule porte logique, pas en enchainant deux ou trois portes à la suite.
[[File:TTL AND-OR-INVERT 1961.png|centre|vignette|upright=2|TTL AND-OR-INVERT 1961]]
===Les désavantages et avantages des circuits TTL===
Pour résumer, le TTL à l'avantage de pouvoir fabriquer des portes logiques avec peu de transistors comparé au CMOS, surtout pour les portes logiques complexes. Et autant vous dire que les concepteurs de puce électroniques ne se gênaient pas pour utiliser ces portes complexes, capables de fusionner 3 à 5 portes en une seule : les économies de transistors étaient conséquentes.
Et pourtant, les circuits TTL étaient beaucoup plus gros que leurs équivalents CMOS. La raison est qu'un transistor bipolaire prend beaucoup de place : il est environ 10 fois plus gros qu'un transistor MOS. Autant dire que les économies réalisées avec des portes logiques complexes ne faisaient que compenser la taille énorme des transistors bipolaires. Et encore, cette compensation n'était que partielle, ce qui fait que les circuits PMOS/NMOS/CMOS se miniaturisent beaucoup plus facilement. Un avantage pour le transistor MOS !
De plus, les schémas précédents montrent que les portes logiques en TTL utilisent une résistance, elle aussi difficile à miniaturiser. Et cette résistance est parcourue en permanence par un courant, ce qui fait qu'elle consomme de l'énergie et chauffe. C'est la même chose en logique NMOS et PMOS, ce qui explique leur forte consommation d'énergie. Les circuits TTL ont donc le même problème.
[[File:TTL Input voltage.svg|vignette|upright=0.5|TTL voltage.]]
Un autre défaut est lié à la une tension d'alimentation. Les circuits TTL utilisent une tension d'alimentation de 5 volts, alors que les circuits CMOS ont une tension d'alimentation beaucoup plus variable. Les circuits CMOS vont de 3 volts à 18 volts pour les circuits commerciaux, avec des tensions de 1 à 3 volts pour les circuits optimisés. Les circuits CMOS sont généralement bien optimisés et utilisent une tension d'alimentation plus basse que les circuits TTL, ce qui fait qu'ils consomment moins d'énergie et de courant.
De plus, rappelons que coder un zéro demande que la tension soit sous un seuil, alors que coder un 1 demande qu'elle dépasse un autre seuil, avec une petite marge de sécurité entre les deux. Les seuils en question sont indiqués dans le diagramme ci-dessous. Il s'agit des seuils VIH et VIL. On voit que sur les circuits TTL, la marge de sécurité est plus faible qu'avec les circuits CMOS. De plus, les marges sont bien équilibrées en CMOS, à savoir que la marge de sécurité est en plein milieu entre la tension max et le zéro volt. Avec le TTL normal, la marge de sécurité est très proche du zéro volt. Un 1 est codé par une tension entre 2 et 5 volts en TTL ! Une version améliorée du TTL, le LVTTL, corrige ce défaut. Elle baisse la tension d'alimentation à 3,3 Volts, mais elle demande des efforts de fabrication conséquents.
[[File:Niveaux logiques CMOS-TTL-LVTTL.png|centre|vignette|upright=2|Niveaux logiques CMOS-TTL-LVTTL]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de conversion analogique-numérique
| prevText=Les circuits de conversion analogique-numérique
| next=Les circuits intégrés
| nextText=Les circuits intégrés
}}
{{autocat}}
</noinclude>
3fvi15ah7gbdjeepvf0vfzu3g2qxuqs
Utilisateur:User h5
2
72790
744731
611181
2025-06-14T17:24:18Z
User h5
82623
744731
wikitext
text/x-wiki
ID <br>
[[w:Anne|Anne]] / Анна <br>
[[w:Psychologue|Psychologue]] / психолог <br>
[[w:Moscou|Moscou]] / Москва <br>
[[w:1977|1977]] <br>
<br>
[[b:en:User:User h5|english]] / [[b:fr:Utilisateur:User h5|françeis]] / [[b:ru:Участник:User h5|русский]]<br>
[[w:fr:Utilisateur:User h5|Wikipedia]] / [[b:fr:Utilisateur:User h5|Wikibooks]] / [[v:fr:Utilisateur:User h5|Wikiversity]]
83t0vfco9tdgaayxsuj3s7bpyypziku
744732
744731
2025-06-14T17:25:04Z
User h5
82623
744732
wikitext
text/x-wiki
ID <br>
[[w:Anne|Anne]] / Анна <br>
[[w:Psychologue|Psychologue]] / психолог <br>
[[w:Moscou|Moscou]] / Москва <br>
[[w:1977|1977]] <br>
<br>
[[b:en:User:User h5|english]] / [[b:fr:Utilisateur:User h5|françeis]] / [[b:ru:Участник:User h5|русский]]
<br>
[[w:fr:Utilisateur:User h5|Wikipedia]] / [[b:fr:Utilisateur:User h5|Wikibooks]] / [[v:fr:Utilisateur:User h5|Wikiversity]]
9d50uiyl3zo6huszofe6bft14pwj6g4
744733
744732
2025-06-14T17:27:20Z
User h5
82623
744733
wikitext
text/x-wiki
ID <br>
[[w:Anne|Anne]] / Анна <br>
[[w:Psychologue|Psychologue]] / психолог <br>
[[w:Moscou|Moscou]] / Москва <br>
[[w:1977|1977]] <br>
<br>
[[b:en:User:User h5|english]] / [[b:fr:Utilisateur:User h5|françeis]] / [[b:ru:Участник:User h5|русский]]<br>
[[w:fr:Utilisateur:User h5|Wikipedia]] / [[b:fr:Utilisateur:User h5|Wikibooks]] / [[v:fr:Utilisateur:User h5|Wikiversity]]
83t0vfco9tdgaayxsuj3s7bpyypziku
744734
744733
2025-06-14T17:28:48Z
User h5
82623
744734
wikitext
text/x-wiki
ID <br>
[[w:Anne|Anne]] / Анна <br>
[[w:Psychologue|Psychologue]] / психолог <br>
[[w:Moscou|Moscou]] / Москва <br>
[[w:1977|1977]] <br>
<br>
[[b:en:User:User h5|english]] / [[b:fr:Utilisateur:User h5|françeis]] / [[b:ru:Участник:User h5|русский]]
<br>
[[w:fr:Utilisateur:User h5|Wikipedia]] / [[b:fr:Utilisateur:User h5|Wikibooks]] / [[v:fr:Utilisateur:User h5|Wikiversity]]
9d50uiyl3zo6huszofe6bft14pwj6g4
744735
744734
2025-06-14T17:29:00Z
User h5
82623
744735
wikitext
text/x-wiki
ID <br>
[[w:Anne|Anne]] / Анна <br>
[[w:Psychologue|Psychologue]] / психолог <br>
[[w:Moscou|Moscou]] / Москва <br>
[[w:1977|1977]] <br>
<br>
[[b:en:User:User h5|english]] / [[b:fr:Utilisateur:User h5|françeis]] / [[b:ru:Участник:User h5|русский]]
[[w:fr:Utilisateur:User h5|Wikipedia]] / [[b:fr:Utilisateur:User h5|Wikibooks]] / [[v:fr:Utilisateur:User h5|Wikiversity]]
29cm8t4xytzhep9dyh0lmx0qph2rj8y
Fonctionnement d'un ordinateur/Quelques exemples de bus et de liaisons point à point
0
72819
744736
711286
2025-06-14T18:29:37Z
Mewtow
31375
/* Le Plug And Play */
744736
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de typoe série, là où le PCI est un bus parallèle ! Il s'agit là de différences fondamentales : d'un côté le PCI utilise un bus partagé de 32 bits, de l'autre le PCI-E utilise des liaisons point à point série.
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Evidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
dve2ergg9qhz0xgv1mkuj3qgbvum7gf
744737
744736
2025-06-14T18:32:41Z
Mewtow
31375
/* Le PCI-Express */
744737
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de typoe série, là où le PCI est un bus parallèle ! Il s'agit là de différences fondamentales : d'un côté le PCI utilise un bus partagé de 32 bits, de l'autre le PCI-E utilise des liaisons point à point série.
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles.
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
dra03nrtbxr8r2x9ouz5jvbpdezum0j
744738
744737
2025-06-14T18:34:21Z
Mewtow
31375
/* La topologie des liaisons PCI-E */
744738
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de typoe série, là où le PCI est un bus parallèle ! Il s'agit là de différences fondamentales : d'un côté le PCI utilise un bus partagé de 32 bits, de l'autre le PCI-E utilise des liaisons point à point série.
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
8g8fftim7ir5d5wd8gvk48abn2x7i6p
744739
744738
2025-06-14T18:37:08Z
Mewtow
31375
/* Le PCI-Express */
744739
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de typoe série, là où le PCI est un bus parallèle ! Il s'agit là de différences fondamentales : d'un côté le PCI utilise un bus partagé de 32 bits, de l'autre le PCI-E utilise des liaisons point à point série.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
fzs3j23l1qp1oecu8m86p9hw9sq3jlc
744740
744739
2025-06-14T18:44:37Z
Mewtow
31375
/* Le PCI-Express */
744740
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
op1idjpaeyiqoeiy0c4anrb3f8onlnr
744741
744740
2025-06-14T18:53:29Z
Mewtow
31375
/* La topologie des liaisons PCI-E */
744741
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCI-E : ''lanes'', ports et signaux===
Une connexion entre un périphérique et le ''Root Complex'' est appelée un '''port PCI-E'''. Vous vous imaginez sans doute qu'il s'agit d'une connexion série entre les deux, mais pas vraiment. Il s'agit d'un concept de haut niveau, la connexion réelle entre les deux peut utiliser une liaison série, ou plusieurs.
Une connexion série entre ''root complex'' et périphérique PCI-E est appelée une '''''lane''''' . Elle contient deux liaisons séries, chacun utilisant une paire différentielle. Une liaison transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison autorise les transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux fils série.
Un port PCI-E contient une ou plusieurs ''lanes''. Typiquement, un port PCI-E peut contenir 1, 2, 4, 8 ou 16 ''lanes''. Le débit binair du port augmente avec le nombre de ''lanes''. Un port à une seule lane Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16.
[[File:PCI Express Terminology.svg|centre|vignette[upright=2|Terminologie des liaisons PCI Express.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
19zhkycuqcsjv29m5gn85s7zgkp7zdh
744742
744741
2025-06-14T18:53:39Z
Mewtow
31375
/* Les liaisons PCI-E : lanes, ports et signaux */
744742
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCI-E : ''lanes'', ports et signaux===
Une connexion entre un périphérique et le ''Root Complex'' est appelée un '''port PCI-E'''. Vous vous imaginez sans doute qu'il s'agit d'une connexion série entre les deux, mais pas vraiment. Il s'agit d'un concept de haut niveau, la connexion réelle entre les deux peut utiliser une liaison série, ou plusieurs.
Une connexion série entre ''root complex'' et périphérique PCI-E est appelée une '''''lane''''' . Elle contient deux liaisons séries, chacun utilisant une paire différentielle. Une liaison transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison autorise les transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux fils série.
Un port PCI-E contient une ou plusieurs ''lanes''. Typiquement, un port PCI-E peut contenir 1, 2, 4, 8 ou 16 ''lanes''. Le débit binair du port augmente avec le nombre de ''lanes''. Un port à une seule lane Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
bmrj88jabhc2jfwt4ikdi7ukowqqonq
744743
744742
2025-06-14T18:55:34Z
Mewtow
31375
/* Les liaisons PCI-E : lanes, ports et signaux */
744743
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCI-E : ''lanes'', ports et signaux===
Une connexion entre un périphérique et le ''Root Complex'' est appelée un '''port PCI-E'''. Vous vous imaginez sans doute qu'il s'agit d'une connexion série entre les deux, mais pas vraiment. Il s'agit d'un concept de haut niveau, la connexion réelle entre les deux peut utiliser une liaison série, ou plusieurs.
Une connexion série entre ''root complex'' et périphérique PCI-E est appelée une '''''lane''''' . Elle contient deux liaisons séries, chacun utilisant une paire différentielle. Une liaison transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison autorise les transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux fils série.
Un port PCI-E contient une ou plusieurs ''lanes''. Typiquement, un port PCI-E peut contenir 1, 2, 4, 8 ou 16 ''lanes''. Le débit binaire du port augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Il faut noter qu'il existe différents connecteurs PCI-E, qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
7ovg40qazeztbiilamlvjpcxc0yltb4
744744
744743
2025-06-14T19:11:46Z
Mewtow
31375
/* Les liaisons PCI-E : lanes, ports et signaux */
744744
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCI-E : ''lanes'', ports et signaux===
Une liaison série PCI-E contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCI-E, il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCI-E peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCI-E et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCI-E est appelée un '''port PCI-E'''.
Un port PCI-E contient donc une ou plusieurs ''lanes''. Typiquement, un port PCI-E peut contenir 1, 2, 4, 8 ou 16 ''lanes''. Le débit binaire du port augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Il faut noter qu'il existe différents connecteurs PCI-E, qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
rfjihx4pijihnfsq9vmvfcxoyfvo786
744745
744744
2025-06-14T19:12:02Z
Mewtow
31375
/* Les liaisons PCI-E : lanes, ports et signaux */
744745
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCI-E : ''lanes'', ports et signaux===
Une liaison série PCI-E contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCI-E, il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCI-E peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCI-E et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCI-E est appelée un '''port PCI-E''', un ''link PCI-E''.
Un port PCI-E contient donc une ou plusieurs ''lanes''. Typiquement, un port PCI-E peut contenir 1, 2, 4, 8 ou 16 ''lanes''. Le débit binaire du port augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Il faut noter qu'il existe différents connecteurs PCI-E, qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
k7i726khvg1vj6a4z9i5cjz5zt10eta
744746
744745
2025-06-14T19:18:50Z
Mewtow
31375
/* Les liaisons PCI-E : lanes, ports et signaux */
744746
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
nu3p3skorlzw152s6awbjy69i6hlf9v
744747
744746
2025-06-14T20:59:25Z
Mewtow
31375
/* Les liaisons PCIe : lanes, ports et signaux */
744747
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
fqon3ltb7ui9aabufocttkh09cquwel
744748
744747
2025-06-14T21:00:52Z
Mewtow
31375
/* Les liaisons PCIe : lanes, ports et signaux */
744748
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
8cnmj34gl507cvb4clxn2u0fayfgh42
744749
744748
2025-06-14T21:06:40Z
Mewtow
31375
/* Les liaisons PCIe : lanes, ports et signaux */
744749
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame, et 6 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=2|PCI-to-PCIe]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
5bsipjhkzxa82j572tsq9ks6ryhicn8
744750
744749
2025-06-14T21:09:09Z
Mewtow
31375
/* Les trames PCIe */
744750
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 6 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
p13agvn1zi9xnsf4jj6izey8orfs0vg
744751
744750
2025-06-14T21:11:49Z
Mewtow
31375
/* Les trames PCIe */
744751
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
4u01m7o7tj80bmqd2c9mtkutwemzxix
744752
744751
2025-06-14T21:17:06Z
Mewtow
31375
/* Les trames PCIe */
744752
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc. Les commandes les plus simples envoient une donnée vers un récepteur, sans rien de plus. Mais il existe aussi des commandes qui demandent de recevoir un ACK, un recu qui indique que la donnée a été transmise sans problème. Le PCIe gére pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Les écritures demandent souvent un ACK, pas les lectures.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
9yjy3mdj7hbcdsxbvfqki10tuy4nflv
744753
744752
2025-06-14T21:21:29Z
Mewtow
31375
/* Les trames PCIe */
744753
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
Le point est que le PCIe gére des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, elle peut être retransmise plus tard. Le périphérique utilisera alors les numéros de trame pour savoir comment remettre les trames dans l'ordre.
Pour gérer cela, le protocole PCIe demande à ce que le périphérique puisse préciser qu'une trame a bien été reçue, qu'il n'y a pas eu de problème de transmission. Pour cela, le protocole PCIe incorpore des commandes qui demandent de recevoir un ACK, un recu qui indique que la donnée a été transmise sans problème. Le PCIe gére pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
g8siyq0w66euv1zonkp69e520bazy4r
744755
744753
2025-06-14T21:27:13Z
Mewtow
31375
/* Les trames PCIe */
744755
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gére des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive. De plus, les trames suivant la trame fautive sont annulées. Pour cela, la commande NACK précise le numéro de la trame fautive. Ainsi, si l'émetteur a envoyé plusieurs trames avant de recevoir un NACK, il saura quelles commandes ont été envoyées en trop.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
3xqvz50m2w2qgy3v9qn1li4tajzy4j4
744756
744755
2025-06-14T21:29:15Z
Mewtow
31375
/* Les trames PCIe */
744756
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gére des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
rkb80c77gt9um0gjipe93mjdpnkppq0
744757
744756
2025-06-14T21:30:31Z
Mewtow
31375
/* Les trames PCIe */
744757
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gére des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
qwe57egy4ppnet0vp5ioe3dyfuyb88r
744758
744757
2025-06-14T21:35:18Z
Mewtow
31375
/* Les trames PCIe */
744758
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gére des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
22i5ewp477xlzd1usqm3b5ae6wiuiy0
744759
744758
2025-06-14T21:39:38Z
Mewtow
31375
/* Les trames PCIe */
744759
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
qrxib575cb2m435tswf1ys6d0arj7fd
744760
744759
2025-06-14T21:53:32Z
Mewtow
31375
/* La topologie des liaisons PCI-E */
744760
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
Le PCIe autorise deux types de transactions : les transferts DMA entre un périphérique et la RAM, un transfert entre deux périphériques. Les transferts entre périphériques peuvent passer par l'intermédiaire du ''root complex'', sans faire intervenir le processeur ou la mémoire RAM.
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
9ssohoz99hd15cbm6yo3fdm0xiu4o93
744761
744760
2025-06-14T21:53:47Z
Mewtow
31375
/* La topologie des liaisons PCI-E */
744761
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
qrxib575cb2m435tswf1ys6d0arj7fd
744762
744761
2025-06-14T21:55:15Z
Mewtow
31375
/* Les trames PCIe */
744762
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les trames PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions), etc.
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Les commandes envoyées sur les liaisons PCIe sont standardisées et chacune correspond à un type de trame. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Le PCIe autorise deux types de transactions : les transferts DMA entre un périphérique et la RAM, un transfert entre deux périphériques. Les transferts entre périphériques peuvent passer par l'intermédiaire du ''root complex'', sans faire intervenir le processeur ou la mémoire RAM. Pour déterminer qui est le destinataire d'une commande, celle-ci intègre l'adresse du destinataire. Le ''root complex'' reçoit la commande, extrait cette adresse, et détermine qui est le destinataire de la commande.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
e5id7lhlcpkqod1ue5ekv1y4gqqmbx6
744763
744762
2025-06-14T21:55:46Z
Mewtow
31375
/* Les trames PCIe */
744763
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les commandes PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Le PCIe autorise deux types de transactions : les transferts DMA entre un périphérique et la RAM, un transfert entre deux périphériques. Les transferts entre périphériques peuvent passer par l'intermédiaire du ''root complex'', sans faire intervenir le processeur ou la mémoire RAM. Pour déterminer qui est le destinataire d'une commande, celle-ci intègre l'adresse du destinataire. Le ''root complex'' reçoit la commande, extrait cette adresse, et détermine qui est le destinataire de la commande.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
a6xj8uz7vqnft9bsb9zeivgrzezoxa8
744764
744763
2025-06-14T21:59:25Z
Mewtow
31375
/* Les commandes PCIe */
744764
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI-Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paire différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7.563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les commandes PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Le PCIe autorise deux types de transactions : les transferts DMA entre un périphérique et la RAM, un transfert entre deux périphériques. Les transferts entre périphériques peuvent passer par l'intermédiaire du ''root complex'', sans faire intervenir le processeur ou la mémoire RAM. Pour déterminer qui est le destinataire d'une commande, celle-ci intègre l'adresse du destinataire. Le ''root complex'' reçoit la commande, extrait cette adresse, et détermine qui est le destinataire de la commande.
Il existe des commandes de gestion de l'alimentation, afin de configurer le récepteur pour le mettre en veille, activer certaines fonctionnalités d'économie d'énergie, etc. Il existe aussi des commandes de '''contrôle de flux'''. Elle permettent de gérer l'occupation des liaisons PCIe. Un périphérique PCIe reçoit des commandes et les accumule dans une mémoire tampon, en attendant de les traiter. L'émetteur peut envoyer des commandes pour savoir si cette mémoire tampon est pleine, en passe de l'être, vide, globalement vide, etc. Il adapte alors ses transferts en fonction.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ tyupe qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quel est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
0zwc47entyk38izwt0rxxb0ihxuumrt
744765
744764
2025-06-14T22:02:17Z
Mewtow
31375
/* Le PCI-Express */
744765
wikitext
text/x-wiki
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.
==Un exemple de liaison point-à-point série : le port série RS-232==
Le port RS-232 est une liaison point à point de type série, utilisée justement sur les ports série qu'on trouvait à l'arrière de nos PC. Celui-ci était autrefois utilisé pour les imprimantes, scanners et autres périphériques du même genre, et est encore utilisé comme interface avec certaines cartes électroniques. Il existe des cartes d'extension permettant d'avoir un port série sur un PC qui n'en a pas, se branchant sur un autre type de port (USB en général).
===Le câblage de la liaison série RS-232===
Le RS-232 est une liaison point à point de type ''full duplex'', ce qui veut dire qu'elle est bidirectionnelle. Les données sont transmises dans les deux sens entre deux composants. Si la liaison est bidirectionnelle, les deux composants ont cependant des rôles asymétriques, ce qui est assez original. Un des deux composants est appelé le ''Data Terminal Equipment'' (DTE), alors que l'autre est appelé le ''Data Circuit-terminating Equipment'' (DCE). Les connecteurs pour ces deux composants sont légèrement différents. Mais mettons cela de côté pour le moment. En raison, de son caractère bidirectionnel, on devine que la liaison RS-232 est composée de deux fils de transmission de données, qui vont dans des sens opposés. À ces deux fils, il faut ajouter la masse, qui est commune entre les deux composants.
[[File:RS-232 Network.png|centre|vignette|upright=2|Liaison point à point RS-232.]]
Certains périphériques RS-232 n'avaient pas besoin d'une liaison bidirectionnelle et ne câblaient pas le second fil de données, et se contentaient d'un fil et de la masse. À l'inverse, d'autres composants ajoutaient d'autres fils, définis par le standard RS-232, pour implémenter un protocole de communication complexe. C'était notamment le cas sur les vieux modems connectés sur le ports série. Généralement, 9 fils étaient utilisés, ce qui donnait un connecteur à 9 broches de type DE-9.
[[File:RS-232 Pinouts for the DE-9 Connector.svg|centre|vignette|upright=2|Connecteur DE-9 et broches RS-232.]]
===La trame RS-232===
Le bus RS-232 est un bus série asynchrone. Une transmission sur ce bus se résume à l'échange d'un octet de donnée. La trame complète se décompose en un bit de ''start'', l'octet de 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.
[[File:Constitution trame uart.png|centre|vignette|upright=2|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 envoyées 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 connecté à 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''.
==Un exemple de bus série : le bus I²c==
Nous allons maintenant voir le fameux '''bus I²c'''. Il s'agit d'un bus série, qui utilise deux fils pour le transport des données et de l'horloge, nommés respectivement SDA (''Serial Data Line'') et SCL (''Serial Clock Line''). Chaque composant compatible I²c a donc deux broches, une pour le fil SDA et une autre pour le fil SCL.
===La spécification électrique===
Les composants I²c ont des entrées et sorties qui sont dites à drain ouvert. Pour rappel, cela veut dire qu'une broche peut mettre le fil à 0 ou le laisser à son état de repos, mais ne peut pas décider de mettre le fil à 1. Pour compenser, les fils sont connectés à la tension d'alimentation à travers une résistance, ce qui garantit que l'état de repos soit à 1.
: Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail aura son importance par la suite. Le contenu du fil peut être lu sans altérer l'état électrique du bus/fil.
[[File:I2C Architecture 2.svg|centre|vignette|upright=2|Bus I2C.]]
En faisant cela, le nombre de composants que l'on peut placer sur le bus est surtout limité par les spécifications électriques du bus, notamment sa capacité. Mais cela a l'avantage que le bus est compatible avec des technologies de fabrication totalement différentes, qu'il s'agisse de composants TTL, CMOS ou autres. En effet, la tension d'alimentation des composants TTL n'est pas la même que celle des composants CMOS. Utiliser des entrées-sorties à drain ouvert fait que la spécification du bus I²c ne spécifie pas la tension d'alimentation du bus, mais la laisse au choix du concepteur. En clair, on peut connecter plusieurs composants TTL sur un même bus, ou plusieurs composants CMOS sur le même bus, mais on ne peut pas connecter composants TTL et CMOS avec des tensions d'alimentation différentes sur un même bus. La compatibilité est donc présente, même si elle n'est pas parfaite.
===L'adressage sur le bus I²c===
Chaque composant connecté à un bus I²c a une adresse unique, qui sert à l’identifier. Les mémoires I²c ne font pas exception. Les adresses I²c sont codées sur 7 bits, ce qui donne un nombre de 128 adresses distinctes. Certaines adresses sont cependant réservées et ne peuvent pas être attribuées à un composant. C'est le cas des adresses allant de 0000 0000 à 0000 0111 et des adresses allant de 1111 1100 à 1111 1111, ce qui fait 8 + 4 = 12 adresses réservées. Les adresses impaires sont des adresses de lecture, alors que les adresses paires sont des adresses d'écriture. En tout, cela fait donc 128 - 12 = 116 adresses possibles, dont 2 par composant, ce qui fait 58 composants maximum.
===Le codage des trames sur un bus I²c===
Le codage d'une trame I²c est assez simple. La trame de données est organisée comme suit : un bit de START, suivi de l'octet à transmettre, suivi par un bit d'ACK/NACK, et enfin d'un bit de STOP. Le bit d'ACK/NACK indique si le récepteur a bien reçu la donnée sans erreurs. Là où les bits START, STOP et de données sont émis par l'émetteur, le bit ACK/NACK est émis par le récepteur.
Vous êtes peut-être étonné par la notion de bit START et STOP et vous demandez comment ils sont codés. La réponse est assez simple quand on se rappelle que les fils SDA et SCL sont mis à 1 à l'état de repos. L'horloge n'est active que lors du transfert effectif des données, et reste à 1 sinon. Si SDA et SCL sont à 1, cela signifie qu'aucun composant ne veut utiliser le bus. Le début d'une transmission demande donc qu'au moins un des fils passe à 0. 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.
{|class="wikitable flexible"
|[[File:I2C START.svg|class=transparent|Bit START.]]
|[[File:I2C RESTART.svg|class=transparent|Bit RESTART]]
|[[File:I2C STOP.svg|class=transparent|Bit STOP.]]
|}
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, car elle n'est valide que pour la transmission d'un bit de données (et les bits d'ACK/NACK). Les bits START et STOP correspondent à une violation de cette règle qui veut qu'il y ait absence de front sur le signal de données entre deux fronts d'horloge.
{|class="wikitable flexible"
|[[File:I2C Encodage.svg|class=transparent|Encodage des données.]]
|[[File:I2C ACK.svg|class=transparent|Bit ACK/NACK.]]
|}
Pour résumer, une transmission I²c est schématisée ci-dessous. Sur ce schéma, S représente le marqueur de début de transmission (''start''), puis chaque période en bleue est celle ou la ligne de donnée peut changer d'état pour le prochain bit de données à transmettre durant la période verte qui suit notée B1, B2... jusqu'à la période finale notée P marquant la fin de transmission (''stop'').
[[File:I2C data transfer.svg|centre|vignette|upright=2|Transfert de données via le protocole I²c.]]
Une trame transmet soit une donnée, soit une adresse. Généralement, la trame transmet un octet, qu'il s'agisse d'un octet de données ou un octet d'adresse. Pour une adresse, l'octet transmis contient une adresse de 7 bits et un bit R/W. Une lecture/écriture est composée de au moins deux transmissions : d'abord on transmet l'adresse, puis la donnée est transmise ensuite. Si je viens de dire "au moins deux transmissions", c'est parce qu'il est possible de lire/écrire des données de 16 ou 32 bits, en plusieurs fois. Dans ce cas, on envoie l'adresse avec la première transmission, puis on envoie/réceptionne plusieurs octets les uns à la suite des autres, avec une transmission par octet. Il est aussi possible d'envoyer une adresse en plusieurs fois,c e qui est très utilisé pour les mémoires I²c : la première adresse envoyée permet de sélectionner la mémoire, l'adresse suivante identifie le ''byte'' voulu dans la mémoire.
[[File:I2c 7bit telegram.png|centre|vignette|upright=3|Transmission de I²c en lecture/écriture.]]
===La synchronisation sur le bus I²c===
Il arrive que des composants lents soient connectés à un bus I²c, comme des mémoires EEPROM. Ils mettent typiquement un grand nombre de cycles avant de faire ce qu'on leur demande, ce qui donne un temps d'attente particulièrement long. Dans ce cas, les transferts de ou vers ces composants doivent être synchronisés d'une manière ou d'une autre. Pour cela, le bus I²c permet de mettre en pause une transmission tant que le composant lent n'a pas répondu, en allongeant la durée du bit d'ACK.
Un périphérique normal répondrait à une transmission comme on l'a vu plus haut, avec un bit ACK. Pour cela, le récepteur met la ligne SDA à 0 pendant que l'horloge SCL est à 1. L'idée est qu'un récepteur lent peut temporairement maintenir la ligne SCL à 0 pendant toute la durée d'attente. Dans ce cas, l'émetteur attend un nouveau front sur l'horloge avant de faire quoi que ce soit. L'horloge est inhibée, le bus I²c est mis en pause. Quand le récepteur lent a terminé, il relâche la ligne d'horloge SDL, et envoie un ACK normal. Cette méthode est utilisée par beaucoup de mémoires EEPROM I²c. Évidemment, cela réduit les performances et la perte est d'autant plus grande que les temps d'attente sont longs.
===L’arbitrage sur le bus I²c===
Le bit START est impliqué dans l'arbitrage du bus : dès que le signal SDA est mis à 0 par un émetteur, les autres composants savent qu'une transmission a commencé et qu'il faut attendre.
Il est malgré tout possible que deux composants émettent chacun une donnée en même temps, car ils émettent un bit START à peu près en même temps. Dans ce cas, l'arbitrage du bus utilise intelligemment le fait que les entrées-sorties sont à drain ouvert. Nous avions dit que le bus est à 1 au repos, mais qu'il est mis à 0 dès qu'au moins un composant veut envoyer un 0. Pour le dire autrement, on peut voir le contenu du bus comme un ET des bits envoyés sur les sorties des composants connectés au bus. Ce détail est utilisé pour l'arbitrage.
Si deux émetteurs envoient chacun une donnée, le bus accepte cette double transmission. Tant que les bits transmis sont identiques, cela ne pose pas de problème : le bus est à 1 si les deux composants veulent envoyer un 1 en même temps, idem pour un 0. Par contre, si un composant veut envoyer un 1 et l'autre un 0, le bus est mis à 0 du fait des sorties à drain ouvert. Le truc est que les émetteurs vérifient si les bits transmis sur le bus correspondent aux bits envoyés. Si l'émetteur émet un 1 et voit un 0 sur le bus, il comprend qu'il y a une collision et cesse sa transmission pour laisser la place à l'autre émetteur. Il retentera une nouvelle transmission plus tard.
==Un exemple de bus parallèle : le bus PCI==
[[File:PCI Slots Digon3.JPG|vignette|Ports PCI version 32 bits sur une carte mère grand public.]]
Le bus PCI est un bus autrefois très utilisé dans les ordinateurs personnels, qui a eu son heure de gloire entre les années 90 et 2010. Il était utilisé pour la plupart des cartes d'extension, à savoir les cartes son, les cartes graphiques et d'autres cartes du genre. Il remplace le bus ISA, un ancien bus devenu obsolète dans les ordinateurs personnels.
: Les lecteurs aguerris qui veulent une description détaillée du bus PCI peuvent lire le livre nommé "''PCI Bus Demystified''".
===Les performances théoriques du bus PCI===
Le bus ISA avait une largeur de seulement 16 bits et une fréquence de 8 MHz, ce qui était suffisant lors de son adoption, mais était devenu trop limitant dès les années 90. Le bus PCI avait de meilleures performances : un bus de 32 bits et une fréquence de 33 MHz dans sa première version, ce qui faisait un débit maximum de 133 mébioctets par secondes. Des extensions faisaient passer le bus de données de 32 à 64 bits, augmentaient la fréquence à 66/133 MHz, ou alors ajoutaient des fonctionnalités. Les versions 64 bits du bus PCI avaient généralement une fréquence plus élevée, de 66 MHz pour le PCI version 2.3, de 133 MHz pour le PCI-X.
===La tension d'alimentation : deux normes===
Il existait aussi une version 3,3 volts et une version 5 volts du bus PCI, la tension faisant référence à la tension utilisée pour alimenter le bus. L'intérêt était de mieux s'adapter aux circuits imprimés de l'époque : certains fonctionnaient en logique TTL à 5 volts, d'autres avec une logique différente en 3,3 volts. La logique ici mentionnée est la manière dont sont construits les transistors et portes logiques. Concrètement, le fait qu'il s'agisse de deux logiques différentes change tout au niveau électrique. La norme du bus PCI en 3,3 volts est fondamentalement différente de celle en 5 volts, pour tout ce qui touche aux spécifications électriques (et elles sont nombreuses). Une carte conçue pour le 3,3 volts ne pourra pas marcher sur un bus PCI 5 volts, et inversement. Il existe cependant des cartes universelles capables de fonctionner avec l'une ou l'autre des tensions d'alimentation, mais elles sont rares. Pour éviter tout problème, les versions 3,3 et 5 volts du bus PCI utilisaient des connecteurs légèrement différents, de même que les versions 32 et 64 bits.
[[File:PCI Keying.svg|centre|vignette|upright=2|Connecteurs et cartes PCI.]]
===L'arbitrage du bus PCI===
Le bus PCI utilise un arbitrage centralisé, avec un arbitre qui commande plusieurs composants maîtres. Chaque composant maitre peut envoyer des données sur le bus, ce qui en fait des émetteurs-récepteurs, contrairement aux composants esclaves qui sont toujours récepteurs. Chaque maître a deux broches spécialisées dans l'arbitrage : un fil REQ (''Request'') pour demander l'accès au bus à l'arbitre, et un fil GNT (''Grant'') pour recevoir l'autorisation d'accès de la part de l'arbitre de bus. Les deux signaux sont actifs à l'état bas, à zéro. Un seul signal GNT peut être actif à la fois, ce qui fait qu'un seul composant a accès au bus à un instant donné.
L'arbitrage PCI gère deux niveaux de priorité pour l'arbitrage. Les composants du premier niveau sont prioritaires sur les autres pour l'arbitrage. En cas d'accès simultané, le composant de niveau 1 aura accès au bus alors que ceux de niveau 2 devront attendre. En général, les cartes graphiques sont de niveau 1, alors que les cartes réseau, son et SCSI sont dans le niveau 2.
Un composant ne peut pas monopoliser le bus en permanence, mais doit laisser la place aux autres après un certain temps. Une fois que l'émetteur a reçu l'accès au bus et démarré une transmission avec le récepteur, il a droit à un certain temps avant de devoir laisser la place à un autre composant. Le temps en question est déterminé par un ''timer'', un compteur qui est décrémenté à chaque cycle d'horloge. Au démarrage de la transaction, ce compteur est initialisé avec le nombre de cycle maximal, au-delà duquel l'émetteur doit laisser le bus. Si le compteur atteint 0, que d'autres composants veulent accéder au bus, et que l'émetteur ait terminé sa transmission, la transmission est arrêtée de force. Le composant peut certes redemander l'accès au bus, mais elle ne lui sera pas accordée car d'autres composants veulent accéder au bus.
Il est possible que, quand aucune transaction n'a lieu, le bus soit attribué à un composant maître choisit par défaut. On appelle cela le '''''bus parking'''''. Cela garantit qu'il y a toujours un composant qui a son signal REQ actif, il ne peut pas avoir de situation où aucun composant PCI n'a accès au bus. Quand un autre composant veut avoir accès au bus, l'autre composant est choisit, sauf si une transmission est en cours. L'avantage est que le composant maître choisit par défaut n'a pas besoin de demander l'accès au bus au cas où il veut faire une transmission, ce qui économise quelques cycles d'horloge. L'arbitre du bus doit cependant être configuré pour. Le réglage par défaut du bus PCI est que le maître choisi par défaut est le dernier composant à avoir émis une donnée sur le bus.
===L'adressage et le bus PCI===
Le bus PCI est multiplexé, ce qui signifie que les mêmes fils sont utilisés pour transmettre successivement adresse ou données. Les adresses ont la même taille que le bus de données : 32 bits ou 64 bits, suivant la version du bus. On trouve aussi un bit de parité, transmis en même temps que les données et adresses. Notons que les composants 32 bits pouvaient utiliser des adresses 64 bits sur un bus PCI : il leur suffit d'envoyer ou de recevoir les adresses en deux fois : les 32 bits de poids faible d'abord, les 32 bits de poids fort ensuite. Fait important, le PCI ne confond pas les adresses des périphériques et de la mémoire RAM. Il existe trois espaces d'adressage distincts : un pour la mémoire RAM, un pour les périphériques, et un pour la configuration qui est utilisé au démarrage de l'ordinateur pour détecter et configurer les périphériques branchés sur le bus.
Le bus de commande possède 4 fils/broches sur lesquelles on peut transmettre une commande à un périphérique. Il existe une commande de lecture et une commande d'écriture pour chaque espace d'adressage. On a donc une commande de lecture pour les adresses en RAM, une commande de lecture pour les adresses de périphériques, une autre pour les adresses de configuration, idem pour les commandes d'écritures. Il existe aussi des commandes pour les adresses en RAM assez spéciales, qui permettent de faire du préchargement, de charger des données à l'avance. Ces commandes permettent de faire une lecture, mais préviennent le contrôleur PCI que les données suivantes seront accédées par la suite et qu'il vaut mieux les précharger à l'avance.
Les commandes en question sont transmises en même temps que les adresses. Lors de la transmission d'une donnée, les 4 broches sont utilisées pour indiquer quels octets du bus sont valides et quels sont ceux qui doivent être ignorés.
{|class="wikitable"
|-
! Bits de commande
! Nom de la commande
! Signification
|-
| 0000 || Interrupt Acknowledge || Commande liée aux interruptions
|-
| 0001 || Special Cycle || Envoie une commande/donnée à tous les périphériques PCI
|-
| 0010 || I/O Read || Lecture dans l'espace d’adressage des périphériques
|-
| 0011 || I/O Write || Écriture dans l'espace d’adressage des périphériques
|-
| 0100 || Reserved ||
|-
| 0101 || Reserved ||
|-
| 0110 || Memory Read || Lecture dans l'espace d’adressage de la RAM
|-
| 0111 || Memory Write || Écriture dans l'espace d’adressage de la RAM
|-
| 1000 || Reserved ||
|-
| 1010 || Reserved ||
|-
| 1011 || Configuration Read || Lecture dans l'espace d’adressage de configuration
|-
| 1011 || Configuration Write || Écriture dans l'espace d’adressage de configuration
|-
| 1100 || Memory Read Multiple || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1101 || Dual-Address Cycle || Lecture de 64 bits, sur un bus PCI de 32 bits
|-
| 1110 || Memory Read Line || Lecture dans l'espace d’adressage de la RAM, avec préchargement
|-
| 1111 || Memory Write and Invalidate || Écriture dans l'espace d’adressage de la RAM, avec préchargement
|}
Plusieurs fils optionnels ajoutent des interruptions matérielles (IRQ), une fonctionnalité que nous verrons d'ici quelques chapitres. Pour le moment, sachez juste qu'il y a quatre fils dédiés aux interruptions, qui portent les noms INTA, INTB, INTC et INTD. En théorie, un composant peut utiliser les quatre fils d'interruptions s'il le veut, mais la pratique est différente. Tous les composants PCI, sauf en quelques rares exceptions, utilisent une seule sortie d'interruption pour leurs interruptions. Sachant qu'il y a généralement quatre ports PCI dans un ordinateur, le câblage des interruptions est simplifié, avec un fil par composant. Lorsqu'une interruption est levée par un périphérique, le composant qui répond aux interruption, typiquement le processeur, répond alors par une commande ''Interrupt Acknowledge''.
===Le protocole de transmission sur le bus PCI===
En tout, 6 fils commandent les transactions sur le bus. On a notamment un fil FRAME qui est maintenu à 0 pendant le transfert d'une trame. Le fil STOP fait l'inverse : il permet à un périphérique de stopper une transaction dont il est le récepteur. Les deux signaux IRDY et TRDY permettent à l'émetteur et le récepteur de se mettre d'accord pour démarrer une transmission. Le signal IRDY (''Initiator Ready'') est mis à 1 par le maître quand il veut démarrer une transmission, le signal TRDY (''Target Ready'') est la réponse que le récepteur envoie pour indiquer qu'il est près à démarrer la transmission. Le signal DEVSEL est mis à zéro quand le récepteur d'une transaction a détecté son adresse sur le bus, ce qui lui permt d'indiquer qu'il a bien compris qu'il était le récepteur d'une transaction.
: Pour la commande ''Special Cycle'', qui envoie une donnée à tous les périphériques PCI en même temps, les signaux IRDY, TRDY et DEVSEL ne sont pas utilisés. Ces signaux n'ont pas de sens dans une situation où il y a plusieurs récepteurs. Seul le signal FRAME est utilisé, ainsi que le bus de données.
Une transaction en lecture procède comme suit :
* En premier lieu, l'émetteur acquiert l'accès au bus et son signal GNT est mis à 0.
* Ensuite, il fait passer le fil FRAME à 0, qui pour indiquer le début d'une transaction, et envoie l'adresse et la commande adéquate.
* Au cycle suivant, le récepteur met le signal IRDY à 0, pour indiquer qu'il est près pour recevoir la donnée lue.
* Dans un délai de 3 cycles d'horloge maximum, le récepteur doit avoir reçu l'adresse et le précise en mettant le signal DEVSEL à 0.
* Le récepteur place la donnée lue sur le bus, et met le signal TRDY à 0.
* Le signal TRDY remonte à 1 une fois la donnée lue. En cas de lecture en rafale, à savoir plusieurs lectures consécutives à des adresses consécutives, on reprend à l'étape précédente pour transmettre une nouvelle donnée.
* Puis tous les signaux du bus repassent à 1 et le bus revient à son état initial, le signal GNT est réattribué à un autre composant.
===Le ''Plug And Play''===
Outre sa performance, le bus PCI était plus simple d'utilisation. La configuration des périphériques ISA était laborieuse. Il fallait configurer des ''jumpers'' ou des interrupteurs sur chaque périphérique impliqué, afin de configurer le DMA, les interruptions et d'autres paramètres cruciaux pour le fonctionnement du bus. La moindre erreur était source de problèmes assez importants. Autant ce genre de chose était acceptable pour des professionnels ou des ''power users'', autant le grand public n'avait ni les compétences ni l'envie de faire cela. Le bus PCI était lui beaucoup plus facile d'accès, car il intégrait la fonctionnalité ''Plug And Play'', qui fait que chaque périphérique est configuré automatiquement lors de l'allumage de l'ordinateur.
==Le PCI Express==
Le PCI Express est le remplaçant du vieux bus PCI. Il est officiellement abrégé PCI-E ou PCIe et nous utiliserons cette abréviation dans ce qui suit. Malgré son nom, il y a de grandes différences entre le bus PCI et le PCI-Express. Notez que je n'ai pas parlé de ''bus PCI-Express'', et ce pour une bonne raison : le PCI-E n'utilise pas un bus, mais des liaisons point-à-point ! De plus, ces liaisons de type série, là où le PCI est un bus parallèle ! Enfin, le bus PCI est un bus ''half-duplex'', alors que le PCI-E est ''full duplex''. Pour résumer ces différences fondamentales :
* bus partagé de 32 bits pour le PCI, liaisons point à point pour le PCI-E ;
* liaison parallèle pour le PCI, série pour le PCI-E ;
* liaison ''half-duplex'' pour le PCI, ''full-duplex'' pour le PCI-E.
[[File:PCIe vs PCI.gif|centre|vignette|upright=3|Différences entre PCIe et PCI.]]
===La topologie des liaisons PCI-E===
Les périphériques PCI-E sont tous connectés à un répartiteur central, appelé le ''Root Complex''. Les liaisons entre périphérique et ''Root Complex'' sont des liaisons point à point série. Le ''Root Complex'' est intégré soit à la carte mère, soit au processeur. Auparavant, il était placé sur la carte mère, soit dans le ''northbridge'' de la carte mère, soit dans le ''southbridge'', les deux étaient possibles. S'il est intégré à la carte mère, il y a généralement un bus dédié pour le relier au processeur s'il est dans le ''northbridge'', ou au ''northbridge'' s'il est dans le ''southbridge''.
[[File:Prinzipielle Darstellung der PCIe-Architektur.svg|centre|vignette|upright=2.5|Liaison entre ''Root Complex'' et périphériques PCI-E.]]
Évidemment, il y a une limite au nombre de périphériques qu'on peut brancher directement sur le ''Root Complex''. Cependant, on peut dépasser cette limite en ajoutant des intermédiaires appelés des ''switchs'', qui servent de répartiteur secondaire. Pour signaler que les périphériques se situent à la fin du réseau d'interconnexion, une fois tous les intermédiaires parcourus, les périphériques sont appelés des ''End Points''.
Le bus PCI-E peut servir à émuler l'ancien bus PCI. Pour cela, il faut connecter un circuit qui fait l'intermédiaire entre le bus PCI et le ''Root Complex'', et qui traduit les trames PCI en commandes compréhensibles par le ''Root Complex''. L'intermédiaire est souvent appelé un '''''Bridge PCIe'''''.
[[File:Example PCI Express Topology.svg|centre|vignette|upright=2|PCI Express, topologie.]]
===Les liaisons PCIe : ''lanes'', ports et signaux===
Une liaison série PCIe contient deux paires différentielles, chacune permettant un transfert série. La première paire différentielle transmet les données/commandes du périphérique vers le ''root complex'', l'autre liaison s'occupe des transferts dans l'autre sens. C'est la raison pour laquelle la liaison est ''full-duplex'' : il y a deux liaisons série en une, une par sens de transfert.
Intuitivement vous vous dites que pour chaque périphérique PCIe , il y a une seule liaison série connectée au ''Root Complex''. Sauf qu'en réalité, un périphérique PCIe peut utiliser plusieurs liaisons série, plusieurs ''lanes''. Pour faire la distinction, il faut faire la distinction entre une liaison série et une connexion entre périphérique PCIe et ''root complex''. Une liaison série est appelée une '''''lane''''', alors qu'une connexion entre ''root complex'' et périphérique PCIe est appelée un '''port PCIe ''', un ''link PCIe ''. Un port PCIe contient donc une ou plusieurs ''lanes'' : 1, 2, 4, 8 ou 16 ''lanes''.
[[File:PCI Express Terminology.svg|centre|vignette|upright=2|Terminologie des liaisons PCI Express.]]
Le débit binaire d'un port PCIe augmente avec le nombre de ''lanes''. Un port à 8 ''lanes'' sera 8 fois plus rapide qu'un port à une seule lane. Le débit binaire d'une ''lane'' dépend de la version du PCIe : elle était de 250 Mb/s pour le PCIe 1.0, 500 Mb/s pour le PCIe 2.0, elle est de 7,563 Gb/s pour la version 6.0. Le débit binaire a presque doublé d'une version a une autre.
Les périphériques ayant besoin de peu de débit binaire utilisent une seule ''lane'', les périphériques ayant des besoins plus importants en utilisent 8 ou 16. Il faut noter qu'il existe différents connecteurs PCIe , qui se différencient par le nombre de ''lanes''. Il y a des connecteurs avec une seule ''lane', un autre avec deux ''lanes'', un autre avec 4, etc. Plus il y a de ''lanes'', plus le connecteur est long.
[[File:Various PCIe Slots.jpg|centre|vignette|upright=2|Various PCIe Slots]]
L'envoi des données sur les différentes ''lanes'' se fait octet par octet. On envoie le premier octet sur la première ''lane'', le second octet sur la seconde ''lane'', le troisième octet sur la troisième ''lane'', et ainsi de suite. La spécification du PCIe appelle cela du '''''data striping'''''.En faisant ainsi, il peut y avoir un petit décalage entre les ''lanes'', à savoir que les octets ne sont pas envoyés exactement en même temps. Le décalage est cependant limité, la norme PCIe impose une limite de l'ordre de quelques nanosecondes entre ''lanes'' d'un même port PCIe.
===Les commandes PCIe===
Les commandes envoyées sur les liaisons PCIe sont standardisées. Une commande PCIe peut demander de faire une lecture ou une écriture, configurer un périphérique, envoyer des messages entre périphérique ou au processeur (les interruptions). Le protocole fait la distinction entre lecture/écriture mémoire et lecture/écriture dans un périphérique. Les commandes de configuration d'un périphérique sont émises par le ''root complex'' vers un périphérique, l’inverse est impossible. Les autres commandes peuvent être émises dans n'importe quel sens.
Le PCIe autorise deux types de transactions : les transferts DMA entre un périphérique et la RAM, un transfert entre deux périphériques. Les transferts entre périphériques peuvent passer par l'intermédiaire du ''root complex'', sans faire intervenir le processeur ou la mémoire RAM. Pour déterminer qui est le destinataire d'une commande, celle-ci intègre l'adresse du destinataire. Le ''root complex'' reçoit la commande, extrait cette adresse, et détermine qui est le destinataire de la commande.
Il existe des commandes de gestion de l'alimentation, afin de configurer le récepteur pour le mettre en veille, activer certaines fonctionnalités d'économie d'énergie, etc. Il existe aussi des commandes de '''contrôle de flux'''. Elles permettent de gérer l'occupation des liaisons PCIe. Un périphérique PCIe reçoit des commandes et les accumule dans une mémoire tampon, en attendant de les traiter. L'émetteur peut envoyer des commandes pour savoir si cette mémoire tampon est pleine, en passe de l'être, vide, globalement vide, etc. Il adapte alors ses transferts en fonction.
===Les trames PCIe===
Les trames PCIe sont beaucoup plus complexes que les trames du bus PCI. Une trame PCIe est composé d'un ''header'' de 12-16 octets, suivi par les données à transmettre. Les données peuvent prendre de 0 à 4096 octets. Le tout peut être complété par 4 à 8 octets de détection/correction d'erreur. En tout, la trame contient donc de 12 à 4116 octets. A cela, il faut ajouter les octets de START/STOP de la trame et 2 octets supplémentaires.
Une trame contient des données essentielles : l'adresse à destination du récepteur, l'identifiant de l'émetteur, un champ type qui indique de quelle commande il s'agit, etc. La taille de la trame est aussi encodée dans le ''header'' à un endroit bien précis. Le champ Type est précédé par un nombre qui indique quelle est la taille du ''header'' et comment il doit être interprété.
[[File:PCI-to-PCIe.JPG|centre|vignette|upright=3|Comparaison entre une trame PCI à gauche, et une trame PCI Express à droite.]]
Détailler le contenu des trames est assez complexe. Il faut dire que le PCIe définit un protocole de communication entre ''root complex'' et périphérique PCIe qui est assez complexe. Il incorpore de nombreuses fonctionnalités qu'on attendrait plutôt d'un protocole réseau ! Les informaticiens seraient étonnés de voir que la spécification du PCIe reprendre des termes courants dans le domaine des réseaux, comme la séparation entre une couche transport, une couche de liaison et une couche physique.
La raison est que le PCIe gère des transferts de taille arbitraire, qui sont découpés en trames envoyées sur un port PCIe. Les trames sont numérotées, afin de se souvenir de leur ordre d'envoi. Cela permet de garantir que les données sont bien envoyées trame par trame, dans l'ordre. Le numéro de trame est intégré dans le ''header'' de la trame, dans les 12-16 octets au début de la trame. Si jamais une trame n'est pas transmise, à cause d'une erreur de transmission détectée par l'ECC, les numéros de trame permettent de détecter qu'un problème a lieu.
Par exemple, imaginez qu'une trame soit perdue. Dans ce cas, deux trames consécutives n'auront pas de numéro de trame consécutifs. Par exemple, une trame aura le numéro 6 et la suivante le numéro 8, ce qui indique que la trame numéro 7 a été perdue en chemin. Un autre exemple est le cas où une trame est transmise, mais où l'ECC détecte une erreur de transmission. Dans ce cas, le receveur prévient l'émetteur qu'une erreur a eu lieu.
Pour gérer cela, le PCIe gère pour cela des commandes d'achèvement pour prévenir qu'une donnée a bien été reçue, qu'une transaction s'est bien déroulée. Le protocole PCIe incorpore deux commandes ACK et NACK : ACK indique que la trame a été transmise sans problème, un NACK indique qu'un problème a eu lieu. Si l'émetteur envoie une trame et recoit un ACK, il envoie la trame suivante. Mais s'il reçoit un NACK, il renvoie la trame fautive.
De plus, les trames suivant la trame fautive sont annulées et sont renvoyées dans l'ordre adéquat. Pour cela, la commande NACK précise le numéro de la trame fautive, pour que l'émetteur sache quelle est la trame fautive et quelles sont les trames à renvoyer. Pour renvoyer les trames, l'émetteur n'efface pas les trames une fois qu'elles sont transmises, mais les conserver en mémoire. Les trames ne sont effacées qu'une fois l'ACK associé reçu. L'émetteur doit aussi savoir quelle est la dernière trame valide envoyée, à savoir la dernière trame à avoir reçu un ACK. Il contient un registre pour mémoriser le numéro de cette trame, qui est incrémenté à chaque réception d'un ACK valide.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les bus électroniques
| prevText=Les bus électroniques
| next=Les différents types de mémoires
| nextText=Les différents types de mémoires
}}{{autocat}}
</noinclude>
icjtdqprvzb8fbogmxbtbgf19lliy9b
Les cartes graphiques/La hiérarchie mémoire d'un GPU
0
74269
744776
724716
2025-06-15T01:35:16Z
Mewtow
31375
/* Le cache de textures */
744776
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir comment est organisée la mémoire d'un GPU, ou plutôt devrait-on dire les mémoires d'un GPU. Eh oui : un GPU contient beaucoup de mémoires différentes. La hiérarchie mémoire des GPUs est assez particulière, que ce soit au niveau des caches ou de la mémoire, parfois des registres. Un GPU contient évidemment une mémoire vidéo, de grande taille, capable de stocker textures, vertices, images et bien d'autres choses nécessaires pour un rendu 3D. On y trouve souvent des mémoires caches dédiées aux textures ou aux vertices.
Les GPUs récents contiennent aussi des caches tout court qui ne sont spécialisés dans les textures ou vertices. De plus, les caches sont complétés par des ''Local Store'', des mémoires normales de petite taille. Elles sont gérés par le logiciel, le programmeur, alors que les caches sont gérés par des circuits de contrôle qui s'occupent du chargement ou de l'éviction des données du cache. Elles servent donc de cache géré par le logiciel, si on peut dire. Un GPU moderne dispose de plusieurs ''local store'' : au moins par cœur.
==La mémoire vidéo==
La mémoire vidéo d'une carte graphique dédiée est nécessaire pour stocker l'image à afficher à l'écran, mais aussi pour mémoriser temporairement des informations importantes. Dans le cas le plus simple, elle sert simplement de ''Framebuffer'' : elle stocke l'image à afficher à l'écran. Au fil du temps, elle s'est vu ajouter d'autres fonctions, comme stocker les textures et les sommets de l'image à calculer, ainsi que divers résultats temporaires.
===Les spécificités des RAM des cartes vidéo dédiées===
Sur les cartes graphiques dédiées, la mémoire vidéo est très proche des mémoires RAM qu'on trouve sous forme de barrettes dans nos PC, à quelques différences près. Le point le plus important est que la mémoire vidéo d'une carte dédiée n'est pas présente sous la forme de barrettes de mémoire. À la place, les puces de mémoire sont soudées sur la puce. La conséquence est que l'on ne peut pas upgrader la RAM d'une carte vidéo. Ce serait sympathique, mais ne serait pas d'une grande utilité, car les jeux vidéos gourmands en mémoire vidéo sont aussi gourmands en puissance de calcul. Upgrader la RAM d'une carte graphique ne sert à rien si celle-ci n'a pas assez de puissance pour jouer à des jeux récents avec un framerate convenable.
Le fait que la mémoire est soudée simplifie la conception de la carte graphique, mais cela a des avantages au niveau électrique, qui permettent d'améliorer les performances. Niveau performances, la mémoire vidéo a des performances radicalement différentes de la RAM des PC. Elle a un temps d'accès très long, de plusieurs centaines de cycles d'horloge. Cela a des conséquences sur l'architecture de la carte graphique, notamment au niveau des processeurs de ''shaders'', qui sont conçus pour gérer ces temps d'accès long, comme on l'a vu dans le précédent chapitre. Par contre, elle a un très grand débit, autrement dit une bande passante élevée, proche de la centaine de gigaoctets par secondes sur les cartes graphiques modernes. Pour rappel, la bande passante d'une mémoire dépend de deux paramètres : sa fréquence et la largueur de son bus mémoire. Détaillons le dernier, qui explique en grande partie pourquoi la mémoire vidéo a un débit supérieur à la mémoire système.
===Le bus mémoire et sa largeur===
Le bus mémoire est ce qui connecte la mémoire au reste de la carte graphique. La largueur de ce bus n'est autre que la quantité de données que celui-ci peut transmettre à chaque cycle d'horloge, le nombre de bits que l'on peut lire/écrire en un cycle d'horloge. Sur la RAM système, le bus est de 64 bits sur les mémoires DDR modernes, mais peut monter à 128 bits en utilisant des techniques comme le ''dual channel'', voire en 192/256 bits avec des techniques de ''triple/quad channel'' qui sont rarement utilisées. Globalement, la configuration classique sur un PC moderne est 128 bits, avec quelques machines bas de gamme en 64 bits. Sur les cartes graphiques modernes, les bus de 128 bits ou moins sont utilisés sur les cartes graphiques de faible performance, le reste ayant un bus mémoire de 192, 256, 384, voire 512 bits. En clair, elles permettent de lire/écrire plus de données par cycle d'horloge qu'une RAM système, de 2 à 8 fois plus.
Le fait que le bus est plus large est lié au fait que les puces mémoires sont soudées. La mémoire vidéo des cartes dédiées est composée de pleins de puces mémoires accessibles en parallèle, ce qui permet de charger des blocs de plusieurs centaines d'octets en une seule fois. Les barrettes de mémoire ont des limites au nombres de broches que leur connecteur peut accepter, qui est proche de 300 pour les DDR actuelles (beaucoup de ces broches ne transfèrent pas des données, ce qui fait qu'on a bien 64 broches dédiées aux données seulement). Sans connecteurs, on est limité à ce que la puce du GPU peut accepter, et on est alors entre 4000 à 6000 broches sur les sockets de CPU ou de GPU actuels.
[[File:Puces mémories d'un GPU et d'une barrette de mémoire.png|centre|vignette|upright=2|Puces mémoires d'un GPU et d'une barrette de mémoire.]]
Pour résumer, sur les cartes graphiques dédiées, la RAM vidéo a un débit proche de la centaine de gigaoctets par secondes. Avec une mémoire RAM unifiée, vous pouvez facilement diviser cette estimation par 10.
===La mémoire vidéo est très lente===
La mémoire vidéo a donc un débit très élevé. Mais par contre, elle a un temps d'accès très lent. Concrètement, cela veut dire qu'un accès mémoire va prendre beaucoup de temps. Par exemple, si je veux lire une texture, entre le moment où j'envoie une demande de lecture à la mémoire vidéo, et le moment celle-ci me renvoie les premiers texels, il va se passer entre 200 à 1000 cycles d'horloge processeur. Par contre, une fois les premiers texels reçus, les texels suivants sont disponibles au cycle suivant, et ainsi de suite. En clair, les données lues mettent du temps avant d'arriver, mais elles arrivent par gros paquets une fois ce temps d'attente passé.
La différence entre débit et temps d'accès est primordiale sur les GPU modernes comme anciens. Toute l'architecture de la carte graphique est conçue de manière à prendre en compte ce temps d'attente. Les techniques employées sont multiples, et ne sont pas inconnues à ceux qui ont déjà lu un cours d'architecture des ordinateurs : mémoire caches, hiérarchie de caches, ''multithrading'' matériel au niveau du processeur, optimisations des accès mémoire comme des ''Load-Store Queues'' larges, des ''coalesing write buffers'', etc. Mais toutes ces techniques sont techniquement incorporées dans les processeurs de ''shaders'' et dans les circuits fixes. Aussi nous ne pouvons pas en parler dans ce chapitre. A une exception près : l'usage de caches et de ''local stores''.
==Les caches d'un GPU==
Les cartes graphiques sont censées avoir peu de caches. Les anciennes cartes graphiques se débrouillaient avec des caches spécialisés pour les textures ou pour les sommets, ce qui leur vaut les noms de caches de texture et de cache de sommets. Ce n'est que par la suite, quand les GPU commencèrent à être utilisés pour du calcul généraliste (scientifique, notamment), que la situation changea. Les GPU utilisèrent alors de plus en plus de caches généralistes, capables de stocker n'importe quelle forme de données.
Les caches en question sont intégrés dans les processeurs de ''shaders'' sur les GPU modernes. Même les caches de texture ou de sommets. Les deux sont d'ailleurs fusionnés sur les GPU modernes, vu que leur jeu d'instruction est unifié et qu'ils peuvent exécuter aussi bien des ''vertex shaders'' que des ''pixel shaders''. Sur les GPU plus anciens, avec des circuits fixes, ces caches étaient intégrés aux circuits non-programmables de gestion des textures et de la géométrie. Les caches de sommet et de texture étaient alors séparés.
===Le cache de textures===
Le '''cache de textures''', comme son nom l'indique, est un cache spécialisé dans les textures. Toutes les cartes graphiques modernes disposent de plusieurs unités de texture, qui disposent chacune de son ou ses propres caches de textures. Pas de cache partagé, ce serait peu utile et trop compliqué à implémenter.
De plus, les cartes graphiques modernes ont plusieurs caches de texture par unité de texture. Généralement, elles ont deux caches de textures : un petit cache rapide, et un gros cache lent. Les deux caches sont fortement différents. L'un est un gros cache, qui fait dans les 4 kibioctets, et l'autre est un petit cache, faisant souvent moins d'1 kibioctet. Mais le premier est plus lent que le second. Sur d'autres cartes graphiques récentes, on trouve plus de 2 caches de textures, organisés en une hiérarchie de caches de textures similaire à la hiérarchie de cache L1, L2, L3 des processeurs modernes.
Notons que ce cache interagit avec les techniques de compression de texture. Les textures sont en effet des images, qui sont donc compressées. Et elles restent compressées en mémoire vidéo, car les textures décompressées prennent beaucoup plus de place, entre 5 à 8 fois plus. Les textures sont décompressées lors des lectures : le processeur de shaders charge quelques octets, les décompresse, et utilise les données décompressées ensuite. Le cache s'introduit quelque part avant ou après la décompression.
On peut décompresser les textures avant de les placer dans le cache, ou laisser les textures compressées dans le cache. Tout est une question de compromis. Décompresser les textures dans le cache fait que la lecture dans le cache est plus rapide, car elle n'implique pas de décompression, mais le cache contient moins de données. A l'inverse, compresser les textures permet de charger plus de données dans le cache, mais rend les lectures légèrement plus lentes. C'est souvent la seconde solution qui est utilisée et ce pour deux raisons. Premièrement, la compression de texture est terriblement efficace, souvent capable de diviser par 6 la taille d'une texture, ce qui augmente drastiquement la taille effective du cache. Deuxièmement, les circuits de décompression sont généralement très rapides, très simples, et n'ajoutent que 1 à 3 cycles d'horloge lors d'une lecture.
Les anciens jeux vidéo ne faisaient que lire les textures, sans les modifier. Aussi, le cache de texture des cartes graphiques anciennes est seulement accessible en lecture, pas en écriture. Cela simplifiait fortement les circuits du cache, réduisant le nombre de transistors utilisés par le cache, réduisant sa consommation énergétique, augmentait sa rapidité, etc. Mais les jeux vidéos 3D récents utilisent des techniques dites de ''render-to-texture'', qui permettent de calculer certaines données et à les écrire en mémoire vidéo pour une utilisation ultérieure. Les textures peuvent donc être modifiées et cela se marie mal avec un cache en lecture seule.
Rendre le cache de texture accessible en écriture est une solution, mais qui demande d'ajouter beaucoup de circuits pour une utilisation somme toute peu fréquente. Une autre solution, plus adaptée, réinitialise le cache de textures quand on modifie une texture, que ce soit totalement ou partiellement. Une fois le cache vidé, les accès mémoire ultérieurs n'ont pas d'autre choix que d'aller lire la texture en mémoire et de remplir le cache avec les données chargées depuis la RAM. Les données de texture en RAM étant les bonnes, cela garantit l’absence d'erreur.
: Ces deux techniques peuvent être adaptées dans le cas où plusieurs caches de textures séparées existent sur une même carte graphique. Les écritures doivent invalider toutes les copies dans tous les caches de texture. Cela nécessite d'ajouter des circuits qui propagent l'invalidation dans tous les autres caches.
===Les caches généralistes===
La hiérarchie mémoire des GPU modernes ressemble de plus en plus à celle des CPU, avec toute une hiérarchie de caches, avec des caches L1, L2, L3, etc. Pour rappel, les processeurs multicœurs modernes ont pleins de mémoires cache, avec au minimum deux niveaux de cache, le plus souvent trois. Les trois niveaux de cache sont appelés les caches L1, L2 et L3. Pour le premier niveau, on trouve deux caches spécialisés par cœur/processeur : un cache pour les instructions et un cache pour les données. Pour le second niveau, on a un cache L2 par cœur/processeur, qui peut stocker indifféremment données et instructions. Le cache L3 est un cache partagé entre tous les cœurs/processeurs. Les GPU ont une organisation similaire, sauf que le nombre de cœurs est beaucoup plus grand que sur un processeur moderne.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2|Partage des caches sur un processeur multicoeurs]]
Les caches d'instruction des GPU sont adaptés aux contraintes du rendu 3D. Le principe du rendu 3D est d'appliquer un shader assez simple sur un grand nombre de données, alors que les programmes généralistes effectuent un grand nombre d'instructions sur une quantité limitée de données. Les shaders sont donc des programmes assez légers, qui ont peu d'instructions. Les caches d'instructions L1 sont généralement assez petits, généralement quelques dizaines ou centaines de kilooctets. Et malgré cela, il n'est pas rare qu'un ''shader'' tienne tout entier dans le cache d'instruction, situation serait impensable sur un processeur généraliste. La seconde caractéristique est qu'un même programme s’exécute sur beaucoup de données. Il n'est pas rare que plusieurs processeurs de shaders exécutent le même ''shader''. Aussi, certains GPU partagent un même cache d’instruction entre plusieurs processeurs de ''shader'', comme c'est le cas sur les GPU AMD d'architecture GCN où un cache d'instruction de 32 kB est partagé entre 4 cœurs.
Pour les caches de données, il faut savoir qu'un shader a peu de chances de réutiliser une donnée qu'il a chargé précédemment. Les processeurs de shaders ont beaucoup de registres, ce qui fait que si accès ultérieur à une donnée il doit y avoir, elle passe généralement par les registres. Cette faible réutilisation fait que les caches de données ne sont pas censé être très utiles. Mais le fait est qu'un shader s’exécute en un grand nombre d'instances, chacune traitant un paquet de données différent. Il est très fréquent que différentes instances s’exécutent chacune sur un même cœur et ces différentes instances tendent à accéder à des données très proches, voire à des données identiques. Si un shader charge une donnée dans le cache, la donnée et ses voisines sont alors disponibles pour les autres instances. Le cas le plus classique est celui de l'accès aux textures : lors du placage de textures, des pixels contiguës accèderont à des texels contiguës. Et outre les textures, les pixels shaders ont tendance à traiter des pixels proches, donc à avoir besoin de données proches en mémoire. Ce qui fait que les caches de données sont quand même utiles.
Dans le même registre, un shader a besoin de certaines informations spécifiques, généralement constantes, pour faire son travail. Toutes les instances du shader manipulent ces données, elles ont besoin de les lire, pour les utiliser lors de l’exécution, les copier dans des registres, etc. Les GPU incorporent des '''caches de constantes''' pour accélérer l'accès à ces données. Ainsi, quand un shader lit une donnée, elle est chargée dans le cache de constante, ce qui fait que les futures instances auront accès à celle-ci dans le cache. Ces caches de constante sont séparés des autres caches de données pour une raison bien précise : les constantes en question sont d'accès peu fréquent. Généralement, on a un accès au début de chaque instance de shader, guère plus. Vu que ce sont des données peu fréquemment utilisées, elles sont censée être évincées en priorité du cache de données, qui privilégie les données fréquemment lues/écrites. Avec un cache séparé, on n'a pas ce problème. Au passage, ce cache de constante a des chances d'être partagé entre plusieurs cœurs, des cœurs différents ayant de fortes chances d’exécuter des instances différentes d'un même shader.
Il faut noter que sur la plupart des cartes graphiques modernes, les caches de données et le cache de texture sont un seul et même cache. Même chose pour le cache de sommets, utilisé par les unités géométrique, qui est fusionné avec les caches de données. La raison est que une économie de circuits qui ne coute pas grand chose en termes de performance. Rappelons que les processeurs de shaders sont unifiés à l'heure actuelle, c'est à dire qu'elles peuvent exécuter pixel shader et vertex shader. En théorie, chaque unité de shader devrait incorporer un cache de sommets et un cache de textures. Mais le processeur de shader exécute soit un pixel shader, soit un vertex shader, mais pas les deux en même temps. Donc, autant utiliser un seul cache, qui sert alternativement de cache de vertex et de cache de texture, afin d'économiser des circuits. Une fois les deux fusionnés, on obtient un cache de donnée généraliste, capable de traiter sommets et pixels, voire d'autres données. La seule difficulté tient au filtrage de textures et à sa décompression, mais cela demande juste de router les données lues vers l'unité de texture ou directement vers les registres/unités de calcul, ce qui est facile à faire.
===La cohérence des caches sur un GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs. Dans les grandes lignes, si on omet les circuits spécialisés pour le rendu 3D comme les circuits de la rastérisation ou les unité de textures, c'est une définition parfaite. La différence est que les GPU ont un très grand nombre de cœurs, bien supérieur aux misérables 4 à 16 cœurs d'un CPU. Par contre, cela signifie que les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU. Le principal est ce qu'on appelle la ''cohérence des caches''. Pour comprendre à quoi cela fait référence, il faut faire un rappel sur les caches d'un processeur multicœurs.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, ont a vu plus haut que certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de coeurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
==La mémoire partagée : un ''local store''==
En plus d'utiliser des caches, les GPU modernes utilisent des ''local stores'', aussi appelés ''scratchpad memories''. Ce sont des mémoires RAM intermédiaires entre la RAM principale et les registres. Ces local stores peuvent être vus comme des caches, mais que le programmeur doit gérer manuellement. Dans la réalité, ce sont des mémoires RAM très rapides mais de petite taille, qui sont adressées comme n'importe quelle mémoire RAM, en utilisant des adresses directement.
[[File:Scratch-Pad-Memory.jpg|centre|vignette|upright=2.0|Scratch-Pad-Memory (SPM).]]
Sur les GPU modernes, chaque processeur de ''shader'' possède un unique ''local store'', appelée la '''mémoire partagée'''. Il n'y a pas de hiérarchie des ''local store'', similaire à la hiérarchie des caches.
[[File:Cuda5.png|centre|vignette|upright=2.0|Local stores d'un GPU.]]
La faible capacité de ces mémoires, tout du moins comparé à la grande taille de la mémoire vidéo, les rend utile pour stocker temporairement des résultats de calcul "peu imposants". L'utilité principale est donc de réduire le trafic avec la mémoire centrale, les écritures de résultats temporaires étant redirigés vers les local stores. Ils sont surtout utilisés hors du rendu 3D, pour les applications de type GPGPU, où le GPU est utilisé comme architecture multicœurs pour du calcul scientifique.
===L'implémentation des ''local store''===
Vous vous attendez certainement à ce que je dise que les ''local store'' sont des mémoires séparées des mémoires caches et qu'il y a réellement des puces de mémoire RAM distinctes dans les processeurs de ''shaders''. Mais en réalité, ce n'est pas le cas pour tous les ''local store''. Le dernier niveau de ''local store'', la mémoire partagée, est bel et bien une mémoire SRAM à part des autres, avec ses propres circuits. Mais les cartes graphiques très récentes fusionnent la mémoire locale avec le cache L1.
L'avantage est une économie de transistors assez importante. De plus, cette technologie permet de partitionner le cache/''local store'' suivant les besoins. Par exemple, si la moitié du ''local store'' est utilisé, l'autre moitié peut servir de cache L1. Si le ''local store'' n'est pas utilisé, comme c'est le cas pour la majorité des rendu 3D, le cache/''local store'' est utilisé intégralement comme cache L1.
Et si vous vous demandez comment c'est possible de fusionner un cache et une mémoire RAM, voici comment le tout est implémenté. L'implémentation consiste à couper le cache en deux circuits, dont l'un est un ''local store'', et l'autre transforme le ''local store'' en cache. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus, et ce genre de cache est parfois utilisés sur les processeurs modernes, dans des processeurs dédiés à l'embarqué ou pour certaines applications spécifiques.
Le premier circuit vérifie la présence des données à lire/écrire dans le cache. Lors d'un accès mémoire, il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Son implémentation est très variable suivant le cache considéré, mais une simple mémoire RAM suffit généralement.
En plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. La mémoire RAM de données en question n'est autre que le ''local store''. En clair, le cache s'obtient en combinant un ''local store'' avec un circuit qui s'occupe de vérifier de vérifier les succès ou défaut de cache, et qui éventuellement identifie la position de la donnée dans le cache.
[[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]]
Pour que le tout puisse servir alternativement de ''local store'' ou de cache, on doit contourner ou non l'unité de tags. Lors d'un accès au cache, on envoie l'adresse à lire/écrire à l'unité de tags. Lors d'un accès au ''local store'', on envoie l'adresse directement sur la mémoire RAM de données, sans intervention de l'unité de tags. Le contournement est d'autant plus simple que les adresses pour le ''local store'' sont distinctes des adresses de la mémoire vidéo, les espaces d'adressage ne sont pas les mêmes, les instructions utilisées pour lire/écrire dans ces deux mémoires sont aussi potentiellement différentes.
[[File:Hydride cache - local store.png|centre|vignette|upright=2.0|Hydride cache - local store]]
Il faut préciser que cette organisation en ''phased cache'' est assez naturelle. Les caches de texture utilisent cette organisation pour diverses raisons. Vu que cache L1 et cache de texture sont le même cache, il est naturel que les caches L1 et autres aient suivi le mouvement en conservant la même organisation. La transformation du cache L1 en hydride cache/''local store'' était donc assez simple à implémenter et s'est donc faite facilement.
{{NavChapitre | book=Les cartes graphiques
| prev=La mémoire unifiée et la mémoire vidéo dédiée
| prevText=La mémoire unifiée et la mémoire vidéo dédiée
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
7xk5wi1ok97ni2o9lcf0e1yp6ihqbud
Les cartes graphiques/L'antialiasing
0
78165
744773
726010
2025-06-15T01:24:41Z
Mewtow
31375
/* Les différentes méthodes d'antialiasing par post-processing */
744773
wikitext
text/x-wiki
[[File:Anti-aliasing demo.svg|vignette|upright=0.5|Effet d'escalier sur les lignes.]]
L'antialiasing est une technologie qui permet d'adoucir les bords des objets. Le fait est que dans les jeux vidéos, les bords des objets sont souvent pixelisés, ce qui leur donne un effet d'escalier illustré ci-contre. Le filtre d'antialiasing rajoute une sorte de dégradé pour adoucir les bords des lignes. Il existe un grand nombre de techniques d'antialiasing différentes. Toutes ont des avantages et des inconvénients en termes de performances ou de qualité d'image.
==Le supersampling==
La plus simple de ces techniques, le SSAA - '''Super Sampling Anti Aliasing''' - calcule l'image à une résolution supérieure, avant de la réduire. Par exemple, pour rendre une image en 1280 × 1024 en antialiasing 4x, la carte graphique calcule une image en 2560 × 2048, avant de la réduire. Si vous regardez les options de vos pilotes de carte graphique, vous verrez qu'il existe plusieurs réglages pour l'antialiasing : 2X, 4X, 8X, etc. Cette option signifie que l'image calculé par la carte graphique contient respectivement 2, 4, ou 8 fois plus de pixels que l'image originale. Cette technique filtre toute l'image, y compris l'intérieur des textures, mais augmente la consommation de mémoire vidéo et de processeur (on calcule 2, 4, 8, 16 fois plus de pixels).
Le rendu de l'image se fait à une résolution 2, 4, 8, 16 fois plus grande. La résolution n'apparait qu'après le rastériseur, et impacte tout le reste du pipeline à sa suite : pixel shaders, unités de textures et ROP. Le rastériseur produit 2, 4, 8, 16 fois plus de pixels, les unités de textures vont 2, 4, 8, 16 fois plus de travail, idem pour les pixels shaders. Par contre, la réduction de l'image s'effectue dans les ROP.
Pour effectuer la réduction de l'image, le ROP découpe l'image en rectangles de 2, 4, 8, 16 pixels, et « mélange » les pixels pour obtenir une couleur uniforme. Ce « mélange » est généralement une simple moyenne pondérée, mais on peut aussi utiliser des calculs plus compliqués comme une série d'interpolations linéaires similaire à ce qu'on fait pour filtrer des textures.
Pour simplifier les explications, nous allons appeler "sous-pixels" les pixels de l'image rendue dans le pipeline, et pixels les pixels de l'image finale écrite dans le ''framebuffer''. On parle aussi de '''''samples''''' au lieu de sous-pixels.
{|
|[[File:Supersampling.svg|vignette|Supersampling]]
|[[File:Supersampling.png|vignette|Supersampling]]
|}
===La position des sous-pixels===
Un point important concernant la qualité de l'antialiasing concerne la position des sous-pixels sur l'écran. Comme vous l'avez vu dans le chapitre sur la rastérisation, notre écran peut être vu comme une sorte de carré, dans lequel on peut repérer des points. Reste que l'on peut placer ces pixels n'importe où sur l'écran, et pas forcément à des positions que les pixels occupent réellement sur l'écran. Pour des pixels, il n'y a aucun intérêt à faire cela, sinon à dégrader l'image. Mais pour des sous-pixels, cela change tout. Toute la problématique peut se résumer en une phrase : où placer nos sous-pixels pour obtenir une meilleure qualité d'image possible.
* La solution la plus simple consiste à placer nos sous-pixels à l'endroit qu'ils occuperaient si l'image était réellement rendue avec la résolution simulée par l'antialiasing. Cette solution gère mal les lignes pentues, le pire cas étant les lignes penchées de 45 degrés par rapport à l'horizontale ou la verticale.
* Pour mieux gérer les bords penchés, on peut positionner nos sous-pixels comme suit. Les sous-pixels sont placés sur un carré penché (ou sur une ligne si l'on dispose seulement de deux sous-pixels). Des mesures expérimentales montrent que la qualité optimale semble être obtenue avec un angle de rotation d'arctan(1/2) (26,6 degrés), et d'un facteur de rétrécissement de √5/2.
* D'autres dispositions sont possibles, notamment une disposition de type Quincunx.
<gallery widths="150px" heights="150px">
Supersampling - Uniform.svg|Antialiasing uniforme.
Supersampling - RGSS.svg|Antialiasing à grille tournée.
Supersampling - Quincunx.svg|Antialiasing Quincunx.
</gallery>
==Le multisampling (MSAA)==
Le '''Multi-Sampling Anti-Aliasing, abrévié en MSAA''' est une amélioration du SSAA qui économise certains calculs. Pour simplifier, c'est la même chose que le SSAA, sauf que les pixels shaders ne calculent pas l'image à une résolution supérieure, alors que tout le reste (rastérisation, ROP) le fait. Avec le MSAA, l'image à afficher est rendue dans une résolution supérieure, mais les fragments sont regroupés en carrés qui correspondent à un pixel. L'application des textures se fait par pixel et non pour chaque sous-pixel, de même que le pixel shader manipule des pixels, mais ne traite pas les sous-pixels. Avec le SSAA, chaque sous-pixel se verrait appliquer un morceau de texture et un pixel shader, alors qu'on applique la texture sur un pixel complet avec le MSAA.
Le calcul de la couleur finale du pixel se fait dans le ROP. Pour cela, le ROP a besoin d'une information quant à la position des sous-pixels. Le pixel final est associé à un triangle précis par la rastérisation. Cependant, cela ne signifie pas que tous les sous-pixels sont associés à ce triangle. En effet, les sous-pixels ne sont pas à la même place que le pixel dans la résolution inférieure. La position des sous-pixels est une chose dont nous parlerons plus en détail ci-dessous.
Toujours est-il que l'étape de rastérisation précise si chaque sous-pixel est associé au triangle du pixel. Il se peut que tous les sous-pixels soient sur le triangle, ce qui est signe qu'on est pile sur l'objet, que les sous-pixels sont tous l'intérieur du triangle. Par contre, si certains sous-pixels sont en dehors, c'est signe que l'on est au bord d'un objet. Par exemple, le sous-pixel le plus à gauche sort du triangle, alors que les autres sont dessus. L'unité de rastérisation calcule un '''masque de couverture''', qui précise, pour chaque pixel, quels sont les sous-pixels qui sont ou non dans le triangle. Si un pixel est composé de N sous-pixels, alors ces N sous-pixels sont numérotés de 0 à N-1 en passant dans l'ordre des aiguilles d'une montre. Le masque est un nombre dont chaque bit est associé à un sous-pixel. Le bit associé est à 1 si le sous-pixel est dans le triangle, 0 sinon.
Une fois calculé par l'unité de rastérisation, le masque de couverture est transmis aux pixels shaders. Les pixels shaders peuvent utiliser le masque de couverture pour certaines techniques de rendu, mais ce n'est pas une nécessité. Dans la plupart des cas, les pixels shaders ne font rien avec le masque de couverture et le passent tel quel aux ROP. Le ROP prend la couleur calculée par le pixel shader et le masque de couverture. Si un sous-pixel est complétement dans le triangle, sa couleur est celle de la texture. Si le sous-pixel est en dehors du triangle, sa couleur est mise à zéro. Le ROP fait la moyenne des couleurs des sous-pixels du bloc comme avec le SSAA. La seule différence avec le SSAA, c'est que la couleur du pixel calculée par le pixel shader est juste pondérée par le nombre de sous-pixels dans le triangle. Le résultat est que le MSAA ne filtre pas toute l'image, mais seulement les bords des objets, seuls endroit où l'effet d'escalier se fait sentir.
===Les avantages et inconvénients comparé au SSAA===
Niveau avantages, le MSAA n'utilise qu'un seul filtrage de texture par pixel, et non par sous-pixel comme avec le SSAA, ce qui est un gain en performance notable. Le gain en calculs niveau pixel shader est aussi très important, tant que les techniques de rendu utilisant le masque de couverture ne sont pas utilisées. Le gain est d'autant plus important que la majorité des pixels sont situés en plein dans un triangle, les bords d'un objet ne concernant qu'une minorité de pixels/sous-pixels. Mais ce gain en performance a un revers : la qualité de l'antialiasing est moindre. Par définition, le MSAA ne filtre pas l'intérieur des textures, mais seulement les bords des objets.
Un défaut de cette technique est que la texture est plaquée au centre du pixel testé. Or, il se peut que le centre du pixel ne soit pas dans la primitive, ce qui arrive si la primitive ne recouvre qu'une petite partie du pixel. Dans un cas pareil, le pixel n'aurait pas été associé à la primitive sans antialiasing, mais il l'est quand l'antialiasing est activé. Un défaut est donc que la texture est appliquée là où elle ne devrait pas l'être. Le résultat est l'apparition d'artefacts graphiques assez légers, mais visibles sur certaines images. Une solution est d'altérer la position des sous-pixels sur le bord des objets pour qu'ils soient dans la primitive. Les sous-pixels sont alors disposés suivant un motif dit centroïde, où tous les sous-pixels sont déplacés de manière à être dans la primitive. Mais un défaut est que les dérivées, le niveau de détail et d'autres données nécessaires au plaquage de texture sont elles aussi altérées, ce qui peut gêner le filtrage de texture. Un autre problème de l'antialiasing tient dans la gestion des textures transparentes, que nous allons détailler dans la section suivante.
===L'antialiasing sur les textures transparentes===
Pour les textures partiellement transparentes, l’antialiasing de type MSAA ne donne pas de bons résultats. Les textures partiellement transparentes servent à rendre des feuillages, des grillages, ou d'autres objets du genre. Prenons l'exemple d'un grillage. La texture de grillage est posée sur une surface carrée, les portions transparentes de la texture correspondant aux trous du grillage entre les grilles, et les portions opaques au grillage lui-même. Dans ce cas, les portions transparentes sont situées dans l'objet et ne sont pas antialiasées. Pourtant, un grillage ou un feuillage sont l'exemple type d'objets où l'effet d’escalier se manifeste. Le problème est surtout visible sur les textures rendues avec la technique de l'''alpha-testing'', où un pixel shader abandonne le rendu d'un pixel si sa transparence dépasse un certain seuil. Les pixels sont coloriés avec une texture, et les pixels trop transparents ne sont pas rendus, alors que les autres pixels sont rendus normalement, avec ''alpha-blending'' dans les ROP et autres.
Tout cela a poussé les fabricants de cartes graphiques à inventer diverses techniques pour appliquer l'antialiasing à l'intérieur des textures transparentes. L'idée la plus simple pour cela est d'appliquer le MSAA sur toute l'image, mais de passer en mode SSAA pour les portions de l'image où on a une texture transparente. Le SSAA n'a pas de problèmes pour filtrer l'intérieur des textures, là où le MSAA ne filtre pas l'intérieur des textures. Cela demande cependant de détecter les textures transparentes au niveau du pixel shader, et de les rendre à plus haute résolution façon SSAA. Cette technique a été utilisée sur les cartes NVIDIA sous le nom de ''transparency adaptive anti-aliasing'' (TAAA) et sur les cartes AMD sous le nom d'''adaptive anti-aliasing''.
Une autre méthode est la technique dite d''''''alpha to coverage''''', abrévié ATC. Son principe s'explique assez bien en comparant ce qu'on a avec ou sans ATC. Imaginons qu'un pixel soit colorié avec une texture transparente, sans ATC : le pixel se voit attribuer une composante alpha provenant de la texture transparente et passe le test alpha pour savoir s'il doit être rendu avant ou non. Avec ATC, le pixel shader génère un masque de couverture à partir de la composante alpha de la texture lue. Le masque de couverture ainsi généré est alors utilisé par les ROP et le reste du pipeline pour faire l'antialiasing. Cela garantit que les textures transparentes soient antialiasées.
===Les optimisations du ''multisampling''===
Avec l'antialiasing, l'image est rendue à une résolution supérieure, avant de subir un redimensionnement pour rentrer dans la résolution voulue. Cela a des conséquences sur le ''framebuffer''. Le ''framebuffer'' a la taille nécessaire pour la résolution finale, cela ne change pas. Mais le z-buffer et les autres tampons utilisés par le ROP sont agrandis, afin de rendre l'image de résolution supérieure. De plus, le rendu de l'image intermédiaire à haute résolution se fait dans une sorte de pseudo-''framebuffer'' temporaire. L'antialiasing rend l'image de haute résolution dans ce ''framebuffer'' temporaire, puis la redimensionne pour donner l'image finale dans le ''framebuffer'' final. Si on prend un antialiasing 4x, soit avec 4 fois plus de pixels que la résolution initiale, le z-buffer prend 4 fois plus de place, le ''framebuffer'' temporaire aussi.
Évidemment, cela prend beaucoup de mémoire vidéo, sans compter que rendre une image à une résolution supérieure prend beaucoup de bande passante, et diverses optimisations ont été inventées pour limiter la casse. Avec le multisampling, il n'est pas rare que plusieurs sous-pixels aient la même couleur. Autant les pixels situés sur les bords d'un objet/triangle ont tendance à avoir des sous-pixels de couleurs différentes, autant les pixels situés à l'intérieur d'un objet sont de couleur uniforme. Cela permet une certaine forme d'optimisation, qui vise à tenir compte de ce cas particulier. L'idée est de compresser le ''framebuffer'' de manière ne pas mémoriser la couleur de chaque sous-pixel pour un pixel uniforme. Au lieu d'écrire quatre couleurs identiques pour 4 sous-pixels, on écrit une seule fois la couleur pour le pixel entier.
Notons cependant qu'il existe un type de GPU pour lesquels ce genre d'optimisation n'est pas nécessaire. Rappelez-vous qu'il existe deux types de GPU : ceux en mode immédiat, sujet de ce cours, et ceux en rendu à tile. Avec ces derniers, l'écran est découpé en tiles qui sont rendues séparément, soit l'une après l'autre, soit en parallèle. Le traitement d'une tile fait que l'on n'a pas besoin d'un z-buffer pour toute l'image, mais d'un z-buffer par tile. Même chose pour le ''framebuffer'' temporaire, qui doit mémoriser la tile, pas plus. Les deux sont tellement petits qu'ils peuvent être mémorisés dans une SRAM intégrée aux ROP, et non en mémoire vidéo. L'antialiasing est donc réalisé intégralement dans les ROP, sans passer par la mémoire vidéo.
==Le ''multisampling'' amélioré : ''Coverage Sampled Anti-Aliasing'' (CSAA) et de ''Enhanced Quality Anti-Aliasing'' (EQAA)==
Les techniques de ''multisampling'' précédentes rendaient l’image à une résolution supérieure, sauf dans les pixels shaders et l'étape de plaquage de textures. Mais la résolution supérieure était la même dans tous les pipeline de la carte graphique. Des techniques améliorées partent du même principe que le ''multisampling'', mais changent la résolution suivant les étapes du pipeline. Concrètement, la résolution utilisée par le rastériseur n'est pas la même que dans les pixels shaders/textures, qui elle-même n'est pas la même que dans le z-buffer, qui n'est pas la même que celle du ''framebuffer'' temporaire, etc. C'est le principe des techniques de '''''Coverage Sampled Anti-Aliasing''''' (CSAA) et de '''''Enhanced Quality Anti-Aliasing''''' (EQAA).
===Un nombre de sous-pixel par pixel qui varie suivant l'étape du pipeline===
Au lieu d'utiliser la résolution, nous allons utiliser le nombre de sous-pixels par pixel. Pour le dire autrement, on peut avoir 16 sous-pixels par pixel en sortie du rastériseur, mais 8 sous-pixels par pixel pour le masque de couverture, puis 4 sous-pixels pour le ''z-buffer'' et le ''framebuffer''. Nous allons donner 4 caractéristiques :
* le nombre de sous-pixels par pixel en sortie de la rastérisation ;
* le nombre de sous-pixels traités par le pixel shader et/ou le plaquage de textures ;
* le nombre de sous-pixels par pixel dans le tampon de profondeur ;
* le nombre de sous-pixels par pixel dans le ''color buffer'', le ''framebuffer'' temporaire.
Ces 5 paramètres seront notés respectivement RSS, SSS, DSS, CSS et CCS.
{|class="wikitable"
|-
! Mode d'AA
! RSS
! SSS
! DSS
! CSS
|-
! Supersampling 8x
| 8 || 8 || 8 || 8
|-
! Multisampling 8x
| 8 || 1 || 8 || 8
|-
! Coverage Sampled Antialiasing 8x
| 8 || 1 || 4 || 4
|-
! Coverage Sampled Antialiasing 16x
| 16 || 1 || 4 || 16
|-
! Coverage Sampled Antialiasing 16xQ
| 16 || 1 || 8 || 16
|-
! Enhanced Quality Antialiasing 2f4x
| 4 || 1 || 2 || 4
|-
! Enhanced Quality Antialiasing 4f8x
| 8 || 1 || 4 || 8
|-
! Enhanced Quality Antialiasing 4f16x
| 16 || 1 || 4 || 16
|-
! Enhanced Quality Antialiasing 8f16x
| 16 || 1 || 8 || 16
|}
En général, si on omet l'étape de pixel shading, la résolution diminue au fur et à mesure qu'on progresse dans le pipeline. La résolution est maximale en sortie du rastériseur et elle diminue ou reste constante à chaque étape suivante. Elle reste constante pour le ''multisampling'' pur, mais diminue dans les autres techniques. Ces dernières fusionnent plusieurs sous-pixels rastérisés en plus gros sous-pixels, qui eux sont stockés dans le ''framebuffer'' et le tampon de profondeur.
===La compression du ''framebuffer'' temporaire===
De plus, ces techniques utilisent des techniques de compression similaires à celles utilisées pour les textures sont aussi utilisées. L'idée est simple : il est rare que tous les sous-pixels aient chacun une couleur différente. Prenons par exemple le cas d'un antialiasing 4x, donc un groupe de 4 sous-pixels par pixel : deux sous-pixels vont avoir la même couleur, les deux auront une autre couleur. Dans ce cas, pas besoin de mémoriser 4 couleurs : on a juste à mémoriser deux couleurs et un tableau de 4 bits qui précise quelle pixel a telle couleur (0 pour la première couleur, 1 pour l'autre). On peut adapter la technique avec un nombre plus élevé de sous-pixels et de couleurs.
Les techniques de compression les plus simples font que l'on mémorise 2 couleurs par tile de sous-pixels, de la même manière que le font les formats de compression de textures. D'autres techniques peuvent mémoriser 4 couleurs pour 8 sous-pixels, etc.
{|class="wikitable"
|-
! Mode d'AA
! Nombre de sous-pixels par pixel
! Nombre de couleurs par pixel
|-
! Supersampling et Multisampling 8x
| 8 || 8
|-
! Coverage Sampled Antialiasing 8x
| 8 || 4
|-
! Coverage Sampled Antialiasing 16x
| 16 || 4
|-
! Coverage Sampled Antialiasing 8xQ
| 8 || 8
|-
! Coverage Sampled Antialiasing 16xQ
| 16 || 4
|-
! Enhanced Quality Antialiasing 2f4x
| 4 || 2
|-
! Enhanced Quality Antialiasing 4f8x
| 8 || 4
|-
! Enhanced Quality Antialiasing 4f16x
| 16 || 4
|-
! Enhanced Quality Antialiasing 8f16x
| 16 || 8
|}
==L'antialiasing temporel==
L''''antialiasing temporel''' (TAA pour ''temporal Anti-Aliasing'') est une technique qui fonctionne comme le MSAA, mais répartie sur plusieurs ''frames'', sur plusieurs images. L'idée de l'antialiasing temporel est que chaque image est mélangée avec les images rendues avant elle pour donner un effet d'antialiasing. Mais il ne s'agit pas d'un mélange bête et méchant où chaque image est la moyenne des précédentes. L'antialiasing temporel subdivise chaque pixel en sous-pixel, sauf qu'au lieu de traiter tous les sous-pixels à chaque image comme le font le ''super-sampling'' et le MSAA, elle ne traite qu'un sous-pixel par pixel à chaque image. Concrètement, si on prend un antialiasing 4x, où chaque pixel est subdivisé en 4 sous-pixels, le premier sous-pixel sera calculé par la première image, le second sous-pixel par la seconde image, etc. Il reste ensuite à appliquer l'opération de mélange sur les 4 images rendues auparavant. Naïvement, on pourrait croire que le filtre de mélange des sous-pixels est effectué toutes les 4 images, mais on peut le faire à chaque rendu d'image en prenant les 4 images précédemment calculées.
[[File:TV ghosting interference.jpg|vignette|Exemple de la trainée de mouvement observée avec le TAA avec des objets en mouvement rapide.]]
L'avantage du TAA est qu'il est relativement léger en calculs. Si le filtre de mélange est calculé toutes les images, comme dans les techniques précédentes, on n'a pas à calculer tous les sous-pixels à chaque image, seulement 1 par pixel. La carte graphique rend chaque image à la même résolution que l'écran, mais chaque image a une position légèrement différente de la précédente, ce qui fait que le mélange de plusieurs images consécutives permet d'affiner la qualité d'image. Cette forme d'antialiasing améliore la qualité de toute l'image, contrairement au MSSA, mais comme le SSAA. Si le TAA marche très bien pour des scènes statiques, il se débrouille assez mal sur les scènes où la caméra bouge vite. Des mouvements trop rapides font que l'image a un flou de mouvement très important, sans compter que les objets en mouvement laissent une sorte de trainée de mouvement visible derrière eux. Pour éviter cela, les moteurs de jeux ont des méthodes pour éviter de rendre les objets en mouvement à certaines images bien placées, mais l'effet peut quand même se faire sentir. Notons cependant que le TAA marche d'autant mieux en qualité que le nombre d'images par secondes est élevé.
==L'antialiasing par ''post-processing''==
L''''antialiasing par ''post-processing''''' regroupe plusieurs techniques d'antialiasing différentes du SSAA et du MSAA. Avec elles, l'image n'est pas rendue à plus haute résolution avant d'être redimensionnée. A la place, l'image est calculée normalement, à sa résolution finale. Une fois l'image finale mémorisée dans le ''framebuffer'', on lui applique un filtre d'antialiasing spécial. Le filtre en question varie selon la technique utilisée, mais l'idée générale est la même. C'est donc des techniques dites de ''post-processing'', où on calcule l'image, avant de lui faire subir des filtres pour l'embellir. Le filtre en question peut être effectué par les ROP ou par un pixel shader, mais c'est surtout la seconde solution qui est retenue de nos jours. L'algorithme des filtres est généralement assez complexe, ce qui rend sont implémentation en matériel peu pertinente.
===Les avantages et inconvénients===
Contrairement au MSAA, l'antialiasing par ''post-processing'' n'a aucune connaissance de la géométrie de la scène, n'a aucune connaissance des informations données par la rastérisation, n'utilise même pas de sous-pixels. C'est un avantage, car le FXAA filtre la totalité de la scène 3D, même à l'intérieur des textures, et même à l'intérieur des textures transparentes.
Par contre, cela peut causer des artefacts graphiques sur certaines portions de l'image. Quand le FXAA est activé, le texte affiché sur une image devient légèrement moins lisible, par exemple. Les techniques de ''post-processing'' ont l'avantage de mieux marcher avec les moteurs de jeux qui utilisent des techniques de rendu différés, dans lesquels une bonne partie des traitements d'éclairage se font sur l'image finale rendue dans le ''framebuffer''.
===Les différentes méthodes d'antialiasing par ''post-processing''===
Le '''''Fast approximate anti-aliasing''''' (FXAA), et le '''''Subpixel Morphological Antialiasing''''' (SMAA) sont les premières techniques d'antialiasing par ''post-processing'' à avoir été intégrées dans les cartes graphiques modernes. Pour le FXAA, le filtre détermine les zones à filtrer en analysant le contraste. Les zones de l'image où le contraste évolue fortement d'un pixel à l'autre sont filtrées, alors que les zones où le contraste varie peu sont gardées intactes. La raison est que les zones où le contraste varie rapidement sont généralement les bords des objets, ou du moins des zones où l'effet d'escalier se fait sentir. l'algorithme exact est dans le domaine public et on peut le trouver facilement sur le net. Par contre, il est difficile d'en expliquer le fonctionnement, pourquoi il marche, aussi je passe cela sous silence.
Les cartes graphiques récentes utilisent des techniques basées sur des réseaux de neurones pour effectuer de l'antialiasing. La première d'entre elle est le '''''Deep learning dynamic super resolution''''' (DLDSR), qui consiste à rendre l'image en plus haute résolution, puis à lui appliquer un filtre pour en réduire la résolution. C'est un peu la même chose que le ''supersampling'', sauf que le ''supersampling'' est réalisé dans les ROP, alors que le DLDSR est effectué avec une phase de rendu complète, suivie par l’exécution d'un shader qui redimensionne l'image. Une technique opposée est le '''''Deep learning super sampling''''' (DLSS), qui rend l'image à une résolution inférieure, mais applqiue un filtre qui redimensionne l'image à une plus haute résolution. La première version utilisait un filtre de ''post-processing'', mais les versions suivantes utilisent de l'antialising temporel. Quoique en soit, toutes les versions de DLSS appliquent une forme d'antialiasing, même si elles ''upscalent'' aussi l'image.
{{NavChapitre | book=Les cartes graphiques
| prev=Le support matériel du lancer de rayons
| prevText=Le support matériel du lancer de rayons
| next=Le multi-GPU
| nextText=Le multi-GPU
}}{{autocat}}
04i5ect7nsqb1bqbt7hq7h3j9j8uqtx
Mathc initiation/a81
0
79157
744726
725765
2025-06-14T13:48:45Z
Xhungab
23827
744726
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
:
'''Sommaire''' ◀ '''''Utilise la commande "Retour en Arrière" de ton navigateur.'''''
{{Partie{{{type|}}}|Logarithmes Exponentielles}}
:
En mathématiques, le logarithme de base b d'un nombre réel strictement positif est la puissance à laquelle il faut élever la base b pour obtenir ce nombre... [https://fr.wikipedia.org/wiki/Logarithme Wikipédia]
:
<br>
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/h81_00|x_a.h ................. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
'''Propriétés des logarithmes :'''
* [[Mathc initiation/h81_a|fa.h ]] <-------> [[Mathc initiation/c81_a| ln(x*y) = ln(x)+ln(y)]]
* [[Mathc initiation/h81_b|fb.h ]] <-------> [[Mathc initiation/c81_b| ln(x/y) = ln(x)-ln(y)]]
* [[Mathc initiation/h81_c|fc.h ]] <-------> [[Mathc initiation/c81_c| ln(x^n) = n*ln(x)]]
* [[Mathc initiation/h81_d|fd.h ]] <-------> [[Mathc initiation/c81_d| log_a(x) = ln(x)/ln(a)]]
'''Propriétés des exponentielles :'''
* [[Mathc initiation/000r|fe.h ]] <-------> [[Mathc initiation/000u| exp(x+y) = exp(x)*exp(y)]]
* [[Mathc initiation/000s|ff.h ]] <-------> [[Mathc initiation/000v| exp(x-y) = exp(x)/exp(y)]]
* [[Mathc initiation/000t|fg.h ]] <-------> [[Mathc initiation/000w| a**x = exp(x ln(a))]]
:
.
:
'''Remarques : Lois pour les fonctions exponentielles'''
<syntaxhighlight lang="c">
Avec a > 0 et b > 0, a et b sont des constantes.
x et y sont des variables réelles.
* a**x a**y = a**(x+y)
* a**x/a**y = a**(x-y)
* (a**x)**y = a**(xy)
* (a b)**x = a**x b**x
* (a/b)**x = a**x/b**x
</syntaxhighlight>
:
.
:
'''Remarques : Lois pour les fonctions logarithmiques'''
<syntaxhighlight lang="c">
Avec a > 0 et b > 0 et c > 0, a ! =0, r un nombre réel.
* log_a(b c) = log_a(b) + log_a(c)
* log_a(b/c) = log_a(b) - log_a(c)
* log_a(b**r) = r log_a(b)
</syntaxhighlight>
:
.
:
'''Remarques : Changement de bases'''
<syntaxhighlight lang="c">
Avec a > 0 et b > 0, a != 1, b != 1.
* log_a(x) = log_b(x)/log_b(a) * log_a(x) = ln(x)/ln(a)
* a**x = b**(x log_b(a)) * a**x = e**(x ln(a))
a**x = b**( log_b(a**x)) b**(log_b(a**x)) = a**x
= b**(x log_b(a) ) log_b(a**x) = x log_b(a)
</syntaxhighlight>
:
.
:
'''Calculons quelques logarithme :'''
* [[Mathc initiation/001D|Une introduction ]]
:
.
:
{{AutoCat}}
gwdwol2mc1c4kfv6qrfhd2g7i83d953
744728
744726
2025-06-14T13:58:33Z
Xhungab
23827
744728
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
:
'''Sommaire''' ◀ '''''Utilise la commande "Retour en Arrière" de ton navigateur.'''''
{{Partie{{{type|}}}|Logarithmes Exponentielles}}
:
En mathématiques, le logarithme de base b d'un nombre réel strictement positif est la puissance à laquelle il faut élever la base b pour obtenir ce nombre... [https://fr.wikipedia.org/wiki/Logarithme Wikipédia]
:
<br>
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/h81_00|x_a.h ................. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
'''Propriétés des logarithmes :'''
* [[Mathc initiation/h81_a|fa.h ]] <-------> [[Mathc initiation/c81_a| ln(x*y) = ln(x)+ln(y)]]
* [[Mathc initiation/h81_b|fb.h ]] <-------> [[Mathc initiation/c81_b| ln(x/y) = ln(x)-ln(y)]]
* [[Mathc initiation/h81_c|fc.h ]] <-------> [[Mathc initiation/c81_c| ln(x^n) = n*ln(x)]]
* [[Mathc initiation/h81_d|fd.h ]] <-------> [[Mathc initiation/c81_d| log_a(x) = ln(x)/ln(a)]]
'''Propriétés des exponentielles :'''
* [[Mathc initiation/000r|fe.h ]] <-------> [[Mathc initiation/000u| exp(x+y) = exp(x)*exp(y)]]
* [[Mathc initiation/000s|ff.h ]] <-------> [[Mathc initiation/000v| exp(x-y) = exp(x)/exp(y)]]
* [[Mathc initiation/000t|fg.h ]] <-------> [[Mathc initiation/000w| a**x = exp(x ln(a))]]
:
.
:
'''Remarques : Lois pour les fonctions exponentielles'''
<syntaxhighlight lang="c">
Avec a > 0 et b > 0, a et b sont des constantes.
x et y sont des variables réelles.
* a**x a**y = a**(x+y)
* a**x/a**y = a**(x-y)
* (a**x)**y = a**(xy)
* (a b)**x = a**x b**x
* (a/b)**x = a**x/b**x
</syntaxhighlight>
:
.
:
'''Remarques : Lois pour les fonctions logarithmiques'''
<syntaxhighlight lang="c">
Avec a > 0 et b > 0 et c > 0, a ! =0, r un nombre réel.
* log_a(b c) = log_a(b) + log_a(c)
* log_a(b/c) = log_a(b) - log_a(c)
* log_a(b**r) = r log_a(b)
</syntaxhighlight>
:
.
:
'''Remarques : Changement de bases'''
<syntaxhighlight lang="c">
Avec a > 0 et b > 0, a != 1, b != 1.
* log_a(x) = log_b(x)/log_b(a) * log_a(x) = ln(x)/ln(a)
* a**x = b**(x log_b(a)) * a**x = e**(x ln(a))
a**x = b**( log_b(a**x)) b**(log_b(a**x)) = a**x
= b**(x log_b(a) ) log_b(a**x) = x log_b(a)
</syntaxhighlight>
:
.
:
'''Calculons quelques logarithmes :'''
* [[Mathc initiation/001D|Une introduction ]]
:
.
:
{{AutoCat}}
s8qfbqt4jesbzdy8cde2b1kvxvpylg6
Les cartes graphiques/La répartition du travail sur les unités de shaders
0
79263
744775
740169
2025-06-15T01:33:35Z
Mewtow
31375
/* La répartition du travail pour les autres tâches que le rendu 3D */
744775
wikitext
text/x-wiki
La répartition du travail sur plusieurs processeurs de ''shaders'' est un vrai défi sur les cartes graphiques actuelles. De plus, elle dépend de l'occupation des circuits fixes, comme l’''input assembler'' ou l'unité de rastérisation. Une carte graphique ne traite pas les sommets ou pixels un par un, mais essaye d'en traiter un grand nombre en même temps, en parallèle. Les cartes graphiques sont conçues pour cela, pour exécuter les mêmes calculs en parallèle sur un grande nombre de données indépendantes. Si on prend l'exemple d'un ''vertex shader'', une carte graphique exécute le même shader sur un grande nombre de sommets en même temps. Pour cela, elle contient plusieurs processeurs de shaders, chacun traitant au moins un sommet à la fois. Il en est de même pour les traitements sur les pixels. Dans ce chapitre, nous allons voir comment le travail est répartit sur les processeurs de shaders.
==Les cartes graphiques implémentent un parallélisme de type Fork/join==
Si une carte graphique exécute de nombreux calculs en parallèle, tout ne peut pas être parallélisé et ce pour deux raisons. La première est tout simplement le pipeline graphique, qui impose que le rendu se fasse en étapes bien précises. La seconde raison est que certaines étapes sont relativement bloquantes et imposent une réduction du parallélisme. Tel est le cas de l'étape de rastérisation ou de l'enregistrement des pixels en mémoire, qui se parallélisent assez mal. Elles servent de point de convergence, de point de blocage. Les deux points interagissent et font que le parallélisme sur les cartes graphiques est un peu particulier.
Concrètement, prenons l'exemple d'une carte graphique simple, illustrée ci-dessous. Le pipeline commence avec une unité d'''input assembly'' qui lit les sommets en mémoire vidéo et les distribue sur plusieurs circuits géométriques (des circuits fixes sur les anciennes cartes 3D, des processeurs de shaders sur les modernes). En sortie des unités géométriques, les sommets sont tous envoyé au rastériseur. En sortie du rastériseur, les pixels sont répartis sur plusieurs circuits de traitement des pixels, généralement des processeurs de shaders. Là encore, comme pour les unité géométriques, on trouve un grand nombre de circuits qui effectuent leur travail en parallèle. puis vient le moment d'enregistrer les pixels finaux en mémoire. Ils sont alors envoyés à un circuit spécialisé, nommé le ROP.
[[File:Parallélisme dans une carte 3D.png|centre|vignette|upright=3|Parallélisme dans une carte 3D]]
On voit que le rastériseur et les ROP finaux sont des points de convergence où convergent plusieurs sommets/pixels. Pour gérer cette convergence, les cartes graphiques disposent de mémoires tampon, dans lesquelles on accumule les sommets ou pixels. Ce mécanisme de mise en attente garantit que les sommets sont mémorisés en attendant que le rastériseur soit libre, ou que les ROP soient libres. La présence des mémoires tampon désynchronise les différentes étapes du pipeline, tout en gardant une exécution des étapes dans l'ordre. Une étape écrit ses résultats dans la mémoire tampon, l'autre étape lit ce tampon quand elle démarre de nouveaux calculs. Comme cela, on n'a pas à synchroniser les deux étapes : la première étape n'a pas à attendre que la seconde soit disponible pour lui envoyer des données. Évidemment, tout cela marche bine tant que les mémoires tampon peuvent accumuler de nouvelles données. Mais si ce n'est pas le cas, si la mémoire tampon est pleine, elle ne peut plus accepter de nouveaux sommet/pixels. Donc, l'étape précédente est bloquée et ne peut plus démarrer de nouveaux calculs en attendant.
==La répartition entre ''pixel shaders'' et ''vertex shaders''==
Un triangle est impliqué dans plusieurs pixels lors de l'étape de rastérisation. Un triangle peut donner quelques pixels lors de l'étape de rastérisation, alors qu'un autre va couvrir 10 fois de pixels, un autre seulement trois fois plus, un autre seulement un pixel, etc. Et vu que chaque triangle est impliqué dans plusieurs pixels, cela fait qu'il y a plus de travail à faire sur les pixels que sur les sommets. C'est un phénoméne d'amplification, qui fait que les processeurs de ''shader'' sont plus occupés à exécuter des ''pixel shaders'' que des ''vertex shader''.
===Les architectures avec unités de ''vertex'' et ''pixels'' séparées===
Les premières cartes graphiques avaient des processeurs séparés pour les ''vertex shaders'' et les ''pixel shaders''. Cette séparation entre unités de texture et de sommets était motivée par le fait qu'à l'époque, le jeu d'instruction pour les ''vertex shaders'' était différent de celui pour les ''pixel shaders''. Notamment, les unités de traitement de pixels doivent accéder aux textures, mais pas les unités de traitement de la géométrie. D'où l'existence d'unités dédiées pour les ''vertex shaders'' et les ''pixel shaders'', chacune avec ses propres capacités et fonctionnalités. Ces architectures ont beaucoup plus de processeurs de ''pixel shaders'' que de processeurs de ''vertex shaders'', vu que les ''pixels shaders'' dominent le temps de calcul par rapport aux ''vertex shaders''.
[[File:Architecture de base d'une carte 3D - 5.png|centre|vignette|upright=1.5|Carte 3D avec pixels et vertex shaders non-unifiés.]]
Pour donner un exemple, c'était le cas de la Geforce 6800. Elle comprenait 16 processeurs pour les ''pixel shaders'', alors qu'elle n'avait que 6 processeurs pour les ''vertex shaders''. D'autres cartes graphiques plus récentes utilisent encore cette stratégie, comme l'ARM Mali 400.
[[File:GeForce 6800.png|centre|vignette|upright=3|Architecture de la GeForce 6800.]]
Malheureusement, il arrivait que certains processeurs de ''shaders'' soient inutilisés. Malheureusement, tous les jeux vidéos n'ont pas les mêmes besoins : certains sont plus lourds au niveau géométrie que d'autres, certains ont des ''pixels shaders'' très gourmands avec des ''vertex shaders'' très ''light'', d'autres font l'inverse, etc. Le fait d'avoir un nombre fixe de processeurs de ''vertex shaders'' fait qu'il va en manquer pour certains jeux vidéo, alors qu'il y en aura de trop pour d'autres. Pareil pour les processeurs de ''pixel shaders''. Au final, le problème est que la répartition entre puissance de calcul pour les sommets et puissance de calcul pour les pixels est fixe, alors qu'elle n'est variable d'un jeu vidéo à l'autre, voire d'un niveau de JV à l'autre.
===Les architectures avec unités de ''shaders'' unifiées===
Depuis DirectX 10, le jeu d'instruction des ''vertex shaders'' et des ''pixels shaders'' a été unifié : plus de différences entre les deux. En conséquence, il n'y a plus de distinction entre processeurs de ''vertex shaders'' et de ''pixels shaders'', chaque processeur pouvant traiter indifféremment l'un ou l'autre.
[[File:Architecture de base d'une carte 3D - 6.png|centre|vignette|upright=1.5|Carte 3D avec ''pixels'' et ''vertex shaders'' unfifiés.]]
: Précisons qu'il existe des architectures DirectX 10 avec des unités séparées pour les ''vertex shaders'' et ''pixels shaders''. L'unification logicielle des ''shaders'' n’implique pas unification matérielle des processeurs de ''shaders'', même si elle l'aide fortement.
Cela a un avantage considérable, car tous les jeux vidéo n'ont pas les mêmes besoins. Certains ont une géométrie très développée mais peu de besoin en termes de textures, ce qui fait qu'ils gagnent à avoir beaucoup d'unités de gestion de la géométrie et peu d'unités de traitement des pixels. À l'inverse, d'autres jeux vidéo ont besoin de beaucoup de puissance de calcul pour les pixels, mais arrivent à économiser énormément sur la géométrie. L'usage de ''shaders'' unifiés permet d'adapter la répartition entre ''vertex shaders'' et ''pixels shaders'' suivant les besoins de l'application, là où la séparation entre unités de vertex et de pixel ne le permettait pas.
==La distribution du travail sur des processeurs de ''shaders''==
L’''input assembler'' lit un flux de sommets depuis la mémoire et l'envoie aux processeurs de ''vertex shader''. La rastérisation, quant à elle, produit un flux de pixels qui sont envoyés aux ''pixels shaders'' et/ou aux unités de textures. Le travail sortant du rastériseur ou de l’''input assembler'' doit donc être envoyé à un processeur de ''shader'' libre, qui n'a rien à faire. Et on ne peut pas prédire lesquels sont libres, pas plus qu'il n'est garanti qu'il y en ait un.
Une des raisons à cela est que tous les ''shaders'' ne mettent pas le même temps à s'exécuter, certains prenant quelques dizaines de cycles d'horloge, d'autres une centaine, d'autres plusieurs milliers, etc. Il se peut qu'un processeur de ''shader'' soit occupé avec un paquet de sommets/pixels depuis plusieurs centaines de cycles, alors que d'autres sont libres depuis un moment. En clair, on peut pas prédire quels processeurs de ''shaders'' seront libres prochainement. Planifier à l'avance la répartition du travail sur les processeurs de ''shaders'' n'est donc pas vraiment possible. Cela perturbe la répartition du travail sur les processeurs de ''shader''. Pour résoudre ce problème, il existe plusieurs méthodes, classées en deux types : statiques et dynamiques.
===La distribution statique===
Avec la distribution statique, le choix n'est pas basé sur le fait que telle unité de shader est libre ou non, mais chaque sommet/pixel est envoyé vers une unité de shader définie à l'avance. L'ARM Mali 400 utilise une méthode de distribution statique très commune. Elle utilise des processeurs de ''vertex'' et de pixels séparés, avec un processeur de ''vertex'' et 4 processeurs de pixel. L'écran est découpé en quatre ''tiles'', quatre quadrants, et chaque quadrant est attribué à un processeur de ''pixel shader''. Quand un triangle est rastérisé, les pixels obtenus lors de la rastérisation sont alors envoyés aux processeurs de pixel en fonction de leur posituion à l'écran. Cette méthode a des avantages. Elle est très simple et n'a pas besoin de circuits complexes pour faire la distribution, l L'ordre de l'API est facile à conserver, la gestion des ressources est simple, la localité des accès mémoire est bonne, etc. Mais la distribution étant statique, il est possible que des unités de shader soient inutilisées. Il n'est pas rare que l'on a des périodes assez courtes où tout le travail sortant du rastériseur finisse sur un seul processeur.
===La distribution dynamique===
La distribution dynamique prend en compte le fait que certaines unités de shader sont inutilisées et tente d'utiliser celles-ci en priorité. Le travail sortant du rastériseur ou de l'''input assembler'' est envoyé aux unités de ''shader'' inutilisées, ou du moins à celles qui sont les moins utilisées. Cela garantit d'utiliser au mieux les processeurs de ''shaders''. On n'a plus de situations où un processeur est inutilisé, alors que du travail est disponible. Tous les processeurs sont utilisés tant qu'il y a assez de travail à faire. Par contre, cela demande de rajouter une '''unité de distribution''', qui se charge de répartir les pixels/fragments sur les différents processeurs de ''shaders''. Elle connait la disponibilité de chaque processeur de ''shader'' et d'autres informations nécessaires pour faire une distribution efficace.
[[File:Dispatch des shaders sur plusieurs processeurs de shaders.png|centre|vignette|upright=2|Dispatch des shaders sur plusieurs processeurs de shaders]]
Par contre, cela implique que les calculs se finissent dans le désordre. Par exemple, si deux pixels sortent du rastériseur l'un après l'autre, ils peuvent sortir de l'unité de ''pixel shader'' dans l'ordre inverse. Il suffit que le premier pixel a pris plus de temps à être calculé que l'autre pour ça. Or, l'API impose que les triangles/pixels soient rendus dans un ordre bien précis, de manière séquentielle. Si les calculs sur les sommets/pixels se font dans le désordre, cet ordre n'est pas respecté. Pour résoudre ce problème, et rester compatible avec les API, les processeurs de ''shader'' sont suivis par un circuit de remise en ordre, qui remet les pixels sortant des processeurs de pixel dans l'ordre de sortie du rastériseur. Le circuit en question est une mémoire qui ressemble un petit peu à une mémoire FIFO, appelée tampon pseudo-FIFO. Les pixels sont ajoutés au bon endroit dans la mémoire FIFO, mais ils quittent celle-ci dans l'ordre de rendu de l'API.
[[File:Exécutiondes shaders dans le désordre et circuits de remise en ordre.png|centre|vignette|upright=2.5|Exécution des shaders dans le désordre et circuits de remise en ordre]]
===Les architectures actuelles font les deux===
La distribution dynamique marche très bien quand il y a un seul rastériseur. Mais les cartes graphiques modernes disposent de plusieurs rastériseurs, pour ces questions de performance. Et cela demande de faire deux formes de répartition : répartir les triangles entre rastériseurs et les laisser faire leur travail en parallèle, puis répartir les fragment/pixels sortant des rastériseurs entre processeurs de ''shader''.
La répartition des fragment/pixels se base sur une distribution dynamique, alors que la répartition des triangles entre rastériseurs se base sur la distribution statique.La distribution statique se base sur l'algorithme avec des quadrants/tiles est utilisé pour la gestion des multiples rastériseurs. L'écran est découpé en ''tiles'' et chacune est attribuée à un rastériseur attitré. Il y a un rastériseur par ''tile'' dans la carte graphique. le découpage en ''tiles'' est aussi utilisée pour l'élimination des pixels non-visibles et pour quelques optimisations du rendu.
==La répartition du travail pour les autres tâches que le rendu 3D==
Plus haut, nous avions dit que rasteriseur et ''input assembler'' s'occupent de la répartition du travail sur les différents processeurs. Ils agissent sous la supervision du '''processeur de commande''' qui se charge de répartir le travail sur les différents circuits de la carte graphique. Il commande le rasteriseur, l'''input assembler'', les processeurs de ''shader'', et les autres circuits. Le processeur de commande est impliqué dans la répartition du travail de manière générale, mais s'occupe surtout du rendu 3D et 2D. Il y aura un chapitre spécialisé sur le fonctionnement du processeur de commande, qui parlera de son rôle en général.
Outre le rendu 3D, les cartes graphiques modernes sont utilisées dans le calcul scientifique, pour accélérer les applications d'IA et bien d'autres. L'usage d'une carte graphique pour autre chose que le rendu 3D porte le nom de '''GPGPU''' (''General Processing GPU''). Le GPGPU est utilisé pour diverses tâches : calcul scientifique, tout ce qui implique des réseaux de neurones, imagerie médicale, etc. De manière générale, tout calcul faisant usage d'un grand nombre de calculs sur des matrices ou des vecteurs est concerné.
En soi, le GPGPU est assez logique : une carte graphique est un monstre de puissance qu'il vaut mieux utiliser dès que possible. Bien que conçues spécifiquement avec le rendu 3D en tête, elles n'en restent pas moins des processeurs multicœurs SIMD/VLIW assez complexes et puissants, qu'on peut parfaitement utiliser en tant que processeur multicœur.
Pour le GPGPU, la programmation est assez simple. L'idée est d'écrire des shaders qui font des calculs génériques, à savoir qu'ils ne travaillent pas sur des pixels ou des vertices provenant du rastériseur ou de l'''input assembler''. Les données manipulées par le shader sont simplement lues ou écrites en mémoire, directement, sans passer par le moindre circuit fixe. Les shaders deviennent alors des programmes comme les autres, les processeurs de shaders sont utilisés comme n'importe quel processeur SIMD normal. Reste à répartir le travail sur les différents processeurs de shaders. Il suffit de programmer un ''shader'' qui effectue le calcul voulu et de configurer le GPU en mode GPGPU.
===La répartition du travail en GPGPU n'est pas celle du mode graphique===
En mode GPGPU, le ''shader'' s'exécute directement, il n'y a pas de passage par le rasterizeur, les ROP ou l'''input assembler'', ou tout autre circuit fixe impliqué dans le rendu 3D. Il s'agit donc d'un mode différent du mode graphique. La répartition du travail en GPGPU et en mode graphique n'est pas du tout la même.
Du point de vue du GPGPU, l'architecture d'une carte graphique récente est illustrée ci-dessous. Les processeurs/cœurs sont les rectangles en bleu/rouge, avec des unités de calcul SIMD en bleu et des unité de calcul pour les calculs complexes (trigonométriques, racines carrées, autres) en rouge. La hiérarchie mémoire est indiquée en vert. Le tout est alimenté par un processeur de commande spécialisé, le ''Thread Execution Control Unit'' en jaune, qui répartit les différentes instances des ''shader'' sur les différents processeurs.
[[File:NVIDIA GPU Accelerator Block Diagram.png|centre|vignette|upright=2.5|Ce schéma illustre l'architecture d'un GPU en utilisant la terminologie NVIDIA. Comme on le voit, la carte graphique contient plusieurs cœurs de processeur distincts. Chacun d'entre eux contient plusieurs unités de calcul généralistes, appelées processeurs de threads, qui s'occupent de calculs simples (en bleu). D'autres calculs plus complexes sont pris en charge par une unité de calcul spécialisée (en rouge). Ces cœurs sont alimentés en instructions par le processeur de commandes, ici appelé ''Thread Execution Control Unit'', qui répartit les différents shaders sur chaque cœur. Enfin, on voit que chaque cœur a accès à une mémoire locale dédiée, en plus d'une mémoire vidéo partagée entre tous les cœurs.]]
Les anciennes cartes graphiques se débrouillaient avec des processeurs de commande optimisés pour le rendu graphique, ce qui était sous-optimal. Mais de nos jours, les processeurs de commande se sont améliorés et sont capable de gérer des commandes de calcul génériques, qui n'utilisent que les processeurs de shaders et contournent les circuits fixes dédiés au rendu graphique.
Un premier point important est que certaines cartes graphiques ont des processeurs de commande séparés pour le GPGPU et le mode graphique. Un exemple est celui de l'architecture GCN d'AMD, qui disposait d'un processeur de commande pour le mode graphique, avec plusieurs processeurs de commande spécifiques au GPGPU. Les processeurs de commande spécifique au GPGPU étaient appelés des ACE, et il y en avait 8, 16 ou 32 selon l'architecture, le nombre ayant augmenté au cours du temps.
[[File:GCN command processing.svg|centre|vignette|upright=2|GCN command processing]]
Les cartes graphiques modernes peuvent gérer un rendu 3D simultané avec des calculs GPGPU. Les deux ont lieu en même temps, dans des processeurs de shaders séparés. Mais pour ne pas se marcher sur les pieds, le GPU dispose de mécanismes compliqués de répartition, mais aussi de priorité. Sur les cartes graphiques avant l'architecture AMD RDNA 1, les tâches de GPGPU avaient la priorité sur le rendu graphique. Maintenant, les cartes AMD modernes gérent un système de priorité plus complexe. De plus, elles ont la possibilité de suspendre complétement l'exécution d'un shader pour passer à une tâche graphique/GPGPU, ou autre, histoire faire passer avant tout les calculs prioritaires.
===La répartition du travail pour le GPGPU===
En GPGPU, un ''shader'' s'exécute sur des regroupements de données bien connus des programmeurs : des tableaux. Les tableaux peuvent être de simples tableaux, des matrices ou tout autre tableau, peu importe. Le processeur envoie à la carte graphique un ensemble comprenant un ''shader'' à exécuter, ainsi que les données/tableaux à manipuler. Les tableaux ont une taille variable, mais sont presque toujours de très grande taille, au moins un millier d’éléments, parfois un bon million, si ce n'est plus.
Il faut préciser que la terminologie du GPGPU est quelque peu trompeuse. Dans la terminologie GPGPU, un ''thread'' correspond à l'exécution d'un ''shader'' sur une seule donnée, un seul entier/flottant provenant du tableau. Beaucoup de monde s'imagine que les processeurs de ''shader'' exécutent des ''threads'', qui sont regroupés à l'exécution en ''warps'' par le matériel. Le regroupement fait que tous les ''threads'' s'exécutent de manière synchrone sur des données différentes, avec un ''program counter'' partagé par tous les ''threads''. Si on sait peu de choses sur la manière dont fonctionnent réellement les GPU modernes, ce n'est vraisemblablement pas ce qui se passe. Il est plus correct de dire qu'un GPU moderne est une sorte de processeur multicœurs amélioré, qui gère des tableaux/vecteurs de taille variable, mais les découpe en vecteurs de taille fixe à l'exécution et répartit le tout sur des processeurs SIMD.
Le processus de répartition du travail est globalement le suivant. Le processeur de commande reçoit une commande de calcul, qui précise quel ''shader'' exécuter, et fournit l'adresse de plusieurs tableaux, ainsi que des informations sur le format des données (entières, flottantes, tableau en une ou deux dimensions, autres). Le processeur de commande se charge de découper le ou les tableaux en vecteurs de taille fixe, que le processeur peut gérer. Par exemple, si un processeur SIMD gère des vecteurs de 32 entiers/flottants, alors le tableau est découpé en morceaux de 32 entiers/flottants, et chaque processeur exécute une instance du ''shader'' sur des morceaux de cette taille.
===La répartition du travail telle que définie par CUDA===
Pour les GPU NVIDIA, le processus de découpage n'est pas très bien connu. Mais on peut en avoir une idée en regardant l'interface logicielle utilisée pour le GPGPU. Chez NVIDIA, celle-ci s'appelle CUDA et ses versions donnent une idée de comment le découpage s'effectue. Premièrement, les ''shaders'' sont appelés des ''kernels''. Les tableaux de taille variable sont appelés des '''''grids'''''. Les données individuelles sont appelées, de manière extrêmement trompeuse, des ''threads''. Rappelons qu'un ''thread'' est, pour simplifier, censé désigner un programme en cours d'exécution, pas des données entières/flottantes regroupées dans un tableau. Aussi, pour éviter toute confusion, je vais renommer les ''thraeds CUDA'' en '''scalaires'''.
Les ''grids'' sont eux-même découpés en '''''thread blocks''''', qui contiennent entre 512 et 1024 données entières ou flottantes, 512 et 1024 scalaires. La taille, 512 ou 1024, dépend de la version de CUDA utilisée, elle-même liée au modèle de carte graphique utilisé. Plutôt que d'utiliser le terme ''thread blocks'', je vais parler de '''bloc de scalaires'''. Le bloc de scalaire est une portion d'un tableau, il correspond donc à un bloc de mémoire, à une suite d'adresse consécutives. Il a donc une adresse de départ.
Chaque scalaire d'un bloc de scalaire a un indice qui permet de déterminer son adresse, qui est calculé par le processeur de commande. Le calcul de l'indice peut se faire de différentes manières, suivant que le tableau soit un tableau unidimensionnel (une suite de nombre) ou bidimensionnel( une matrice). CUDA gère les deux cas et les dernières cartes graphiques gèrent aussi des tableaux à trois dimensions. Le calcul de l'adresse d'un scalaire se fait en prenant l'adresse de départ du bloc de scalaire, et la combinant avec les indices.
[[File:Block-thread.svg|centre|vignette|upright=2|Découpage des tableaux avec CUDA.]]
Un bloc de scalaires s'exécute sur un processeur de ''shader'', qui est aussi appelé un '''''streaming multiprocessor''''' (terme encore une fois trompeur, vu qu'il s'agit d'un processeur unique, bien que multithreadé. Une fois lancé sur un processeur de ''shader'', il y reste définitivement : il ne peut pas migrer sur un autre processeur en cours d'exécution. Un processeur de ''shader'' peut exécuter plusieurs instances de ''shaders'' travaillant sur des bloc de scalaires différents. Dans le meilleur des cas, il peut traiter en parallèle environ 8 à 16 bloc de scalaires différents en même temps.
[[File:Software-Perspective for thread block.jpg|centre|vignette|upright=2|Exécution des ''thread blocks'' sur les processeurs de ''shaders''.]]
Le bloc de scalaires est découpé en vecteurs d'environ 32 éléments. Les vecteurs en question sont appelés des '''''warps''''' dans la terminologie NVIDIA/CUDA, des '''''wavefronts''''' dans la terminologie AMD. Ces termes sont aussi utilisés pour décrire non pas les données, les vecteurs, mais aussi l'instance du ''shader'' qui s'occupe de faire des calculs dessus. La terminologie est donc assez confuse. Un ''warp''/''wavefront'' est donc en réalité un ''thread'', un programme, une instance de ''shader'', qui manipule des vecteurs SIMD de 32 éléments. Il y a le même problème avec le terme bloc de scalaires, qui désigne aussi bien les données que les instances du ''shader'' qui les manipule.
Cela fait en tout 16 à 32 vecteurs suivant la version de CUDA utilisée. Les 16 à 32 ''warps'' sont exécutés en même temps sur le processeur de ''shader'', via ''multithreading'' matériel. Un ''warp'' exécute des instructions SIMD sur des vecteurs de taille fixe, le processeur ne connait que des vecteurs et des ''warps/wavefronts'', il ne connait pas de ''shader'' qui travaille sur des données uniques non-SIMD. Le découpage en ''warps'' est encore une fois le fait du processeur de commande. Même le processeur de commande ne traite jamais de ''thread'' sur des données uniques non-SIMD, il gère des tableaux de taille variable.
Pour résumer, plus un GPU contient de processeurs de ''shaders'', plus le nombre de blocs de scalaires qu'il peut traiter en même temps est important. Par contre, la taille des ''warps'' ne change pas trop et reste la même d'une génération sur l'autre. Cela ne signifie pas que la taille des vecteurs reste la même, mais elle est assez contrainte.
{{NavChapitre | book=Les cartes graphiques
| prev=Les processeurs de shaders
| prevText=Les processeurs de shaders
| next=La microarchitecture des processeurs de shaders
| nextText=La microarchitecture des processeurs de shaders
}}
{{autocat}}
iok2aw229rcrckbui8ojp38i89hmgtt
Fonctionnement d'un ordinateur/Les circuits intégrés
0
79310
744786
740788
2025-06-15T01:55:32Z
Mewtow
31375
/* La conception logique */
744786
wikitext
text/x-wiki
De nos jours, les portes logiques et/ou transistors sont rassemblés dans des '''circuits intégrés'''. Les circuits intégrés modernes regroupent un grand nombre de transistors qui sont reliés entre eux par des interconnexions métalliques. Par exemple, les derniers modèles de processeurs peuvent utiliser près d'un milliard de transistors. Cette orgie de transistors permet d'ajouter des fonctionnalités aux composants électroniques. C'est notamment ce qui permet aux processeurs récents d'intégrer plusieurs cœurs, une carte graphique, etc.
==Les circuits intégrés : généralités==
[[File:MOS6502.svg|vignette|Broches du processeur MOS6502.]]
Les circuits intégrés se présentent le plus souvent sous la forme de boitiers rectangulaires, comme illustré ci-contre. D'autres ont des boitiers de forme carrées, comme ceux que l'on peut trouver sur les barrettes de mémoire RAM, ou à l'intérieur des clés USB/ disques SSD. Enfin, certains circuits intégrés un peu à part ont des formes bien différentes, comme les processeurs ou les mémoires RAM. Quoiqu'il en soit, il est intéressant de voir l'interface d'un circuit intégré et ce qu'il y a à l'intérieur.
===L'interface d'un circuit intégré===
Les circuits intégrés ont, comme les portes logiques, des '''broches métalliques''' sur lesquelles on envoie des tensions. Quelques broches vont recevoir la tension d'alimentation (broche VCC), d'autres vont être reliées à la masse (broche GND), et surtout : les broches restantes vont porter des bits de données ou de contrôle. Ces dernières peuvent se classer en trois types : les entrées, sorties et entrée-sorties. Les entrées sont celles sur lesquelles on place des bits à envoyer au circuit imprimé, les sorties sont là où le circuit imprimé envoie des informations vers l'extérieur, les entrées-sorties servent alternativement de sortie ou d'entrée.
La plupart des circuits actuels, processeurs et mémoires, comprennent un grand nombre de broches : plusieurs centaines ! Si on prend l'exemple du processeur MC68000, un vieux processeur inventé en 1979 présent dans les calculatrices TI-89 et TI-92, celui-ci contient 68000 transistors (d'où son nom : MC68000). Il s'agit d'un vieux processeur complètement obsolète et particulièrement simple. Et pourtant, celui-ci contient pas mal de broches : 37 au total ! Pour comparer, sachez que les processeurs actuels utilisent entre 700 et 1300 broches d'entrée et de sortie. À ce jeu là, notre pauvre petit MC68000 passe pour un gringalet !
Pour être plus précis, le nombre de broches (entrées et sorties) d'un processeur dépend du socket de la carte mère. Par exemple, un socket LGA775 est conçu pour les processeurs comportant 775 broches d'entrée et de sortie, tandis qu'un socket AM2 est conçu pour des processeurs de 640 broches. Certains sockets peuvent carrément utiliser 2000 broches (c'est le cas du socket G34 utilisé pour certains processeurs AMD Opteron). Pour la mémoire, le nombre de broches dépend du format utilisé pour la barrette de mémoire (il existe trois formats différents), ainsi que du type de mémoire. Certaines mémoires obsolètes (les mémoires FPM-RAM et EDO-RAM) se contentaient de 30 broches, tandis que la mémoire DDR2 utilise entre 204 et 244 broches.
===L'intérieur d'un circuit intégré et sa fabrication===
Après avoir vu les boitiers d'un circuit imprimé et leurs broches, voyons maintenant ce qu'il y a dans le circuit imprimé. Si vous découpez le boitier d'un circuit imprimé, vous allez voir que le boitier en plastique entoure une sorte de carré/rectangle de couleur grisâtre, appelé le '''''die''''' du circuit imprimé, ou encore la puce électronique. Le ''die'' est un bloc de matériau semi-conducteur. C'est là où se trouvent les transistors et les interconnexions entre eux. Les broches métalliques sont connectées à des endroits bien précis du die. Le ''die'' est très petit, quelques millimètres de côté, guère plus. Il est très variable d'un circuit intégré à l'autre et il est difficile de faire des généralités dessus.
[[File:Intel C8751H.jpg|centre|vignette|upright=2|Intérieur du circuit intégré Intel C8751H.]]
[[File:Fale - Monaco - 88.jpg|vignette|Lingot de silicium (imparfaitement cylindrique car c'est un des premiers cylindre fabriqué).]]
Les ''dies'' sont fabriqués à partir de silicium, à l'exception de quelques ''die'' fabriqués avec du gernanium, peu utilisés et encore en cours de recherche. Le silicium a des propriétés semiconductrices très intéressantes, qui font que c'est le matériau le plus utilisé dans l'industrie actuellement. La fabrication d'un circuit électronique moderne part d'un lingot de silicium pur, qui a une forme cylindrique. Un tel lingot est illustré ci-contre. Le lingot est découpé en tranches circulaires sur lesquelles on vient graver le ''die''. Les tranches circulaires sont appelées des ''wafers''.
[[File:Wafer 2 Zoll bis 8 Zoll 2.jpg|centre|vignette|upright=2|Wafer de silicium pur, avec quelques ''dies'' gravés dessus.]]
[[File:Wafermap showing fully and partially patterned dies.svg|vignette|Pertes aux bords d'un ''wafer''.]]
Avant d'expliquer ce qui arrive aux ''wafers'' pour qu'on vienne graver des ''dies'' dessus, précisons que la forme des ''waffer'' n'est pas très compatible avec celle des ''dies''. Un ''wafer'' est circulaire, un ''die'' est carré/rectangulaire. L’incompatibilité se manifeste sur les bords du ''wafer'', qui sont gâchés car on ne peut pas y mettre de ''die'', comme indiqué dans le schéma ci-contre. Il y a donc un léger gâchis en silicium, qu'il est préférable de réduire au plus possible.
De plus, les ''dies'' gravés ne sont pas tous fonctionnels. Il n'est pas rare que certains ne fonctionnent pas à cause d'un défaut de gravure. Il faut dire que graver des transistors de quelques nanomètres de diamètre est un procédé très compliqué qui ne peut pas marcher à tous les coups. Il suffit d'un grain de poussière mal placé pour qu'un ''die'' soit irrémédiablement perdu. Lors de la fabrication, il y a un certain pourcentage moyen de ''dies'' gravés sur un ''wafer'' qui sont défectueux. Le nombre de ''dies'' fonctionnels sur le nombre total de ''dies'' gravés est appelé le '''''Yield'''''. Idéalement, il faudrait que le ''yield'' soit le plus élevé possible.
Pour augmenter le ''yield'' et réduire les pertes aux bords du ''wafer'', il y a une solution qui marche pour les deux problèmes : utiliser des ''dies'' très petits, le plus petit possible. Plus les ''dies'' sont petits, plus la perte sur les bords du ''wafer'' sera faible. Mais réduire le ''die'' signifie réduire la taille du circuit intégré, et donc son nombre de transistors. Il semblerait qu'il y a donc un compromis à faire : soit avoir des circuits bourrés de transistors mais avec un ''yield'' bas, ou avoir un ''yield'' élevé pour des circuits simples. Mais il y a une solution pour obtenir le meilleur des deux mondes.
[[File:Wafer die's yield model (10-20-40mm) - Version 2 - EN.png|centre|vignette|upright=2.5|Evolution du yield en fonction de la taille des ''dies''.]]
===Les chiplets et circuits imprimés en 3D===
Il existe des boitiers qui regroupent plusieurs boitiers et/ou plusieurs ''dies'', plusieurs puces électroniques. Ils sont appelés des circuits intégrés '''''Multi-chip Module''''' (MCM). Les puces électroniques d'un MCM sont appelées des '''''chiplets''''', pour les différencier des autres ''dies''. L'idée est qu'il vaut mieux combiner plusieurs ''dies'' simples que d'utiliser un gros ''die'' bien complexe.
[[File:Pentiumpro moshen.jpg|centre|vignette|upright=2|Exemple de circuit intégré MCM : le processeur Pentium Pro.]]
[[File:Wireless TSV (model).PNG|vignette|Wireless TSV (model)]]
Les '''circuits imprimés en 3D''' sont une sous-classe de circuits imprimés MCM conçus en empilant plusieurs circuits plats l'un au-dessus de l'autre, dans le même boitier. Ils sont composés de plusieurs couches, chacune contenant des transistors MOS, empilées les unes au-dessus des autres. Les différentes couches sont connectées entre elles par des fils métalliques qui traversent les différentes couches, au nom à coucher dehors : ''Through-silicon via'' (TSV), ''Cu-Cu connections'', etc. Le nom de la technique en anglais est ''3DS die stacking''.
Le ''3DS die stacking'' regroupe un grand nombre de technologies différentes, qui partagent la même idée de base, mais dont l'implémentation est fortement différente. Mais les différences sont difficiles à expliquer ici, car la fabrication de circuits imprimés est un domaine complexe, faisant intervenir physique des matériaux et ingénierie.
Les avantages du ''3DS die stacking'' est qu'on peut mettre plus de circuits dans un même boitier, en l'utilisant en hauteur plutôt qu'en largeur. Par contre, la dissipation de la chaleur est plus compliquée. Un circuit électronique chauffe beaucoup et il faut dissiper cette chaleur. L'idéal pour dissiper la chaleur est d'avoir une surface plane, avec un volume faible. Plus le rapport surface/volume d'un circuit à semi-conducteur est élevé, mieux c'est pour dissiper la chaleur, physique de la dissipation thermique oblige. Et empiler des couches augmente le volume sans trop augmenter la surface. D'où le fait que la gestion de la température est plus compliquée.
Le ''3DS die stacking'' est surtout utilisé sur les mémoires, bien moins sur les autres types de circuits. Elle est surtout utilisée pour les mémoires FLASH, mais quelques mémoires RAM en utilisent. Pour les mémoires RAM proprement dit, deux standards incompatibles s'opposent. D'un côté la technologie ''High Bandwidth Memory'', de l'autre la technologie ''Hybrid Memory Cube''.
[[File:3DS die stacking concept model.PNG|centre|vignette|upright=2|3DS die stacking]]
L'avantage de cette technique pour les mémoires est qu'elle permet une plus grande capacité, à savoir qu'elles ont plus de gibioctets. De plus, elle ne nuit pas aux performances de la mémoire. En effet, la performance des mémoires/circuits dépend un peu de la longueur des interconnexions : plus elles sont longues, plus le temps pour lire/écrire une donnée est important. Et il vaut mieux avoir de courtes interconnexions en hauteur, que de longues interconnexions sur une surface.
Il y a quelques processeurs dont la mémoire cache utilise le ''3DS die stacking'', on peut notamment citer les processeurs AMD de microarchitecture Zen 3, Zen 4 et Zen 5. Le premier processeur disposant d'une mémoire cache en 3D a été le R7 5800X3D. Il succédait aux anciens processeurs de microarchitecture Zen 3, qui disposaient d'un cache L3 de 32 mébioctets. Le 5800X3D ajoutait 64 mébioctets, ce qui fait au total 96 mébioctets de mémoire cache L3. Et surtout : la rapidité du cache était la même sur le 5800X3D et les anciens Zen 3. À peine quelques cycles d'horloge de plus pour un cache dont le temps d'accès se mesure en 50-100 cycles d'horloge.
==La miniaturisation des circuits intégrés et la loi de Moore==
En 1965, le cofondateur de la société Intel, spécialisée dans la conception de mémoires et de processeurs, a affirmé que la quantité de transistors présents dans un circuit intégré doublait tous les 18 mois : c'est la '''première loi de Moore'''. En 1975, il réévalua cette affirmation : ce n'est pas tous les 18 mois que le nombre de transistors d'un circuit intégré double, mais tous les 2 ans. Elle est respectée sur la plupart des circuits intégrés, mais surtout par les processeurs et les cartes graphiques, les mémoires RAM et ROM, bref : tout ce qui est majoritairement constitué de transistors.
[[File:Loi de Moore.png|centre|vignette|upright=2.0|Nombre de transistors en fonction de l'année.]]
===La miniaturisation des transistors===
L'augmentation du nombre de transistors n'aurait pas été possible sans la miniaturisation, à savoir le fait de rendre les transistors plus petits. Il faut savoir que les circuits imprimés sont fabriqués à partir d'une plaque de silicium pur, un ''wafer'', sur laquelle on vient graver le circuit imprimé. On ne peut pas empiler deux transistors l'un sur l'autre, du moins pas facilement. Il y a bien des technologiques pour faire ça, mais elles sont complexes et nous les omettons ici. Les transistors sont donc répartis sur une surface plane, qui a une forme approximativement rectangulaire et qui a une certaine aire. L'aire en question est la même pour tous les processeurs, qui font tous la même taille, leur circuits imprimés sont les mêmes.
Les transistors sont des structures en 3D, mais ils sont posés sur une surface en 2D. En clair, on n'empile pas les transistors les uns sur les autres, on les mets les uns à côté des autres. Leur épaisseur peut se réduire avec le temps, mais cela n'a pas d'importance pour la loi de Moore. Par contre, ils ont souvent une largeur et une longueur qui sont très proches, et qui diminuent avec l'évolution des technologies de fabrication. Pour simplifier, la taille des transistors est aussi appelée la '''finesse de gravure'''. Elle s'exprime le plus souvent en nanomètres.
Doubler le nombre de transistors signifie qu'on peut mettre deux fois plus de transistors sur une même surface. Pour le dire autrement, la surface occupée par un transistor a été divisée par deux. On s'attendrait à ce que leur taille soit divisée par deux tous les 2 ans, comme le dit la loi de Moore. Mais c’est là une erreur de raisonnement.
Rappelez-vous que la taille d'un processeur reste la même, ils gardent la même surface carrée d'un modèle à l'autre. Si on divise la taille des transistors par deux, l'aire prise par un transistor sur cette surface carrée sera divisée par 4, donc on pourra en mettre 4 fois plus. Incompatible avec la loi de Moore ! En réalité, diviser une surface carrée/rectangulaire par deux demande de diviser la largeur et la longueur par <math>\sqrt{2}</math>. Ainsi, la finesse de gravure est divisée par <math>\sqrt{2}</math>, environ 1,4, tous les deux ans. Une autre manière de le dire est que la finesse de gravure est multipliée par 0,7 tous les deux ans, soit une diminution de 30 % tous les deux ans. En clair, la taille des transistors décroit de manière exponentielle avec le temps !
[[File:SemiconductorProcessSize.png|centre|vignette|upright=2.0|Évolution de la finesse de gravure au cours du temps pour les transistors CMOS.]]
===La fin de la loi de Moore===
Néanmoins, la loi de Moore n'est pas vraiment une loi gravée dans le marbre. Si celle-ci a été respectée jusqu'à présent, c'est avant tout grâce aux efforts des fabricants de processeurs, qui ont tenté de la respecter pour des raisons commerciales. Vendre des processeurs toujours plus puissants, avec de plus en plus de transistors est en effet gage de progression technologique autant que de nouvelles ventes.
Il arrivera un moment où les transistors ne pourront plus être miniaturisés, et ce moment approche ! Quand on songe qu'en 2016 certains transistors ont une taille proche d'une vingtaine ou d'une trentaine d'atomes, on se doute que la loi de Moore n'en a plus pour très longtemps. Et la progression de la miniaturisation commence déjà à montrer des signes de faiblesses. Le 23 mars 2016, Intel a annoncé que pour ses prochains processeurs, le doublement du nombre de transistors n'aurait plus lieu tous les deux ans, mais tous les deux ans et demi. Cet acte de décès de la loi de Moore n'a semble-t-il pas fait grand bruit, et les conséquences ne se sont pas encore faites sentir dans l'industrie. Au niveau technique, on peut facilement prédire que la course au nombre de cœurs a ses jours comptés.
On estime que la limite en terme de finesse de gravure sera proche des 5 à 7 nanomètres : à cette échelle, le comportement des électrons suit les lois de la physique quantique et leur mouvement devient aléatoire, perturbant fortement le fonctionnement des transistors au point de les rendre inutilisables. Et cette limite est proche : des finesses de gravure de 10 nanomètres sont déjà disponibles chez certaines fondeurs comme TSMC. Autant dire que si la loi de Moore est respectée, la limite des 5 nanomètres sera atteinte dans quelques années, à peu-près vers l'année 2020. Ainsi, nous pourrons vivre la fin d'une ère technologique, et en voir les conséquences. Les conséquences économiques sur le secteur du matériel promettent d'être assez drastiques, que ce soit en terme de concurrence ou en terme de réduction de l'innovation.
Quant cette limite sera atteinte, l'industrie sera face à une impasse. Le nombre de cœurs ou la micro-architecture des processeurs ne pourra plus profiter d'une augmentation du nombre de transistors. Et les recherches en terme d'amélioration des micro-architectures de processeurs sont au point mort depuis quelques années. La majeure partie des optimisations matérielles récemment introduites dans les processeurs sont en effet connues depuis fort longtemps (par exemple, le premier processeur superscalaire à exécution dans le désordre date des années 1960), et ne sont améliorables qu'à la marge. Quelques équipes de recherche travaillent cependant sur des architectures capables de révolutionner l'informatique. Le calcul quantique ou les réseaux de neurones matériels sont une première piste, mais qui ne donneront certainement de résultats que dans des marchés de niche. Pas de quoi rendre un processeur de PC plus rapide.
==L'invention du microprocesseur==
Le processeur est le circuit de l'ordinateur qui effectue des calculs sur des nombres codés en binaire, c’est la pièce maitresse de l'ordinateur. C'est un circuit assez complexe, qui utilise beaucoup de transistors. Avant les années 1970, il n'était pas possible de produire un processeur en un seul morceau. Impossible de mettre un processeur dans un seul boitier, les processeurs étaient fournis en pièces détachées qu'il fallait relier entre elles.
Un exemple de processeur conçu en kit est la série des Intel 3000. Elle regroupe plusieurs circuits séparés : l'Intel 3001 est le séquenceur, l'Intel 3002 est le chemin de données (ALU et registres), le 3003 est un circuit d'anticipation de retenue censé être combiné avec l'ALU, le 3212 est une mémoire tampon, le 3214 est une unité de gestion des interruptions, les 3216/3226 sont des interfaces de bus mémoire. On pourrait aussi citer la famille de circuits intégrés AMD Am2900.
===L'intel 4004 : le premier microprocesseur===
Par la suite, les progrès de la miniaturisation ont permis de mettre un processeur entier dans un seul circuit intégré. C'est ainsi que sont nés les '''microprocesseurs''', à savoir des processeurs qui tiennent tout entier sur une seule puce de silicium. Les tout premiers microprocesseurs étaient des processeurs à application militaire, comme le processeur du F-14 CADC ou celui de l'''Air data computer''.
Le tout premier microprocesseur commercialisé au grand public est le 4004 d'Intel, sorti en 1971. L'intel 4004 comprenait environ 2300 transistors, avait une fréquence de 740 MHz, pouvait faire 46 opérations différentes, et manipulait des entiers de 4 bits. De plus, le processeur manipulait des entiers en BCD, ce qui fait qu'il pouvait manipuler un chiffre BCD à la fois (un chiffre BCD est codé sur 4 bits). Il était au départ un processeur de commande, prévu pour être intégré dans la calculatrice Busicom calculator 141-P, mais il fut utilisé pour d'autres applications quelque temps plus tard. Son successeur, l'Intel 4040, garda ces caractéristiques et n'apportait que quelques améliorations mineures : plus de registres, plus d'opérations, etc.
Le 4004 était commercialisé dans un boitier DIP simple, fort différent des boitiers et sockets des processeurs actuels. Le boitier du 4004 avait seulement 16 broches, ce qui était permis par le fait qu'il s'agissait d'un processeur 4 bits. On trouve 4 broches pour échanger des données avec le reste de l'ordinateur, 5 broches pour communiquer avec la mémoire (4 broches d'adresse, une pour indiquer s'il faut faire une lecture ou écriture), le reste est composé de broches pour la tension d'alimentation VDD, la masse VSS et pour le signal d'horloge (celui qui décide de la fréquence).
{|
|[[File:Intel 4004.jpg|vignette|Intel 4004]]
|[[File:4004 dil.png|vignette|upright=1|Broches du 4004.]]
|}
Immédiatement après le 4004, les premiers microprocesseurs 8 bits furent commercialisés. Le 4004 fut suivi par le 8008 et quelques autres processeurs 8 bits extrêmement connus, comme le 8080 d'Intel, le 68000 de Motorola, le 6502 ou le Z80. Ces processeurs utilisaient là encore des boitiers similaires au 4004, mais avec plus de broches, vu qu'ils étaient passés de 4 à 8 bits. Par exemple, le 8008 utilisait 18 broches, le 8080 était une version améliorée du 8008 avec 40 broches. Le 8086 fut le premier processeur 16 bits.
===Le passage des boitiers aux slots et sockets===
La forme des processeurs a changé au cours du temps. Ils sont devenus plats et carrés. Les raisons qui expliquent la forme des boitiers des processeurs actuels sont assez nombreuses. La première est que les techniques de fabrications des puces électroniques actuelles font qu'il est plus simple d'avoir un circuit planaire, relativement peu épais. De plus, la forme carrée s'explique par la fabrication des puces de silicium, où un cristal de silicium est coupé en tranches, elles-mêmes découpées en puces carrées identiques, ce qui facilite la conception. Un autre avantage de cette forme est que la dissipation de la chaleur est meilleure. Les processeurs actuels sont devenus plus puissants que ceux d'antan, mais au prix d'une dissipation thermique augmentée. Dissiper cette chaleur est devenu un vrai défi sur les processeurs actuels, et la forme des microprocesseurs actuels aide à cela, couplé à des radiateurs et ventilateurs.
Un autre changement tient dans la manière dont le processeur est relié à la carte mère. Les premiers processeurs 8 et 16 bits étaient soudés à la carte mère. Les retirer demandait de les dé-souder, ce qui n'était pas très pratique, mais ne posait pas vraiment de problèmes à l'époque. Il faut noter que certains processeurs assez anciens étaient placés sur des cartes intégrées, elles-mêmes connectées à la carte mère par un slot d'extension, similaire à celui des cartes graphiques.
[[File:Intel Pentium II 400 SL357 SECC2.jpg|centre|vignette|upright=2|Circuit du Pentium 2..]]
[[File:Asus P3C2000 - Slot 1-8626.jpg|centre|vignette|upright=2|Slot 1-8626, utilisé pour connecter les processeurs Pentium 2 sur la carte mère.]]
De nos jours, les processeurs n'utilisent plus les boitiers soudés d'antan. Les processeurs sont clipsés dans un connecteur spécial sur la carte mère, appelé le ''socket''. Grâce à ce système, il est plus simple d'ajouter ou de retirer un processeur de la carte mère. L'''upgrade'' d'un processeur est ainsi fortement facilitée. Les broches sont composées de billes ou de pins métalliques qui font contact avec le connecteur.
{|
|[[File:XC68020 bottom p1160085.jpg|vignette|upright=1.5|XC68020 bottom p1160085]]
|[[File:Kl Intel Pentium MMX embedded BGA Bottom.jpg|vignette|Kl Intel Pentium MMX embedded BGA Bottom]]
|}
===L'invention des processeurs multicœurs===
Avec l'avancée des processus de fabrication, il est devenu possible de mettre plusieurs processeurs sur une même puce de silicium, et c'est ainsi que sont nés les processeurs multicœurs. Pour simplifier, les processeurs multicœurs regroupent plusieurs processeurs, soit sur une même puce de silicium, soit dans un même boitier. Les processeurs en question sont appelés des cœurs. Il arrive donc qu'un processeur multicœurs ait en réalité 8 cœurs/processeurs sur la même puce, ou 4, ou 2, parfois 16, 32, 64, rarement plus. Les processeurs multicœurs contenant 2 processeurs sont aujourd'hui obsolète, la norme est entre 4 et 16.
Les fabricants ont généralement plusieurs modèles d'un même processeur : un modèle entrée de gamme peu cher et peu performant, un modèle haut de gamme très cher et performant, et un modèle milieu de gamme aux prix et performances entre les deux précédents. Et ces trois modèles n'ont pas le même nombre de cœurs. Et bien sachez qu'en réalité, tous ces processeurs sortent de la même usine et sont fabriqués de la même manière, avec le même nombre de cœurs. Par exemple, imaginez qu'un modèle entrée de gamme ait 4 cœurs , le milieu de gamme 8 cœurs, et le haut de gamme en ait 16. Et bien ils sont fabriqués à partir d'un modèle haut de gamme à 16 cœurs, dont on désactive certains cœurs pour obtenir les modèles bas et milieu de gamme.
Après leur fabrication, les processeurs subissent des tests pour vérifier si le processeur fonctionne normalement. Et il arrive qu'un cœur soit défectueux et ne fonctionne pas, mais que les autres fonctionnent parfaitement. Par exemple, si on prend un processeur à 8 cœurs , il se peut que deux d'entre eux ne fonctionne pas et les 6 autres soient fonctionnels. Dans ce cas, on en fait un modèle milieu ou entrée de gamme en désactivant les cœurs défectueux. La désactivation est généralement matérielle, en coupant des fils pour déconnecter les cœurs défectueux.
===La révolution des ''chiplets''===
Les processeurs multicœurs modernes utilisent la technique des ''chiplets''. Pour donner un exemple, prenons celui du processeur POWER 5, autrefois utilisé sur d'anciens ordinateurs Macintosh. Chaque coeur avait son propre boitier rien que pour lui. Il en a existé deux versions. La première était dite double cœur, à savoir qu'elle intégrait deux processeurs dans la même puce. Le seconde version étiat quadruple coeur, avec 4 processeurs dans un même boitier, avec 4 ''dies''. La dernière version est illustrée ci-dessous. On voit qu'il y a quatre boitier rouges, un par coeur, et quatre autres en vert qui correspondent à de la mémoire cache (le cache L3).
[[File:Power5.jpg|centre|vignette|upright=2|POWER 5 MCM.]]
Un autre exemple est celui des processeurs AMD récents, d'architectures Zen 2/3/4/5. Ils incorporent deux puces dans le même boitier : une puce qui contient les processeurs, les cœurs, et une autre pour les interconnexions avec le reste de l'ordinateur. La puce pour les interconnexions gère l'interface avec la mémoire RAM, les bus PCI-Express pour la carte graphique, et quelques autres. Les deux puces n'ont pas la même finesse de gravure, ni les mêmes performances.
[[File:AMD@7nm(12nmIO)@Zen2@Matisse@Ryzen 5 3600@100-000000031 BF 1923SUT 9HM6935R90062 DSCx2@Infrared.jpg|centre|vignette|upright=2|AMD@7nm(12nmIO)@Zen2@Matisse@Ryzen 5 3600@100-000000031 BF 1923SUT 9HM6935R90062 DSCx2@Infrared]]
Certains processeurs AMD Epyc avaient plusieurs chiplets pour les processeurs/coeurs, combinés avec un chiplet pour les interconnexions. L'image ci-dessous montre un processeur AMD Epyc 7702, avec un chiplet central pour les interconnexions, et les chiplets autour qui contiennent chacun 4 cœurs.
[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]]
[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]]
==La conception d'un circuit intégré==
[[File:Integrated circuit design.png|vignette|Étapes de conception d'un circuit intégré.]]
La conception d'un circuit intégré se fait en une série d'étapes assez complexes, dont certaines sont aidées par ordinateur. Inutile de voir dire que concevoir un circuit intégré est généralement assez complexe et demande des compétences très variées. Concevoir une puce électronique doit se faire à plusieurs niveaux d'abstraction, que nous allons détailler dans ce qui suit. Nous allons grandement simplifier le tout en donnant une description assez sommaire.
===La conception logique===
La première étape est de créer une sorte de cahier des charge, de spécification qui décrit comment fonctionne le circuit. La spécification décrit son '''architecture externe''', à savoir comment le circuit se comporte. Elle décrit comment le circuit réagit quand on envoie telle donnée sur telle entrée, qu'est-ce qu'on retrouve sur ses sorties si... , etc. Pour un circuit combinatoire, cela revient à écrire sa table de vérité. Mais il va de soit que pour des circuits complexes, la spécification est beaucoup plus complexe.
La seconde étape est d'implémenter le circuit en utilisant les circuits vus dans les chapitres précédents, à savoir des registres, bascules, portes logiques, décodeurs, multiplexeurs, additionneurs et autres circuits basiques. La conception se fait en utilisant un '''langage de description matérielle''', qui a des ressemblances superficielles avec un langage de programmation.
Le résultat est une description du circuit assez haut niveau, appelée le '''''Register-transfer level''''' (RTL), qui combine registres, portes logiques et autres circuits combinatoires basiques. Les circuits de base utilisés lors de cette étape sont appelés des ''cellules standard''. La RTL ressemble aux schémas vus dans les chapitres précédents, et ce n'est pas un hasard : de tels schémas sont des RTL simples de circuits eux-mêmes simples.
===La conception physique===
[[File:FlowPhysicalDesign.png|vignette|Design physique, la troisième étape.]]
La troisième étape traduit la RTL en un plan à appliquer sur le ''die'' physique, à graver dessus. Elle traduit les portes logiques en montages à base de transistors, comme vu dans le chapitre précédents. Les autres cellules standards sont elles aussi directement traduites en un montage à base de transistors, conçu à l'avance par des ingénieurs spécialisé, qui est potentiellement optimisé qu'un montage à base de portes logiques.
Les cellules sont placées sur la puce par un algorithme, qui cherche à optimiser l'usage du ''die''. Les interconnexions métalliques entre transistors sont ajoutées, de même que le signal d'horloge, la masse et la tension d'alimentation. L'arbre d'horloge est généré à cette étape, de même que l'arbre qui transmet la tension d'alimentation aux portes logiques. Le résultat est une sorte de description physique du ''die''.
[[File:Vlsiopamp2.gif|centre|vignette|upright=2|Description physique d'un amplificateur opérationnel basique.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les transistors et portes logiques
| prevText=Les transistors et portes logiques
| next=L'interface électrique entre circuits intégrés et bus
| nextText=L'interface électrique entre circuits intégrés et bus
}}{{autocat}}
</noinclude>
4xtsgec624vwv7o3qpwqwpvi37mx3ju
744787
744786
2025-06-15T01:56:11Z
Mewtow
31375
/* La conception logique */
744787
wikitext
text/x-wiki
De nos jours, les portes logiques et/ou transistors sont rassemblés dans des '''circuits intégrés'''. Les circuits intégrés modernes regroupent un grand nombre de transistors qui sont reliés entre eux par des interconnexions métalliques. Par exemple, les derniers modèles de processeurs peuvent utiliser près d'un milliard de transistors. Cette orgie de transistors permet d'ajouter des fonctionnalités aux composants électroniques. C'est notamment ce qui permet aux processeurs récents d'intégrer plusieurs cœurs, une carte graphique, etc.
==Les circuits intégrés : généralités==
[[File:MOS6502.svg|vignette|Broches du processeur MOS6502.]]
Les circuits intégrés se présentent le plus souvent sous la forme de boitiers rectangulaires, comme illustré ci-contre. D'autres ont des boitiers de forme carrées, comme ceux que l'on peut trouver sur les barrettes de mémoire RAM, ou à l'intérieur des clés USB/ disques SSD. Enfin, certains circuits intégrés un peu à part ont des formes bien différentes, comme les processeurs ou les mémoires RAM. Quoiqu'il en soit, il est intéressant de voir l'interface d'un circuit intégré et ce qu'il y a à l'intérieur.
===L'interface d'un circuit intégré===
Les circuits intégrés ont, comme les portes logiques, des '''broches métalliques''' sur lesquelles on envoie des tensions. Quelques broches vont recevoir la tension d'alimentation (broche VCC), d'autres vont être reliées à la masse (broche GND), et surtout : les broches restantes vont porter des bits de données ou de contrôle. Ces dernières peuvent se classer en trois types : les entrées, sorties et entrée-sorties. Les entrées sont celles sur lesquelles on place des bits à envoyer au circuit imprimé, les sorties sont là où le circuit imprimé envoie des informations vers l'extérieur, les entrées-sorties servent alternativement de sortie ou d'entrée.
La plupart des circuits actuels, processeurs et mémoires, comprennent un grand nombre de broches : plusieurs centaines ! Si on prend l'exemple du processeur MC68000, un vieux processeur inventé en 1979 présent dans les calculatrices TI-89 et TI-92, celui-ci contient 68000 transistors (d'où son nom : MC68000). Il s'agit d'un vieux processeur complètement obsolète et particulièrement simple. Et pourtant, celui-ci contient pas mal de broches : 37 au total ! Pour comparer, sachez que les processeurs actuels utilisent entre 700 et 1300 broches d'entrée et de sortie. À ce jeu là, notre pauvre petit MC68000 passe pour un gringalet !
Pour être plus précis, le nombre de broches (entrées et sorties) d'un processeur dépend du socket de la carte mère. Par exemple, un socket LGA775 est conçu pour les processeurs comportant 775 broches d'entrée et de sortie, tandis qu'un socket AM2 est conçu pour des processeurs de 640 broches. Certains sockets peuvent carrément utiliser 2000 broches (c'est le cas du socket G34 utilisé pour certains processeurs AMD Opteron). Pour la mémoire, le nombre de broches dépend du format utilisé pour la barrette de mémoire (il existe trois formats différents), ainsi que du type de mémoire. Certaines mémoires obsolètes (les mémoires FPM-RAM et EDO-RAM) se contentaient de 30 broches, tandis que la mémoire DDR2 utilise entre 204 et 244 broches.
===L'intérieur d'un circuit intégré et sa fabrication===
Après avoir vu les boitiers d'un circuit imprimé et leurs broches, voyons maintenant ce qu'il y a dans le circuit imprimé. Si vous découpez le boitier d'un circuit imprimé, vous allez voir que le boitier en plastique entoure une sorte de carré/rectangle de couleur grisâtre, appelé le '''''die''''' du circuit imprimé, ou encore la puce électronique. Le ''die'' est un bloc de matériau semi-conducteur. C'est là où se trouvent les transistors et les interconnexions entre eux. Les broches métalliques sont connectées à des endroits bien précis du die. Le ''die'' est très petit, quelques millimètres de côté, guère plus. Il est très variable d'un circuit intégré à l'autre et il est difficile de faire des généralités dessus.
[[File:Intel C8751H.jpg|centre|vignette|upright=2|Intérieur du circuit intégré Intel C8751H.]]
[[File:Fale - Monaco - 88.jpg|vignette|Lingot de silicium (imparfaitement cylindrique car c'est un des premiers cylindre fabriqué).]]
Les ''dies'' sont fabriqués à partir de silicium, à l'exception de quelques ''die'' fabriqués avec du gernanium, peu utilisés et encore en cours de recherche. Le silicium a des propriétés semiconductrices très intéressantes, qui font que c'est le matériau le plus utilisé dans l'industrie actuellement. La fabrication d'un circuit électronique moderne part d'un lingot de silicium pur, qui a une forme cylindrique. Un tel lingot est illustré ci-contre. Le lingot est découpé en tranches circulaires sur lesquelles on vient graver le ''die''. Les tranches circulaires sont appelées des ''wafers''.
[[File:Wafer 2 Zoll bis 8 Zoll 2.jpg|centre|vignette|upright=2|Wafer de silicium pur, avec quelques ''dies'' gravés dessus.]]
[[File:Wafermap showing fully and partially patterned dies.svg|vignette|Pertes aux bords d'un ''wafer''.]]
Avant d'expliquer ce qui arrive aux ''wafers'' pour qu'on vienne graver des ''dies'' dessus, précisons que la forme des ''waffer'' n'est pas très compatible avec celle des ''dies''. Un ''wafer'' est circulaire, un ''die'' est carré/rectangulaire. L’incompatibilité se manifeste sur les bords du ''wafer'', qui sont gâchés car on ne peut pas y mettre de ''die'', comme indiqué dans le schéma ci-contre. Il y a donc un léger gâchis en silicium, qu'il est préférable de réduire au plus possible.
De plus, les ''dies'' gravés ne sont pas tous fonctionnels. Il n'est pas rare que certains ne fonctionnent pas à cause d'un défaut de gravure. Il faut dire que graver des transistors de quelques nanomètres de diamètre est un procédé très compliqué qui ne peut pas marcher à tous les coups. Il suffit d'un grain de poussière mal placé pour qu'un ''die'' soit irrémédiablement perdu. Lors de la fabrication, il y a un certain pourcentage moyen de ''dies'' gravés sur un ''wafer'' qui sont défectueux. Le nombre de ''dies'' fonctionnels sur le nombre total de ''dies'' gravés est appelé le '''''Yield'''''. Idéalement, il faudrait que le ''yield'' soit le plus élevé possible.
Pour augmenter le ''yield'' et réduire les pertes aux bords du ''wafer'', il y a une solution qui marche pour les deux problèmes : utiliser des ''dies'' très petits, le plus petit possible. Plus les ''dies'' sont petits, plus la perte sur les bords du ''wafer'' sera faible. Mais réduire le ''die'' signifie réduire la taille du circuit intégré, et donc son nombre de transistors. Il semblerait qu'il y a donc un compromis à faire : soit avoir des circuits bourrés de transistors mais avec un ''yield'' bas, ou avoir un ''yield'' élevé pour des circuits simples. Mais il y a une solution pour obtenir le meilleur des deux mondes.
[[File:Wafer die's yield model (10-20-40mm) - Version 2 - EN.png|centre|vignette|upright=2.5|Evolution du yield en fonction de la taille des ''dies''.]]
===Les chiplets et circuits imprimés en 3D===
Il existe des boitiers qui regroupent plusieurs boitiers et/ou plusieurs ''dies'', plusieurs puces électroniques. Ils sont appelés des circuits intégrés '''''Multi-chip Module''''' (MCM). Les puces électroniques d'un MCM sont appelées des '''''chiplets''''', pour les différencier des autres ''dies''. L'idée est qu'il vaut mieux combiner plusieurs ''dies'' simples que d'utiliser un gros ''die'' bien complexe.
[[File:Pentiumpro moshen.jpg|centre|vignette|upright=2|Exemple de circuit intégré MCM : le processeur Pentium Pro.]]
[[File:Wireless TSV (model).PNG|vignette|Wireless TSV (model)]]
Les '''circuits imprimés en 3D''' sont une sous-classe de circuits imprimés MCM conçus en empilant plusieurs circuits plats l'un au-dessus de l'autre, dans le même boitier. Ils sont composés de plusieurs couches, chacune contenant des transistors MOS, empilées les unes au-dessus des autres. Les différentes couches sont connectées entre elles par des fils métalliques qui traversent les différentes couches, au nom à coucher dehors : ''Through-silicon via'' (TSV), ''Cu-Cu connections'', etc. Le nom de la technique en anglais est ''3DS die stacking''.
Le ''3DS die stacking'' regroupe un grand nombre de technologies différentes, qui partagent la même idée de base, mais dont l'implémentation est fortement différente. Mais les différences sont difficiles à expliquer ici, car la fabrication de circuits imprimés est un domaine complexe, faisant intervenir physique des matériaux et ingénierie.
Les avantages du ''3DS die stacking'' est qu'on peut mettre plus de circuits dans un même boitier, en l'utilisant en hauteur plutôt qu'en largeur. Par contre, la dissipation de la chaleur est plus compliquée. Un circuit électronique chauffe beaucoup et il faut dissiper cette chaleur. L'idéal pour dissiper la chaleur est d'avoir une surface plane, avec un volume faible. Plus le rapport surface/volume d'un circuit à semi-conducteur est élevé, mieux c'est pour dissiper la chaleur, physique de la dissipation thermique oblige. Et empiler des couches augmente le volume sans trop augmenter la surface. D'où le fait que la gestion de la température est plus compliquée.
Le ''3DS die stacking'' est surtout utilisé sur les mémoires, bien moins sur les autres types de circuits. Elle est surtout utilisée pour les mémoires FLASH, mais quelques mémoires RAM en utilisent. Pour les mémoires RAM proprement dit, deux standards incompatibles s'opposent. D'un côté la technologie ''High Bandwidth Memory'', de l'autre la technologie ''Hybrid Memory Cube''.
[[File:3DS die stacking concept model.PNG|centre|vignette|upright=2|3DS die stacking]]
L'avantage de cette technique pour les mémoires est qu'elle permet une plus grande capacité, à savoir qu'elles ont plus de gibioctets. De plus, elle ne nuit pas aux performances de la mémoire. En effet, la performance des mémoires/circuits dépend un peu de la longueur des interconnexions : plus elles sont longues, plus le temps pour lire/écrire une donnée est important. Et il vaut mieux avoir de courtes interconnexions en hauteur, que de longues interconnexions sur une surface.
Il y a quelques processeurs dont la mémoire cache utilise le ''3DS die stacking'', on peut notamment citer les processeurs AMD de microarchitecture Zen 3, Zen 4 et Zen 5. Le premier processeur disposant d'une mémoire cache en 3D a été le R7 5800X3D. Il succédait aux anciens processeurs de microarchitecture Zen 3, qui disposaient d'un cache L3 de 32 mébioctets. Le 5800X3D ajoutait 64 mébioctets, ce qui fait au total 96 mébioctets de mémoire cache L3. Et surtout : la rapidité du cache était la même sur le 5800X3D et les anciens Zen 3. À peine quelques cycles d'horloge de plus pour un cache dont le temps d'accès se mesure en 50-100 cycles d'horloge.
==La miniaturisation des circuits intégrés et la loi de Moore==
En 1965, le cofondateur de la société Intel, spécialisée dans la conception de mémoires et de processeurs, a affirmé que la quantité de transistors présents dans un circuit intégré doublait tous les 18 mois : c'est la '''première loi de Moore'''. En 1975, il réévalua cette affirmation : ce n'est pas tous les 18 mois que le nombre de transistors d'un circuit intégré double, mais tous les 2 ans. Elle est respectée sur la plupart des circuits intégrés, mais surtout par les processeurs et les cartes graphiques, les mémoires RAM et ROM, bref : tout ce qui est majoritairement constitué de transistors.
[[File:Loi de Moore.png|centre|vignette|upright=2.0|Nombre de transistors en fonction de l'année.]]
===La miniaturisation des transistors===
L'augmentation du nombre de transistors n'aurait pas été possible sans la miniaturisation, à savoir le fait de rendre les transistors plus petits. Il faut savoir que les circuits imprimés sont fabriqués à partir d'une plaque de silicium pur, un ''wafer'', sur laquelle on vient graver le circuit imprimé. On ne peut pas empiler deux transistors l'un sur l'autre, du moins pas facilement. Il y a bien des technologiques pour faire ça, mais elles sont complexes et nous les omettons ici. Les transistors sont donc répartis sur une surface plane, qui a une forme approximativement rectangulaire et qui a une certaine aire. L'aire en question est la même pour tous les processeurs, qui font tous la même taille, leur circuits imprimés sont les mêmes.
Les transistors sont des structures en 3D, mais ils sont posés sur une surface en 2D. En clair, on n'empile pas les transistors les uns sur les autres, on les mets les uns à côté des autres. Leur épaisseur peut se réduire avec le temps, mais cela n'a pas d'importance pour la loi de Moore. Par contre, ils ont souvent une largeur et une longueur qui sont très proches, et qui diminuent avec l'évolution des technologies de fabrication. Pour simplifier, la taille des transistors est aussi appelée la '''finesse de gravure'''. Elle s'exprime le plus souvent en nanomètres.
Doubler le nombre de transistors signifie qu'on peut mettre deux fois plus de transistors sur une même surface. Pour le dire autrement, la surface occupée par un transistor a été divisée par deux. On s'attendrait à ce que leur taille soit divisée par deux tous les 2 ans, comme le dit la loi de Moore. Mais c’est là une erreur de raisonnement.
Rappelez-vous que la taille d'un processeur reste la même, ils gardent la même surface carrée d'un modèle à l'autre. Si on divise la taille des transistors par deux, l'aire prise par un transistor sur cette surface carrée sera divisée par 4, donc on pourra en mettre 4 fois plus. Incompatible avec la loi de Moore ! En réalité, diviser une surface carrée/rectangulaire par deux demande de diviser la largeur et la longueur par <math>\sqrt{2}</math>. Ainsi, la finesse de gravure est divisée par <math>\sqrt{2}</math>, environ 1,4, tous les deux ans. Une autre manière de le dire est que la finesse de gravure est multipliée par 0,7 tous les deux ans, soit une diminution de 30 % tous les deux ans. En clair, la taille des transistors décroit de manière exponentielle avec le temps !
[[File:SemiconductorProcessSize.png|centre|vignette|upright=2.0|Évolution de la finesse de gravure au cours du temps pour les transistors CMOS.]]
===La fin de la loi de Moore===
Néanmoins, la loi de Moore n'est pas vraiment une loi gravée dans le marbre. Si celle-ci a été respectée jusqu'à présent, c'est avant tout grâce aux efforts des fabricants de processeurs, qui ont tenté de la respecter pour des raisons commerciales. Vendre des processeurs toujours plus puissants, avec de plus en plus de transistors est en effet gage de progression technologique autant que de nouvelles ventes.
Il arrivera un moment où les transistors ne pourront plus être miniaturisés, et ce moment approche ! Quand on songe qu'en 2016 certains transistors ont une taille proche d'une vingtaine ou d'une trentaine d'atomes, on se doute que la loi de Moore n'en a plus pour très longtemps. Et la progression de la miniaturisation commence déjà à montrer des signes de faiblesses. Le 23 mars 2016, Intel a annoncé que pour ses prochains processeurs, le doublement du nombre de transistors n'aurait plus lieu tous les deux ans, mais tous les deux ans et demi. Cet acte de décès de la loi de Moore n'a semble-t-il pas fait grand bruit, et les conséquences ne se sont pas encore faites sentir dans l'industrie. Au niveau technique, on peut facilement prédire que la course au nombre de cœurs a ses jours comptés.
On estime que la limite en terme de finesse de gravure sera proche des 5 à 7 nanomètres : à cette échelle, le comportement des électrons suit les lois de la physique quantique et leur mouvement devient aléatoire, perturbant fortement le fonctionnement des transistors au point de les rendre inutilisables. Et cette limite est proche : des finesses de gravure de 10 nanomètres sont déjà disponibles chez certaines fondeurs comme TSMC. Autant dire que si la loi de Moore est respectée, la limite des 5 nanomètres sera atteinte dans quelques années, à peu-près vers l'année 2020. Ainsi, nous pourrons vivre la fin d'une ère technologique, et en voir les conséquences. Les conséquences économiques sur le secteur du matériel promettent d'être assez drastiques, que ce soit en terme de concurrence ou en terme de réduction de l'innovation.
Quant cette limite sera atteinte, l'industrie sera face à une impasse. Le nombre de cœurs ou la micro-architecture des processeurs ne pourra plus profiter d'une augmentation du nombre de transistors. Et les recherches en terme d'amélioration des micro-architectures de processeurs sont au point mort depuis quelques années. La majeure partie des optimisations matérielles récemment introduites dans les processeurs sont en effet connues depuis fort longtemps (par exemple, le premier processeur superscalaire à exécution dans le désordre date des années 1960), et ne sont améliorables qu'à la marge. Quelques équipes de recherche travaillent cependant sur des architectures capables de révolutionner l'informatique. Le calcul quantique ou les réseaux de neurones matériels sont une première piste, mais qui ne donneront certainement de résultats que dans des marchés de niche. Pas de quoi rendre un processeur de PC plus rapide.
==L'invention du microprocesseur==
Le processeur est le circuit de l'ordinateur qui effectue des calculs sur des nombres codés en binaire, c’est la pièce maitresse de l'ordinateur. C'est un circuit assez complexe, qui utilise beaucoup de transistors. Avant les années 1970, il n'était pas possible de produire un processeur en un seul morceau. Impossible de mettre un processeur dans un seul boitier, les processeurs étaient fournis en pièces détachées qu'il fallait relier entre elles.
Un exemple de processeur conçu en kit est la série des Intel 3000. Elle regroupe plusieurs circuits séparés : l'Intel 3001 est le séquenceur, l'Intel 3002 est le chemin de données (ALU et registres), le 3003 est un circuit d'anticipation de retenue censé être combiné avec l'ALU, le 3212 est une mémoire tampon, le 3214 est une unité de gestion des interruptions, les 3216/3226 sont des interfaces de bus mémoire. On pourrait aussi citer la famille de circuits intégrés AMD Am2900.
===L'intel 4004 : le premier microprocesseur===
Par la suite, les progrès de la miniaturisation ont permis de mettre un processeur entier dans un seul circuit intégré. C'est ainsi que sont nés les '''microprocesseurs''', à savoir des processeurs qui tiennent tout entier sur une seule puce de silicium. Les tout premiers microprocesseurs étaient des processeurs à application militaire, comme le processeur du F-14 CADC ou celui de l'''Air data computer''.
Le tout premier microprocesseur commercialisé au grand public est le 4004 d'Intel, sorti en 1971. L'intel 4004 comprenait environ 2300 transistors, avait une fréquence de 740 MHz, pouvait faire 46 opérations différentes, et manipulait des entiers de 4 bits. De plus, le processeur manipulait des entiers en BCD, ce qui fait qu'il pouvait manipuler un chiffre BCD à la fois (un chiffre BCD est codé sur 4 bits). Il était au départ un processeur de commande, prévu pour être intégré dans la calculatrice Busicom calculator 141-P, mais il fut utilisé pour d'autres applications quelque temps plus tard. Son successeur, l'Intel 4040, garda ces caractéristiques et n'apportait que quelques améliorations mineures : plus de registres, plus d'opérations, etc.
Le 4004 était commercialisé dans un boitier DIP simple, fort différent des boitiers et sockets des processeurs actuels. Le boitier du 4004 avait seulement 16 broches, ce qui était permis par le fait qu'il s'agissait d'un processeur 4 bits. On trouve 4 broches pour échanger des données avec le reste de l'ordinateur, 5 broches pour communiquer avec la mémoire (4 broches d'adresse, une pour indiquer s'il faut faire une lecture ou écriture), le reste est composé de broches pour la tension d'alimentation VDD, la masse VSS et pour le signal d'horloge (celui qui décide de la fréquence).
{|
|[[File:Intel 4004.jpg|vignette|Intel 4004]]
|[[File:4004 dil.png|vignette|upright=1|Broches du 4004.]]
|}
Immédiatement après le 4004, les premiers microprocesseurs 8 bits furent commercialisés. Le 4004 fut suivi par le 8008 et quelques autres processeurs 8 bits extrêmement connus, comme le 8080 d'Intel, le 68000 de Motorola, le 6502 ou le Z80. Ces processeurs utilisaient là encore des boitiers similaires au 4004, mais avec plus de broches, vu qu'ils étaient passés de 4 à 8 bits. Par exemple, le 8008 utilisait 18 broches, le 8080 était une version améliorée du 8008 avec 40 broches. Le 8086 fut le premier processeur 16 bits.
===Le passage des boitiers aux slots et sockets===
La forme des processeurs a changé au cours du temps. Ils sont devenus plats et carrés. Les raisons qui expliquent la forme des boitiers des processeurs actuels sont assez nombreuses. La première est que les techniques de fabrications des puces électroniques actuelles font qu'il est plus simple d'avoir un circuit planaire, relativement peu épais. De plus, la forme carrée s'explique par la fabrication des puces de silicium, où un cristal de silicium est coupé en tranches, elles-mêmes découpées en puces carrées identiques, ce qui facilite la conception. Un autre avantage de cette forme est que la dissipation de la chaleur est meilleure. Les processeurs actuels sont devenus plus puissants que ceux d'antan, mais au prix d'une dissipation thermique augmentée. Dissiper cette chaleur est devenu un vrai défi sur les processeurs actuels, et la forme des microprocesseurs actuels aide à cela, couplé à des radiateurs et ventilateurs.
Un autre changement tient dans la manière dont le processeur est relié à la carte mère. Les premiers processeurs 8 et 16 bits étaient soudés à la carte mère. Les retirer demandait de les dé-souder, ce qui n'était pas très pratique, mais ne posait pas vraiment de problèmes à l'époque. Il faut noter que certains processeurs assez anciens étaient placés sur des cartes intégrées, elles-mêmes connectées à la carte mère par un slot d'extension, similaire à celui des cartes graphiques.
[[File:Intel Pentium II 400 SL357 SECC2.jpg|centre|vignette|upright=2|Circuit du Pentium 2..]]
[[File:Asus P3C2000 - Slot 1-8626.jpg|centre|vignette|upright=2|Slot 1-8626, utilisé pour connecter les processeurs Pentium 2 sur la carte mère.]]
De nos jours, les processeurs n'utilisent plus les boitiers soudés d'antan. Les processeurs sont clipsés dans un connecteur spécial sur la carte mère, appelé le ''socket''. Grâce à ce système, il est plus simple d'ajouter ou de retirer un processeur de la carte mère. L'''upgrade'' d'un processeur est ainsi fortement facilitée. Les broches sont composées de billes ou de pins métalliques qui font contact avec le connecteur.
{|
|[[File:XC68020 bottom p1160085.jpg|vignette|upright=1.5|XC68020 bottom p1160085]]
|[[File:Kl Intel Pentium MMX embedded BGA Bottom.jpg|vignette|Kl Intel Pentium MMX embedded BGA Bottom]]
|}
===L'invention des processeurs multicœurs===
Avec l'avancée des processus de fabrication, il est devenu possible de mettre plusieurs processeurs sur une même puce de silicium, et c'est ainsi que sont nés les processeurs multicœurs. Pour simplifier, les processeurs multicœurs regroupent plusieurs processeurs, soit sur une même puce de silicium, soit dans un même boitier. Les processeurs en question sont appelés des cœurs. Il arrive donc qu'un processeur multicœurs ait en réalité 8 cœurs/processeurs sur la même puce, ou 4, ou 2, parfois 16, 32, 64, rarement plus. Les processeurs multicœurs contenant 2 processeurs sont aujourd'hui obsolète, la norme est entre 4 et 16.
Les fabricants ont généralement plusieurs modèles d'un même processeur : un modèle entrée de gamme peu cher et peu performant, un modèle haut de gamme très cher et performant, et un modèle milieu de gamme aux prix et performances entre les deux précédents. Et ces trois modèles n'ont pas le même nombre de cœurs. Et bien sachez qu'en réalité, tous ces processeurs sortent de la même usine et sont fabriqués de la même manière, avec le même nombre de cœurs. Par exemple, imaginez qu'un modèle entrée de gamme ait 4 cœurs , le milieu de gamme 8 cœurs, et le haut de gamme en ait 16. Et bien ils sont fabriqués à partir d'un modèle haut de gamme à 16 cœurs, dont on désactive certains cœurs pour obtenir les modèles bas et milieu de gamme.
Après leur fabrication, les processeurs subissent des tests pour vérifier si le processeur fonctionne normalement. Et il arrive qu'un cœur soit défectueux et ne fonctionne pas, mais que les autres fonctionnent parfaitement. Par exemple, si on prend un processeur à 8 cœurs , il se peut que deux d'entre eux ne fonctionne pas et les 6 autres soient fonctionnels. Dans ce cas, on en fait un modèle milieu ou entrée de gamme en désactivant les cœurs défectueux. La désactivation est généralement matérielle, en coupant des fils pour déconnecter les cœurs défectueux.
===La révolution des ''chiplets''===
Les processeurs multicœurs modernes utilisent la technique des ''chiplets''. Pour donner un exemple, prenons celui du processeur POWER 5, autrefois utilisé sur d'anciens ordinateurs Macintosh. Chaque coeur avait son propre boitier rien que pour lui. Il en a existé deux versions. La première était dite double cœur, à savoir qu'elle intégrait deux processeurs dans la même puce. Le seconde version étiat quadruple coeur, avec 4 processeurs dans un même boitier, avec 4 ''dies''. La dernière version est illustrée ci-dessous. On voit qu'il y a quatre boitier rouges, un par coeur, et quatre autres en vert qui correspondent à de la mémoire cache (le cache L3).
[[File:Power5.jpg|centre|vignette|upright=2|POWER 5 MCM.]]
Un autre exemple est celui des processeurs AMD récents, d'architectures Zen 2/3/4/5. Ils incorporent deux puces dans le même boitier : une puce qui contient les processeurs, les cœurs, et une autre pour les interconnexions avec le reste de l'ordinateur. La puce pour les interconnexions gère l'interface avec la mémoire RAM, les bus PCI-Express pour la carte graphique, et quelques autres. Les deux puces n'ont pas la même finesse de gravure, ni les mêmes performances.
[[File:AMD@7nm(12nmIO)@Zen2@Matisse@Ryzen 5 3600@100-000000031 BF 1923SUT 9HM6935R90062 DSCx2@Infrared.jpg|centre|vignette|upright=2|AMD@7nm(12nmIO)@Zen2@Matisse@Ryzen 5 3600@100-000000031 BF 1923SUT 9HM6935R90062 DSCx2@Infrared]]
Certains processeurs AMD Epyc avaient plusieurs chiplets pour les processeurs/coeurs, combinés avec un chiplet pour les interconnexions. L'image ci-dessous montre un processeur AMD Epyc 7702, avec un chiplet central pour les interconnexions, et les chiplets autour qui contiennent chacun 4 cœurs.
[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]]
[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]]
==La conception d'un circuit intégré==
[[File:Integrated circuit design.png|vignette|Étapes de conception d'un circuit intégré.]]
La conception d'un circuit intégré se fait en une série d'étapes assez complexes, dont certaines sont aidées par ordinateur. Inutile de voir dire que concevoir un circuit intégré est généralement assez complexe et demande des compétences très variées. Concevoir une puce électronique doit se faire à plusieurs niveaux d'abstraction, que nous allons détailler dans ce qui suit. Nous allons grandement simplifier le tout en donnant une description assez sommaire.
===La conception logique===
La première étape est de créer une sorte de cahier des charge, de spécification qui décrit comment fonctionne le circuit. La spécification décrit son '''architecture externe''', à savoir comment le circuit se comporte. Elle décrit comment le circuit réagit quand on envoie telle donnée sur telle entrée, qu'est-ce qu'on retrouve sur ses sorties si... , etc. Pour un circuit combinatoire, cela revient à écrire sa table de vérité. Mais il va de soit que pour des circuits complexes, la spécification est beaucoup plus complexe.
La seconde étape est d'implémenter le circuit en utilisant les circuits vus dans les chapitres précédents, à savoir des registres, bascules, portes logiques, décodeurs, multiplexeurs, additionneurs et autres circuits basiques. La conception se fait en utilisant un '''langage de description matérielle''', qui a des ressemblances superficielles avec un langage de programmation.
Le langage de desciption matériel est ensuite utilisé pour produire une description du circuit assez haut niveau, appelée le '''''Register-transfer level''''' (RTL), qui combine registres, portes logiques et autres circuits combinatoires basiques. Les circuits de base utilisés lors de cette étape sont appelés des ''cellules standard''. La RTL ressemble aux schémas vus dans les chapitres précédents, et ce n'est pas un hasard : de tels schémas sont des RTL simples de circuits eux-mêmes simples.
===La conception physique===
[[File:FlowPhysicalDesign.png|vignette|Design physique, la troisième étape.]]
La troisième étape traduit la RTL en un plan à appliquer sur le ''die'' physique, à graver dessus. Elle traduit les portes logiques en montages à base de transistors, comme vu dans le chapitre précédents. Les autres cellules standards sont elles aussi directement traduites en un montage à base de transistors, conçu à l'avance par des ingénieurs spécialisé, qui est potentiellement optimisé qu'un montage à base de portes logiques.
Les cellules sont placées sur la puce par un algorithme, qui cherche à optimiser l'usage du ''die''. Les interconnexions métalliques entre transistors sont ajoutées, de même que le signal d'horloge, la masse et la tension d'alimentation. L'arbre d'horloge est généré à cette étape, de même que l'arbre qui transmet la tension d'alimentation aux portes logiques. Le résultat est une sorte de description physique du ''die''.
[[File:Vlsiopamp2.gif|centre|vignette|upright=2|Description physique d'un amplificateur opérationnel basique.]]
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les transistors et portes logiques
| prevText=Les transistors et portes logiques
| next=L'interface électrique entre circuits intégrés et bus
| nextText=L'interface électrique entre circuits intégrés et bus
}}{{autocat}}
</noinclude>
qkxgldimcr6vunzzsehhu1gam9vbouj
Fonctionnement d'un ordinateur/Les timers et diviseurs de fréquence
0
79341
744781
741423
2025-06-15T01:45:37Z
Mewtow
31375
/* Le Time Stamp Counter des processeurs x86 */
744781
wikitext
text/x-wiki
Les compteurs servent à créer divers circuits fortement liés la gestion de la fréquence, ainsi qu'à la mesure du temps. L'idée derrière ces circuits est tout simplement de compter les cycles d'horloge. Vu qu'un compteur/décompteur est cadencé par le signal d'horloge, on peut l'incrémenter ou le décrémenter à chaque cycle d'horloge, ce qui lui fait compter les cycles d'horloge. Compter les cycles d'horloge a plusieurs utilités. On peut s'en servir pour mesurer des durées, ou pour diviser une fréquence. Dans ce qui va suivre, nous allosn voire deux types de circuits : les diviseurs de fréquence, et les ''timers''.
==Les diviseurs de fréquence==
Les '''diviseurs de fréquence''' sont des circuits qui prennent en entrée un signal d'horloge et fournissent en sortie un autre signal d'horloge de fréquence plus faible. Plus précisément, la fréquence de sortie est 2, 3, 4, ou 18 fois plus faible que la fréquence d'entrée. La fréquence est donc divisée par un nombre N, qui dépend du diviseur de fréquence. Il existe des diviseurs de fréquence qui divisent la fréquence par 2, d'autres par 4, d'autres par 13, etc.
===Les diviseurs de fréquence basés sur des compteurs simples===
Leur implémentation est simple : il suffit d'un compteur auquel on rajoute une sortie. Pour être plus précis, il faut utiliser un compteur modulo. Pour rappel, le compteur modulo est un compteur qui est remis à zéro quand il atteint une valeur limite. Pour un diviseur de fréquence par N, il faut plus précisément un compteur modulo par N. Tous les N cycles, le compteur déborde, à savoir qu'il dépasse sa valeur maximale et est remis à zéro. Une sortie du compteur indique si le compteur déborde : elle est mise à 1 lors d'un débordement et reste à 0 sinon. L'idée est de compter le nombre de cycles d'horloges, et de mettre à 1 la sortie quand le compteur déborde.
Par exemple, pour diviser une fréquence par 8, on prend un compteur 3 bits. A chaque fois que le compteur déborde et est réinitialisé, on envoie un 1 en sortie. Le résultat est un signal qui est à 1 tous les 8 cycles d'horloge, à savoir un signal de fréquence 8 fois inférieure. La même idée marche avec un diviseur de fréquence par 6, sauf que l'on doit alors utiliser un compteur modulo par 6, ce qui veut dire qu'il compte de 0 à 5 comme suit : 0, 1, 2, 3, 4, 5, 0, 1, 2, ... Le compteur déborde tous les 6 cycles d’horloge, ce qui fait que sa sortie de débordement est à 1 tous les 6 cycles, ce qui est demandé.
Si n'importe quel compteur fait l'affaire, il est cependant utile d'utiliser les compteurs les plus adaptés à la tâche. Pour faire un diviseur de fréquence, on utilise rarement un compteur complet, mais souvent des compteurs plus simples, comme un circuit incrémenteur ou des compteurs en anneau.
===Les diviseurs de fréquence basés sur des compteurs en anneau===
Il est rare que l'on doive diviser une fréquence par 50 ou par 100, par exemple. Un diviseur de fréquence divise une fréquence par N, avec N très petit. Or, les compteurs ''one-hot'', aussi appelés compteurs en anneau, sont particulièrement adaptés pour compter jusqu'à des valeurs assez faibles : 6, 10, 12, 25, etc. Il est donc naturel d'utiliser un compteur en anneau dans un diviseur de fréquence.
Pour diviser une fréquence par N, il suffit de prendre un compteur en anneau qui compte de 0 à N-1 (inclut). Le signal de sortie est mis à 1 et/ou inversé quand le compteur est remis à zéro, c'est à dire quand son bit de poids faible est à 1. Une méthode alternative consiste à regarder au contraire le bit de poids fort : le compteur atteint N quand ce bit est à 1, ce qui fait qu'il a compté jusqu'à N. En clair, la sortie est obtenue en regardant la valeur du bit de poids faible/fort, sans même utiliser de comparateur. Pas besoin de comparateur, donc.
Et au-delà de ça, le circuit obtenu est beaucoup plus simple qu’avec un compteur normal. Et c'est la raison pour laquelle les diviseurs de fréquence sont souvent conçus en utilisant des compteurs ''one-hot''. Plus on divise une fréquence par un N très petit, plus les compteurs auront d'avantages : très simples, demandent peu de portes logiques, sont très rapides, prennent peu de place, permettent de se passer de circuit comparateur.
===Les diviseurs de fréquence basés sur des incrémenteurs à bascule T===
Il est aussi possible de concevoir des diviseurs de fréquence en utilisant un banal incrémenteur. Si l'incrémenteur est un incrémenteur non-modulo, on se retrouve avec un diviseur de fréquence qui divise la fréquence d'entrée par une puissance de deux. Pour comprendre comment les fabriquer, nous allons étudier le cas le plus simple : celui qui divise par 2 la fréquence d'entrée. Et bien sachez qu'il s'agit d'une simple bascule T. En effet, regardons ce qui se passe quand on envoie un signal constamment à 1 sur son entrée T. Dans ce cas, la bascule s'inversera une fois par chaque cycle d'horloge. Un cycle d'horloge sur la sortie correspond au temps passé entre deux inversions.
[[File:Frequency divider by 2.svg|centre|vignette|upright=2|Diviseur de fréquence par 2.]]
Pour créer un diviseur de fréquence par 4, il suffit d'enchainer deux fois le circuit précédent. La sortie de la première bascule T doit être envoyée sur l'entrée T de la seconde bascule. Et pour créer un diviseur de fréquence par 8, il suffit d'enchainer trois fois le circuit précédent. Et ainsi de suite. Au final, un diviseur de fréquence qui divise la fréquence d'entrée par 2^N est un enchainement de N bascules T, qui n'est autre qu'un circuit incrémenteur. La sortie d'un tel diviseur de fréquence se situe en sortie de la dernière bascule.
[[File:Freq Divider mod 8.svg|centre|vignette|upright=2|Diviseur de fréquence par 8.]]
On peut en profiter pour créer un circuit à plusieurs sorties, en mettant une sortie par bascule. Le circuit, illustré ci-dessous, fournit donc plusieurs fréquences de sortie : une à la moitié de la fréquence initiale, une autre au quart de la fréquence d'entrée, une autre au huitième, etc.
[[File:Frequency divider animation.gif|centre|vignette|upright=2|Diviseur de fréquence multiple.]]
==Les ''timers''==
Les '''''timers''''', aussi appelés ''Programmable interval timer'', sont des circuits capables de compter des durées. Leur fonctionnement est assez simple : on leur envoie un certain nombre de cycles d'horloge en entrée, et ils émettent un signal quand ce nombre de cycles est écoulé. Le signal en question est disponible sur une sortie de 1 bit, et correspond tout simplement au fait que cette sortie est mise à 1, pendant un cycle d'horloge. Ils permettent de compter des durées, exprimées en cycles d'horloge. On peut aussi générer un signal qui surviendra après 50 cycles d'horloge, ou après 100 cycles d'horloge, etc.
Les ''timers'' sont composés d'un compteur/décompteur cadencé par un signal d'horloge. Le compteur initialisé à 0, puis est incrémenté à chaque signal d'horloge, jusqu’à atteinte d'une valeur limite où il génère un signal. Pour un décompteur, c'est la même chose, sauf que le décompteur est initialisé à sa valeur limite et est décrémenté à chaque cycle, et envoie un signal quand il atteint 0. Les ''timers'' basés sur des décompteurs sont nettement plus simples que les autres, ce qui fait qu'ils sont plus utilisés. Pour que les ''timers'' soient configurables, on doit pouvoir préciser combien de cycles il faut (dé-)compter avant d'émettre un signal. On peut ainsi préciser s'il faut émettre le signal après 32 cycles d'horloge, après les 50 cycles, tous les 129 cycles, etc. Pour cela, il suffit de préciser le nombre de cycles à compter/décompter en entrée et d'initialiser le compteur/décompteur avec.
Les ''timers'' matériels peuvent compter de deux manières différentes, appelées '''mode une fois''' et '''mode périodique'''. Concrètement, le mode périodique divise la fréquence d'entrée, alors que le mode une fois compte durant une durée fixe avant de s'arrêter.
* En mode une fois, le ''timer'' s'arrête une fois qu'il a atteint la limite configurée. On doit le réinitialiser manuellement, par l'intermédiaire du logiciel, pour l'utiliser une nouvelle fois. Cela permet de compter une certaine durée, exprimée en nombre de cycles d'horloge.
* En mode périodique, le ''timer'' se réinitialise automatiquement avec la valeur de départ, ce qui fait qu'il reboucle à l'infini. En clair, le ''timer'' se comporte comme un diviseur de fréquence. Si le compteur est réglé de manière à émettre un signal tous les 9 cycles d'horloge, la fréquence de sortie sera de 9 fois moins celle de la fréquence d'entrée du compteur.
Un ordinateur est rempli de ''timers'' divers. Dans ce qui va suivre, nous allons voir les principaux ''timers'', qui sont actuellement intégrés dans les PC modernes. Ils se trouvent sur la carte mère ou dans le processeur, tout dépend du ''timer''.
===Le ''watchdog timer''===
Le '''''watchdog timer''''' est un ''timer'' spécifique dont le but est d'éteindre ou de redémarrer automatiquement l'ordinateur si jamais celui-ci ne répond plus ou plante. Tous les ordinateurs n'ont pas ce genre de ''timer'', et beaucoup de PC s'en passent. Mais ce ''timer'' est très fréquent dans les architectures embarquées.
Le ''watchdog timer'' est un compteur/décompteur qui doit être réinitialisé régulièrement. S'il n'est pas réinitialisé, le ''watchdog timer'' déborde (revient à 0 ou atteint 0) et envoie un signal qui redémarre le système. Le système est conçu pour réinitialiser le ''watchdog timer'' régulièrement, ce qui signifie que le système n'est pas censé redémarrer. Si jamais le système dysfonctionne gravement, le système ne pourra pas réinitialiser le ''watchdog timer'' et le système est redémarré automatiquement ou mis en arrêt.
[[File:SimpleWatchdogTimer.gif|centre|vignette|upright=2|Le ''Watchdog Timer'' et l'ordinateur.]]
===Le ''Time Stamp Counter'' des processeurs x86===
Tous les processeurs des PC actuels sont des processeurs dits x86. Nous ne pouvons pas expliquer ce que cela signifie pour le moment, retenez juste ce terme. Sachez que tous les processeurs x86 contiennent un compteur de 64 bits, appelé le '''''Time Stamp Counter''''', qui mémorise le nombre de cycles d'horloge qu'a effectué le processeur depuis son démarrage. Les programmes peuvent accéder à ce registre assez facilement, ce qui est utile pour faire des mesures ou comparer les performances de deux applications. Il permet de compter combien de cycles d'horloge met un morceau de code à s’exécuter, combien de cycles prend une instruction à s’exécuter, etc. Les processeurs non-x86 ont un registre équivalent, que ce soit les processeurs ARM ou d'autres.
Malheureusement, ce compteur est tombé en désuétude pour tout un tas de raisons. La principale est que les processeurs actuels ont une fréquence variable. Nous expliquerons cela plus en détail dans quelques chapitres, mais les processeurs actuels font varier leur fréquence suivant les besoins. Ils augmentent leur fréquence quand on leur demande de faire beaucoup de calculs, et se mettent en mode basse(fréquence pour économiser de l'énergie si on ne leur demande pas grand chose. Avec une fréquence variable, le ''Time Stamp Counter'' perd complétement en fiabilité.
Intel a tenté de corriger ce défaut en incrémentant ce registre à une fréquence constante, différente de celle du processeur, ce qui est encore le cas sur les processeurs Intel actuels. Le comportement est un peu différent sur les processeurs AMD, mais il compte par cycle d'horloge, avec des mécanismes de synchronisation assez complexes pour corriger l'effet de la fréquence variable.
===L'horloge temps réel===
L''''horloge temps réel''' est un ''timer'' qui génère une fréquence de 1024 Hz, soit près d'un Kilohertz. Dans ce qui suit, nous la noterons RTC, ce qui est l'acronyme du terme anglais ''Real Time Clock''. La RTC prend en entrée un signal d'horloge de 32KHz, généré par un oscillateur à Quartz, et fournit en sortie un signal de fréquence 32 fois plus faible, c'est à dire de 1 KHz. Pour cela, elle est réglée en mode répétitif et son décompteur interne est initialisé à 32. La RTC génère donc un signal toutes les millisecondes, qui est envoyé au processeur. On peut, en théorie, changer la fréquence de la RTC, mais c'est rarement une bonne idée.
En théorie, la RTC permet de compter des durées assez courtes, comme le ''ping'' (le temps de latence d'un réseau, pour simplifier), le temps de rafraichissement de l'écran, ou bien d'autres choses. Mais dans les faits, les systèmes d'exploitation modernes ne l'utilisent pas pour ça. L'horloge temps réel est trop imprécise et sa fréquence n'aide pas. En effet, 1024 Hz est proche de 1000, mais pas assez pour faire des mesures à la missliseconde près, chose qui est nécessaire pour mesurer le ''ping'' ou d'autres choses utiles.
A la place, l'ordinateur l'utiliser pour compter les secondes, afin que l'ordinateur soit toujours à l'heure. Vous savez déjà que l'ordinateur sait quelle heure il est (vous pouvez regarder le bureau de Windows dans le coin inférieur droite de votre écran pour vous en convaincre) et il peut le faire avec une précision de l'ordre de la seconde. Mais pour savoir quel jour, heure, minute et seconde il est, l'ordinateur doit faire deux choses : mémoriser la date exacte à la seconde près, et avoir la capacité de compter le temps qui s'écoule, seconde par seconde. Pour cela, un ordinateur contient une CMOS RAM qui mémorise la date, et la RTC.
===Le ''Programmable Interval Timer'' : l'Intel 8253===
[[File:Intel 8253 and 8254.svg|vignette|Intel 8253 and 8254]]
L'Intel 8253 est un ''timer'' programmable qui était autrefois intégré dans les cartes mères des ordinateurs personnels de type PC. Les premiers processeurs x86 étaient souvent secondés avec un Intel 8253 soudé à la carte mère. Il fût suivi par l'Intel 8254, qui en était une légère amélioration. S'il n'est plus présent dans un boitier de la carte mère, on trouve toujours un circuit semblable au 8253 à l'intérieur du ''chipset'' de la carte mère, voire à l'intérieur du processeur, pour des raisons de compatibilité. Sur les PC, il est cadencé par une horloge maitre, générée par un oscillateur à Quartz, dont la fréquence est de 32 768 Hertz, soit 2^15 cycles d'horloge par seconde. La fréquence générée par un compteur va donc de 18,2 Hz à environ 500 KHz. Il était utilisé pour dériver un grand nombre de fréquences utilisées dans l'ordinateur. Par exemple, le second compteur était utilisé par défaut pour le rafraichissement de la mémoire (D)RAM, mais il était souvent reprogrammé pour servir à générer des fréquences spécifiques par le BIOS ou la carte graphique.
L'intérieur de l'Intel 8253 est illustré ci-dessous. Nous allons expliquer l'ensemble de ce schéma, rassurez-vous, mais les explications seront plus simples à comprendre si vous survolez ce schéma en premier lieu.
[[File:Intel 8253 block diagram.svg|centre|vignette|upright=2|Intel 8253, intérieur.]]
L'Intel 8253 contient trois compteurs de 16 bits, numérotés de 0 à 2. Chaque compteur possède deux entrées et une sortie : l'entrée CLOCK est celle de l'horloge de 32 MHz, l'entrée GATE active ou désactive le compteur, la sortie fournit le signal voulu et/ou la fréquence de sortie.
L'Intel 8253 lui-même possède plusieurs entrées et sorties. En premier lieu, on voit un port de 8 bits connecté aux trois compteurs, qui permet à l'Intel 8253 de communiquer avec le reste de l'ordinateur. La communication se fait dans les deux sens : soit de l'ordinateur vers les compteurs, soit des compteurs vers l'ordinateur. Dans le sens ordinateur -> compteurs, cela permet à l'ordinateur de programmer les compteurs, de les initialiser. Dans l'autre sens, cela permet de récupérer le contenu des compteurs, même si ce n'est pas très utilisé.
Ensuite, on trouve un registre de 8 bits, le ''Control Word register'' qui mémorise la configuration de l'Intel 8253. Le contenu de ce registre détermine le mode de fonctionnement du compteur, de combien doit compter le compteur et bien d'autres choses. Pour programmer les trois compteurs, il faut écrire un mot de 8 bits dans le ''Control Word register''. La configuration de l'Intel 8253 fournie en sur le port de 8 bits pendant un cycle d'horloge, puis est mémorisée dans ce registre et reste pour les cycles suivants.
Mais l'écriture a lieu à condition que les 5 entrées de configuration soit bien réglées. Les 5 entrées de configuration sont les suivantes :
* Deux bits A0 et A1 pour sélectionner le compteur voulu avec son numéro, ou le ''control word register''.
* Un bit RD à mettre à 0 pour que l'ordinateur récupère le compteur sélectionné ou le ''control word register'' sur le port de 8 bits.
* Un bit WR à mettre à 0 pour que l'ordinateur modifie le compteur sélectionné ou le ''control word register'', en envoyant le nombre pour l'initialisation sur le port de 8 bits.
* Un bit CS qui active ou désactive l'Intel 8253 et permet de l'allumer ou de l’éteindre.
Pour écrire dans le ''Control Word register'', il faut mettre le bit CS à 0 (on active l'Intel 8253), mettre à 1 le bit RD et à 0 le bit WR (on indique qu'on fait une écriture), et sélectionner le ''Control Word register'' en mettant les deux bits A0 et A1 à 1. Pour écrire dans un compteur, il faut faire la même chose, sauf que les bits A0 et A1 doivent être configurés de manière à donner le numéro du compteur voulu. LA lecture s'effectue elle aussi de la même manière, mais il faut inverser les bits RD et WR.
===Le ''High Precision Event Timer'' (HPET)===
De nos jours, l'horloge temps réel et l'Intel 8253/8254 tendent à être remplacé par un autre ''timer'', le ''High Precision Event Timer'' (HPET). Il s'agit d'un compteur de 64 bits, dont la fréquence est d'au moins 10 MHz. Il s'agit bien d'un compteur et non d'un décompteur. Il est couplé à plusieurs comparateurs, qui vérifient chacun une valeur limite, une valeur à laquelle générer un signal. La valeur limite peut être programmée, ce qui fait que chaque comparateur est associé à un registre pour mémoriser la valeur limite. Il doit y avoir au moins trois comparateurs, mais le nombre peut monter jusqu’à 256. Chaque comparateur doit pouvoir fonctionner en mode une fois, et au moins un comparateur doit pouvoir fonctionner en mode périodique.
[[File:High Precision Event Timer.png|centre|vignette|upright=2|High Precision Event Timer]]
Il faut noter que les systèmes d'exploitation conçus avant le HPET ne peuvent pas l'utiliser, pour des raisons techniques de compatibilité matérielle. C'est le cas de Windows XP avant le Service Pack 3. C'est la raison pour laquelle les cartes mères possèdent encore un PIT et une RTC, ou au moins qu'elles émulent RTC et PIT dans leurs circuits. D'ailleurs, pour économiser des circuits, les cartes mères modernes émulent le PIT et la RTC avec le HPET. Le HPET est configuré de manière à ce que le premier comparateur fournisse une fréquence de 1024 Hz, comme la RTC, et les 3 comparateurs suivants remplacent l'Intel 8253.
Le HPET gère de nombreux modes de fonctionnement : ses comparateurs peuvent être configuré en mode une fois ou périodique, on peut lui demander d'émuler la RTC et le PIT, etc. Aussi, il contient aussi de nombreux registres de configuration. En tout, on trouve 3 registres de configuration. à Cela, il faut ajouter trois registres pour configurer chaque comparateur indépendamment les uns des autres. Notons qu'il est aussi possible de lire ou écrire dans le compteur de 64 bits, mais ce n'est pas recommandé.
==La génération de nombres pseudo-aléatoires==
Les compteurs peuvent aussi être utilisés pour générer des nombres "aléatoires". Je dis aléatoires entre guillemets car ils ne sont pas vraiment aléatoires, mais s'en rapprochent suffisamment pour être considérés comme tels. Pour mettre en avant cela, on parle aussi de nombres "pseudo-aléatoires". De nombreuses situations demandent de générer des nombres pseudo-aléatoires. C'est très utile dans des applications cryptographiques, statistiques, mathématiques, dans les jeux vidéos, et j'en passe. L'aléatoire dans les jeux vidéos est un bon exemple : pas besoin d'un aléatoire de qualité, un simple algorithme pseudo-aléatoire suffit.
Dans certaines situations, il est nécessaire de générer des nombres aléatoires de manière matérielle. Cela peut servir pour sélectionner une ligne de cache à remplacer lors d'un défaut de cache, pour implémenter des circuits cryptographiques, pour calculer la durée d'émission sur un bus Ethernet à la suite d'une collision, et j'en passe.
Les méthodes que nous allons voir produisent un nombre pseudo-aléatoire un bit à la fois, à quelques exceptions près. Les circuits que nous allons voir fournissent un bit sur leur sortie et ce bit varie de manière assez aléatoire. Les bits en sortie du circuit sont accumulés dans un registre à décalage normal, pour former un nombre aléatoire. Nous appellerons ce registre : l'accumulateur.
===L'usage de registres à décalage à rétroaction===
[[File:Nonlinear-combo-generator.png|thumb|Nonlinear-combo-generator]]
La première solution utilise des ''registres à décalages à rétroaction'', aussi appelés ''Feedback Shift Registers'', abréviés LSFR. Un LSFR seul ne fournit pas un aléatoire digne de ce nom, car il boucle, il a une période comme tout compteur. Par contre, il est possible de combiner plusieurs LSFR pour obtenir une meilleure approximation de l'aléatoire. Avec cette technique, plusieurs registres à décalages à rétroaction sont reliés à un circuit combinatoire non-linéaire. Ce circuit prendra en entrée un (ou plusieurs) bit de chaque registre à décalage à rétroaction, et combinera ces bits pour fournir un bit de sortie.
[[File:A5-1 GSM cipher.svg|centre|thumb|upright=2|Exemple avec trois LSFR différents, de taille différentes : le bit envoyé à l'accumulateur est un XOR du bit sortant des trois LSFR.]]
Pour rendre le tout encore plus aléatoire, il est possible de cadencer les LSFR à des fréquences différentes. Cette technique est utilisée dans les générateurs ''stop-and-go'', ''alternative step'', et à ''shrinking''.
* Dans le générateur ''alternative step'', on utilise trois LSFR. Le premier commande un multiplexeur qui choisit la sortie parmi les deux restants.
* Dans le générateur ''stop-and-go'', on utilise deux LSFR. Le premier est relié à l'entrée d'horloge du second et le bit de sortie du second est utilisé comme résultat. Une technique similaire était utilisée dans les processeurs VIA C3, pour l'implémentation de leurs instructions cryptographiques.
* Dans le ''shrinking generator'', deux LSFR sont cadencés à des vitesses différentes. Si le bit de sortie du premier vaut 1, alors le bit de sortie du second est utilisé comme résultat. Par contre, si le bit de sortie du premier vaut 0, aucun bit n'est fourni en sortie, le bit de sortie du second registre est oublié.
===L'aléatoire généré par des ''timers'' ou des compteurs d'horloge===
Les LSFR ne permettent pas d'obtenir du vrai aléatoire, compte tenu de leur comportement totalement déterministe. Pour obtenir un aléatoire un peu plus crédible, il est possible d'utiliser des moyens non-déterministes. Et certains d'entre eux utilisent le signal d'horloge.
Par exemple, une technique très simple utilise un simple ''timer''. Si on a besoin d'un nombre pseudo-aléatoire, il suffit de lire le ''timer'' et d'utiliser le nombre lu comme nombre pseudo-aléatoire. Si le délai entre deux demandes est irrégulier, le résultat semblera aléatoire. Mais il s'agit là d'une technique assez peu fiable dans le monde réel et seules quelques applications bien spécifiques se satisfont de cette méthode.
Une solution un peu plus fiable utilise ce qu'on appelle la '''dérive de l'horloge'''. Il faut savoir qu'un signal d'horloge n'est jamais vraiment très précis. Une horloge censée tourner à 1 Ghz ne tournera pas en permanence à 1Ghz exactement, mais verra sa fréquence varier de quelques Hz ou Khz de manière irrégulière. Ces variations peuvent venir de variations aléatoires de température, des variations de tension, des perturbations électromagnétiques, ou à des phénomènes assez compliqués qui peuvent se produire dans tout circuit électrique (comme le ''shot noise'').
L'idée la plus simple utilise deux horloges : une horloge lente et une horloge rapide, dont la fréquence est un multiple de l'autre. Par exemple, on peut choisir une fréquence de 1 Mhz et une autre de 100 Hz : la fréquence la plus grande est égale à 10000 fois l'autre. La dérive d'horloge fera son œuvre, les deux horloges seront très légèrement désynchronisées en permanence, et cette désynchronisation peut être utilisée pour produire des nombres aléatoires. Par exemple, on peut compter le nombre de cycles d'horloge produit par l'horloge rapide durant une période de l'horloge lente. Si ce nombre est pair, on produit un bit aléatoire qui vaut 1 , il vaut 0 si ce nombre est pair. Pour information, c'est exactement cette technique qui était utilisée dans l'''Intel 82802 Firmware Hub''.
===L'aléatoire généré par la tension d'alimentation===
Il existe d'autres solutions matérielles qui utilisent le bruit thermique. Tous les circuits électroniques de l'univers sont soumis à de microscopiques variations de température, dues à l'agitation thermique des atomes. Plus la température est élevée, plus les atomes qui composent les fils métalliques des circuits s'agitent. Vu que les particules d'un métal contiennent des charges électriques, ces vibrations font naître des variations de tensions assez infimes. Il suffit d'amplifier ces variations pour obtenir un résultat capable de représenter un zéro ou un 1. Ce principe a été utilisé sur des anciens processeurs Intel qui géraient l'instruction RDRAND, une instruction qui produisait un nombre aléatoire.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits compteurs et décompteurs
| prevText=Les circuits compteurs et décompteurs
| next=Les circuits de décalage et de rotation
| nextText=Les circuits de décalage et de rotation
}}
</noinclude>
5ry39dwm9hmytqy80etnuaghwevij9h
744782
744781
2025-06-15T01:46:03Z
Mewtow
31375
/* Le Programmable Interval Timer : l'Intel 8253 */
744782
wikitext
text/x-wiki
Les compteurs servent à créer divers circuits fortement liés la gestion de la fréquence, ainsi qu'à la mesure du temps. L'idée derrière ces circuits est tout simplement de compter les cycles d'horloge. Vu qu'un compteur/décompteur est cadencé par le signal d'horloge, on peut l'incrémenter ou le décrémenter à chaque cycle d'horloge, ce qui lui fait compter les cycles d'horloge. Compter les cycles d'horloge a plusieurs utilités. On peut s'en servir pour mesurer des durées, ou pour diviser une fréquence. Dans ce qui va suivre, nous allosn voire deux types de circuits : les diviseurs de fréquence, et les ''timers''.
==Les diviseurs de fréquence==
Les '''diviseurs de fréquence''' sont des circuits qui prennent en entrée un signal d'horloge et fournissent en sortie un autre signal d'horloge de fréquence plus faible. Plus précisément, la fréquence de sortie est 2, 3, 4, ou 18 fois plus faible que la fréquence d'entrée. La fréquence est donc divisée par un nombre N, qui dépend du diviseur de fréquence. Il existe des diviseurs de fréquence qui divisent la fréquence par 2, d'autres par 4, d'autres par 13, etc.
===Les diviseurs de fréquence basés sur des compteurs simples===
Leur implémentation est simple : il suffit d'un compteur auquel on rajoute une sortie. Pour être plus précis, il faut utiliser un compteur modulo. Pour rappel, le compteur modulo est un compteur qui est remis à zéro quand il atteint une valeur limite. Pour un diviseur de fréquence par N, il faut plus précisément un compteur modulo par N. Tous les N cycles, le compteur déborde, à savoir qu'il dépasse sa valeur maximale et est remis à zéro. Une sortie du compteur indique si le compteur déborde : elle est mise à 1 lors d'un débordement et reste à 0 sinon. L'idée est de compter le nombre de cycles d'horloges, et de mettre à 1 la sortie quand le compteur déborde.
Par exemple, pour diviser une fréquence par 8, on prend un compteur 3 bits. A chaque fois que le compteur déborde et est réinitialisé, on envoie un 1 en sortie. Le résultat est un signal qui est à 1 tous les 8 cycles d'horloge, à savoir un signal de fréquence 8 fois inférieure. La même idée marche avec un diviseur de fréquence par 6, sauf que l'on doit alors utiliser un compteur modulo par 6, ce qui veut dire qu'il compte de 0 à 5 comme suit : 0, 1, 2, 3, 4, 5, 0, 1, 2, ... Le compteur déborde tous les 6 cycles d’horloge, ce qui fait que sa sortie de débordement est à 1 tous les 6 cycles, ce qui est demandé.
Si n'importe quel compteur fait l'affaire, il est cependant utile d'utiliser les compteurs les plus adaptés à la tâche. Pour faire un diviseur de fréquence, on utilise rarement un compteur complet, mais souvent des compteurs plus simples, comme un circuit incrémenteur ou des compteurs en anneau.
===Les diviseurs de fréquence basés sur des compteurs en anneau===
Il est rare que l'on doive diviser une fréquence par 50 ou par 100, par exemple. Un diviseur de fréquence divise une fréquence par N, avec N très petit. Or, les compteurs ''one-hot'', aussi appelés compteurs en anneau, sont particulièrement adaptés pour compter jusqu'à des valeurs assez faibles : 6, 10, 12, 25, etc. Il est donc naturel d'utiliser un compteur en anneau dans un diviseur de fréquence.
Pour diviser une fréquence par N, il suffit de prendre un compteur en anneau qui compte de 0 à N-1 (inclut). Le signal de sortie est mis à 1 et/ou inversé quand le compteur est remis à zéro, c'est à dire quand son bit de poids faible est à 1. Une méthode alternative consiste à regarder au contraire le bit de poids fort : le compteur atteint N quand ce bit est à 1, ce qui fait qu'il a compté jusqu'à N. En clair, la sortie est obtenue en regardant la valeur du bit de poids faible/fort, sans même utiliser de comparateur. Pas besoin de comparateur, donc.
Et au-delà de ça, le circuit obtenu est beaucoup plus simple qu’avec un compteur normal. Et c'est la raison pour laquelle les diviseurs de fréquence sont souvent conçus en utilisant des compteurs ''one-hot''. Plus on divise une fréquence par un N très petit, plus les compteurs auront d'avantages : très simples, demandent peu de portes logiques, sont très rapides, prennent peu de place, permettent de se passer de circuit comparateur.
===Les diviseurs de fréquence basés sur des incrémenteurs à bascule T===
Il est aussi possible de concevoir des diviseurs de fréquence en utilisant un banal incrémenteur. Si l'incrémenteur est un incrémenteur non-modulo, on se retrouve avec un diviseur de fréquence qui divise la fréquence d'entrée par une puissance de deux. Pour comprendre comment les fabriquer, nous allons étudier le cas le plus simple : celui qui divise par 2 la fréquence d'entrée. Et bien sachez qu'il s'agit d'une simple bascule T. En effet, regardons ce qui se passe quand on envoie un signal constamment à 1 sur son entrée T. Dans ce cas, la bascule s'inversera une fois par chaque cycle d'horloge. Un cycle d'horloge sur la sortie correspond au temps passé entre deux inversions.
[[File:Frequency divider by 2.svg|centre|vignette|upright=2|Diviseur de fréquence par 2.]]
Pour créer un diviseur de fréquence par 4, il suffit d'enchainer deux fois le circuit précédent. La sortie de la première bascule T doit être envoyée sur l'entrée T de la seconde bascule. Et pour créer un diviseur de fréquence par 8, il suffit d'enchainer trois fois le circuit précédent. Et ainsi de suite. Au final, un diviseur de fréquence qui divise la fréquence d'entrée par 2^N est un enchainement de N bascules T, qui n'est autre qu'un circuit incrémenteur. La sortie d'un tel diviseur de fréquence se situe en sortie de la dernière bascule.
[[File:Freq Divider mod 8.svg|centre|vignette|upright=2|Diviseur de fréquence par 8.]]
On peut en profiter pour créer un circuit à plusieurs sorties, en mettant une sortie par bascule. Le circuit, illustré ci-dessous, fournit donc plusieurs fréquences de sortie : une à la moitié de la fréquence initiale, une autre au quart de la fréquence d'entrée, une autre au huitième, etc.
[[File:Frequency divider animation.gif|centre|vignette|upright=2|Diviseur de fréquence multiple.]]
==Les ''timers''==
Les '''''timers''''', aussi appelés ''Programmable interval timer'', sont des circuits capables de compter des durées. Leur fonctionnement est assez simple : on leur envoie un certain nombre de cycles d'horloge en entrée, et ils émettent un signal quand ce nombre de cycles est écoulé. Le signal en question est disponible sur une sortie de 1 bit, et correspond tout simplement au fait que cette sortie est mise à 1, pendant un cycle d'horloge. Ils permettent de compter des durées, exprimées en cycles d'horloge. On peut aussi générer un signal qui surviendra après 50 cycles d'horloge, ou après 100 cycles d'horloge, etc.
Les ''timers'' sont composés d'un compteur/décompteur cadencé par un signal d'horloge. Le compteur initialisé à 0, puis est incrémenté à chaque signal d'horloge, jusqu’à atteinte d'une valeur limite où il génère un signal. Pour un décompteur, c'est la même chose, sauf que le décompteur est initialisé à sa valeur limite et est décrémenté à chaque cycle, et envoie un signal quand il atteint 0. Les ''timers'' basés sur des décompteurs sont nettement plus simples que les autres, ce qui fait qu'ils sont plus utilisés. Pour que les ''timers'' soient configurables, on doit pouvoir préciser combien de cycles il faut (dé-)compter avant d'émettre un signal. On peut ainsi préciser s'il faut émettre le signal après 32 cycles d'horloge, après les 50 cycles, tous les 129 cycles, etc. Pour cela, il suffit de préciser le nombre de cycles à compter/décompter en entrée et d'initialiser le compteur/décompteur avec.
Les ''timers'' matériels peuvent compter de deux manières différentes, appelées '''mode une fois''' et '''mode périodique'''. Concrètement, le mode périodique divise la fréquence d'entrée, alors que le mode une fois compte durant une durée fixe avant de s'arrêter.
* En mode une fois, le ''timer'' s'arrête une fois qu'il a atteint la limite configurée. On doit le réinitialiser manuellement, par l'intermédiaire du logiciel, pour l'utiliser une nouvelle fois. Cela permet de compter une certaine durée, exprimée en nombre de cycles d'horloge.
* En mode périodique, le ''timer'' se réinitialise automatiquement avec la valeur de départ, ce qui fait qu'il reboucle à l'infini. En clair, le ''timer'' se comporte comme un diviseur de fréquence. Si le compteur est réglé de manière à émettre un signal tous les 9 cycles d'horloge, la fréquence de sortie sera de 9 fois moins celle de la fréquence d'entrée du compteur.
Un ordinateur est rempli de ''timers'' divers. Dans ce qui va suivre, nous allons voir les principaux ''timers'', qui sont actuellement intégrés dans les PC modernes. Ils se trouvent sur la carte mère ou dans le processeur, tout dépend du ''timer''.
===Le ''watchdog timer''===
Le '''''watchdog timer''''' est un ''timer'' spécifique dont le but est d'éteindre ou de redémarrer automatiquement l'ordinateur si jamais celui-ci ne répond plus ou plante. Tous les ordinateurs n'ont pas ce genre de ''timer'', et beaucoup de PC s'en passent. Mais ce ''timer'' est très fréquent dans les architectures embarquées.
Le ''watchdog timer'' est un compteur/décompteur qui doit être réinitialisé régulièrement. S'il n'est pas réinitialisé, le ''watchdog timer'' déborde (revient à 0 ou atteint 0) et envoie un signal qui redémarre le système. Le système est conçu pour réinitialiser le ''watchdog timer'' régulièrement, ce qui signifie que le système n'est pas censé redémarrer. Si jamais le système dysfonctionne gravement, le système ne pourra pas réinitialiser le ''watchdog timer'' et le système est redémarré automatiquement ou mis en arrêt.
[[File:SimpleWatchdogTimer.gif|centre|vignette|upright=2|Le ''Watchdog Timer'' et l'ordinateur.]]
===Le ''Time Stamp Counter'' des processeurs x86===
Tous les processeurs des PC actuels sont des processeurs dits x86. Nous ne pouvons pas expliquer ce que cela signifie pour le moment, retenez juste ce terme. Sachez que tous les processeurs x86 contiennent un compteur de 64 bits, appelé le '''''Time Stamp Counter''''', qui mémorise le nombre de cycles d'horloge qu'a effectué le processeur depuis son démarrage. Les programmes peuvent accéder à ce registre assez facilement, ce qui est utile pour faire des mesures ou comparer les performances de deux applications. Il permet de compter combien de cycles d'horloge met un morceau de code à s’exécuter, combien de cycles prend une instruction à s’exécuter, etc. Les processeurs non-x86 ont un registre équivalent, que ce soit les processeurs ARM ou d'autres.
Malheureusement, ce compteur est tombé en désuétude pour tout un tas de raisons. La principale est que les processeurs actuels ont une fréquence variable. Nous expliquerons cela plus en détail dans quelques chapitres, mais les processeurs actuels font varier leur fréquence suivant les besoins. Ils augmentent leur fréquence quand on leur demande de faire beaucoup de calculs, et se mettent en mode basse(fréquence pour économiser de l'énergie si on ne leur demande pas grand chose. Avec une fréquence variable, le ''Time Stamp Counter'' perd complétement en fiabilité.
Intel a tenté de corriger ce défaut en incrémentant ce registre à une fréquence constante, différente de celle du processeur, ce qui est encore le cas sur les processeurs Intel actuels. Le comportement est un peu différent sur les processeurs AMD, mais il compte par cycle d'horloge, avec des mécanismes de synchronisation assez complexes pour corriger l'effet de la fréquence variable.
===L'horloge temps réel===
L''''horloge temps réel''' est un ''timer'' qui génère une fréquence de 1024 Hz, soit près d'un Kilohertz. Dans ce qui suit, nous la noterons RTC, ce qui est l'acronyme du terme anglais ''Real Time Clock''. La RTC prend en entrée un signal d'horloge de 32KHz, généré par un oscillateur à Quartz, et fournit en sortie un signal de fréquence 32 fois plus faible, c'est à dire de 1 KHz. Pour cela, elle est réglée en mode répétitif et son décompteur interne est initialisé à 32. La RTC génère donc un signal toutes les millisecondes, qui est envoyé au processeur. On peut, en théorie, changer la fréquence de la RTC, mais c'est rarement une bonne idée.
En théorie, la RTC permet de compter des durées assez courtes, comme le ''ping'' (le temps de latence d'un réseau, pour simplifier), le temps de rafraichissement de l'écran, ou bien d'autres choses. Mais dans les faits, les systèmes d'exploitation modernes ne l'utilisent pas pour ça. L'horloge temps réel est trop imprécise et sa fréquence n'aide pas. En effet, 1024 Hz est proche de 1000, mais pas assez pour faire des mesures à la missliseconde près, chose qui est nécessaire pour mesurer le ''ping'' ou d'autres choses utiles.
A la place, l'ordinateur l'utiliser pour compter les secondes, afin que l'ordinateur soit toujours à l'heure. Vous savez déjà que l'ordinateur sait quelle heure il est (vous pouvez regarder le bureau de Windows dans le coin inférieur droite de votre écran pour vous en convaincre) et il peut le faire avec une précision de l'ordre de la seconde. Mais pour savoir quel jour, heure, minute et seconde il est, l'ordinateur doit faire deux choses : mémoriser la date exacte à la seconde près, et avoir la capacité de compter le temps qui s'écoule, seconde par seconde. Pour cela, un ordinateur contient une CMOS RAM qui mémorise la date, et la RTC.
===Le ''Programmable Interval Timer'' : l'Intel 8253===
[[File:Intel 8253 and 8254.svg|vignette|Intel 8253 and 8254]]
L'Intel 8253 est un ''timer'' programmable qui était autrefois intégré dans les cartes mères des ordinateurs personnels de type PC. Les premiers processeurs x86 étaient souvent secondés avec un Intel 8253 soudé à la carte mère. Il fût suivi par l'Intel 8254, qui en était une légère amélioration. S'il n'est plus présent dans un boitier de la carte mère, on trouve toujours un circuit semblable au 8253 à l'intérieur du ''chipset'' de la carte mère, voire à l'intérieur du processeur, pour des raisons de compatibilité.
Sur les PC, il est cadencé par une horloge maitre, générée par un oscillateur à Quartz, dont la fréquence est de 32 768 Hertz, soit 2^15 cycles d'horloge par seconde. La fréquence générée par un compteur va donc de 18,2 Hz à environ 500 KHz. Il était utilisé pour dériver un grand nombre de fréquences utilisées dans l'ordinateur. Par exemple, le second compteur était utilisé par défaut pour le rafraichissement de la mémoire (D)RAM, mais il était souvent reprogrammé pour servir à générer des fréquences spécifiques par le BIOS ou la carte graphique.
L'intérieur de l'Intel 8253 est illustré ci-dessous. Nous allons expliquer l'ensemble de ce schéma, rassurez-vous, mais les explications seront plus simples à comprendre si vous survolez ce schéma en premier lieu.
[[File:Intel 8253 block diagram.svg|centre|vignette|upright=2|Intel 8253, intérieur.]]
L'Intel 8253 contient trois compteurs de 16 bits, numérotés de 0 à 2. Chaque compteur possède deux entrées et une sortie : l'entrée CLOCK est celle de l'horloge de 32 MHz, l'entrée GATE active ou désactive le compteur, la sortie fournit le signal voulu et/ou la fréquence de sortie.
L'Intel 8253 lui-même possède plusieurs entrées et sorties. En premier lieu, on voit un port de 8 bits connecté aux trois compteurs, qui permet à l'Intel 8253 de communiquer avec le reste de l'ordinateur. La communication se fait dans les deux sens : soit de l'ordinateur vers les compteurs, soit des compteurs vers l'ordinateur. Dans le sens ordinateur -> compteurs, cela permet à l'ordinateur de programmer les compteurs, de les initialiser. Dans l'autre sens, cela permet de récupérer le contenu des compteurs, même si ce n'est pas très utilisé.
Ensuite, on trouve un registre de 8 bits, le ''Control Word register'' qui mémorise la configuration de l'Intel 8253. Le contenu de ce registre détermine le mode de fonctionnement du compteur, de combien doit compter le compteur et bien d'autres choses. Pour programmer les trois compteurs, il faut écrire un mot de 8 bits dans le ''Control Word register''. La configuration de l'Intel 8253 fournie en sur le port de 8 bits pendant un cycle d'horloge, puis est mémorisée dans ce registre et reste pour les cycles suivants.
Mais l'écriture a lieu à condition que les 5 entrées de configuration soit bien réglées. Les 5 entrées de configuration sont les suivantes :
* Deux bits A0 et A1 pour sélectionner le compteur voulu avec son numéro, ou le ''control word register''.
* Un bit RD à mettre à 0 pour que l'ordinateur récupère le compteur sélectionné ou le ''control word register'' sur le port de 8 bits.
* Un bit WR à mettre à 0 pour que l'ordinateur modifie le compteur sélectionné ou le ''control word register'', en envoyant le nombre pour l'initialisation sur le port de 8 bits.
* Un bit CS qui active ou désactive l'Intel 8253 et permet de l'allumer ou de l’éteindre.
Pour écrire dans le ''Control Word register'', il faut mettre le bit CS à 0 (on active l'Intel 8253), mettre à 1 le bit RD et à 0 le bit WR (on indique qu'on fait une écriture), et sélectionner le ''Control Word register'' en mettant les deux bits A0 et A1 à 1. Pour écrire dans un compteur, il faut faire la même chose, sauf que les bits A0 et A1 doivent être configurés de manière à donner le numéro du compteur voulu. LA lecture s'effectue elle aussi de la même manière, mais il faut inverser les bits RD et WR.
===Le ''High Precision Event Timer'' (HPET)===
De nos jours, l'horloge temps réel et l'Intel 8253/8254 tendent à être remplacé par un autre ''timer'', le ''High Precision Event Timer'' (HPET). Il s'agit d'un compteur de 64 bits, dont la fréquence est d'au moins 10 MHz. Il s'agit bien d'un compteur et non d'un décompteur. Il est couplé à plusieurs comparateurs, qui vérifient chacun une valeur limite, une valeur à laquelle générer un signal. La valeur limite peut être programmée, ce qui fait que chaque comparateur est associé à un registre pour mémoriser la valeur limite. Il doit y avoir au moins trois comparateurs, mais le nombre peut monter jusqu’à 256. Chaque comparateur doit pouvoir fonctionner en mode une fois, et au moins un comparateur doit pouvoir fonctionner en mode périodique.
[[File:High Precision Event Timer.png|centre|vignette|upright=2|High Precision Event Timer]]
Il faut noter que les systèmes d'exploitation conçus avant le HPET ne peuvent pas l'utiliser, pour des raisons techniques de compatibilité matérielle. C'est le cas de Windows XP avant le Service Pack 3. C'est la raison pour laquelle les cartes mères possèdent encore un PIT et une RTC, ou au moins qu'elles émulent RTC et PIT dans leurs circuits. D'ailleurs, pour économiser des circuits, les cartes mères modernes émulent le PIT et la RTC avec le HPET. Le HPET est configuré de manière à ce que le premier comparateur fournisse une fréquence de 1024 Hz, comme la RTC, et les 3 comparateurs suivants remplacent l'Intel 8253.
Le HPET gère de nombreux modes de fonctionnement : ses comparateurs peuvent être configuré en mode une fois ou périodique, on peut lui demander d'émuler la RTC et le PIT, etc. Aussi, il contient aussi de nombreux registres de configuration. En tout, on trouve 3 registres de configuration. à Cela, il faut ajouter trois registres pour configurer chaque comparateur indépendamment les uns des autres. Notons qu'il est aussi possible de lire ou écrire dans le compteur de 64 bits, mais ce n'est pas recommandé.
==La génération de nombres pseudo-aléatoires==
Les compteurs peuvent aussi être utilisés pour générer des nombres "aléatoires". Je dis aléatoires entre guillemets car ils ne sont pas vraiment aléatoires, mais s'en rapprochent suffisamment pour être considérés comme tels. Pour mettre en avant cela, on parle aussi de nombres "pseudo-aléatoires". De nombreuses situations demandent de générer des nombres pseudo-aléatoires. C'est très utile dans des applications cryptographiques, statistiques, mathématiques, dans les jeux vidéos, et j'en passe. L'aléatoire dans les jeux vidéos est un bon exemple : pas besoin d'un aléatoire de qualité, un simple algorithme pseudo-aléatoire suffit.
Dans certaines situations, il est nécessaire de générer des nombres aléatoires de manière matérielle. Cela peut servir pour sélectionner une ligne de cache à remplacer lors d'un défaut de cache, pour implémenter des circuits cryptographiques, pour calculer la durée d'émission sur un bus Ethernet à la suite d'une collision, et j'en passe.
Les méthodes que nous allons voir produisent un nombre pseudo-aléatoire un bit à la fois, à quelques exceptions près. Les circuits que nous allons voir fournissent un bit sur leur sortie et ce bit varie de manière assez aléatoire. Les bits en sortie du circuit sont accumulés dans un registre à décalage normal, pour former un nombre aléatoire. Nous appellerons ce registre : l'accumulateur.
===L'usage de registres à décalage à rétroaction===
[[File:Nonlinear-combo-generator.png|thumb|Nonlinear-combo-generator]]
La première solution utilise des ''registres à décalages à rétroaction'', aussi appelés ''Feedback Shift Registers'', abréviés LSFR. Un LSFR seul ne fournit pas un aléatoire digne de ce nom, car il boucle, il a une période comme tout compteur. Par contre, il est possible de combiner plusieurs LSFR pour obtenir une meilleure approximation de l'aléatoire. Avec cette technique, plusieurs registres à décalages à rétroaction sont reliés à un circuit combinatoire non-linéaire. Ce circuit prendra en entrée un (ou plusieurs) bit de chaque registre à décalage à rétroaction, et combinera ces bits pour fournir un bit de sortie.
[[File:A5-1 GSM cipher.svg|centre|thumb|upright=2|Exemple avec trois LSFR différents, de taille différentes : le bit envoyé à l'accumulateur est un XOR du bit sortant des trois LSFR.]]
Pour rendre le tout encore plus aléatoire, il est possible de cadencer les LSFR à des fréquences différentes. Cette technique est utilisée dans les générateurs ''stop-and-go'', ''alternative step'', et à ''shrinking''.
* Dans le générateur ''alternative step'', on utilise trois LSFR. Le premier commande un multiplexeur qui choisit la sortie parmi les deux restants.
* Dans le générateur ''stop-and-go'', on utilise deux LSFR. Le premier est relié à l'entrée d'horloge du second et le bit de sortie du second est utilisé comme résultat. Une technique similaire était utilisée dans les processeurs VIA C3, pour l'implémentation de leurs instructions cryptographiques.
* Dans le ''shrinking generator'', deux LSFR sont cadencés à des vitesses différentes. Si le bit de sortie du premier vaut 1, alors le bit de sortie du second est utilisé comme résultat. Par contre, si le bit de sortie du premier vaut 0, aucun bit n'est fourni en sortie, le bit de sortie du second registre est oublié.
===L'aléatoire généré par des ''timers'' ou des compteurs d'horloge===
Les LSFR ne permettent pas d'obtenir du vrai aléatoire, compte tenu de leur comportement totalement déterministe. Pour obtenir un aléatoire un peu plus crédible, il est possible d'utiliser des moyens non-déterministes. Et certains d'entre eux utilisent le signal d'horloge.
Par exemple, une technique très simple utilise un simple ''timer''. Si on a besoin d'un nombre pseudo-aléatoire, il suffit de lire le ''timer'' et d'utiliser le nombre lu comme nombre pseudo-aléatoire. Si le délai entre deux demandes est irrégulier, le résultat semblera aléatoire. Mais il s'agit là d'une technique assez peu fiable dans le monde réel et seules quelques applications bien spécifiques se satisfont de cette méthode.
Une solution un peu plus fiable utilise ce qu'on appelle la '''dérive de l'horloge'''. Il faut savoir qu'un signal d'horloge n'est jamais vraiment très précis. Une horloge censée tourner à 1 Ghz ne tournera pas en permanence à 1Ghz exactement, mais verra sa fréquence varier de quelques Hz ou Khz de manière irrégulière. Ces variations peuvent venir de variations aléatoires de température, des variations de tension, des perturbations électromagnétiques, ou à des phénomènes assez compliqués qui peuvent se produire dans tout circuit électrique (comme le ''shot noise'').
L'idée la plus simple utilise deux horloges : une horloge lente et une horloge rapide, dont la fréquence est un multiple de l'autre. Par exemple, on peut choisir une fréquence de 1 Mhz et une autre de 100 Hz : la fréquence la plus grande est égale à 10000 fois l'autre. La dérive d'horloge fera son œuvre, les deux horloges seront très légèrement désynchronisées en permanence, et cette désynchronisation peut être utilisée pour produire des nombres aléatoires. Par exemple, on peut compter le nombre de cycles d'horloge produit par l'horloge rapide durant une période de l'horloge lente. Si ce nombre est pair, on produit un bit aléatoire qui vaut 1 , il vaut 0 si ce nombre est pair. Pour information, c'est exactement cette technique qui était utilisée dans l'''Intel 82802 Firmware Hub''.
===L'aléatoire généré par la tension d'alimentation===
Il existe d'autres solutions matérielles qui utilisent le bruit thermique. Tous les circuits électroniques de l'univers sont soumis à de microscopiques variations de température, dues à l'agitation thermique des atomes. Plus la température est élevée, plus les atomes qui composent les fils métalliques des circuits s'agitent. Vu que les particules d'un métal contiennent des charges électriques, ces vibrations font naître des variations de tensions assez infimes. Il suffit d'amplifier ces variations pour obtenir un résultat capable de représenter un zéro ou un 1. Ce principe a été utilisé sur des anciens processeurs Intel qui géraient l'instruction RDRAND, une instruction qui produisait un nombre aléatoire.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits compteurs et décompteurs
| prevText=Les circuits compteurs et décompteurs
| next=Les circuits de décalage et de rotation
| nextText=Les circuits de décalage et de rotation
}}
</noinclude>
kifykzgmlzdtzjl71wpdmejzp15s9is
Les cartes graphiques/Le support matériel du lancer de rayons
0
80578
744771
744449
2025-06-15T01:24:13Z
Mewtow
31375
/* Un historique rapide des cartes graphiques dédiées au lancer de rayon */
744771
wikitext
text/x-wiki
Les cartes graphiques actuelles utilisent la technique de la rastérisation, qui a été décrite en détail dans le chapitre sur les cartes accélératrices 3D. Mais nous avions dit qu'il existe une seconde technique générale pour le rendu 3D, totalement opposée à la rastérisation, appelée le '''lancer de rayons'''. Cette technique a cependant été peu utilisée dans les jeux vidéo, jusqu'à récemment. La raison est que le lancer de rayons demande beaucoup de puissance de calcul, sans compter que créer des cartes accélératrices pour le lancer de rayons n'est pas simple.
Mais les choses commencent à changer. Quelques jeux vidéos récents intègrent des techniques de lancer rayons, pour compléter un rendu effectué principalement en rastérisation. De plus, les cartes graphiques modernes incorporent quelques circuits pour accélérer le lancer de rayons, même s'ils restent marginaux des compléments au rendu par rastérisation. S'il a existé des cartes accélératrices totalement dédiées au rendu en lancer de rayons, elles sont restées confidentielles. Aussi, nous allons nous concentrer sur les cartes graphiques récentes, et allons peu parler des cartes accélératrices dédiées au lancer de rayons.
==Le lancer de rayons==
Le lancer de rayons et la rastérisation. commencent par générer la géométrie de la scène 3D, en plaçant les objets dans la scène 3D, et en effectuant l'étape de transformation des modèles 3D, et les étapes de transformation suivante. Mais les ressemblances s'arrêtent là. Le lancer de rayons effectue l'étape d'éclairage différemment, sans compter qu'il n'a pas besoin de rastérisation.
===Le ''ray-casting'' : des rayons tirés depuis la caméra===
La forme la plus simple de lancer de rayon s'appelle le '''''ray-casting'''''. Elle émet des lignes droites, des '''rayons''' qui partent de la caméra et qui passent chacun par un pixel de l'écran. Les rayons font alors intersecter les différents objets présents dans la scène 3D, en un point d'intersection. Le moteur du jeu détermine alors quel est le point d'intersection le plus proche, ou plus précisément, le sommet le plus proche de ce point d'intersection. Ce sommet est associé à une coordonnée de textures, ce qui permet d'associer directement un texel au pixel associé au rayon.
[[File:Raytrace trace diagram.png|centre|vignette|upright=2|Raycasting, rayon simple.]]
En somme, l'étape de lancer de rayon et le calcul des intersections remplacent l'étape de rasterisation, mais les étapes de traitement de la géométrie et des textures existent encore. Après tout, il faut bien placer les objets dans la scène 3D/2D, faire diverses transformations, les éclairer. On peut gérer la transparence des textures assez simplement, si on connait la transparence sur chaque point d'intersection d'un rayon, information présente dans les textures.
[[File:Anarch short gameplay.gif|vignette|Exemple de rendu en ray-casting 2D dans un jeu vidéo.]]
En soi, cet algorithme est simple, mais il a déjà été utilisé dans pas mal de jeux vidéos. Mais sous une forme simple, en deux dimensions ! Les premiers jeux IdSoftware, dont Wolfenstein 3D et Catacomb, utilisaient cette méthode de rendu, mais dans un univers en deux dimensions. Cet article explique bien cela : [[Les moteurs de rendu des FPS en 2.5 D\Le moteur de Wolfenstein 3D|Le moteur de rendu de Wolfenstein 3D]]. Au passage, si vous faites des recherches sur le ''raycasting'', vous verrez que le terme est souvent utilisé pour désigner la méthode de rendu de ces vieux FPS, alors que ce n'en est qu'un cas particulier.
[[File:Simple raycasting with fisheye correction.gif|centre|vignette|upright=2|Simple raycasting with fisheye correction]]
===Le ''raytracing'' proprement dit===
Le lancer de rayon proprement dit est une forme améliorée de ''raycasting'' dans la gestion de l'éclairage et des ombres est modifiée. Le lancer de rayon calcule les ombres assez simplement, sans recourir à des algorithmes compliqués. L'idée est qu'un point d'intersection est dans l'ombre si un objet se trouve entre lui et une source de lumière. Pour déterminer cela, il suffit de tirer un trait entre les deux et de vérifier s'il y a un obstacle/objet sur le trajet. Si c'est le cas, le point d'intersection n'est pas éclairé par la source de lumière et est donc dans l'ombre. Si ce n'est pas le cas, il est éclairé avec un algorithme d'éclairage.
Le trait tiré entre la source de lumière et le point d'intersection est en soi facile : c'est rayon, identique aux rayons envoyés depuis la caméra. La différence est que ces rayons servent à calculer les ombres, ils sont utilisés pour une raison différente. Il faut donc faire la différence entre les '''rayons primaires''' qui partent de la caméra et passent par un pixel de l'écran, et les '''rayon d'ombrage''' qui servent pour le calcul des ombres.
[[File:Ray trace diagram.svg|centre|vignette|upright=2|Principe du lancer de rayons.]]
Les calculs d'éclairage utilisés pour éclairer/ombrer les points d'intersection vous sont déjà connus : la luminosité est calculée à partir de l'algorithme d'éclairage de Phong, vu dans le chapitre "L'éclairage d'une scène 3D : shaders et T&L". Pour cela, il faut juste récupérer la normale du sommet associé au point d'intersection et l'intensité de la source de lumière, et de calculer les informations manquantes (l'angle normale-rayons de lumière, autres). Il détermine alors la couleur de chaque point d'intersection à partir de tout un tas d'informations.
===Le ''raytracing'' récursif===
[[File:Glasses 800 edit.png|vignette|Image rendue avec le lancer de rayons récursif.]]
La technique de lancer de rayons précédente ne gère pas les réflexions, les reflets, des miroirs, les effets de spécularité, et quelques autres effets graphiques de ce style. Pourtant, ils peuvent être implémentés facilement en modifiant le ''raycasting'' d'une manière très simple.
Il suffit de relancer des rayons à partir du point d'intersection. La direction de ces '''rayons secondaires''' est calculée en utilisant les lois de la réfraction/réflexion vues en physique. De plus, les rayons secondaires peuvent eux-aussi créer des rayons secondaires quand ils sont reflétés/réfractés, etc. La technique est alors appelée du ''lancer de rayons récursif'', qui est souvent simplement appelée "lancer de rayons".
[[File:Recursive raytracing.svg|centre|vignette|upright=2|Lancer de rayon récursif.]]
==Les optimisations du lancer de rayons==
Les calculs d'intersections sont très gourmands en puissance de calcul. Sans optimisation, on doit tester l'intersection de chaque rayon avec chaque triangle. Mais diverses optimisations permettent d'économiser des calculs. Elles consistent à regrouper plusieurs triangles ensemble pour rejeter des paquets de triangles en une fois. Pour cela, la carte graphique utilise des '''structures d'accélération''', qui mémorisent les regroupements de triangles, et parfois les triangles eux-mêmes.
===Les volumes englobants===
[[File:BoundingBox.jpg|vignette|Objet englobant : la statue est englobée dans un pavé.]]
L'idée est d'englober chaque objet par un pavé appelé un ''volume englobant''. Le tout est illustré ci-contre, avec une statue représentée en 3D. La statue est un objet très complexe, contenant plusieurs centaines ou milliers de triangles, ce qui fait que tester l'intersection d'un rayon avec chaque triangle serait très long. Par contre, on peut tester si le rayon intersecte le volume englobant facilement : il suffit de tester les 6 faces du pavé, soit 12 triangles, pas plus. S'il n'y a pas d'intersection, alors on économise plusieurs centaines ou milliers de tests d'intersection. Par contre, s'il y a intersection, on doit vérifier chaque triangle. Vu que les rayons intersectent souvent peu d'objets, le gain est énorme !
L'usage seul de volumes englobant est une optimisation très performante. Au lieu de tester l'intersection avec chaque triangle, on teste l'intersection avec chaque objet, puis l'intersection avec chaque triangle quand on intersecte chaque volume englobant. On divise le nombre de tests par un facteur quasi-constant, mais de très grande valeur. Il faut noter que les calculs d'intersection sont légèrement différents entre un triangle et un volume englobant. Il faut dire que les volumes englobant sont généralement des pavées, ils utilisent des rectangles, etc. Les différences sont cependant minimales.
Mais on peut faire encore mieux. L'idée est de regrouper plusieurs volumes englobants en un seul. Si une dizaine d'objets sont proches, leurs volumes englobants seront proches. Il est alors utile d'englober leurs volumes englobants dans un super-volume englobant. L'idée est que l'on teste d'abord le super-volume englobant, au lieu de tester la dizaine de volumes englobants de base. S'il n'y a pas d'intersection, alors on a économisé une dizaine de tests. Mais si intersection, il y a, alors on doit vérifier chaque sous-volume englobant de base, jusqu'à tomber sur une intersection. Vu que les intersections sont rares, on y gagne plus qu'on y perd.
Et on peut faire la même chose avec les super-volumes englobants, en les englobant dans des volumes englobants encore plus grands, et ainsi de suite, récursivement. On obtient alors une '''hiérarchie de volumes englobants''', qui part d'un volume englobant qui contient toute la géométrie, hors skybox, qui lui-même regroupe plusieurs volumes englobants, qui eux-mêmes...
[[File:Example of bounding volume hierarchy.svg|centre|vignette|upright=2|Hiérarchie de volumes englobants.]]
Le nombre de tests d'intersection est alors grandement réduit. On passe d'un nombre de tests proportionnel aux nombres d'objets à un nombre proportionnel à son logarithme. Plus la scène contient d'objets, plus l'économie est importante. La seule difficulté est de générer la hiérarchie de volumes englobants à partir d'une scène 3D. Divers algorithmes assez rapides existent pour cela, ils créent des volumes englobants différents. Les volumes englobants les plus utilisés dans les cartes 3D sont les '''''axis-aligned bounding boxes (AABB)'''''.
La hiérarchie est mémorisée en mémoire RAM, dans une structure de données que les programmeurs connaissent sous le nom d'arbre, et précisément un arbre binaire ou du moins d'un arbre similaire (''k-tree''). Traverser cet arbre pour passer d'un objet englobant à un autre plus petit est très simple, mais a un défaut : on saute d'on objet à un autre en mémoire, les deux sont souvent éloignés en mémoire. La traversée de la mémoire est erratique, sautant d'un point à un autre. On n'est donc dans un cas où les caches fonctionnent mal, ou les techniques de préchargement échouent, où la mémoire devient un point bloquant car trop lente.
===La cohérence des rayons===
Les rayons primaires, émis depuis la caméra, sont proches les uns des autres, et vont tous dans le même sens. Mais ce n'est pas le cas pour des rayons secondaires. Les objets d'une scène 3D ont rarement des surfaces planes, mais sont composés d'un tas de triangles qui font un angle entre eux. La conséquence est que deux rayons secondaires émis depuis deux triangles voisins peuvent aller dans des directions très différentes. Ils vont donc intersecter des objets très différents, atterrir sur des sources de lumières différentes, etc. De tels rayons sont dits '''incohérents''', en opposition aux rayons cohérents qui sont des rayons proches qui vont dans la même direction.
Les rayons incohérents sont peu fréquents avec le ''rayctracing'' récursif basique, mais deviennent plus courants avec des techniques avancées comme le ''path tracing'' ou les techniques d'illumination globale. En soi, la présende de rayons incohérents n'est pas un problème et est parfaitement normale. Le problème est que le traitement des rayons incohérent est plus lent que pour les rayons cohérents. Le traitement de deux rayons secondaires voisins demande d'accéder à des données différentes, ce qui donne des accès mémoire très différents et très éloignés. On ne peut pas profiter des mémoires caches ou des optimisations de la hiérarchie mémoire, si on les traite consécutivement. Un autre défaut est qu'une même instance de ''pixels shaders'' va traiter plusieurs rayons en même temps, grâce au SIMD. Mais les branchements n'auront pas les mêmes résultats d'un rayon à l'autre, et cette divergence entrainera des opérations de masquage et de branchement très couteuses.
Pour éviter cela, les GPU modernes permettent de trier les rayons en fonction de leur direction et de leur origine. Ils traitent les rayons non dans l'ordre usuel, mais essayent de regrouper des rayons proches qui vont dans la même direction, et de les traiter ensemble, en parallèle, ou l'un après l'autre. Cela ne change rien au rendu final, qui traite les rayons en parallèle. Ainsi, les données chargées dans le cache par le premier rayon seront celles utilisées pour les rayons suivants, ce qui donne un gain de performance appréciable. De plus, cela évite d'utiliser des branchements dans les ''pixels shaders''. Les méthodes de '''tri de rayons''' sont nombreuses, aussi en faire une liste exhaustive serait assez long, sans compter qu'on ne sait pas quelles sont celles implémentées en hardware, ni commetn elles le sont.
===Les avantages et désavantages comparé à la rastérisation===
[[File:ViewFrustum.svg|vignette|upright=1|Volume délimité par la caméra (''view frustum'').]]
L'avantage principal du lancer de rayons est la détermination des surfaces visibles. La rastérisation a tendance à calculer inutilement des portions non-rendues de la scène 3D, malgré l'usage de techniques de ''culling'' ou de ''clipping'' aussi puissantes qu'imparfaites. De nombreux fragments/pixels sont éliminés à la toute fin du pipeline, grâce au z-buffer, après avoir été calculés et texturés. Le lancer de rayons ne calcule pas les portions invisibles de l'image par construction : pas besoin de ''culling'', de ''clipping'', ni même de z-buffer. D'ailleurs, l'absence de z-buffer réduit grandement le nombre d'accès mémoire.
Les réflexions et la réfraction sont gérées naturellement par le lancer de rayon récursif, alors qu'elles demandent des ruses de sioux pour obtenir un résultat correct avec la rastérisation. L'éclairage et les ombres se rendent difficilement avec la rastérisation, cela demande de précalculer des textures comme des ''shadowmaps'' ou des ''lightmaps''. Avec le lancer de rayon, pas besoin de précalculer ces textures, tout est calculé avec le lancer de rayons, ce qui réduit les accès aux textures. Mieux : les processeurs de shaders n'ont plus besoin d'écrire dans des textures. Grâce à cela, les caches de texture sont en lecture seule, leurs circuits sont donc assez simples et performants, les problèmes de cohérence des caches disparaissent.
Mais tous ces avantages sont compensés par le fait que le lancer de rayons est plus lent sur un paquet d'autres points. Déjà, les calculs d'intersection sont très lourds, ils demandent beaucoup de calculs et d'opérations arithmétiques, plus que pour l'équivalent en rastérisation. Ensuite, le lancer de rayon n'est pas économe niveau accès mémoire. Ce qu'on économise avec l’absence de tampon de profondeur et l'absence de textures d'éclairage précalculées, on le perd au niveau de la BVH et des accès aux textures.
Par contre, la BVH est un énorme problème. Les BVH étant ce que les programmeurs connaissent sous le nom d'arbre binaire, elles dispersent les données en mémoire. Et les GPU et CPU modernes préfèrent des données consécutives en RAM. Leur hiérarchie mémoire est beaucoup plus efficace pour accéder à des données proches en mémoire qu'à des données dispersées et il n'y a pas grand chose à faire pour changer la donne. La traversée d'une BVH se fait avec des accès en mémoire vidéo complétement désorganisés, là où le rendu 3D a des accès plus linéaires qui permettent d'utiliser des mémoires caches.
==Le matériel pour accélérer le lancer de rayons==
En théorie, il est possible d'utiliser des ''shaders'' pour effectuer du lancer de rayons, mais la technique n'est pas très performante. Il vaut mieux utiliser du hardware dédié. Heureusement, cela ne demande pas beaucoup de changements à un GPU usuel.
Le lancer de rayons ne se différencie de la rastérisation que sur deux points dont le plus important est : l'étape de rastérisation est remplacée par une étape de lancer de rayons. Les deux types de rendu ont besoin de calculer la géométrie, d'appliquer des textures et de faire des calculs d'éclairage. Sachant cela, les GPU actuels ont juste eu besoin d'ajouter quelques circuits dédiés pour gérer le lancer de rayons, à savoir des unités de génération des rayons et des unités pour les calculs d'intersection.
===Les circuits spécialisés pour les calculs liés aux rayons===
Toute la subtilité du lancer de rayons est de générer les rayons et de déterminer quels triangles ils intersectent. La seule difficulté est de gérer la transparence, mais aussi et surtout la traversée de la BVH. En soit, générer les rayons et déterminer leurs intersections n'est pas compliqué. Il existe des algorithmes basés sur du calcul vectoriel pour déterminer si intersection il y a et quelles sont ses coordonnées. C'est toute la partie de parcours de la BVH qui est plus compliquée à implémenter : faire du ''pointer chasing'' en hardware n'est pas facile.
Et cela se ressent quand on étudie comment les GPU récents gèrent le lancer de rayons. Ils utilisent des shaders dédiés, qui communiquent avec une '''unité de lancer de rayon''' dédiée à la traversée de la BVH. Le terme anglais est ''Ray-tracing Unit'', ce qui fait que nous utiliseront l'abréviation RTU pour la désigner. Les shaders spécialisés sont des '''shaders de lancer de rayon''' et il y en a deux types.
* Les '''''shaders'' de génération de rayon''' s'occupent de générer les rayons
* Les '''''shaders'' de ''Hit/miss''''' s'occupent de faire tous les calculs une fois qu'une intersection est détectée.
Le processus de rendu en lancer de rayon sur ces GPU est le suivant. Les shaders de génération de rayon génèrent les rayons lancés, qu'ils envoient à la RTU. La RTU parcours la BVH et effectue les calculs d'intersection. Si la RTU détecte une intersection, elle lance l'exécution d'un ''shader'' de ''Hit/miss'' sur les processeurs de ''shaders''. Ce dernier finit le travail, mais il peut aussi commander la génération de rayons secondaires ou d'ombrage. Suivant la nature de la surface (opaque, transparente, réfléchissante, mate, autre), ils décident s'il faut ou non émettre un rayon secondaire, et commandent les shaders de génération de rayon si besoin.
[[File:Implementation hardware du raytracing.png|centre|vignette|upright=2|Implémentation hardware du raytracing]]
===La RTU : la traversée des structures d'accélérations et des BVH===
Lorsqu'un processeur de shader fait appel à la RTU, il lui envoie un rayon encodé d'une manière ou d'une autre, potentiellement différente d'un GPU à l'autre. Toujours est-il que les informations sur ce rayon sont mémorisées dans des registres à l'intérieur de la RTU. Ce n'est que quand le rayon quitte la RTU que ces registres sont réinitialisés pour laisser la place à un autre rayon. Il y a donc un ou plusieurs '''registres de rayon''' intégrés à la RTU.
La RTU contient beaucoup d''''unités de calculs d'intersections''', des circuits de calcul qui font les calculs d'intersection. Il est possible de tester un grand nombre d'intersections de triangles en parallèles, chacun dans une unité de calcul séparée. L'algorithme de lancer de rayons se parallélise donc très bien et la RTU en profite. En soi, les circuits de détection des intersections sont très simples et se résument à un paquet de circuits de calcul (addition, multiplications, division, autres), connectés les uns aux autres. Il y a assez peu à dire dessus. Mais les autres circuits sont très intéressants à étudier.
Il y a deux types d'intersections à calculer : les intersections avec les volumes englobants, les intersections avec les triangles. Volumes englobants et triangles ne sont pas encodés de la même manière en mémoire vidéo, ce qui fait que les calculs à faire ne sont pas exactement les mêmes. Et c'est normal : il y a une différence entre un pavé pour le volume englobant et trois sommets/vecteurs pour un triangle. La RTU des GPU Intel, et vraisemblablement celle des autres GPU, utilise des circuits de calcul différents pour les deux. Elle incorpore plus de circuits pour les intersections avec les volumes englobants, que de circuits pour les intersections avec un triangle. Il faut dire que lors de la traversée d'une BVH, il y a une intersection avec un triangle par rayon, mais plusieurs pour les volumes englobants.
L'intérieur de la RTU contient aussi de quoi séquencer les accès mémoire nécessaires pour parcourir la BVH. Traverser un BVH demande de tester l'intersection avec un volume englobant, puis de décider s'il faut passer au suivant, et rebelotte. Une fois que la RTU tombe sur un triangle, elle l'envoie aux unités de calcul d'intersection dédiées aux triangles.
Les RTU intègrent des '''caches de BVH''', qui mémorisent des portions de la BVH au cas où celles-ci seraient retraversées plusieurs fois de suite par des rayons consécutifs. La taille de ce cache est de l'ordre du kilo-octet ou plus. Pour donner des exemples, les GPU Intel d'architecture Battlemage ont un cache de BVH de 16 Kilo-octets, soit le double comparé aux GPU antérieurs. Le cache de BVH est très fortement lié à la RTU et n'est pas accesible par l'unité de texture, les processeurs de ''shader'' ou autres.
[[File:Raytracing Unit.png|centre|vignette|upright=2|Raytracing Unit]]
Pour diminuer l’impact sur les performances, les cartes graphiques modernes incorporent des circuits de tri pour regrouper les rayons cohérents, ceux qui vont dans la même direction et proviennent de triangles proches. Ce afin d'implémenter les optimisations vues plus haut.
===La génération des structures d'accélération et BVH===
La plupart des cartes graphiques ne peuvent pas générer les BVH d'elles-mêmes. Pareil pour les autres structures d'accélération. Elles sont calculées par le processeur, stockées en mémoire RAM, puis copiées dans la mémoire vidéo avant le démarrage du rendu 3D. Cependant, quelques rares cartes spécialement dédiées au lancer de rayons incorporent des circuits pour générer les BVH/AS. Elles lisent le tampon de sommet envoyé par le processeur, puis génèrent les BVH complémentaires. L'unité de génération des structures d'accélération est complétement séparée des autres unités. Un exemple d'architecture de ce type est l'architecture Raycore, dont nous parlerons dans les sections suivantes.
Cette unité peut aussi modifier les BVH à la volée, si jamais la scène 3D change. Par exemple, si un objet change de place, comme un NPC qui se déplace, il faut reconstruire le BVH. Les cartes graphiques récentes évitent de reconstruire le BVH de zéro, mais se contentent de modifier ce qui a changé dans le BVH. Les performances en sont nettement meilleures.
==Un historique rapide des cartes graphiques dédiées au lancer de rayon==
Les premières cartes accélératrices de lancer de rayons sont assez anciennes et datent des années 80-90. Leur évolution a plus ou moins suivi la même évolution que celle des cartes graphiques usuelles, sauf que très peu de cartes pour le lancer de rayons ont été produites. Par même évolution, on veut dire que les cartes graphiques pour le lancer de rayon ont commencé par tester deux solutions évidentes et extrêmes : les cartes basées sur des circuits programmables d'un côté, non programmables de l'autre.
La première carte pour le lancer de rayons était la TigerShark, et elle était tout simplement composée de plusieurs processeurs et de la mémoire sur une carte PCI. Elle ne faisait qu'accélérer le calcul des intersections, rien de plus.
Les autres cartes effectuaient du rendu 3D par voxel, un type de rendu 3D assez spécial que nous n'avons pas abordé jusqu'à présent, dont l'algorithme de lancer de rayons n'était qu'une petite partie du rendu. Elles étaient destinées au marché scientifique, industriel et médical. On peut notamment citer la VolumePro et la VIZARD et II. Les deux étaient des cartes pour bus PCI qui ne faisaient que du ''raycasting'' et n'utilisaient pas de rayons d'ombrage. Le ''raycasting'' était exécuté sur un processeur dédié sur la VIZARD II, sur un circuit fixe implémenté par un FPGA sur la Volume Pro Voici différents papiers académiques qui décrivent l'architecture de ces cartes accélératrices :
* [https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=ccda3712ba0e6575cd08025f3bb920de46201cac The VolumePro Real-Time Ray-Casting System].
* [https://www.researchgate.net/publication/234829060_VIZARD_II_A_reconfigurable_interactive_volume_rendering_system VIZARD II: A reconfigurable interactive volume rendering system]
La puce SaarCOR (Saarbrücken's Coherence Optimized Ray Tracer) et bien plus tard par la carte Raycore, étaient deux cartes réelement dédiées au raycasting pur, sans usage de voxels, et étaient basées sur des FPGA. Elles contenaient uniquement des circuits pour accélérer le lancer de rayon proprement dit, à savoir la génération des rayons, les calculs d'intersection, pas plus. Tout le reste, à savoir le rendu de la géométrie, le placage de textures, les ''shaders'' et autres, étaient effectués par le processeur. Voici des papiers académiques sur leur architecture :
* [https://gamma.cs.unc.edu/SATO/Raycore/raycore.pdf RayCore: A ray-tracing hardware architecture for mobile devices].
Le successeur de la SaarCOR, le Ray Processing Unit (RPU), était une carte hybride : c'était une SaarCOR basée sur des circuits fixes, qui gagna quelques possibilités de programmation. Elle ajoutait des processeurs de ''shaders'' aux circuits fixes spécialisés pour le lancer de rayons. Les autres cartes du genre faisaient pareil, et on peut citer les cartes ART, les cartes CausticOne, Caustic Professional's R2500 et R2100.
Les cartes 3D modernes permettent de faire à la fois de la rasterisation et du lancer de rayons. Pour cela, les GPU récents incorporent des unités pour effectuer des calculs d'intersection, qui sont réalisés dans des circuits spécialisés. Elles sont appelées des RT Cores sur les cartes NVIDIA, mais les cartes AMD et Intel ont leur équivalent, idem chez leurs concurrents de chez Imagination technologies, ainsi que sur certains GPU destinés au marché mobile. Peu de choses sont certaines sur ces unités, mais il semblerait qu'il s'agisse d'unités de textures modifiées.
De ce qu'on en sait, certains GPU utilisent des unités pour calculer les intersections avec les triangles, et d'autres unités séparées pour calculer les intersections avec les volumes englobants. La raison est que, comme dit plus haut, les algorithmes de calcul d'intersection sont différents dans les deux cas. Les algorithmes utilisés ne sont pas connus pour toutes les cartes graphiques. On sait que les GPU Wizard de Imagination technology utilisaient des tests AABB de Plücker. Ils utilisaient 15 circuits de multiplication, 9 additionneurs-soustracteurs, quelques circuits pour tester la présence dans un intervalle, des circuits de normalisation et arrondi. L'algorithme pour leurs GPU d'architecture Photon est disponible ici, pour les curieux : [https://www.highperformancegraphics.org/slides23/2023-06-_HPG_IMG_RayTracing_2.pdf].
{{NavChapitre | book=Les cartes graphiques
| prev=Les Render Output Target
| prevText=Les Render Output Target
| next=L'antialiasing
| prevNext=L'antialiasing
}}{{autocat}}
ln23vwwas93jg47cynt12p2izhtbayr
Mathc initiation/001D
0
82435
744727
2025-06-14T13:58:04Z
Xhungab
23827
news
744727
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/a81| Sommaire]]
<syntaxhighlight lang="C">
********************
log_3(81) = 4 (3*3*3*3 = 81)
4 est l'exposant qu'il faut donner à la base 3 pour obtenir 81 (3**4 = 81)
4 est le logarithme en base 3 de 81
********************
log_sqrt(3) (9) = 4 (sqrt(3)*sqrt(3)*sqrt(3)*sqrt(3) = 9)
4 est l'exposant qu'il faut donner à la base sqrt(3) pour obtenir 9 (sqrt(3)**4 = 9)
4 est le logarithme en base sqrt(3) de 9
********************
log_4 (1) = 0
0 est l'exposant qu'il faut donner à la base 4 pour obtenir 1 (4**0 = 1)
0 est le logarithme en base 4 de 1
********************
log_2 (16**2) = 2 log2 (16) = 2 (4) = 8 (log x**n = n log x)
log_2 (16) = 4
4 est l'exposant qu'il faut donner à la base 2 pour obtenir 16 (2**4 = 16)
4 est le logarithme en base 2 de 16
********************
log_2 (0.125) = log_2 (125/1000) = log_2 (1/8)
= log_2 (1) - log_2 (8)
= 0 - 3 = -3
log_2 (1) = 0
0 est l'exposant qu'il faut donner à la base 2 pour obtenir 1 (2**0 = 1)
0 est le logarithme en base 2 de 1
log_2 (8) = 3
3 est l'exposant qu'il faut donner à la base 2 pour obtenir 8 (2**3 = 8)
3 est le logarithme en base 2 de 8
********************
log_(1/7) (343) = -3 (7**3 = 343) (1/7)**3 = 1/343) (1/7)**(-3) = 343)
-3 est l'exposant qu'il faut donner à la base (1/7) pour obtenir 343 [(1/7)**(-3) = 343]
-3 est le logarithme en base (1/7) de 343
********************
log_(5/2) (125/8) = 3 (5**3 = 125 2**3 = 8)
3 est l'exposant qu'il faut donner à la base (5/2) pour obtenir (125/8) [(5/2)**3 = (125/8)]
3 est le logarithme en base (5/2) de (125/8)
</syntaxhighlight>
{{AutoCat}}
3f98c9i4wvzcqy11hbygikcfu7g8nr9