Étape 2: Un générateur de signaux vidéo

ATTENTION : TAG GIT pour cette ETAPE : "VGA"

0) Objectif

Il s'agit de construire le code d'un dispositif générant des signaux vidéo et les signaux de synchronisation associés permettant de contrôler un écran LCD. De plus, nous voulons plus qu'un simple "code qui marche", mais bel et bien un code paramétrable et réutilisable.

 

 

1) La norme VGA (Video Graphics Array)

La norme d'affichage VGA a été conçue pour afficher des flux vidéo sur un moniteur. Il s'agit d'une suite de pixels correspondant au balayage ligne par ligne, puis colonne par colonne des pixels de l'écran.  Le schéma de transmission reste très fortement influencé par les limitations des anciens moniteurs analogiques.

  • L'horloge pixel n'était pas transmise, mais on indiquait un début d'image ou un début de ligne par une signalisation particulière.
  • Pour permettre au faisceau électronique d'avoir le temps de revenir en début de chaque ligne, ou en début de chaque image, l'image était transmise en ménageant des intervalles de temps neutres (entre lignes et images). Ces intervalles sont appelés intervalles de suppression ou "blanking" en anglais.

Cela aboutit au schéma de transmission suivant pour notre carte DE10-Nano:

  • Le flux vidéo est cadencé par l'horloge pixel_clk.
  • Chaque pixel RGB est un mot de 24 bits, contenant  les 3 composantes colorées Rouge, Vert et Bleu codées chacune sur  8 bits. 
  • Chaque image est transmise de haut en bas ligne par ligne.
  • Chaque ligne est transmise de gauche à droite  pixel par pixel. 
  • Le début d'une image est signalé par un signal de synchronisation spécifique VS pour Vertical Synchro actif à l'état bas.
  • Le début d'une ligne  est signalé par un signal de synchronisation spécifique HS pour Horizontal Synchro actif à l'état bas.
  • Les intervalles de suppression ou "blanking" , c'est à dire sans transmission de pixel,  sont signalés par un signal spécifique BLANK actif à l'état bas.

Les durées des lignes, des images, des signaux de synchronisation et des intervalles de blanking sont normalisées (en nombre de cycles de pixel_clk) et doivent être respectées pour garantir la bonne synchronisation de l'écran.

L'évolution temporelle des signaux peut être représentée sur un diagramme à deux dimensions (la dimension horizontale correspondant aux pixels, la dimension verticale correspondant aux lignes).

 

 

 

Les paramètres temporels choisis pour notre affichage LCD sont les suivant :

Paramètre Commentaire Valeur Unité
Fpix fréquence pixel 32 Mhz
Fdisp fréquence image 66 images/sec
HDISP Largeur de l'image affichée 800 pixels
VDISP Hauteur de l'image affichée 480 lignes
HFP Horizontal Front Porch40 40 pixels
HPULSE Largeur de la synchro ligne 48 pixels
HBP Horizontal Back Porch 40 pixels
VFP Vertical Front Porch 13 lignes
VPULSE Largeur de la sync image 3 lignes
VBP Vertical Back Porch 29 lignes

 

2/ Squelette du contrôleur VGA

  • Dans le répertoire SoCFPGA/src, créez un fichier vga.sv qui contiendra un module nommé vga.  

Pour simplifier la définition des entrées/sorties, nous avons préparé la définition d'une interface video_if dans le fichier  ips/interfaces/video_if.sv

  • Créez le squelette du module (module vga... endmodule) en lui attribuant les entrées/sorties suivantes :
Nom de l'entrée/sortie Type de l'entrée sortie Commentaire
pixel_clk Entrée horloge entrante du module
pixel_rst Entrée initialisation,  actif à l'état haut
video_ifm Modport master de l'interface video_if  Tous les signaux destinés à l'écran passeront par cette interface. Le module vga est un maître.

3/ Génération des signaux de synchro (dans le module vga)

Pour faire simple, il s'agit de ne générer que les signaux de synchronisation associés au flux video.

  • Le cœur du contrôleur est donc constitué de deux compteurs: un compteur de pixels dans une ligne, et un compteur de lignes dans une image. 
  • Tous les signaux sont synchrones de  l'horloge pixel_clk. Le signal de remise à zéro sera pixel_rst
  • Les constantes pour les timings seront fournies via des paramètres locaux (localparam) dont les noms sont indiqués en gras dans le tableau du paragraphe 1.
  • Les constantes  HDISP et VDISP seront des paramètres (parameter) et devront pouvoir être modifiées lors de l'instantiation du module vga.
  • Les valeurs des paramètres par défaut seront celles correspondant à  l'écran LCD de la maquette (image 800x480).
  • Le dimensionnement des différents compteurs (nombre de bits) et les différentes comparaisons de valeurs devront se faire par calcul à partir des paramètres prédéfinis,  à l'exclusion de tout codage en "dur" de constantes. 
  • Les  signaux de synchronisation  (video_ifm.HS, video_ifm.VS, video_ifm.BLANK)  seront calculés à partir de l'état des compteurs pixels et lignes. Ils devront eux même être synchrones.
  • Enfin, le signal video_ifm.CLK sera simplement  pixel_clk.  
  • Ne cherchez pas à simuler pour l'instant, compilez éventuellement avec vlog pour vérifier la syntaxe.

4/ Génération d'une mire (dans le module vga)

