Étape 1: Un squelette de projet

ATTENTION : TAG GIT pour cette ETAPE : "SQUELETTE" 

0) Objectif

Nous allons mettre en place un squelette de projet, utilisant quelques ressources disponibles sur la carte fpga DE1-SoC. Nous utiliserons quelques interrupteurs, boutons poussoirs, leds et horloges qui servirons tout au long du projet. Plutôt que de vous fournir un squelette "prêt à servir" nous vous laisserons bâtir vous-même ce squelette de façon à vous familiariser avec l'ensemble des données et informations à fournir pour mettre en œuvre un design réel sur la carte.

1) Mise en place de l'environnement de travail

  • Placez-vous dans votre dépot  git personnel (cd )
  • Importez les codes de testbench de votre futur bloc:  ​​
  • git checkout master
  • git remote add projet_video git@gitlab.enst.fr:se204/projet_video.git
  • git remote update
  • git merge --allow-unrelated-histories projet_video/étape1

2) Arborescence de travail 

Nous partons de rien, ou presque... Nous utiliserons une arborescence de répertoires telle qu'indiquée ci-dessous. Pour chaque  bloc matériel créé, nous disposerons d'un répertoire spécifique destiné à contenir les fichiers source, de simulation ou de synthèse concernés.

Veuillez respecter les noms proposés pour faciliter un déboguage ultérieur, et la création de scripts automatiques...

-- projet_video
   -- fgpa
​      -- src
      -- tb_src
      -- simu
      -- synth
   -- bloc1
      -- src
      -- tb_src
      -- simu
   -- bloci ...

 

  • Pour cette étape nous ne travaillerons que sur le bloc fpga, au fur et à mesure que vous ajouterez d'autres  blocs.
  • Les sous-répertoires supplémentaires sont:
    • Le répertoire src qui ne devra contenir que du code synthétisables de vos blocs.
    • Le répertoire tb_src qui peut contenir tout code pouvant servir à la simulation (testbenchs...)
    • Le répertoire simu, répertoire dans lequel se trouvent les scripts de simulation et dans lequel se déroule la simulation.
    • Le répertoire synth,  répertoire dans le quel se trouvent les scripts de synthèse et dans lequel se déroule la synthèse. Il n'est pas vraiment indispensable pour les sous blocs
  • Le nom des répertoires devra être identique au nom  des modules concernés.

3) Le module fpga

Dans le répertoire "fpga/src" créez un premier fichier SystemVerilog  fpga.sv destiné à contenir le code global du fpga. Créez dans ce fichier un module nommé fpga et dont les entrées et sorties sont les suivantes:

 

Nom Type Nombre de bits Utilisation
fpga_CLK entrée 1 Horloge (à 50 Mhz)
fpga_CLK_AUX entrée 1 Horloge auxiliaire (à 27 Mhz)
fpga_LEDR0 sortie 1 Affichage LED
fpga_LEDR1 sortie 1 Affichage LED
fpga_LEDR2 sortie 1 Affichage LED
fpga_LEDR3 sortie 1 Affichage LED
fpga_SW0 entrée 1 commande 0/1
fpga_SW1 entrée 1 commande 0/1
fpga_NRST entrée 1 commande 0/1
fpga_SEL_CLK_AUX sortie 1 pilotage de l'horloge auxiliaire (l'horloge auxiliaire n'est disponible que si ce signal est forcé à 1)

Les entrées/sorties indiquées correspondront à de réelles entrées/sorties du FPGA. La carte DE1-SoC fournit  au fpga une horloge à 50 MHz ainsi qu'une horloge pilotable à 27Mhz

 Écrivez un code dans le module permettant :

  • de recopier la valeur  du signal fpga_SW0 vers fpga_LEDR0. 
  • de recopier la valeur du signal fpga_SW1  vers fpga_SEL_CLK_AUX 
  • de  faire clignoter le signal fpga_ LEDR1 à  approximativement 1Hz en utilisant l'horloge à 27 Mhz (utilisez un simple compteur binaire dont le reset, actif à l'état bas,  provient du signal fpga_NRST)
  • de faire clignoter le signal  fpga_LEDR2 à  approximativement 1Hz en utilisant l'horloge à 50 Mhz (utilisez un simple compteur binaire dont le reset, actif à l'état bas, provient du signal fpga_NRST)
  • de recopier l'état de fpga_NRST vers fpga_LEDR3.

4) Premier Environnement de simulation

Il s'agit de créer un environnement minimaliste reproduisant les conditions de fonctionnement réelles du FPGA.

