Les cartes graphiques/Le Video Display Controler
Dans les années 70-80, un système vidéo pouvait être fabriqué de deux grandes manières différentes. La première concevait la carte d'affichage à partir de composants très simples, comme des portes logiques ou des transistors, à partir de zéro, sans réutiliser de matériel existant. De telles cartes vidéos avaient des performances et des fonctionnalités très variables, mais étaient très complexes à concevoir et coutaient cher.
La seconde catégorie utilisait des Video Display Controler (VDC), des circuits déjà tout près, placés dans un boitier, produits en masse, qu'il suffisait de compléter avec une mémoire vidéo et quelques autres circuits pour obtenir un système vidéo. De tels circuits permettaient d'obtenir des performances décentes, voire très bonnes, pour un prix nettement inférieur. Les deux fonctionnent de la même manière, peu importe qu'il s'agisse d'un VDC ou d'un circuit fait main. Les deux contiennent globalement les mêmes circuits, ils fonctionnent de la même manière.
Dans le chapitre sur les cartes d'affichage, nous avons vu qu'une carte d'affichage contient trois à quatre circuits distincts : un framebuffer, un circuit de contrôle, le circuit d’interfaçage électrique avec l'écran (le RAMDAC) et éventuellement une connexion avec le bus. Le VDC correspond au circuit de contrôle. Les fonctionnalités d'un VDC sont très variables. Ils s'occupent des choses de base, comme gérer la résolution, l'envoi de l'image à afficher à l'écran, ce genre de choses. Il ne s'occupe pas de la transmission avec le bus, il ne gère pas vraiment l’interfaçage électrique.

Si la plupart des VDC communiquent avec la mémoire vidéo, il existe quelques exceptions qui se débrouillent sans mémoire vidéo ! C'est le cas des Video shifters dont nous parlerons plus tard. Les Video shifters sont vraiment à part des autres VDP, leur design basé sur l'absence de mémoire vidéo est responsable de différences vraiment profondes comparé aux autres VDP, ce qui fait qu'ils auront droit à leur propre section à la fin du chapitre.
La meilleure manière d'aborder les VDC est de d'abord les voir comme des espèces de boite noire, dont on ne se préoccupe pas du contenu en premier lieu. Un VDC communique avec l'écran, le processeur et avec la mémoire vidéo. Dans ce chapitre, nous allons voir comment il communique avec l'écran et le processeur. Nous laissons de côté l'interface avec la mémoire vidéo, car elle dépend du VDC et n'est pas la même selon que la carte d'affichage utilise ou non un framebuffer.
Le tout est illustré ci-dessous. L'interface VDC-écran correspond aux flèches en rouge et sera vue dans la première section. L'interface VDC-processeur correspond aux flèches en bleu et est le sujet de la seconde section. Enfin, l'interface entre mémoire vidéo et processeur correspond aux flèches en vert. En soi, elle n'est pas liée directement au VDC, mais nous allons quand même la voir dans ce chapitre.

L'interface du VDC avec l'écran
[modifier | modifier le wikicode]
Un écran est considéré par la carte graphique comme un tableau de pixels, organisé en lignes et en colonnes. Les écrans LCD sont bel et bien conçus comme cela, c'est plus compliqué sur les écrans CRT, mais cela ne change rien du point de vue de la carte graphique. Chaque pixel est localisé sur l'écran par deux coordonnées : sa position en largeur et en hauteur. Par convention, on suppose que le pixel de coordonnées (0,0) est celui situé tout haut et tout à gauche de l'écran. Le pixel de coordonnées (X,Y) est situé sur la X-ème colonne et la Y-ème ligne. Le tout est illustré ci-contre.
Le balayage progressif et l'entrelacement
[modifier | modifier le wikicode]L'écran peut afficher une image en utilisant deux modes principaux : le balayage progressif, et le balayage entrelacé.
Avec le balayage progressif, la carte graphique doit envoyer les pixels ligne par ligne, colonne par colonne : de haut en bas et de gauche à droite. Le balayage progressif est utilisé sur tous les écrans LCD moderne, mais il était plus adapté aux écrans CRT. Sur les écrans plats, l'image est transmise à l'écran, mais est affichée une fois qu'elle est intégralement reçue, d'un seul coup. Mais sur les anciens écrans de télévision, les choses étaient différentes.
Les vieux écrans CRT fonctionnaient sur ce principe : un canon à électrons balayait l'écran en commençant en haut à gauche, et balayait l'écran ligne par ligne. Ce scan progressif de l'image faisait apparaître l'image progressivement et profitait de la persistance rétinienne pour former une image fixe. L'image était donc affichée en même temps qu'elle était envoyée et le scan progressif correspondait à l'ordre d'allumage des pixels à l'écran.


La technique du balayage progressif n'avait pas de défauts particuliers, ce qui fait que tous les écrans d’ordinateurs CRT l'utilisait. Mais les télévisions de l'époque utilisaient une méthode différente, appelée l'entrelacement. Avec elle, l'écran faisait un scan pour les lignes paires, suivi par un scan pour les lignes impaires. Le tout est illustré dans l'animation ci-contre.

