Étape 3: Utilisation d'une mémoire tampon pour l'image.

ATTENTION : TAG GIT pour cette ETAPE : "SDRAM"

0) Objectif

La carte  DE10-Nano comporte 1GB de mémoire SDRAM (voir DDR3, Figure 2-1 de la documentation de la carte). Cette mémoire est partagée entre le HPS (les processeurs ARM) et la partie FPGA.  Nous allons utiliser une zone de cette mémoire comme mémoire d'image, ou  "frame buffer", pour stocker la mire plutôt que de la générer "à la volée".

  • Le module vga viendra donc lire de façon régulière le contenu de cette zone mémoire, de manière à fournir les pixels pour l'écran.

  • La mire, elle même, sera créée par le système Linux qui tourne sur le HPS.

1) Les "Synchronous Dynamic Ram" et les contrôleurs de DRAM.

Avant d'utiliser cette mémoire, il est bon de connaitre quelques particularités de ce type de composant afin de bien comprendre la nécéssité d'utiliser un "contrôleur de DRAM". L'accès aux données d'une SDRAM n'est pas aussi simple que l'accès aux données d'une mémoire statique. L'opération de transfert, que ce soit en lecture ou en écriture, nécessite une séquence d'opérations mettant en oeuvre plusieurs signaux de contrôle. D'autre part, cette séquence d'opérations dépend d'un état interne de la SDRAM.  Enfin, à la différence de la mémoire statique, la SDRAM finit par perdre ses données même si son alimentation est maintenue. Il faut mettre en oeuvre, de manière régulière, des séquences dites de rafraichissement, pour maintenir les données dans la mémoire. En conséquence, nous retiendrons les points suivants :

  • L'usage d'une SDRAM nécessite l'utilisation d'une unité de contrôle spécialisée, prenant en charge le séquencement des opérations d'accès ainsi que le rafraichissement de la mémoire.
  • Le nombre de cycles nécessaires à  l'accès à une donnée (que ce soit en lecture ou en écriture) n'est pas garanti, car il dépend de l'état interne de la SDRAM.
  • Enfin, la SDRAM étant partagée entre le HPS et le FPGA, le temps d'accès peut dépendre de la charge logicielle.

Nous vous fournissons pour cette étape, un accès au contrôleur de sdram compatible avec le bus Wishbone. Vous n'aurez donc pas à vous soucier du détail de l'accès à la SDRAM, mais simplement à générer des requêtes Wishbone correctes.

2) Première adaptation du module VGA

  • Dans les entrées/sorties du module vga, ajoutez un port correspondant à l' interface wshb_if  de type master et nommée wshb_ifm. Cette interface permettra d'échanger des données de 32 bits avec une fréquence de bus de 100MHz.

Dans une première phase, nous allons modifier le module vga pour créer un maitre Wishbone "bidon" dont le seul rôle est de vérifier une instanciation correcte des différents éléments:

  • Générez sur wshb_ifm des requètes d'écriture permanentes, en assignant les valeurs constantes suivantes:
Nom valeur Commentaire
wshb_ifm.dat_ms 32'hBABECAFE Donnée 32 bits émises
wshb_ifm.adr '0 Adresse d'écriture
wshb_ifm.cyc 1'b1 Le bus est sélectionné
wshb_ifm.sel 4'b1111 Les 4 octets sont à écrire
wshb_ifm.stb 1'b1 Nous demandons une transaction
wshb_ifm.we 1'b1 Transaction en écriture
wshb_ifm.cti '0

Transfert classique

wshb_ifm.bte '0 sans utilité.

4) Adaptation du module Top

  • Dans le module Top, vous disposez  déja d'une instance de l'interface wshb_if_sdram  vers le contrôleur de sdram, le code est le suivant:
  wshb_if #( .DATA_BYTES(4)) wshb_if_sdram  (sys_clk, sys_rst);
  • Cette interface est pour le moment neutralisée. Il faut supprimer cette neutralisation. Pour cela, commentez la zone de code où l'on assigne des valeurs constantes aux signaux de wshb_if_sdram.
  • Enfin, modifiez l'instanciation de vga dans Top de manière à connecter le port wshb_ifm de Top à l'interface wshb_if_sdram.

5) Première simulation

Le testbench Tb_Top.sv contient déjà tous les modules nécessaires pour simuler l'accès à la SDRAM. Il suffit donc de lancer la simulation:

  • Compilez (make compile) et simulez (make simu_batch) la chaine totale tb_Top.
  • Corrigez les différentes erreurs.
  • Relancez la simulation en mode graphique  et visualisez les signaux du module Top ainsi que les signaux internes à l'interface wshb_if_sdram dans Top
  • Vérifiez que le bloc vidéo fonctionne toujours.
  • Vérifiez le bon fonctionnement du bus wshb_if_sdram: des transactions en écriture démarrent, des acquittements sont générés,  les  données "BABECAFE" transitent vers la SDRAM.

Si vous ne voyez pas tout cela, en désespoir de cause, faites appel à un encadrant...

6) Adaptation finale du module VGA pour la lecture de la SDRAM

Modifiez le module vga pour qu'il fonctionne de la façon suivante (lisez toute l'explication avant de commencer le codage):