Dans le répertoire fgpa/tb_src, créez un fichier SystemVerilog tb_fgpa.sv destiné à contenir le testbench. Créez dans ce fichier un module tb_fpga contenant 

  • Un processus générateur d'horloge à 50Mhz
  • Un processus générateur d'horloge à 27Mhz piloté par fpga_SEL_CLK_AUX ( l'horloge ne fonctionne que si fpga_SEL_CLK_AUX est égal à 1).
  • Un processus générant quelques actions sur le signal fpga_SW0 (test de l'allumage et l'extinction de la led)
  • Un processus générant quelques actions sur fpga_SW1 (vérification du pilotage de l'horloge auxiliaire)
  • Un processus générant quelques actions sur le signal fpga_NRST
  • l'instanciation du module fpga.

Pour les générateurs d'horloge, vous pouvez vous inspirer du testbench du filtre médian "median-tb.sv"  dans le répertoire simulation de votre projet median. Vous remarquerez que l'on fait simplement une boucle qui fait avancer le temps d'une demi période d'horloge entre chaque changement du signal d'horloge.  La valeur des unités de temps peut être indiquée, et cela peut être un nombre réel.  Attention la précision de ce nombre dépend de la précision avec laquelle le simulateur calcule le temps. Nous avons réglé pour vous cette précision à 1ps dans le Makefile de simulation.

Enfin, le générateur d'horloge à 27Mhz est un peu plus compliqué car il doit être commandable: faite attention à la manière dont vous écrivez son code car il est très facile de réaliser un processus avec boucle infinie ou le temps n'avance pas....

5) Première simulation

Dans le répertoire fpga/simu, vous disposez un fichier Makefile initial que vous adapterez au fur et à mesure des différentes étapes.  

Avant de lancer la simulation, prenez le temps d'une petite réflexion: Pour compter une seconde avec une horloge à 50Mhz, il faut simuler quelques millions de cycles de l'horloge, vous risquez d'attendre longtemps devant votre écran avant de voir bouger les signaux fpga_LEDR1 ou fpga_LEDR2.

Vous pouvez obtenir un comportement différent en simulation et en synthèse en paramétrant vos compteurs de façon conditionnelle : 

  • Nous avons défini, dans le Makefile, au moment de la compilation des sources, une variable du préprocesseur nommée "SIMULATION". 
  • Vous pouvez utiliser les commandes du préprocesseur `ifdef, `else, `endif  pour définir des paramètres de comptage différents en fonction de la situation

Voici un exemple  (les valeurs proposées ne sont ici totalement arbitraires)

`ifdef SIMULATION
  localparam hcmpt=5 ;
`else
  localparam hcmpt=18 ;
`endif

Vérifiez "visuellement" (chronogrammes) le bon fonctionnement de votre "fpga" et de son environnement associé.

6) Premier environnement de synthèse 

La synthèse en vue d'un téléchargement sur la maquette DE1-SoC nécessite de connaître les ressources disponibles et leurs connexions au FPGA. D'autre part, les entrées/sorties d'un FPGA peuvent être programmées pour s'ajuster aux contraintes éléctriques et temporelles des composants extérieurs. Les outils de synthèse permettent de préciser cela à l'aide de fichiers de contrainte.

Vous disposez des fichiers suivants, prenez le temps de les examiner, car vous devrez adapter certains d'entre eux

Makefile:

Ce fichier permet de :

  • lancer la synthèse   : make syn
  • programmation du FPGA : make  program
  • nettoyer le  répertoire de synthèse : make clean

Ce fichier n'est normalement pas à éditer.  Deux variables d'environnement sont définies pour une utilisation dans les scripts de synthèse.

  • PROJET : c'est le nom du module principal (ici  fpga)
  • TOPDIR : le répertoire  principal du projet.

syn_DE1-SoC_quartus.tcl:

Ce fichier est le script de synthèse principal. Il est écrit en langage Tcl qui est un langage très utilisé dans le monde de la CAO électronique. Ce script met à jour le projet et fait appel aux différents scripts de configuration avant de lancer la synthèse. 

device_assignment.tcl:

Ce script permet de sélectionner le FPGA cible pour la synthèse. Il s'agit dans notre cas de celui de la carte DE1-SoC. Il faut être précis dans la désignation du circuit pour être d'une part sûr de son brochage et de l'estimation de performances faite par l'outil de synthèse. Si le script ne contient pas les lignes suivantes, alors vous risquez de ne pouvoir programmer votre FPGA...

set_global_assignment -name FAMILY "Cyclone V"
set_global_assignment -name DEVICE 5CSEMA5F31C6

pins_assignment.tcl:  

Ce  script permet de définir les entrées/sorties du FPGA.  Prenez le temps de lire les nombreux commentaires de ce fichier qui expliquent  les différents choix faits pour adapter convenablement les signaux entrant et sortant du FPGA aux caractéristiques des circuits externes.

