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 7 , puis 6,, puis 5... puis 0 et retour à 7 etc...
Pour mémoriser l’état précédent je vous propose d'utiliser 8 bascules D, une pour chaque LED
Créez le projet TP4 sous ISE, si ce n'est déjà fait.
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 7 à '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_valie: 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(7:0) et clk; en sortie led(7:0). dans cette architecture il faudra instancier 8 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 fpd7 afin d'assurer la rotation. Voici un schéma pour un registre de longueur 4.
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.
d7: fpd
GENERIC MAP(init_value => '1')
PORT MAP(
d => Q0,
q => Q7,
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<= Q7&Q6&Q5&Q4&Q3&Q2&Q1&Q0 ;
Une fois les codes sans erreur, il faut regarder ce que cela produit avec RTL View et comment çà fonctionne avec Isim. Vous savez comment forcer une clock il me semble ;-) Vous devriez observer un bit qui circule sur les 8 led à chaque top d'jhorloge....
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 8 bits. A chaque top cette variable est mise à jour afin de réaliser la rotation des 8 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, comparez le circuit généré par RTL View, vérifiez le fonctionnement avec Isim.
C'est plus simple à écrire, mais il faut être sur que ça s'implémente comme vous le pensez!!
Souvenez-vous il faut un fichier ucf afin d'associer vos signaux aux broches du FPGA. Sur le site de DIGILENT on peut trouver le fichier complet pour la carte nexys3. Je vous en mets une copie en bas de page... il faudra dé-commenter les ports 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 via Adept.
Vérifiez toujours avant la génération du .bit que le fichier ucf est bien dans la hiérarchie du top module, sinon aucune I/O ne sera connectée!
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 ISE, non portable alors.( cf fin de TP)
Je vous propose d'implanter une solution simple et en VHDL. Votre carte Nexys3 tourne à 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 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 ISE utilise comme circuit via RTL View, ç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 .ucf. 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 7 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 Core généré par ISE avec l'outil Clocking Wizard, Que peut-on en faire pour notre diviseur de clock?