6.1/ Lecture en SDRAM

  • Les signaux d'horloge et de remise à zéro à utiliser doivent être ceux du bus wshb_ifm.
  • Après la remise à zéro, un contrôleur (codé par vous même) génère des requètes de lectures de pixels sur l'interface Wishbone, en bouclant en permance pour lire une mire supposée être stockée dans la mémoire SDRAM.
    • Les pixels sont stockés par mots de 32bits, la couleur étant codée sur les 3 octets de poids faible
    • Seuls les pixels de la zone affichable de taille VDISPxHDISP sont stockés.
    • Les pixels sont stockés de manière consécutive dans la mémoire, à partir de l'adresse 0.
    • Attention les adresses Wishbone sont des adresses "octet" : un  pixel de coordonnée (x,y) sera accessible en adresse 4*(HDISP*y+x).
    • Les transactions sur le bus Wishbone sont validées par le signal stb. Le signal cyc étant maintenu à 1 en permanence. 
    • Seul le retour du ack en provenance du contrôleur  mémoire fait avancer le processus à la lecture du pixel suivant.
    • Un fois l'image entièrement lue, le processus boucle sur lui-même indéfiniment pour relire en permanence toute l'image.

Après codage, simulez en l'état votre système. Le testbench que nous fournissons contient une mire dont vous devriez voir les pixels défiler. Vérifiez bien que vous lisez exactement HDISPxVDISP pixels et que vous réenchaînez bien des relectures successives de l'image.

6.2/ Ecriture en FIFO

Les pixels lus arrivent à un rythme qui dépend des disponiblilités du contrôleur de SDRAM et qui ne correspond pas à celui de la vidéo. De plus,  l'horloge du bus Wishbone n'est pas corrélée avec celle de l'affichage VGA. Nous allons utiliser une FIFO asynchrone pour faire le tampon entre le processus d'écriture et l'affichage des pixels.

  • Nous utiliserons une fifo de 256 données de 32 bits. Son code se trouve dans le fichier ips/fifos/async_fifo .
  • Attention, le paramètre DEPTH_WIDTH ne représente pas le la profondeur de la FIFO mais le log2 de la profondeur (fournir 8 pour obtenir 256 données) 
  • Instanciez la FIFO dans le code de vga en créant de nouveaux signaux dont vous affecterez les valeurs plus tard.
  • Le signal de remise à zéro de la fifo sera celui du bus Wishbone.
  • Les pixels lus sur le bus Wishbone sont écrits dans la FIFO au rythme de leur arrivée et de l'horloge Wishbone.
  • Si la FIFO est pleine, le contrôleur de lecture doit arrêter de faire des requêtes sur le bus et doit attendre que la FIFO ne soit plus pleine (il faut modifier votre contrôleur précédent pour cela).
  • Simulez votre système: vous devriez voir une série de lectures sur le bus Wishbone suivie d'un arrêt dès que la FIFO est pleine.

6.3/ Lecture de la FIFO.

Le contrôleur vga devra piloter la lecture des pixels dans la FIFO, tout en garantissant que la première donnée lue corresponde au premier pixel affichable de l'écran. Pour cela le dispositif sera le suivant:

  • La lecture dans la FIFO se fait au rythme de l'horloge pixel, uniquement dans la zone d'affichage (BLANK=1).
  • La toute première lecture ne pourra pas avoir lieu tant que la FIFO n'aura pas été pleine au moins une fois avant le début d'une zone d'affichage.
  • Attention, le signal wfull de la FIFO est dans le domaine de l'horloge wshb_ifm.clk et vous devrez l'exploiter dans le domaine de l'horloge pixel_clk. Vous savez ce qu'il faut faire...
  • Supprimez le générateur de mire: La donnée lue dans la FIFO sera directement affectée au pixel à afficher.

7) Simulation et vérification du résultat

Si vous avez respecté ce cahier des charges et utilisé les mếmes résolutions pour la simulation que celles proposées dans les étapes précédentes, vous devriez obtenir ceci:

8) Synthèse 

  • Ajoutez le  module FIFO  dans le fichier  syn/scripts/project_list.tcl
​set_global_assignment -name SYSTEMVERILOG_FILE $PROJECT_DIR/ips/fifos/async_fifo.sv
  • Lancez la synthèse.
  • Corrigez les éventuelles erreurs (si nécessaire, refaites la simulation après correction.)

9) Test sur la maquette 

Attention: Comme indiqué en introduction, la mire affichée est générée par le HPS. Ainsi, pour que tout fonctionne bien, il faut démarrer le système Linux.

  • Programmez le FPGA (make program)
  • Redémarrez le système Linux. Pour cela il faut appuyer sur le bouton WARM_RST que vous pouvez trouver dans la vue suivante.
  • Il faut attendre 10 à 30 secondes et vous devriez voir une mire.
  • Vous pouvez utiliser une autre mire en changeant la position de l'interrupteur SW[0], le plus a droite des 4 interrupteurs entourés en vert.

10) Conclusion

  •  N'OUBLIEZ PAS  : TAG GIT pour cette ETAPE : "SDRAM"