L'entrelacement donne l'illusion de doubler la fréquence d'affichage, ce qui est très utile sur les écrans à faible fréquence de rafraîchissement. Pour comprendre pourquoi, il faut comparer ce qui se passe entre un écran à scan progressif non-entrelacé et un écran entrelacé. Avec l'écran non-entrelacé, l'image met un certain temps à s'afficher, qui correspond au temps que met le canon à électron à balayer la totalité de l'écran, ligne par ligne. Avec l'entrelacement, le temps mis pour balayer l'écran est le même, car le nombre de lignes à balayer reste le même, seul l'ordre change.
Sur l'écran entrelacé, l'image s'affiche à moitié une première fois (sur les lignes paires) avant que l'image complète s'affiche. La moitié d'image affichée par l'écran entrelacé a une résolution suffisante pour que le cerveau humain soit trompé et perçoive une image presque complète. En clair, le cerveau verra deux images par balayage complet : une image partielle lors du balayage des lignes paires et une image complète lors du balayage des lignes impaires. Sans entrelacement, le cerveau ne verra qu'une seule image lors de chaque balayage complet.
L'effet est d'autant plus important que la résolution verticale (le nombre de lignes) est important. De plus, l'effet est encore plus important si l'ordinateur calcule un grand nombre d'images par secondes. Par exemple, pour un écran avec une fréquence de rafraîchissement de 60 Hz et un jeu vidéo qui tourne deux fois plus vite (à 120 images par secondes, donc), l'image sur les lignes impaires sera plus récente que celle sur les lignes paires. Le cerveau humain sera sensible à cela et verra une image plus fluide (bien qu'imparfaitement fluide).
Le nombre de lignes est toujours impair (normes analogiques : 625 en Europe, 525 en Amérique), ce qui fait un nombre non entier de lignes pour chacune des 2 trames (impaires et paires). Par exemple, pour 625 lignes cela fait 312,5 lignes par trame. Le balayage vertical étant progressif durant le balayage horizontal, les lignes sont imperceptiblement penchées. À la fin du balayage d'une trame, le rayon se retrouve au milieu de la ligne horizontale, soit un décalage vertical d'une demie-ligne (voir image ci-dessous).

La fréquence de rafraichissement
[modifier | modifier le wikicode]Même si cela commence à changer de nos jours, l'écran affiche un certain nombre d'images par secondes, le nombre en question étant désigné sous le terme de fréquence de rafraîchissement. Pour un écran avec une fréquence de rafraîchissement de 60 Hz (60 images par secondes), la carte graphique doit envoyer une nouvelle image tous les (1 seconde / 60) = 16,666... millisecondes.
Sur les écrans LCD, la fréquence de rafraîchissement ne dépend pas de la résolution utilisée, en raison de différences de technologie. Sur les anciens écrans CRT, la fréquence de rafraîchissement dépendait de la résolution utilisée, et la carte d'affichage devait alors gérer le couple résolution-fréquence elle-même et la gestion de la fréquence de rafraîchissement était donc plus compliquée.
Depuis environ 2016, quelques écrans supportent une fréquence de rafraichissement variable. Variable dans le sens : peut varier entre une fréquence minimale et une fréquence maximale selon les besoins. L'écran reçoit des images de part de la carte graphique, et les affiche immédiatement, sans attendre un signal de synchronisation vertical de fréquence fixe. Tant que la carte d'affichage ne va pas trop vite, l'écran suit, il affiche les images dès qu'il les reçoit. Par contre, au-delà d'un certain flux d'image, il bloque à une fréquence de rafraichissement maximale.
Les bénéfices d'une fréquence de rafraichissement variable sont nombreux. Déjà, le temps de latence est réduit, l'input lag si cher aux joueurs compétitifs est réduit de quelques millisecondes. De plus, la qualité d'image est améliorée du fait de l'absence de screen tearing sur lequel on reviendra plus tard.
La gestion des timings pour la communication avec l'écran
[modifier | modifier le wikicode]Le câble qui relie la carte graphique à l'écran transmet au mieux un seul pixel à la fois, voire un seul bit à la fois. On ne peut pas envoyer l'image d'un seul coup à l'écran, et on doit l'envoyer pixel par pixel. L'écran traite alors ce flux de pixels de deux manières différentes. Dans le cas des écrans LCD, le plus intuitif, l'écran accumule les pixels reçus dans une mémoire tampon et affiche l'image une fois qu'elle est totalement reçue. Pour les écrans CRT, l'écran affiche les pixels reçus immédiatement dès leur réception sur l'entrée. Dans les deux cas, il faut envoyer les pixels dans un certain ordre bien précis.
Un point important est que la carte graphique ne peut pas envoyer un flux de pixels n'importe quand et doit respecter des timings bien précis. Le flux de pixel envoyé à l'écran est souvent structuré d'une certaine manière, avec des temps de pause, un temps de maintien minimum pour chaque pixel, etc.
Déjà, il faut tenir compte des timings liés à la transmission de l'image elle-même. La carte graphique doit envoyer les pixels avec des timings tout aussi stricts, qui dépendent du standard vidéo utilisé. Chaque pixel doit être maintenu durant un certain temps bien précis, il y a un certain temps entre la transmission de deux pixels, etc. Et le circuit d’interfaçage doit gérer le temps de transmission d'un pixel. Pour cela, le VDC envoie un signal d'horloge dont la période correspond au temps de transmission/affichage d'un pixel. En, clair, le VDC envoie un pixel à chaque cycle d'horloge.
Ensuite, il faut prévenir l'écran qu'on a fini de transmettre une image avec un signal de synchronisation verticale, qui indiquait à l'écran qu'une image entière vient d'être transmise. Le VDC transmet l'image pixel par pixel, et lève ce signal de synchronisation verticale une fois l'image intégralement transmise. Ce signal était transmis sur un fil spécialisé, qu'on trouve sur la plupart des connecteurs VGA. De nos jours, sur les standards HDMI, DisplayPort, et autres, les choses sont plus compliquées, mais ce signal est quand même transmis, bien que pas forcément sur un fil spécialisé.
Enfin, il faut aussi tenir compte d'autres timings pour gérer la résolution. Les pixels sont envoyés ligne par ligne, mais une ligne de pixel n'a pas la même taille suivant la résolution : 640 pixels pour du 640 × 480, 1280 pour du 1280 × 1024, etc. La carte graphique doit donc indiquer quand commencent et se terminent chaque ligne dans le flux de pixels. Sans cela, on ne pourrait pas gérer des résolutions différentes. Pour cela, le VDC envoie un signal de synchronisation horizontale une fois qu'il a fini d'envoyer une ligne.
En tout, cela fait au minimum trois signaux : une horloge pour la transmission des pixels, un signal de synchronisation verticale, et un signal de synchronisation horizontale. Sans cela, impossible d'envoyer des pixels à l'écran ou de gérer la résolution convenablement. Et il y a d'autres contraintes de timings dont nous parlerons plus bas, qui ne sont pas évidentes pour le moment. Par exemple, sur les écrans CRT, il y a un temps de latence à la fin d'une ligne pour que le canon à électron se déplace sur le début de la ligne suivante. Et cela impose de ne pas démarrer l'envoie de la ligne suivante avant un certain temps. Cela il n'existe plus sur les écrans LCD, mais il fallait le prendre en compte à l'époque.
L'exemple du standard VGA
[modifier | modifier le wikicode]Un bon exemple est le standard VGA, qui était le seul utilisé pour connecter les écrans CRT, mais qui est encore utilisé de nos jours sur les écrans LCD. Avec ce standard, le connecteur contenait trois fils R, G, et B pour envoyer la couleur, codée en analogique. Il existait un fil H-SYNC pour indiquer qu'on transmettait une nouvelle ligne et un fil V-SYNC pour indiquer qu'on envoie une nouvelle image. Une nouvelle ligne ou image est indiquée en mettant un 0 sur le fil adéquat. Jusque là, rien de surprenant, c'est une redite de ce qu'on a dit plus haut. On trouve aussi plusieurs fils pour la masse, à savoir le 0 Volt, ainsi qu'une tension d'alimentation. Il y a une masse générale, ainsi que plusieurs masses, une par signal RGB.
Et enfin, il faut citer la connexion DDE/DDC qui permet de communiquer des informations de configuration à l'écran. Quand vous branchez l'écran à une carte graphique, celle-ci communique avec l'écran pour savoir quelles sont les résolutions supportées, quelle fréquence de rafraichissement est supporté, si l'écran supporte des couleurs 32 bits, etc. Sans cela, impossible de configurer la résolution. Pour cela, l'écran contient une petite mémoire ROM, dont le contenu est standardisé, qui contient toutes les informations nécessaires pour configurer l'écran.LA carte graphique lit cette ROM en passant par un bus appelé le bus Display Data Channel, qui permet à la carte graphique de lire cette ROM, d'interroger l'écran sur les résolutions et fonctionnalités supportées. Le bus est un dérivé du bus I²c, et a trois fils dédiés : un pour l'horloge, l'autre pour la transmission des données, et une masse dédiée.

Les premières subtilités du standard VGA viennent des timings des signaux HSYCN et VSYNC. Le signal HSYNC n'est pas envoyé dès la fin de la ligne : il y a un temps d'attente de quelques microsecondes entre la fin de la ligne et l'envoie du signal HSYNC. Le signal HSYNC est maintenu durant quelques microsecondes, la durée d'envoi est fixe. Puis, on a encore un nouveau temps d'attente avant l'envoi de la prochaine ligne, durant lequel le signal HSYNC n'est pas envoyé. Durant ces trois périodes (deux temps d'attentes, envoi de HSYNC), aucun pixel n'est envoyé à l'écran.

