Developpez.com - Systèmes embarqués

Le Club des Développeurs et IT Pro

FPGA : programmer un contrôleur pour écran VGA avec une carte de développement FPGA

Un tutoriel de f-leb

Le 2022-09-16 20:49:53, par f-leb, Responsable Arduino et Systèmes Embarqués
à tous,

Pour cette rentrée, je vous propose ce tutoriel pour apprendre à configurer un contrôleur d’écran VGA sur une carte de développement FPGA :

Programmer un contrôleur pour écran VGA avec une carte de développement FPGA


Kit FPGA Terasic DE0-nano et interface VGA

Objectif découvrir le protocole VGA et configurer la carte FPGA pour générer les signaux qui vont produire des images et autres animations vidéo à la résolution 640x480@60Hz.

Bonne lecture, et bonne rentrée à tous
  Discussion forum
15 commentaires
  • f-leb
    Responsable Arduino et Systèmes Embarqués
    Merci

    Le langage (Verilog ici) en lui-même n'est pas très compliqué : des affectations, des assignations de signaux, des if/else ou switch/case et on a l'essentiel

    Alors c'est vrai qu'il faut penser différemment, par exemple dans ce test (la coloration syntaxique Verilog n'est pas active sur le forum, donc copie d'écran) :

    Le bloc always est évalué à chaque front montant de l'horloge (synchronisme).
    Le programmeur (non avisé) pourrait croire que si l'entrée in est à 1, la valeur out est d'abord mise à 1 (ligne 9), puis ensuite mise à 3 (ligne 10).
    Et donc si l'entrée in est maintenu à 1, la valeur out "clignote" en étant constamment rabaissée à 1 (ligne 9) avant de remonter à 3 (ligne 10).

    Que nenni, un FPGA n'est pas un CPU qui exécute séquentiellement l'instruction de la ligne 9, suivie de l'instruction de la ligne 10.

    L'ordre des lignes est important, mais seulement pour donner des priorités.
    Quand une variable est affectée plusieurs fois, c'est la dernière affectation qui est prioritaire.
    Ainsi, si in est à 1 :
    - la ligne 9 voudrait affecter 1 à out;
    - la ligne 10 voudrait affecter 3 à out;

    C'est donc la dernière affectation de la ligne 10 qui est prioritaire sur l'affectation de la ligne 9, et le circuit configuré fera en sorte que out prenne la valeur 3 si l'entrée in est à 1.



    C'est un peu fatigant au début ces subtilités (et à la fin aussi d'ailleurs)

    Edit 20/09/22 :
    Je précise que le code précédent est rédigé pour illustrer un phénomène, mais du point de vue du style de codage, il est mauvais On écrira plutôt avec un else :

    Code verilog :
    1
    2
    if (in) out <= 3; 
    else out <= 1;
  • Auteur
    Expert éminent sénior
    pour cet article.
    La conclusion qui décrit bien les difficultés que l'on peut rencontrer quand on souhaite débuter la programmation des FPGA. Il faut donc que j'apprenne à réfléchir autrement pour me lancer dans cette aventure.

    Envoyé par f-leb
    il faudra se débarrasser de certains réflexes de programmeurs, s’approprier une nouvelle culture et comprendre que des notions de logique séquentielle et combinatoire sont indispensables pour comprendre ce qui se passe dans vos circuits
  • f-leb
    Responsable Arduino et Systèmes Embarqués
    Un pt'it code pour illustrer :
    Code verilog :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    module test(); 
      reg [2:0] a;  // cas le + fréquent 
      reg [0:2] b;   
    	 
      initial begin 
        a = 3'b100; 
        b = 3'b100; 
    		 
        $display("%0b %0b %0b", a[2], a[1], a[0]); 
        $display("%0b %0b %0b", b[2], b[1], b[0]);  
      end	 
    endmodule

    Résultat :
    Code :
    1
    2
    3
    1 0 0 
    0 0 1 
    Done
    Envoyé par Auteur 
    Et l'affectation est le symbole "<=" ce qui est assez perturbant pour moi qui suis habitué au "="

    Le plus perturbant, c'est que les deux symboles existent pour les affectations : <= ou =, affectation non-bloquante (non-blocking) vs affectation bloquante (blocking).

    Et il y a bien une différence de comportement entre les deux !! Euhh... je reviens plus tard pour expliquer
  • Vincent PETIT
    Modérateur
    Salut,
    Envoyé par Artemus24 
    Et c'est quoi la différence entre reconfigurable et reprogrammable ? Pour moi, c'est synonyme.

    Effectivement sémantiquement les mots sont proches néanmoins moi aussi je préfère utiliser reconfigurer pour un FPGA. Je m'explique !

    Imaginons un calcul de CRC 16 bits (polynôme 0x8005).

    En C le programmeur voit ça :
    Code C :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    unsigned int crc16(unsigned int crcValue, unsigned char newByte)  
    { 
    	unsigned char i; 
      
    	for (i = 0; i < 8; i++) { 
      
    		if (((crcValue & 0x8000) >> 8) ^ (newByte & 0x80)){ 
    			crcValue = (crcValue << 1)  ^ 0x8005; 
    		}else{ 
    			crcValue = (crcValue << 1); 
    		} 
      
    		newByte <<= 1; 
    	} 
      
    	return crcValue; 
    }

    Le designer FPGA lui par contre voit ça (les blocs "stage" sont des bascules logiques) :



    Etant donné qu'un FPGA est un réseau de composants logiques qu'on peut rerouter ou reconfigurer "un peu" comme on veut, je pense en tout cas que le terme "reprogrammer" s'y prête un peu moins. Le Verilog et le VHDL sont des langages qui permettent de décrire une config matérielle. La fonction réalisée par cette config matérielle peut aussi, comme le cas du calcul de CRC, être réalisée par du soft.
  • Auteur
    Expert éminent sénior
    Et deux remarques :
    1- je vois que la déclaration de out est la suivante :
    Code :
    output reg  [1:0] out

    et non (bah, oui programmeur avant tout )
    Code :
    output reg  [0:1] out
    2- Ce qui me surprend c'est que tu puisses affecter 3 à une sortie. Dans mon esprit logique c'est 1 ou 0.
    ah non finalement.... out est un registre 2 bits donc 3 ça passe

    Et l'affectation est le symbole "<=" ce qui est assez perturbant pour moi qui suis habitué au "="
  • Auteur
    Expert éminent sénior
    Envoyé par f-leb 
    Un pt'it code pour illustrer :
    Code verilog :
    1
    2
    3
    module test(); 
      reg [2:0] a;  // cas le + fréquent 
      reg [0:2] b;

    Résultat :
    Code :
    1
    2
    1 0 0 
    0 0 1

    ah... j'ai compris : little-endian et big-endian ?

    Envoyé par f-leb 
    Le plus perturbant, c'est que les deux symboles existent pour les affectations : <= ou =, affectation non-bloquante (non-blocking) vs affectation bloquante (blocking).

    Et il y a bien une différence de comportement entre les deux !! Euhh... je reviens plus tard pour expliquer

    ah oui, mais non.
  • f-leb
    Responsable Arduino et Systèmes Embarqués
    Envoyé par Auteur
    ah... j'ai compris : little-endian et big-endian ?
    C'est bien ça !

    Envoyé par Auteur
    ah oui, mais non.
    Ah oui, mais si.
  • Auteur
    Expert éminent sénior
    Hello

    Envoyé par Artemus24

    Le "=" n'est pas un symbole d'affectation mais un symbole d'égalité.
    Quand tu fais de l'algorithmique, le symbole de l'affectation est bien une flèche dirigée vers la gauche.
    Je reconnais que ce n'est pas très judicieux de prendre "<=" pour une flèche car ce symbole est une comparaison dont le sens est "inférieur ou égale".
    A vrai dire, chaque langage veut se démarquer par l'usage de certains symboles qui peuvent nous induire en erreur.
    L'exemple même est le "===" de javascipt que je trouve totalement absurde. Le prochain langage fera "====". Où va-t-on s'arrêter en nombre de "=" ?
    le "=" dans la majorité des langages de programmation est une affectation. En algorithmie, il s'agit bien d'une flèche orientée vers la gauche.
    En javascript le "==" est une égalité sur les valeurs, mais "===" est une égalité stricte et l'interpréteur va non seulement comparer les valeurs, mais également les types (entier, chaine, etc.). Il ne s'agit pas d'une lubie du concepteur.

    Envoyé par Artemus24

    Ca veut dire quoi une affectation bloquante ???
    La première affectation bloque, et les autres ne pourront pas se faire.
    Comme l'a indiqué f-leb il faut penser matériel pas programmation. En automatisme, au démarrage, je me souviens qu'il fallait initialiser certains composants (forcer à 1 ou forcer à 0) pour éviter des états indéterminés et donc des comportements étranges de l'automate. Il doit s'agir de quelque chose de similaire.
  • f-leb
    Responsable Arduino et Systèmes Embarqués
    Salut,

    Envoyé par Artemus24
    Je ne te le fais pas dire. C'est absurde d'utiliser deux symboles pour une affectation.
    J'ai juste dit que c'était perturbant, mais cela n'a rien d'absurde. La comparaison avec d'autres langages n'est pas pertinente ici, Verilog/systemVerilog n'est pas un langage de programmation, c'est un langage de description de matériel (Hardware Description Language). Les lignes du code ne sont pas compilées pour fournir des instructions exécutées séquentiellement par un CPU. On s'attache à décrire des processus qui fonctionneront en parallèle (de façon concurrente). Et quand une « variable » est lue dans un processus pendant qu'elle est modifiée en même temps dans un processus concurrent, on peut se demander quel sera le résultat de l'affectation A noter que les évolutions plus récentes de Verilog (devenu SystemVerilog) avec ses nouveaux blocs always_comb (pour la logique combinatoire) et always_ff (pour la logique séquentielle) facilite la vie du développeur sur les affectations autorisées.

    Envoyé par Artemus24
    Ca veut dire quoi une affectation bloquante ???
    La première affectation bloque, et les autres ne pourront pas se faire.
    C'est l'idée... Je suis désolé de ne pas pouvoir apporter une explication immédiate et rapide mais cette notion d'affectation bloquante/non-bloquante n'est pas évidente du tout à expliquer et va me prendre du temps.
    Je suis en train de regarder un document qui raconte dès le synopsis :
    One of the most misunderstood constructs in the Verilog language is the nonblocking assignment. Even very experienced Verilog designers do not fully understand how nonblocking assignments are scheduled in an IEEE compliant Verilog simulator and do not understand when and why nonblocking assignments should be used...
    Rien que ça...

    En pratique, tu suis sans trop te poser de questions les quelques guidelines pour savoir quand utiliser l'une ou l'autre et ça marche très bien.
    Il faut que je me documente un peu et que je trouve des exemples pertinents, mais j'essaierai d'apporter un éclairage (dans un billet de blog peut-être).
  • f-leb
    Responsable Arduino et Systèmes Embarqués
    Envoyé par Artemus24
    Ben si, car l'état d'une variable n'a aucun rapport avec une affectation.
    Tu débloques, tu affectes et tu bloques à nouveau. Là, c'est logique.
    C'est logique en programmation, mais en langage HDL d'autres questions se posent. Le problème avec les FPGA, c'est de savoir quand tu débloques, et quand tu affectes. J'essaierai de donner un exemple plus tard...

    Envoyé par Artemus24
    Ce que tu me décris est une section critique sous windows, que l'on nomme sous linux un mutex (Mutual Exclusion). Cela se gère par une couche logiciel qui va gérer les accès au matériel.
    Certes, mais ces concepts qui sont gérés dans les systèmes d'exploitation n'ont rien à voir avec les FPGA...

    Envoyé par Artemus24
    Envoyé par f-leb
    Il faut que je me documente un peu et que je trouve des exemples pertinents, mais j'essaierai d'apporter un éclairage (dans un billet de blog peut-être).
    Je n'en demande pas autant...
    Quand bien même ça n'intéresserait que ma petite personne, ce forum n'échappera pas à un petit message ou billet d'explication... Fallait pas m'inviter