A VOUS DE JOUER

  • Vous trouverez  dans le répertoire "documentations" la documentation technique de la carte DE1-SoC.
  • Pour chacune des entrées/sorties de votre module (horloge à 27 MHz,  horloge à 50 Mhz, bouton poussoir, interrupteur, leds)  déterminez un élément extérieur au FPGA susceptible d'être utilisé en vous aidant du tableau ci-dessous
  • Nom de la pin Ressource pouvant être utilisée
    fpga_CLK voir chapitre 3.5 Clock Circuitry
    fpga_CLK_AUX voir chapitre 3.6.7 TV Decoder TD_CLK27
    fpga_SEL_CLK_AUX voir chapitre 3.6.7 TD_RESET_N
    fpga_LEDR* voir chapitre 3.6.1  "LEDs"
    fpga_SW* voir chapitre 3.6.1 "Slide Switches"
    fpga_NRST voit chapitre 3.6.1 "Push-buttons"
  • Pour chacun de ces éléments, déterminez la patte du fpga qui est connectée.
  • Complétez le fichier "pins_assignments.tcl"  sans oublier de définir le standard "électrique" de la connexion voulue

La syntaxe générale à utiliser est :

set_location_assignment PIN_xxx -to yyy

Ou xxx est le numéro de la patte choisie, et yyy est le nom du signal dans votre module fpga.

file_list.tcl:

Ce script permet de définir les fichiers HDL à charger (une ligne par fichier). 

La syntaxe générale à utiliser est :

set_global_assignment -name langage_source chemin_vers_le_fichier

Où  langage_source est une des trois valeurs SYSTEMVERILOG_FILE, VERILOG_FILE, VHDL_FILE suivant le langage utilisé et chemin_vers_le_fichier est le nom du fichier HDL à utiliser. Le chemin peut être absolu, ou relatif. Vous pouvez utiliser l'expression ${TOPDIR} pour vous référer à la racine du projet.

A VOUS DE JOUER:

Ajoutez le source de votre fpga dans la liste des fichiers

project_assignment.tcl:

Ce script permet de définir le comportement général souhaité par le synthétiseur. Dans notre cas nous demandons à l'outil Quartus d'optimiser les tentatives de synthèses successives en ne recompilant pas les fichiers déjà compilés sans erreur. C'est la raison pour laquelle, si vous recompilez alors qu'aucun fichier source n'a changé, Quartus ne fait rien...

timing_constraints.sdc:

 Ce script utilise une syntaxe devenue quasiment une norme de fait en CAO électronique pour définir les contraintes temporelles d'un design. Le suffixe "sdc" signifie "Synopsys Design Constraints" du nom de la société de CAD qui a créé cette syntaxe. Le concepteur doit fournir dans ce fichier toutes les informations "temporelles" susceptibles d'être une contrainte pour le design.

Pour le moment, dans notre cas la première contrainte est d'informer le synthétiseur de l'existence d'un domaine d'horloge nommé fpga_CLK ayant une fréquence de 50Mhz et associé au port fpga_CLK de notre design.  Nous faisons de même pour l'horloge auxiliaire.

create_clock -name {fpga_CLK} -period 20.0 -waveform { 0.0 10.0 } [get_ports {CLK}]
create_clock -name {fpga_CLK_AUX} -period 37.037  [get_ports {fpga_CLK_AUX}]

Enfin, nous informons l'outil qu'il n'y a pas de correlation entre ces deux horloges (il ne faudra pas tenter d'évaluer des temps de propagation entre des registres du premier domaine d'horloge et des registres du deuxième domaine d'horloge).

set_clock_groups -exclusive \
         -group {fpga_CLK} \
         -group {fpga_CLK_AUX}

Enfin, l'outil de synthèse ne pouvant pas deviner ce qu'est une LED, un switch ou un bouton poussoir, nous l'informons qu'il n'y a aucune contrainte de timing sur ces objets. Ces commandes définissent tout chemin temporel entre le switch ou les leds et tout noeud du design est à ignorer.

set_false_path -from [get_ports fpga_SW*] -to *
set_false_path -from [get_ports fpga_NRST*] -to *
set_false_path -from * -to [get_ports {fpga_LEDR*}]

7/ Première synthèse

Après avoir complété vos fichiers, lancez la synthèse (make).  Les messages apparaissant à la console sont colorés en vert pour les informations, en bleu pour les warnings, et en rouge pour les erreurs.  

Corrigez les éventuelles erreurs, et vérifiez les messages de Warning. Il ne devrait rester qu'un seul message de Warning :

Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings report for details

Si nous examinons le fichier de rapport fpga.fit.rpt, nous trouvons une ligne contenant "I/O Assignment Warnings" suivie d'une liste de Pin pour lesquelles le message est "Missing slew rate".