Et il y a la même chose avec les signaux VSYNC, même si les timings sont différents. On devait attendre un certain temps entre la transmission de deux lignes, ce qui introduisait des vides dans le flux de pixels. Même chose entre deux images, sauf que le temps d'attente était plus long que le temps d'attente entre deux lignes. Le tout est détaillé dans le schéma ci-dessous, qui détaille le cas pour une résolution de 640 par 480.

Les registres d'interface processeur du VDC
[modifier | modifier le wikicode]Pour le processeur, le VDC a une interface similaire à celle de n'importe quel périphérique : un paquet de registres, et éventuellement des mémoires SRAM intégrées. La mémoire vidéo peut être intégrée dans le VDP ou être séparée, les deux sont possibles. Mais nous allons partir du principe que la mémoire vidéo est séparée. Dans cette section, nous allons voir ce qui a trait aux échanges entre CPU et VDC proprement dit, la communication VDC-VRAM sera le sujet d'une section ultérieure.
Les registres de configuration du VDC
[modifier | modifier le wikicode]La programmation d'un VDC se fait par en configurant des registres de configuration interne, qui permettent de configurer la résolution, la fréquence d’affichage, la position du curseur de souris, etc. Le processeur a juste à écrire dans ces registres, pour configurer la carte d'affichage comme souhaité.
En général, les registres de configuration sont accessibles directement par le processeur. Quelques adresses mémoire sont détournées pour pointer, non pas vers la mémoire RAM, mais vers les registres de configuration. Ce n'est ni plus ni moins que la technique des entrée-sorties mappées en mémoire, que vous connaissez sans doute si vous avez déjà lu un cours d'architecture des ordinateurs. Il y a typiquement une adresse mémoire par registre, le processeur a juste à écrire dans cette adresse pour configurer le registre.
Plus rarement, l'écriture des registres de configuration se fait via une adresse unique, partagée entre tous les registres de configuration. La configuration d'un registre se fait en deux temps : il écrit le numéro du registre à configurer, puis la donnée à écrire. La carte graphique reçoit ces deux informations l'une après l'autre, et les utilise pour configurer le registre elle-même.
Le registre d'état du VDC
[modifier | modifier le wikicode]Le VDC incorpore presque toujours un registre d'état, ou un registre de statut qui permet au processeur de connaitre l'état du VDC. Il permet de savoir si le VDC est libre, s'il est en train d'afficher une ligne, si une erreur a eu lieu et laquelle. Le processeur a juste à lire le registre en question, pour vérifier l'état de la carte graphique. Chaque bit du registre de statut a une interprétation fixée à l'avance et fournit une information précise.
Plusieurs bits du registre de statut sont réservés au traitement des erreurs. Si le VDC rencontre une erreur, il met une valeur bien précise dans ces bits, appelée le code d'erreur. Typiquement, la valeur 0 indique qu'il n'y a pas d'erreur, les autres valeurs précisent une erreur. Le code d'erreur dépend de l'erreur en question et du VDC, il n'y a pas de standard pour ça.
La synchronisation entre CPU et VDC pour l'accès à la RAM vidéo
[modifier | modifier le wikicode]Un point qui va nous intéresser dans ce qui suit est la gestion des accès mémoire. Aussi bien le processeur que le VDC accèdent à la mémoire vidéo. Et ils ne faut pas qu'ils se marchent sur les pieds. Les deux ne peuvent pas accéder en même temps à la mémoire vidéo, ils doivent y accéder à tour de rôle. Et pour cela, divers mécanismes sont implémentés. Le mécanisme le plus simple est le suivant : quand le VDC lit des pixels à afficher en mémoire vidéo, ils prévient le processeur que la RAM vidéo est occupée. Le processeur attend alors que le VDC libère la RAM vidéo.
L'idée part du principe que l'affichage d'une image se fait à fréquence régulière. La carte d'affichage accède à la mémoire vidéo durant un certain temps pour envoyer l'image à l'écran, mais la laisse libre le reste du temps. Par exemple, sur un écran à 60 Hz, avec une image accédée toute les 16.66666 millisecondes, la carte d'affichage accède à la RAM vidéo pendant 5 à 10 millisecondes, le reste du temps est laissé au processeur.
De même, il y a un certain temps de libre entre l'affichage de deux lignes, le temps que le canon à électron du CRT se repositionne au début de la ligne suivante. Cela laissait un petit peu de temps au processeur pour changer la configuration de la carte graphique, par exemple pour changer la palette de couleur, changer des sprites, écrire dans la mémoire vidéo, ou tout autre chose. Le tout est très utile pour rendre certains effets graphiques.
Si le processeur sait quand la carte d'affichage affiche une image/ligne à l'écran, il sait quand la mémoire est libre et peut alors accéder à la mémoire vidéo. Reste à indiquer au processeur que la carte d'affichage n'utilise pas la mémoire vidéo. Pour prévenir le processeur, deux méthodes sont utilisées : le pooling et les interruptions.
La synchronisation CPU-VDC par pooling
[modifier | modifier le wikicode]La solution du pooling utilise le registre d'état de la carte d'affichage. Avant d’accéder à la mémoire vidéo, le processeur vérifiait ce registre pour savoir si le VDC accède à al mémoire vidéo. Si c'est le cas, le processeur attend que la mémoire vidéo soit libre. Sinon, le processeur accédait à la mémoire vidéo.
Pour cela, le registre de statut du VDC contient un bit qui précise que l'écran est en train d'afficher une ligne. Il est appelé le bit de blanking horizontal. En général, ce bit est à 0 quand le VDC est en train de transmettre une ligne à l'écran, à 1 quand la mémoire vidéo est libre. Notons que ce signal n'est pas équivalent au signal HSYNC. Pour reprendre l'exemple du standard VGA, il y a deux temps d'attente avant et après l'envoi du signal HSYNC, où l'écran n'envoie pas de données. Le signal HSYNC est alors à 0, alors que le bit de blanking est bien à 1.
La synchronisation CPU-VDC via raster interrupts
[modifier | modifier le wikicode]L'usage d'un bit de blanking permet au VDC de prévenir le processeur qu'il ne peut pas écrire en RAM vidéo. Mais les VDC de ce type sont assez rudimentaires. Une autre méthode pour ce faire utilise une technique appelée les interruptions matérielles. Pour rappel, les interruptions sont des fonctionnalités du processeur, qui interrompent temporairement l’exécution d'un programme pour réagir à un événement extérieur (matériel, erreur fatale d’exécution d'un programme…). Lors d'une interruption, le processeur suit la procédure suivante :
- arrête l'exécution du programme en cours et sauvegarde l'état du processeur (registres et program counter) ;
- exécute un petit programme nommé routine d'interruption ;
- restaure l'état du programme sauvegardé afin de reprendre l'exécution de son programme là ou il en était.

Les interruptions matérielles, aussi appelées IRQ, sont des interruptions déclenchées par un périphérique et ce sont celles qui vont nous intéresser dans ce qui suit. Les IRQ qui nous intéressent sont générées par la carte graphique quand c'est nécessaire. Pour que la carte graphique puisse déclencher une interruption sur le processeur, on a juste besoin de la connecter à une entrée sur le processeur, appelée l'entrée d'interruption, souvent notée INTR ou INT. Lorsque la carte graphique envoie un 1 dessus, le processeur passe en mode interruption.
- Si vous avez déjà lu un cours d'architecture des ordinateurs, vous savez sans doute que les choses sont assez compliquées, qu'un ordinateur moderne contient un contrôleur d'interruption pour gérer les interruptions de plusieurs périphériques, mais nous n'avons pas besoin de parler de tout cela ici. Nous avons juste besoin de voir le cas simple où la carte graphique est connectée directement sur le processeur.
Les cartes graphiques d'antan géraient deux types d'interruptions, qui sont regroupées sous le terme de Raster Interrupt. Grâce à ces interruptions, le processeur sait quand la mémoire vidéo est libre.
- La première indiquait que la carte graphique a fini d'afficher une image. Elle s'appelle la Vertical blank interrupt (VBI). Elle servait à implémenter la synchronisation verticale.
- Le second type est l'horizontal blank interrupt, qui indique que l'écran a fini d'afficher une ligne à l'écran, et donc que la mémoire vidéo est libre le temps que le canon à l'électron se mette en place.
La Vertical blank interrupt elle était parfois utilisée pour d'autres choses qui n'ont rien à voir avec l'écran ou le rôle d'une carte graphique. Par exemple, sur les anciens ordinateurs qui ne disposaient pas de timers sur la carte mère, la VBI était utilisée pour timer les échanges avec le clavier et la souris. A chaque VBI, la routine d'interruption vérifiait si le clavier ou la souris avaient envoyé quelque chose à l'ordinateur.
L''horizontal blank interrupt sert à implémenter certains effets graphiques, appelés des raster effects. Par exemple, il est possible de changer la couleur de l'arrière-plan à partir d'une certaine ligne, afin de séparer le ciel du sol. Ou encore, on peut implémenter un défilement à parallaxe, qu'on verra dans quelques chapitres. Et bien d'autres effets graphiques sont rendus possibles grâce à cela.
Le VDC contient donc une sortie dédiée aux interruptions, connectée à l'entrée d'interruption du CPU (directement ou par l'intermédiaire d'un contrôleur d'interruption). Les signaux de raster interrupt ne sont pas identiques aux signaux de synchronisation verticale et horizontale, ni aux signaux de blanking, même s'ils se ressemblent. La différence est que les signaux de synchronisation verticale/horizontale ont des contraintes de timing différents. Par exemple, le standard VGA impose que ces deux signaux soient maintenus durant un certain temps à l'écran, alors que les raster interrupts sont remises à zéro dès que le processeur est a pris en compte.
L'interface processeur - mémoire vidéo
[modifier | modifier le wikicode]L'usage de raster interrupts est très efficace, mais a pour défaut de beaucoup utiliser le processeur. Diverses optimisations permettent de se passer de raster interrupts, ou du moins d'en réduire le cout en performance. Mais ces techniques demandent de modifier la mémoire vidéo, précisément la manière dont le processeur communique avec la mémoire vidéo. Nous allons voir ces techniques dans cette section.
Les mémoires vidéo double port
[modifier | modifier le wikicode]Sur les premières consoles de jeu et les premières cartes graphiques, le framebuffer était mémorisé dans une mémoire vidéo spécialisée appelée une mémoire vidéo double port. Par double port, on veut dire qu'elles avaient deux entrée-sorties sur lesquelles on pouvait lire ou écrire leur contenu simultanément.
Le premier port était connecté au processeur ou à la carte graphique, alors que le second port était connecté à un écran CRT. Aussi, nous appellerons ces deux port le port CPU/GPU et l'autre sera appelé le port CRT. Le premier port était utilisé pour enregistrer l'image à calculer et faire les calculs, alors que le second port était utilisé pour envoyer à l'écran l'image à afficher. Le port CPU/GPU est tout ce qu'il y a de plus normal : on peut lire ou écrire des données, en précisant l'adresse mémoire de la donnée, rien de compliqué. Le port CRT est assez original : il permet d'envoyer un paquet de données bit par bit.
De telles mémoires étaient des mémoires dont le support de stockage était organisé en ligne et colonnes. Une ligne à l'intérieur de la mémoire correspond à une ligne de pixel à l'écran, ce qui se marie bien avec le fait que les anciens écrans CRT affichaient les images ligne par ligne. L'envoi d'une ligne à l'écran se fait bit par bit, sur un câble assez simple comme un câble VGA ou autre. Le second port permettait de faire cela automatiquement, en permettant de lire une ligne bit par bit, les bits étant envoyés l'un après l'autre automatiquement.
Pour cela, les mémoires vidéo double port incorporaient un registre capable de stocker une ligne entière. Le registre en question était un registre à décalage, à savoir un registre dont le contenu est décalé d'un rang à chaque cycle d'horloge. Le bit sortant est récupéré sur une sortie du registre, sortie qui était directement connectée au port CRT. Lors de l'accès au second port, la carte graphique fournissait un numéro de ligne et la ligne était chargée dans le tampon de ligne associé à l'écran. La carte graphique envoyait un signal d'horloge de même fréquence que l'écran, qui commandait le tampon de ligne à décalage : un bit sortait à chaque cycle d'écran et les bits étaient envoyé dans le bon ordre.
Le multiplexage temporel des accès mémoire
[modifier | modifier le wikicode]Les mémoires double port n'étaient pas si rares, mais elles n'étaient pas la solution la plus utilisée. La majorité des micro-ordinateurs et consoles utilisaient une mémoire vidéo normale, simple port, bien plus courante et bien moins chère. Mais il ajoutaient de circuits annexes ou utilisaient des ruses pour éviter que le processeur et la carte d'affichage se marchent sur les pieds. L'idée est de garantir que le processeur et la carte d'affichage n'accèdent pas à la mémoire en même temps. On parle de multiplexage temporel.
Un première mise en œuvre fait en sorte que la moitié des cycles d'horloge de la mémoire soit réservé au processeur, l'autre à la carte d'affichage. En clair, on change d’utilisateur à chaque cycle : si un cycle est attribué au processeur, le suivant l'est à la carte d'affichage. L'implémentation la plus simple utilise une mémoire qui va à une fréquence double de celle du processeur et de la carte d'affichage, les deux étant cadencés à la même fréquence. Un exemple est celui du micro-ordinateur BBC Micro, qui avait une fréquence de 4 MHz avec un processeur à 2 MHz et une carte d'affichage de 2 MHz lui aussi. Les fréquences du CPU et de la carte d'affichage étaient décalées d'une moitié de cycle, ce qui fait que leurs cycles correspondaient à des cycles mémoire différents. Le défaut est que cette technique demande une RAM très rapide, ce qui est un un gros problème.
Une autre solution laissait le processeur accéder en permanence à la mémoire vidéo. La carte d'affichage ne peut pas accéder à la mémoire vidéo quand le CPU écrit dedans, car des circuits annexes désactivent la carte d'affichage quand le processeur écrit dedans. Le micro-ordinateur TRS-80 faisait ainsi. Un défaut de cette méthode est qu'elle cause des artefacts graphiques à l'écran. Des pixels ne sont pas affichés et des écritures processeur trop longues peuvent causer des lignes noires à l'écran.
Enfin, une autre solution utilisait les mécanismes d'arbitrage du bus, qui gèrent les accès concurrents sur un bus. Le processeur et la mémoire sont reliés à la mémoire par le même ensemble de fils, et non par des ports séparés. La carte d'affichage et la mémoire envoient des demandes d'accès mémoire sur le bus, et elles sont ou non acceptées selon l'état de la mémoire. La carte d'affichage a la priorité, ce qui fait que si le processeur lance une demande d'accès à la mémoire pendant que la carte d'affichage y accède, le bus lui envoie un signal indiquant que le bus est occupé. Le processeur se met en attente tant que ce signal est à 1.
L'usage de tampons de synchronisation FIFO
[modifier | modifier le wikicode]Une dernière solution est l'usage de mémoires tampon entre le processeur et la mémoire vidéo. Le processeur n'écrivait pas directement dans la mémoire vidéo, mais dans une mémoire intermédiaire. La mémoire intermédiaire est une mémoire FIFO, à savoir qu'elle mémorise les données à écrire et leur adresse dans leur ordre d'arrivée. Elle sert à mettre en attente les accès mémoire du processeur tant que la mémoire vidéo est occupée.
Ainsi, si la mémoire vidéo est libre, le processeur peut écrire directement dans la mémoire vidéo, sans intermédiaire. Mais si la carte d'affichage accède à la mémoire vidéo, les écritures du processeur sont mises en attente dans la mémoire FIFO. Elles s'accumulent tant que la mémoire vidéo est occupée, elles sont conservées dans l'ordre d'envoi par le processeur. Dès que la mémoire vidéo se libère, les données présentes dans la FIFO sont écrites dans la mémoire vidéo, au rythme d'une écriture par cycle d'horloge de la VRAM : la mémoire FIFO se vide progressivement.
Si la mémoire FIFO est pleine, elle prévient le processeur en lui envoyant un bit/signal, et le processeur agit en conséquence en cessant les écritures et en se mettant en pause.

Sur les cartes d'affichage, le processeur n'adresse pas la mémoire vidéo directement. A la place, le processeur envoie des données sur le bus, sur le connecteur de la carte d'affichage. La carte d'affichage récupère les données transmises sur le bus et les mets en attente dans une mémoire FIFO assez similaire. Elle les écrit en mémoire vidéo si besoin quand elle est libre. En conséquence, les cartes graphiques modernes n'ont pas besoin de raster interrupts, qui étaient utilisées sur les premiers PC ou les premières consoles. A la place, c'est la carte graphique qui s'occupe de tout, et notamment son circuit de contrôle qui gère la mémoire vidéo. D'ailleurs, c'est ce circuit de contrôle qui gère la synchronisation verticale, pas le processeur, pas besoin de vertical blanking interrupt.
La génération des signaux de commande pour l'écran
[modifier | modifier le wikicode]Les VDC contiennent tous de quoi générer les signaux de commande à destination de l'écran, ainsi que des signaux d'interruption à destination du processeur. Le premier signal à générer est le signal d'horloge transmission des pixels, à savoir le signal d'horloge dont la période est égale au temps mis pour envoyer un pixel à l'écran. Ce signal est souvent transmis à l'écran, via un fil dédié. Les VDC contiennent de quoi générer cette fréquence, grâce à un circuit oscillateur dédié.
Il faut aussi générer les signaux de synchronisation verticale/horizontale, ainsi que les raster interrupts. Et ils se trouve que les deux sont générés par les mêmes circuits, à peu de choses près. Dans ce qui va suivre, nous allons voir comment sont générés ces signaux, quels sont les circuits qui s'en chargent. Ils sont assez simples : ce sont de simples compteurs reliés à des comparateurs !
La génération des signaux de synchronisation verticale/horizontale
[modifier | modifier le wikicode]Le VDC gère les signaux de synchronisation verticale ou horizontale. Pour cela, ils intègrent deux compteurs (des circuits qui comptent de 0 à N). Le premier compteur compte les lignes transmises, l'autre les pixels dans une ligne, ce qui leur vaut les noms de compteur de colonne et de compteur de ligne. Les deux compteurs sont initialisés à 0 avant la transmission et sont incrémentés automatiquement quand on passe d'un pixel à l'autre, ou bien d'une ligne à l'autre. Quand le compteur atteint la valeur adéquate, il émet un signal de synchronisation verticale/horizontale. Au passage à la ligne suivante, le compteur de colonne est réinitialisé à 0, idem pour le compteur de ligne quand une image a été affichée totalement.
Ils sont configurés de manière à prendre en compte la résolution de l'écran, mais pas de la manière dont vous le pensez. Par exemple, pour une résolution de 640 par 480 : vous imaginez sans doute que le compteur de colonne est configuré pour compter de 0 à 639, alors que l'autre compte de 0 à 479. Par exemple, pour une résolution de 640 par 480, les deux compteurs sont initialisés à 0. Le compteur de colonne est incrémenté à chaque envoi de pixel, et il déclenche le signal de synchronisation horizontale une fois que le compteur atteint 640. Le compteur de colonne est alors réinitialisé après un certain temps, alors que le compteur de ligne est incrémenté. Le compteur de ligne est donc incrémenté à chaque nouvelle ligne. De plus, il émet un signal de synchronisation verticale quand il atteint 480, et est réinitialisé après cela.
Il est possible de faire ainsi, mais ce n'est pas la solution idéale. En réalité, il faut tenir compte du fait que les signaux de HSYNC et VSYNC, qui sont eux aussi générés par les deux compteurs. Imaginons que le signal HSYNC prenne 20 cycles d'horloge, et le signal VSYNC 150 cycles. Pour une résolution de 640 par 480, on utilise un compteur de colonne qui compte de 0 à 640 + 20, et un compteur de ligne qui compte de 0 à 480 + 150.
L'idée est d'utiliser des comparateurs pour générer les signaux HSYNC et VSYNC, un pour le signal HSYNC et un autre pour le signal VSYNC. En reprenant les valeurs mentionnées précédemment, on utilise un comparateur qui vérifie si le compteur de colonne est supérieur ou égal à 640, et un autre comparateur qui vérifie si le compteur de ligne est égal ou dépasse 480. La sortie des deux comparateurs fournit directement les signaux HSYNC et VSYNC.
Une autre solution remplace les comparateurs par une mémoire ROM. L'idée est d'envoyer les compteurs sur l'entrée d'adresse, la ROM fournit en sortie les signaux de commande destinés à l'écran. En remplissant la ROM avec les valeurs adéquates, la technique fonctionne à merveille et on peut se passer des circuits comparateurs. Pour les haute résolutions, il est possible d'utiliser deux ROMs : une pour le compteur de ligne, une pour le compteur de colonne.
Le VDC peut gérer plusieurs résolutions différentes, et les timings sont différents suivant les résolutions. Idéalement, il faut envoyer quelques bits de commande pour choisir la résolutions en entrée de la mémoire ROM pour choisir les bons timings. Avec des comparateurs, la technique demande d'utiliser les mêmes comparateurs, mais d'ajouter des circuits pour gérer les différentes résolutions.
L'exemple des timings du standard VGA
[modifier | modifier le wikicode]Reprenons l'exemple du standard VGA. Avec ce standard, il existait un fil H-SYNC pour indiquer qu'on transmettait une nouvelle ligne et un fil V-SYNC pour indiquer qu'on envoie une nouvelle image. Une nouvelle ligne ou image est indiquée en mettant un 0 sur le fil adéquat. De plus, on devait attendre un certain temps entre la transmission de deux lignes, ce qui introduisait des vides dans le flux de pixels. Même chose entre deux images, sauf que le temps d'attente était plus long que le temps d'attente entre deux lignes. Le tout est détaillé dans le schéma ci-dessous, qui détaille le cas pour une résolution de 640 par 480.

Le compteur de colonne est cadencé à une fréquence bien précise, qui détermine le temps mis pour passer d'un pixel à l'autre. Le temps de transmission d'un pixel est de 25,6 µs / 640 = 0,04 µs, ce qui correspond à une fréquence de 25 MégaHertz. Et cela permet d'implémenter facilement les deux temps d'attente avant et après l'affichage d'une ligne. Les temps d'attente de 1,54 et 0,64 µs correspondent respectivement à 38 et 16 cycles du compteur, la durée de 3,8 µs du signal H-sync correspond à 95 cycles. En tout, cela fait 640 + 95 + 16 + 38 = 789. Il faut donc un compteur qui compte de 0 à 788.
La transmission des pixels commence quand le compteur commence à compter. Puis, le compteur continue de compter pendant 0,64 µs alors qu'aucun pixel n'est envoyé, afin de gérer le temps d'attente après le signal H-sync. Puis, au 640 + 16ème cycle, le signal H-sync est généré pendant 95 cycles. Enfin, le compteur continue de compter pendant 38 cycles pour le second temps d'attente, avant le prochain envoi de ligne. Le signal H-sync est donc généré quand le compteur a une valeur comprise entre 656 et 751 : il suffit d'ajouter un comparateur qui vérifie si le compteur est dans cet intervalle, et donc la sortie est à zéro si c'est le cas. L'adresse n'est pas calculée si le compteur n'a pas une valeur comprise entre 0 et la largeur indiquée par la résolution.
La même logique s'applique avec le signal V-sync, mais avec des timings différents, illustrés plus haut.
Pour implémenter tout cela, il suffit de combiner les deux compteurs avec des circuits comparateurs, qui vérifient si la valeur du compteur est dans tel ou tel intervalle. Il faut au minimum deux circuits comparateurs, un pour le signal HSYNC, un autre pour le signal VSYNC. D'autres compteurs peuvent être utilisés pour générer les bits de blanking ou pour réinitialiser le compteur à la valeur adéquate. Les comparateurs peuvent être remplacés par une mémoire ROM, comme dit plus haut.

La génération des raster interrupts et des bits de blanking
[modifier | modifier le wikicode]Les mêmes compteurs ou la ROM sont souvent utilisés pour générer les raster interrupts et le bit de blanking, qui permettent de prévenir le processeur quand la carte d'affichage a terminé d'envoyer une ligne et/ou une image entière à l'écran.
Notons qu'il est possible d'implémenter les interruptions à partir du bit de blanking, cela demande juste aux compteurs de générer ce bit de blanking et de l'utiliser pour générer les raster interrupt. Au passage, les compteurs de ligne et colonne ne servent pas qu'à générer des signaux : on verra dans la section sur le CRTC que quand on dispose de ces deux compteurs, ajouter de quoi parcourir le framebuffer est trivial !