Il s'agit de générer une image calculée à la volée pour vérifier le bon fonctionnement de l'ensemble du module. La mire demandée sera très simple:

  • Un fond noir, des colonnes blanches tous les 16 pixels, des lignes blanches toutes les 16 lignes. Attention, les signaux de couleur sont codés sur 8 bits (min=0, max=255)
  • Générez le signal video_ifm.RGB  de manière synchrone de façon à créer la mire en vous appuyant sur les valeurs des compteurs de la synchronisation. Les octets R, G et B sont concaténés dans le mot RGB sous la forme d'un vecteur {R,G,B}.
  • Attention: avant de générer la mire, il peut être utile de calculer des coordonnées relatives des pixels actifs à partir des compteurs, de façon a obtenir une coordonnée (0,0) pour le premier pixel actif de l'image.
  • Ne cherchez pas à simuler pour l'instant, compilez éventuellement pour vérifier la syntaxe.

5/ Mise à jour du module Top pour intégrer le module vga.

Le contrôleur vga est un sous-module de Top . Vous l'intégrerez de la façon suivante:

  1. Créez une instance de vga dans Top.
  2. Connectez le signal pixel_clk au signal de même nom de votre instance (l'horloge à 32 Mhz)
  3. Connectez le signal pixel_rst au signal de même  nom de votre instance.
  4. Enfin, connectez le port d'interface video_ifm à une interface de même nom.

Cette dernière interface doit être directement connectée au monde extérieur :

  1. Modifier la liste des E/S de Top en ajoutant un port nommé video_ifm de type modport master d'une interface de type video_if (la syntaxe est identique à celle utilisée dans vga).
  2. Enfin, rendez le module Top paramétrable:
    1. Ajoutez deux paramètres HDISP et VDISP ayant pour valeurs par défaut 800 et 480.
    2. Modifiez l'instanciation de vga de façon à ce que les paramètres HDISP et VDISP ainsi créés se propagent à vga.
  3. Ne cherchez pas à simuler pour l'instant, compilez éventuellement pour vérifier la syntaxe.

6/ Mise à jour du testbench de fpga pour évaluer votre générateur VGA.

La véritable interface video_if à laquelle est connecté le contrôleur vga via le module Top doit être instanciée dans le tesbench:

  • Dans tb_Top.sv, ajoutez une instance de video_if. Donnez-lui un nom quelconque, par exemple video_if0.
video_if video_if0() ;
  • Modifiez l'instanciation de Top  de façon à ce que les paramètres HDISP et VDISP soient forcés à  160 et 90. Il s'agit, une fois de plus,  de limiter en simulation le nombre de cycles à attendre de manière raisonnable tout en conservant en synthèse les valeurs par défaut.
  • Connectez le module Top à l'interface video video_if0.

7/ Première simulation 

Lancez la simulation en mode graphique, visualisez les signaux "importants" (ceux de l'interface video_if0) et tentez de vérifier "visuellement" le bon comportement de votre contrôleur vga. 

8/ Une vérification plus complète.

Nous avons préparé pour vous un module screen "testeur" de signaux vga. Ce testeur a deux fonctions:

  • Vérifier la validité des signaux de synchronisation en fonction du mode souhaité.
  • Créer un fichier contenant l'image générée par votre code.

Modifiez tb_Top de manière à instancier  le module screen connecté à l'interface video_if0 . Cela devrait ressembler à quelque chose comme ceci 

 screen #(.mode(13),.X(160),.Y(90)) screen0(.video_ifs(video_if0))  ;

Le paramètre mode égal à 13 permet de sélectionner des paramètres de synchro d'images correspondant à votre écran LCD, les paramètres X et Y permettent d'ajuster la taille de l'image à quelque chose de plus raisonnable. Évidemment vous devez choisir les mêmes que ceux choisis pour instancier Top.

Relancez la simulation (le mode batch suffit).

  • Vous devriez voir apparaître des messages d'erreur explicites sur le non respect des valeurs attendues.
  • Si vous êtes chanceux, les seuls messages indiquent les générations successives d'images.

Après la simulation, vous pouvez afficher la dernière image générée :

  • display work/screen_dump.ppm

Enfin, si vous faites évoluer la mire et si vous simulez suffisamment de temps, vous pouvez afficher approximativement la séquence vidéo en lançant l'affichage en parallèle avec la simulation:

  • display -update 1 screen.ppm 

9/  Adaptation de la synthèse à la nouvelle version du code

Définitions des pattes d'entrée/sortie (fichier syn/scripts/pins_assignement.tcl)

  • Nous avons préparé un fichier de contraintes spécifique pour les E/S VGA. Vous devez simplement modifier le fichier pins_assignement.tcl en supprimant le commentaire dans la ligne incluant le source pins_assignement_vga.tcl. Le résultat devrait être:
# Le E/S du contrôleur VGA
source pins_assignment_vga.tcl

Liste des fichiers pour la synthèse (fichier syn/scripts/project_list.tcl)

  • Compléter le fichier project_list.tcl par les définitions des nouveaux fichiers source nécessaires au module vga. 
  • Il s'agit des fichiers suivants:
    • $PROJECT_DIR/ips/interfaces/video_if.sv
    • $PROJECT_DIR/SoCFPGA/src/vga.sv
  • Attention les définitions doivent être placées avant  celle du fichier Top.sv

10 / Synthèse

  • Synthétisez votre design, corrigez les erreurs.
  • En ce qui concerne les Warnings, interrogez vos encadrants pour déterminer s'ils sont importants ou non.

11/ Test sur la maquette

  • Programmez votre maquette (make program)
  • Faites un reset de la maquette (key[0])/
  • Et constatez le bon fonctionnement éventuel.
  • Si cela ne fonctionne pas, revérifiez toutes les étapes.

13/ Conclusion

  • Nous sommes maintenant prêts à stocker les images en mémoire (framebuffer) plutôt que de les générer à la volée. C'est l'objectif de l'étape suivante.

 

N'OUBLIEZ PAS ! : TAG GIT pour cette ETAPE : "VGA"