Nous avons en effet oublié de préciser à l'outil quel est le temps  de transition désiré sur les pattes d'entrée sortie du FPGA. Il est possible de régler grossièrement ce temps  de transition par un code entier valant 0 (Lent) ou 1 Rapide) . Le constructeur du FPGA donne cette possibilité pour laisser au concepteur le  choix d'un compromis entre la vitesse d'une entrée sortie et le bruit qu'elle génère dans le circuit.

A VOUS DE JOUER:

Modifiez le fichier pins_assignment.tcl de manière à définir pour chaque sortie le temps de transition des sorties suivant la syntaxe suivante: 

set_instance_assignment -name SLEW_RATE 1 -to le_nom_de_la_sortie

Vous pouvez vous inspirer des autres contraintes (IO_STANDARD, CURRENT_STRENGTH_NEW) pour affecter cette contrainte à toutes pins nommées "fpga_quelquechose"

Resynthétisez votre design et vérifiez que tous les Warnings ont disparu. 

8) Test sur maquette

La maquette DE2 étant connectée au PC par le câble USB (port USB-Blaster), tapez la commande make program pour charger le fpga. Vérifiez le fonctionnement du design, et entre autres

  • Que la LED 0 reproduit l'état du switch 0
  • Que la LED 1 (témoin du 27 Mhz) clignote si le switch 1 est à 1
  • Que la LED 2 (témoin du 50 Mhz) clignote
  • Que la LED 3 est allumée, et s'éteind si on appuie sur le bouton poussoir choisi pour fpga_NRST.

9) Le problème du RESET et de la métastabilité.

Nous avons utilisé un bouton poussoir comme signal de remise à zéro. Ce signal provient d'un monde qui n'est pas du tout synchrone avec l'horloge. Au moment du passage en reset, il n'y a pas de problème, que le signal soit utilisé comme reset synchrone ou asynchrone : le système finit toujours par être réinitialisé. Par contre, lorsque le bouton est relâché, il peut y avoir des phénomènes de métastabilité : dans un système complexe, certaines bascules changent d'état avant d'autres ce qui peut être très dangereux pour des machines à état (passage dans un état non prévu...)

Le schéma suivant se propose de résoudre ce problème d'une façon similaire à celle étudiée pour les changements de domaine d'horloge. Le signal fpga_NRST entrant doit servir de reset actif à l'état bas.  Il est connecté à l'entrée de reset (active à l'état bas ) des deux bascules D. Lorsque fpga_NRST vaut "0" les deux bascules D sont forcées à "0", donc n_rst=0. Lorsque NRST passe à "1", le signal constant "1" est échantillonné sur deux cycles et n_rst finit par passer à "1" de manière synchrone avec l'horloge. Le démarrage des systèmes seront bien synchrones avec leur horloge.

 

Resynchronisation du reset

A VOUS DE JOUER:

Réalisez un module (dans un fichier à part, stocké dans le répertoire fpga) de resynchronisation du reset. Nous aurons, plus tard, besoin de plusieur de ces modules. Ce module devra pouvoir être paramétrable. Les contraintes sont les suivantes:

  1. Une horloge entrante
  2. Un reset rentrant, actif à l'état bas.
  3. Un reset sortant actif, soit à l'état bas, soit à l'état haut selon la valeur d'un paramêtre fourni au module.

Modifiez votre design pour inclure le module de rééchantillonnage du reset proposé. (vous pouvez l'utiliser par exemple pour générer le reset du compteur sur l'horloge à 50Mhz).

Vérifiez la resynchronisation en simulation, faite la synthèse, puis rechargez le design.

10) CONCLUSION

 Vous disposez d'un squelette permettant de développer votre projet: 

  1. Conservez tout au long du projet les LEDs clignotantes qui permettent de savoir que votre design est actif. 
  2. Conservez de même le dispositif permettant de générer un reset "propre". 
  3. Les messages du synthétiseur sont nombreux. Dans les prochains travaux, vous devrez évidemment éliminer les Erreurs, mais aussi examiner soigneusement les Warnings et déterminer s'ils correspondent à un problème potentiel ou non. Dans la mesure du possible, tentez d'éliminer ces Warnings de façon à détecter plus facilement de nouveau problèmes au fur et a mesure que vous faites évoluer le code.
  4. Vous pouvez à tout moment utiliser la version graphique de l'outil Quartus pour examiner les résultats de synthèse. Pour cela il vous suffit d'ouvrir le projet créé dans l'outil. Faites cependant attention, si vous modifiez votre projet dans l'outil, vous ne retrouverez pas ces modifications dans vos scripts, et ainsi vous ne pourrez plus utiliser le Makefile pour recompiler votre projet.

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