Vous allez construire un petit jeu sur la carte qui consiste à jouer au "ping pong" : la balle est simulée par l'affichage d'une et une seule led et les raquettes sont simulées par les switch. Plusieurs étapes vous sont proposées, à vous d'aller le plus loin possible durant la séance voire en salle à accès libre ensuite...
Vous allez créer une chenillard sur les LED avec des bascules D. Un chenillard c'est simplement une led allumée qui circule sur les LED de manière cyclique . On commencera par afficher la LED 0 , puis 1,, puis 2... puis 15 et retour à 0 etc...
Pour mémoriser l’état précédent je vous propose d'utiliser 16 bascules D, une pour chaque LED
Créez le projet TP4 sous VIVADO, si ce n'est déjà fait. Vérifiez toujours si le projet utilise bien la carte Basys3 comme cible.
Créer une nouvelle source VHDL dans votre projet appelée fpd , inspirez-vous du cours 4. Afin de pouvoir démarrer avec la bascule 0 à '1' , vous ajoutez une generic qui permettra de choisir lors de l'instanciation la valeur initiale du port de sortie, il se place dans la définition de l'entity comme ceci:
entity fpd is
GENERIC (init_value: STD_Logic := '0');
Port ( d : in STD_LOGIC;
q : out STD_LOGIC:= init_value;
clk : in STD_LOGIC);
end fpd;
Facile de comprendre ce que fera init_value: par défaut la sortie Q est initialisée à '0' et si on utilise le init_value lors de l’instanciation on peut choisir la valeur '0' ou '1' explicitement.
Créer maintenant un module VHDL nommé shift avec en entrée btn(4:0), sw(15:0) et clk; en sortie led(15:0). dans cette architecture il faudra instancier 15 bascules fpd en reliant les sorties des unes avec les entrées des autres afin de créer le registre à décalage! Il faut aussi relier la sortie de fpd0 à l'entrée de fpd15 afin d'assurer la rotation. Voici un schéma pour un registre de longueur 4 ici la rotation se fait dans le sens inverse.
Pour initialisation de la sortie q d'une instance à '1' utilisez le code suivant avec un GENERIC MAP qui référence le GENERIC de tout à l'heure.
d0: fpd
GENERIC MAP(init_value => '1')
PORT MAP(
d => Q15,
q => Q0,
clk => clk
);
Toutes les sorties Qi doivent être finalement concaténée vers le signal led afin de visualiser la valeur du registre sur les led: voici le code à placer dans votre architecture.
led<= Q15&Q14&.....&Q5&Q4&Q3&Q2&Q1&Q0 ;
Une fois les codes sans erreur, comment çà fonctionne avec simulation, puis il faut regarder ce que cela produit avec Schematic. Vous savez comment forcer une clock il me semble ;-) Vous devriez observer un bit qui circule sur les 16 led à chaque top d'horloge....
Pour ceux qui veulent en savoir plus sur vhdl et comment instancier les 16 fpd en utilisant une boucle d'instanciation, vous pouvez essayer de ré-écrire votre code en vous inspirant de cet exemple For Generate (vous pourrez aussi le faire en fin de séance ou chez vous si vous avez le temps)
VHDL permet de construire un tel registre avec une description fonctionnelle. Il s'agit toujours d'un process sensible à clk. Il suffit de déclarer une variable temp de type std_logic_vector de 16 bits. A chaque top cette variable est mise à jour afin de réaliser la rotation des 16 bits du vecteur.temp
C'est cette variable temp qui est affectée au signal led à chaque changement d'état du process. Voici un des nombreux exemples qui existent sur le net.
Ecrire un nouveau module VHDL shift_vector, vérifiez le fonctionnement avec Simulation , comparez le circuit généré par Schematic, .
C'est plus simple à écrire, mais il faut être sûr que ça s'implémente comme vous le pensez!!
Souvenez-vous il faut un fichier xdc afin d'associer vos signaux aux broches du FPGA. Je vous ai déjàç donner le fichier complet pour la carte Basys3. Il faudra dé-commenter les ports (y compris pour la clock) que vous utiliserez et s'assurer qu'ils aient les mêmes noms.Il vous reste à générer le .bit et le charger sur la carte pour tester.
On ne voit pas grand chose sur la carte :-( En fait la clock va tellement vite que les led n'ont pas le temps de s'allumer!! Il faudra inclure dans votre design un diviseur de clock. Les diviseurs de clock sont assez fréquent dans les circuits. il y a plein de solutions portables écrites en VHDL, Xilinx propose aussi des générateurs automatiques à partir de VIVADO, non portable alors.( cf fin de TP)
Je vous propose d'implanter une solution simple et en VHDL. Le fichier xdc utilise une clock à 100Mhz, pour observer notre jeu, nous voudrions obtenir un 4 Hz, 4 shift /seconde. Il suffit de compter le nombre de cycle de la clock de référence et tous les 25 000 000 tops on inverse un signal qui servira alors de clock à 4 Hz.
Construisez un nouveau module VHDL clk_div avec en entrée clk et en sortie clk_4hz. Placez- y un process sensible à clk, à chaque front montant vous incrémentez un compteur initialisé à 0. Si ce compteur atteint la valeur X"BEBC20" , c'est de l'hexa, alors il suffit d'inverser le signal de sortie clk_4hz. Pour le compteur, une variable dans le process fera l'affaire!
N'oubliez par de remettre à 0 votre compteur à chaque changement de clk_4hz! Pour vous aider, quelques morceaux de code:
- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;
-- declaration d un compteur sur 24 bits
variable counter : unsigned(23 downto 0):= (others => '0');
Vérifiez par simulation sur 1 seconde que clk_4hz donne le résultat attendu. Regardez ce que VIVADO utilise comme circuit via Schematic, ça doit être systématique!
Créez alors un nouveau module VHDL chenillard, instanciez votre clk_div et votre shitf_vector. Finaliser les connections des fils entre tous les ports et signaux si besoin il y a, vérifiez la prise en compte de votre xdc dans le projet. Validez sur la carte. Faire valider par votre enseignant!!!
Si vous voulez garder une trace de la 1er étape, faites une copie de projet de TP4 sur TP4.1 en excluant les fichier générés.
Je vous propose une première extension qui consiste à changer le sens du chenillard lorsque le btn'(0) - au centre- est appuyé.
Transformez le code de shift_vector pour prendre en compte la valeur de btn(0) lors de chaque activation. On décale dans un sens ou dans l'autre en fonction de la valeur de btn(0).
Validez sur la carte.
Maintenant au lieu de changer de direction en fonction du btn, vous allez changer de direction seulement si la led allumée rencontre un switch positionné à '1' à la même position sur la carte. Dans ce cas il y a changement de sens.
Il est sans doute plus simple de garder en mémoire avec une bascule le sens de déplacement en cours par une variable interne au process droite_gauche. En cas de collision avec la raquette on inverse cette direction. En fonction de la direction on shift à droite ou à gauche. Ici l'état courant est pris en considération pour calculer l'état suivant de notre circuit. On verra comment généraliser ce mode de fonctionnement la semaine prochaine...
Dans la version précédente, lorsque la balle arrive sur un bord sans avoir rencontré de raquette, la balle se retrouve de l'autre coté de la table. Proposez une modification de la dernière version afin de prendre en compte le bord de la table : la led 15 ou 0 allumée doit également inverser le sens de la balle.
Validez sur la carte
Faire validez votre dernier développement avant de quitter la salle de ping pong ;-)
Pour ceux qui ont fini avant la fin et qui sont curieux, essayez de voir ce que propose l'IP Cataloggénéré par VIVADO avec l'outil Clocking Wizard, Que peut-on en faire pour notre diviseur de clock?