Very High Speed Integrated Circuit Hardware Description Language/Le MicroBlaze
Nous avions affirmé dans l’introduction de ce livre que nous laisserions le domaine des 16 bits et des 32 bits à un autre livre. En effet, ce type de processeurs, est utilisé en général avec un système d'exploitation : n'existe-t-il pas un Linux pour le MicroBlaze? Mais nous avons quand même décidé de réaliser un projet autour du MicroBlaze avec nos étudiants d'où l'apparition de ce nouveau chapitre. Nous resterons cependant éloignés des aspects systèmes d'exploitation et des aspects systèmes temps réel.
Le MicroBlaze est spécifique à Xilinx ce qui signifie que ce chapitre est peu lisible par les étudiants ou enseignants utilisant Altera par exemple. Il faut savoir quand même qu’il existe une description libre du MicroBlaze chez Opencores, ce qui veut dire qu'on peut l'implanter dans n’importe quel F.P.G.A.. Pour montrer que nous sommes en aucun cas lié avec un constructeur nous espérons réaliser un chapitre sur le N.I.O.S. qui est le concurrent 32 bits de chez Altera. Il est actuellement en cours de rédaction.
Pour les impatients de découvrir le N.I.O.S., reportez-vous aussi au livre de P. P. Chu "Embedded SoPC Design with NIOS II Processor and VHDL Examples".
Nous avons classé ce chapitre au niveau 16 même si nous avons l'intention de travailler avec des étudiants de niveau 14. Cela est dû au fait que le projet sera très encadré et l'autonomie des étudiants assez réduite. Il nous faut bien essayer de travailler dans ce contexte avant d’en tirer des conclusions.
La partie logicielle destinée à gérer l’ensemble des opérations nécessaires à la mise en œuvre d'un processeur embarqué MicroBlaze s’appelle l'E.D.K. (Embedded Development Kit). Il s'agit en fait d'une collection de scripts Tcl/Tk liant divers outils en ligne de commande, et d'une interface graphique couvrant l'ensemble.
- Xilinx "Embedded Development Kit" sera appelé EDK dans la suite de ce chapitre comme dans toute la documentation Xilinx
- "Xilinx Platform Studio" sera appelé XPS dans la suite de ce chapitre. C'est une des composantes de l'EDK.
- "Software Development Kit" sera appelé SDK dans la suite de ce chapitre. C'est une des composantes de l'EDK.
L'EDK est donc composé essentiellement de deux logiciels :
- XPS (Xilinx Platform Studio) destiné à gérer la partie matérielle
- SDK (Software Development Kit) destiné à mettre à jour la partie logicielle.
Comme ces logiciels sont destinés à en cacher d'autres, ce chapitre sera émaillé de boîtes déroulantes qui elles aussi cacheront ce que Xilinx voudrait cacher avec son environnement de développement. Mais ici, elles peuvent être déroulées pour en apprendre plus.
Réaliser le tutoriel
[modifier | modifier le wikicode]Xilinx prétend vous faire mettre un MicroBlaze en moins de trente minutes dans un F.P.G.A. et nous pensons sincèrement qu’ils n'ont pas menti à ce sujet. Cela peut laisser croire cependant que les choses sont simples, ce qui est très loin d’être le cas. Dès que vous cherchez à modifier d'une manière ou d'une autre ce tutoriel, vous êtes confrontés à des problèmes. Un exemple peut être ? Si vous avez réalisé votre propre carte F.P.G.A., vous n'avez aucune chance de démarrer le tutoriel : cela ne veut pas dire que c’est impossible, mais cela veut dire que vous prenez le problème dans un sens qui n’est pas prévu par Xilinx comme un point de départ.
Il n’est pas difficile de trouver des tutoriels sur Internet mais en trouver qui correspondent vraiment à la version de votre EDK c’est une autre histoire. Contrairement à ce que l’on trouve sur Internet, nous n'allons pas présenter de copie d'écran qui risquent de devenir obsolètes bien trop vite.
Tutoriel pour carte spartan3
[modifier | modifier le wikicode]Nous n'allons pas décrire en détail la carte spartan3. Disons qu'elle possède une liaison série, une liaison PS/2, une liaison VGA, 8 interrupteurs, 4 boutons poussoirs, 8 leds, et 4 afficheurs sept segments.
Nous allons décrire brièvement comment faire tourner un MicroBlaze dans une carte spartan 3. Il suffit de suivre le tutoriel pour cela. Vous pourrez suivre plus ou moins facilement notre description sommaire car elle dépend de la version de l'EDK utilisée. Pour information, nous avons utilisé la version 9.2 pour faire ce travail.
- "Base System Builder" sera appelé BSB dans la suite de ce chapitre.
- BSB est une composante de XPS Xilinx Platform Sudio
Lancer BSB (Base System Builder)
[modifier | modifier le wikicode]La création d'un projet se fait avec BSB qui propose une série de boîtes de dialogues parmi les quelles on peut naviguer avec les célèbres "next" et "previous". Nous vous proposons de manière simplifiée la série des valeurs que l’on a pris pour notre projet.
- Project File Browse
- I would like to create a new Design
- board vendor : xilink
- choisir sa carte : spartan-3 Starter Board
- Board revision : E
- next
- recapitulatif
- next
- 50 Mhz 50 Mhz
- No Debug
- 16 KB
- next
- XPS UART light
- XPS GPIO pour LEDs_8Bit
- XPS GPIO pour LEDs_7SEGMENT
- XPS GPIO pour PUSHBUTTON_3Bit
- XPS GPIO pour DIP_switch_8Bit
- XPS MCH EMC pour SRAM
- next
- next
- STDIN : RS232
- STDOUT : RS232
- Boot memory : ilmb_cltrl
- next
- Memory test : rien changer
- next
- Peripheral test : ruen changer
- next
- Generate
- Finish
La présentation ci-dessus est difficile à lire : chaque décalage à droite correspond à une nouvelle boîte de dialogue. Elle n'est donnée qu’à titre d'exemple en espérant que le lecteur intéressé par réaliser le tutoriel avec une autre version de l'EDK y trouve suffisamment d'informations pour se lancer et peu importe la façon dont les boîtes de dialogues sont agencées.
Tout est prêt à ce niveau. D'ailleurs BSB se ferme et l’on se retrouve dans XPS. Les fichiers ainsi générés sont :
- un fichier qui a l'extension .mhs qui sert à décrire le matériel
- un fichier d'extension .mss qui sert à décrire le logiciel
Il nous faudra apprendre à manipuler ce type de fichier, mais nous ne nous y intéressons pas pour le moment.
Et la suite est laissée à XPS...
[modifier | modifier le wikicode]Une fois revenu sous XPS faire :
Harware → Generate Bitsream nous lance une série de commandes qui ont pour objectif de réaliser le fichier .bit.
En fait comme montré dans la boîte déroulante ci-dessous la génération du fichier .bit se fait à l'aide d'un certain nombre de commandes que l’on ne vous demande pas de détailler pour le moment. C'est naturellement pour cela qu'on les a mis dans une boîte déroulante.
xbash -q -c "cd /cygdrive/d/MBProjet/; /usr/bin/make -f demoMB5.make bits; exit;" started...
****************************************************
Creating system netlist for hardware specification..
****************************************************
platgen -p xc3s200ft256-4 -lang vhdl demoMB5.mhs
Running XST synthesis ...
Running NGCBUILD ...
bash -c "cd synthesis; ./synthesis.sh"
xst -ifn demoMB5_xst.scr -intstyle silent
xilperl D:/EDK/data/fpga_impl/manage_fastruntime_opt.pl -reduce_fanout no
xflow -wd implementation -p xc3s200ft256-4 -implement xflow.opt demoMB5.ngc
ngdbuild -p xc3s200ft256-4 -nt timestamp -bm demoMB5.bmm
"D:/MBProjet/implementation/demoMB5.ngc" -uc demoMB5.ucf demoMB5.ngd
#----------------------------------------------#
# Starting program map
# map -o demoMB5_map.ncd -pr b -ol high -timing demoMB5.ngd demoMB5.pcf
#----------------------------------------------#
#----------------------------------------------#
# Starting program par
# par -w -ol high demoMB5_map.ncd demoMB5.ncd demoMB5.pcf
#----------------------------------------------#
#----------------------------------------------#
# Starting program post_par_trce
# trce -e 3 -xml demoMB5.twx demoMB5.ncd demoMB5.pcf
#----------------------------------------------#
Il est temps de faire un petit résumé sur XPS.
On voit que le logiciel XPS est capable de partir d'une spécification générée par une série de boîtes de dialogues et d’en faire un projet complet qui finira donc par le célèbre fichier .bit : il est même possible de lancer le chargement du fichier .bit dans le FPGA. En clair, on peut se passer de l'ISE : plus exactement on n'a pas besoin de le lancer mais en fait il faut l'installer car tous ses outils sont nécessaires.
Le morceau de phrase "on peut se passer de l'ISE" dans l'encadré précédent n’est pas exact pour toutes les versions de l'EDK. Il me semble qu'au début la synthèse se faisait avec l'ISE puis que vers la version 9 (nous utilisons la 9.2 pour écrire ce chapitre) on en avait plus besoin et que pour les nouvelles versions on soit revenu en arrière ! Tout ceci nous montre les errements de Xilinx du point de vue de ses logiciels de conceptions. Nous n'avons pas du tout d'expérience avec les logiciels concurrents pour dire si c’est la même chose... Si quelqu’un peut y ajouter son expérience...
Tutoriel pour carte spartan3E
[modifier | modifier le wikicode]Nous n'allons pas décrire en détail la carte spartan3E. Disons qu'elle possède une liaison série, une liaison PS/2, une liaison VGA, 4 interrupteurs, 4 boutons poussoirs, 8 leds, un afficheur LCD.
Arrivé à ce point nous sommes très satisfaits d’avoir réussi à réaliser le tutoriel. Il nous reste cependant à être capable de modifier ce tutoriel pour l'adapter à nos propres besoins. Deux types de modifications s'offrent à nous :
- utiliser le matériel existant et réaliser des logiciels qui le mettent en œuvre
- modifier le matériel existant pour l'adapter à nos besoins.
Comme la première modification est de très très loin la plus simple, nous allons commencer par elle.
Modifier le programme qui s'exécute
[modifier | modifier le wikicode]Le logiciel responsable de la mise à jour du logiciel s’appelle le SDK. Nous nous sommes longtemps posé la question de l'interaction entre le SDK et XPS, à savoir, nous venons de modifier notre programme comment le faire savoir a XPS ? En fait c’est toute simple :
- vous modifiez le programme dans le SDK
- une sauvegarde lance sa compilation
- on revient dans XPS : "Device Configuration" → Update Bitstream
- toujours dans XPS : "Device Configuration" → download Bitstream
Si dans l'étape 3 le logiciel vous dit qu’il a rien à faire c’est probablement parce que vous n'avez aucun projet de choisi. Cela se fait en choisissant le projet dans la partie projet où vous pouvez voir plusieurs projets. Dans l'onglet "Applications" choisissez votre projet par un click droit et "Mark to Initialize BRAM".
Une autre manière de faire peut être d’utiliser le SDK seul et voici présenté sous forme de principe la façon de faire :
Une fois votre partie matérielle réalisée le SDK peut être utilisé seul pour développer la partie logicielle.
- "Device Configuration" → "Program Hardware Settings..." permet de déterminer quel programme va mettre à jour le fichier .BIT
- "Device Configuration" → "Program Harware" ou l'icone correspondant permet de charger dans le FPGA.
Comme d'habitude, cet ensemble d'actions réalisées par des menus cache des commandes plus ou moins complexes que l’on vous a mis dans une boîte déroulante pour les cacher pour l'instant.
make all
mb-gcc -c -mno-xl-soft-mul -mxl-pattern-compare -mcpu=v7.00.a -I../../microblaze_0_sw_platform/microblaze_0/include -xl-mode-executable -g -O2 -oTestApp_Memory.o ../../../TestApp_Memory/src/TestApp_Memory.c
Building target: TestApp_Memory.elf
mb-gcc -o TestApp_Memory.elf TestApp_Memory.o -mno-xl-soft-mul -mxl-pattern-compare -mcpu=v7.00.a -L../../microblaze_0_sw_platform/microblaze_0/lib -xl-mode-executable -T../../../TestApp_Memory/src/TestApp_Memory_LinkScr.ld
Finished building: TestApp_Memory.elf
La mise à jour du logiciel dans le cœur se fait par une commande "data2mem" déjà évoquée dans un chapitre précédent. Si mon projet s’appelle demoMB4 alors la commande est :
data2mem -bm "implementation/demoMB4_bd" -bt "implementation/demoMB4.bit" -bd
Interaction logicielle avec les logicores de base pour la carte spartan3
[modifier | modifier le wikicode]L'ensemble de ce qui va être réalisé maintenant correspond à des choses assez standard dans le domaine des microcontrôleurs, comme allumer des Diodes électroluminescentes (LEDs), gérer des afficheurs sept segments lire des valeurs sur des interrupteurs... La seule chose à apprendre est comment lire un PORT et comment ces PORTs sont connectés au matériel ?
Lire des interrupteurs
[modifier | modifier le wikicode]Nos interrupteurs d'entrée sont reliées à un port. Notre problème est donc d'apprendre comment lire le PORT.
//****** C MicroBlaze *******
#include "xio.h" // n’est pas présent par défaut
// ....
Xuint32 data;
//....
data = XIo_In32(XPAR_DIP_SWITCHES_8BIT_BASEADDR);
La fonction qui permet de lire un PORT est donc XIo_In32 et elle demande un nom majuscule comme paramètre. Ce nom est à trouver quelque part. C'est le fichier xparameters.h qui contient ces définitions importantes et il est facile à trouver et à lire avec le SDK. Voici les détails de ce qui nous intéresse :
//****** Extrait du fichier xparameters.h *******
/* Definitions for peripheral DIP_SWITCHES_8BIT */
#define XPAR_DIP_SWITCHES_8BIT_BASEADDR 0x81420000
#define XPAR_DIP_SWITCHES_8BIT_HIGHADDR 0x8142FFFF
#define XPAR_DIP_SWITCHES_8BIT_DEVICE_ID 3
#define XPAR_DIP_SWITCHES_8BIT_INTERRUPT_PRESENT 0
#define XPAR_DIP_SWITCHES_8BIT_IS_DUAL 0
En fait vous n'avez pas besoin de connaître la valeur de l'adresse de base (ici 0x81420000) mais par contre il vous faut connaître comment la désigner.
Allumer des LEDs
[modifier | modifier le wikicode]Allumer des leds est l'opération inverse de la lecture de valeurs à partir des interrupteurs. Cela veut dire que ce qui était entrée devient maintenant sortie. Sans grande surprise maintenant, la fonction qui s'occupe des sorties s’appelle "XIo_Out32".
//****** C MicroBlaze *******
#include "xio.h" // n’est pas présent par défaut
// ....
Xuint32 data;
//....
XIo_Out32(XPAR_LEDS_8BIT_BASEADDR, data);
Encore une fois cette constante à rallonge est à trouver dans le fichier xparameters.h À noter que les LEDs sont câblées à l’envers : le poids faible de la variable data allumera la led de gauche (qui porte le numéro 7). Voici, encore une fois, comment j’ai trouvé le nom du premier paramètre, en examinant le fichier d'entête :
//****** Extraits du fichier xparameters.h *******
/* Definitions for peripheral LEDS_8BIT */
#define XPAR_LEDS_8BIT_BASEADDR 0x81460000
#define XPAR_LEDS_8BIT_HIGHADDR 0x8146FFFF
#define XPAR_LEDS_8BIT_DEVICE_ID 0
#define XPAR_LEDS_8BIT_INTERRUPT_PRESENT 0
#define XPAR_LEDS_8BIT_IS_DUAL 0
Allumer des afficheurs sept segments
[modifier | modifier le wikicode]Notre carte est dotée de quatre afficheurs sept segments qu’il nous faut apprendre à utiliser. Bon commençons par chercher l'adresse, toujours avec le fichier d'entête :
//****** Extraits du fichier xparameters.h *******
//* Definitions for peripheral LED_7SEGMENT */
#define XPAR_LED_7SEGMENT_BASEADDR 0x81440000
#define XPAR_LED_7SEGMENT_HIGHADDR 0x8144FFFF
#define XPAR_LED_7SEGMENT_DEVICE_ID 1
#define XPAR_LED_7SEGMENT_INTERRUPT_PRESENT 0
#define XPAR_LED_7SEGMENT_IS_DUAL 0
Mais les choses se compliquent ensuite car vous devez envoyer 8 bits (pour les sept segments et le point) et quatre bits ensuite pour sélectionner un afficheur.
Quand on part des poids faibles pour monter vers les poids forts, on montre que les 4 bits poids faibles sont pour la sélection de l'afficheur (gauche en premier puis droite). La sélection d'un afficheur se fait par un zéro. Vient ensuite le point, puis les segments gfedcba (dans cet ordre) qui sont actifs à l'état bas.
Voici donc un bout de code capable d'afficher une valeur hexadécimale sur un afficheur sept segments :
#include "xio.h"
//....
Xuint32 data;
unsigned char t[]={0x01,0x4F,0x12,0x06,0x4C,0x24,0x20,0x0F,0x00,0x04,0x08,0x60,0x31,0x42,0x30,0x38};
//...
// acquisition de data d'une manière ou d'une autre
//....
XIo_Out32(XPAR_LED_7SEGMENT_BASEADDR, t[data&0x000F]<<5|0x1E);
Il est important pour le lecteur d'essayer de comprendre ce code.
Utiliser des logicores moins standard
[modifier | modifier le wikicode]Lors de la réalisation du tutoriel nous avons choisi d’utiliser un logicore appelé "XPS MCH EMC pour SRAM". Il sert à adresser de la mémoire statique qui est à l'extérieur du FPGA et qui est présente sur la carte spartan-3. Nous allons nous poser la question de son utilisation dans cette section.
Réaliser son propre périphérique et le connecter
[modifier | modifier le wikicode]C'est là que les problèmes commencent vraiment. Nous allons commencer par un exemple très simple trouvé dans un tutoriel (import_peripheral_tutorial.pdf) qui ne semble pas être un document officiel de Xilinx. Légèrement modifié, il consistera à réaliser notre propre afficheur sur les 8 leds de la carte. C'est un périphérique très simple mais suffisamment complexe pour montrer toutes les facettes du problème.
Présentation générale
[modifier | modifier le wikicode]- "Create or Import Peripheral" sera appelé CIP dans la suite ce de chapitre.
- "Processor Local Bus" sera appelé PLB dans la suite de ce chapitre.
- "Intellectual-Property Interface" sera appelé IPIF dans la suite de ce chapitre. C'est une librairie hautement paramétrisable et vérifiée donc très conseillée à l'utilisation.
- "IP Interconnect" sera appelé IPIC dans la suite de ce document. C'est un module VHDL généré automatiquement.
La création d'un nouveau périphérique passe donc par un certain nombre d'étapes que nous allons détailler maintenant.
Étape 1
[modifier | modifier le wikicode]Pour créer un périphérique il faut lancer CIP. Cela se fait à partir de "Xilinx Platform Studio" dans le menu
- "Hardware" → "Create or Import Peripheral" (avec la version 9.2)
Cela permet de choisir un certain nombre d'options concernant :
- la connexion au processeur : par PLB ou autre
- les services IPIF que l’on utilise
- "Interrupt Service" : choisir si l’on utilise les interruptions
- IPIC sera généré automatiquement. Il nécessite l’utilisation de signaux que l’on vous conseillera fortement d'utiliser.
Ces choix permettent de fabriquer un certain nombre de fichiers :
- un fichier d'extension mdd
- des fichiers utilisables avec l'ISE
Étape 2
[modifier | modifier le wikicode]Si cette étape est optionnelle, elle est fortement conseillée. Elle consiste à utiliser l'ISE pour modifier et surtout compiler pour vérification votre périphérique. Bien sûr à ce stade il vous sera très difficile de vérifier votre périphérique car il ne fonctionne correctement, en principe, que connecté au bus du processeur. Pourtant, vous pourrez détecter toutes les fautes de syntaxe que vous êtes susceptibles de faire et c’est déjà pas mal. J'insiste : vous ne pouvez pas faire autre chose que de la vérification de syntaxe à ce stade car les modèles générés automatiquement à l'étape précédente utilisent une librairie inconnue par l'ISE. Nous n'avons pas eu le courage de la rechercher pour l'ajouter au projet pour le moment.
Étape 3
[modifier | modifier le wikicode]On utilise de nouveau CIP mais en partant dans "Import existing peripheral". Cette phase peut sembler étrange et beaucoup d'entre-vous penseront qu'on pouvait le faire dès l'étape 1. En fait je pense que Xilinx a choisi cette technique de réutiliser CIP, car elle vient après la mise au point de l'étape 2. Son objectif est donc de choisir quel types de fichiers vont être associés au périphérique : vhdl, edn, edf, ngc ou ngo. Il est possible que ce soit à ce moment aussi que les fichiers de programme C soient générés (ou à l'étape 1 ou 4 ???). L'autre différence avec l'étape 1 est qu'ici on dispose des fichiers VHDL alors que l'étape 1 avait comme objectif de les construire.
L'autre point c’est que si l’on suit les étapes comme indiqué dans ce cours, on dispose déjà du modèle de périphérique : il faudra donc choisir "Use Peripheral Analysis Order file (*.pao)" quelque part dans cette étape.
Dernier point : cette étape compile les fichiers VHDL du périphérique.
Étape 4
[modifier | modifier le wikicode]La connexion du périphérique se fait simplement dans XPS. Il vous faudra utiliser pour cela absolument les trois onglets "Bus Interfaces", "PORT" et "Adresses" pour réaliser ce travail.
Ne pas oublier aussi à ce stade de modifier le fichier ucf.
Mise en œuvre pratique
[modifier | modifier le wikicode]Nous allons reprendre dans cette section l’ensemble des étapes précédemment décrites pour notre cahier des charges.
Notre cahier des charges
[modifier | modifier le wikicode]Notre périphérique va être très simple : il consiste à réaliser un PORT de sortie qui soit capable d'allumer les 8 leds de la carte.
Détails sur l'étape 1 : créer le modèle de périphérique
[modifier | modifier le wikicode]Son objectif est donc de créer le modèle du périphérique que l’on veut réaliser. C'est dans XPS que tout va se passer.
- Harware → Create or Import Peripheral puis Next
- cocher "Create template for a new peripheral" puis Next
- cocher "to an XPS Project" puis Next
- donner un nom et choisir une version (on choisira comme nom myleds8) puis Next
- cocher "Processor Local Bus (PLB V4.6)" puis Next
- laisser coché "user logic software register" puis Next
- ne rien changer puis Next
- Choisir un registre (de 32 bits) puis Next
- ne rien changer pour les bus puis Next
- ne rien changer deux fois puis "Finish"
- ne rien changer pour les bus puis Next
- Choisir un registre (de 32 bits) puis Next
- ne rien changer puis Next
- laisser coché "user logic software register" puis Next
- cocher "Processor Local Bus (PLB V4.6)" puis Next
- donner un nom et choisir une version (on choisira comme nom myleds8) puis Next
- cocher "to an XPS Project" puis Next
- cocher "Create template for a new peripheral" puis Next
Encore une fois la présentation ci-dessus n’est pas facile à lire mais elle donne les informations principales dont vous aurez besoin quelle que soit la version de l'EDK utilisée. Les décalages à droites représentent des boîtes de dialogues successives.
Détails sur l'étape 2 : vérifier la syntaxe du périphérique
[modifier | modifier le wikicode]Il nous faut maintenant reprendre ce modèle avec l'ISE, le modifier et vérifier sa syntaxe. L'étape une précédente a généré un fichier de projet pour l'ISE mais avec ma version il n'y a rien dedans. Il faut donc choisir le FPGA cible et ajouter les fichiers à modifier à la main. Puisque notre nouveau périphérique s’appelle "myleds8", deux fichiers VHDL sont générés à l'étape précédente (dans pcore/myleds8_v1_00_a/hdl/vhdl) :
- user_logic.vhd est un fichier d'interface au bus PLB qui est toujours généré mais qui n’est pas complètement identique à chaque fois. Il dépend en effet du nombre de registres que vous avez choisi dans votre périphérique.
- myleds8.vhd : son nom dépend de votre nom de périphérique.
Vous avez peut être remarqué qu’à l'étape précédente on ne nous a jamais demandé si l’on avait des sorties dans notre périphérique qui sortiront du FPGA. C'est maintenant que nous allons les ajouter.
Modification du fichier myleds8.vhd
[modifier | modifier le wikicode]Vous devez chercher dans ce fichier l'entité qui s’appelle myleds8 puis la partie PORT de cette entité et ajouter dans la partie faite pour cela votre sortie qui s’appelle leds8 ici :
port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
--USER ports added here
leds8 : out std_logic_vector(7 downto 0);
-- ADD USER PORTS ABOVE THIS LINE ------------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol ports, do not add to or delete
SPLB_Clk : in std_logic;
-- ....
Vous devez connecter cette sortie au bon composant. Il y en a deux et c’est le deuxième qui a une entité "user_logic".
------------------------------------------
-- instantiate User Logic
------------------------------------------
USER_LOGIC_I : entity myleds8_v1_00_a.user_logic
generic map
(
-- MAP USER GENERICS BELOW THIS LINE ---------------
--USER generics mapped here
-- MAP USER GENERICS ABOVE THIS LINE ---------------
C_SLV_DWIDTH => USER_SLV_DWIDTH,
C_NUM_REG => USER_NUM_REG
)
port map
(
-- MAP USER PORTS BELOW THIS LINE ------------------
--USER ports mapped here
leds8 => leds8,
-- MAP USER PORTS ABOVE THIS LINE ------------------
Bus2IP_Clk => ipif_Bus2IP_Clk,
Bus2IP_Reset => ipif_Bus2IP_Reset,
-- ......
où l’on a ajouté que leds8 est relié à leds8. Mais "user_logic" se trouve dans le deuxième fichier et n'a pas cette sortie pour le moment.
Modification de user_logic.vhd
[modifier | modifier le wikicode]On commence par ajouter la sortie dans l'entité :
entity user_logic is
generic
(
-- ADD USER GENERICS BELOW THIS LINE ---------------
--USER generics added here
-- ADD USER GENERICS ABOVE THIS LINE ---------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol parameters, do not add to or delete
C_SLV_DWIDTH : integer := 32;
C_NUM_REG : integer := 1
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
--USER ports added here
leds8 : out std_logic_vector(7 downto 0);
-- ADD USER PORTS ABOVE THIS LINE ------------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol ports, do not add to or delete
Bus2IP_Clk : in std_logic;
Bus2IP_Reset : in std_logic;
Bus2IP_Data : in std_logic_vector(0 to C_SLV_DWIDTH-1);
Bus2IP_BE : in std_logic_vector(0 to C_SLV_DWIDTH/8-1);
Bus2IP_RdCE : in std_logic_vector(0 to C_NUM_REG-1);
Bus2IP_WrCE : in std_logic_vector(0 to C_NUM_REG-1);
IP2Bus_Data : out std_logic_vector(0 to C_SLV_DWIDTH-1);
IP2Bus_RdAck : out std_logic;
IP2Bus_WrAck : out std_logic;
IP2Bus_Error : out std_logic
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
Maintenant, il va nous falloir relier tout le monde. Ceci est fait en modifiant la fin de l'architecture (regardez de près l'extrait donné ci-dessous, il finit par le "END" de l'architecture) :
------------------------------------------
-- Example code to drive IP to Bus signals
------------------------------------------
IP2Bus_Data <= slv_ip2bus_data when slv_read_ack = '1' else
(others => '0');
IP2Bus_WrAck <= slv_write_ack;
IP2Bus_RdAck <= slv_read_ack;
IP2Bus_Error <= '0';
-- ceci est ajouté par nos soin :
leds8 <= slv_reg0(24 to 31);
end IMP;
Nous avons perdu du temps pour trouver que nos 8 bits sont reliés aux fils 24:31 puisque nous avons essayé 0:7 auparavant sans succès.
Résumé schématique du travail réalisé
[modifier | modifier le wikicode]Voici schématiquement ce que nous avons fait :
Pour cette figure les chiffres 1, 2, 3 et 4 désignent l’ordre des opérations présentées ci-dessus (dans les deux sections précédentes) avec leurs quatre extraits en VHDL.
D'autre part les signes + devant des entrées ou sorties veulent dire que l’on a ajouté ces entrées ou sorties dans les entités correspondantes.
Nous avons utilisé un seul registre de 32 bits : le signal correspondant est généré automatiquement et s’appelle "slv_reg0". Seuls 8 bits de ce signal nous seront utiles.
Détails sur l'étape 3 : enregistrer le périphérique dans l'EDK
[modifier | modifier le wikicode]Pour entreprendre l'étape 3 qui consiste à enregistrer votre périphérique dans l'EDK, il vous faut des fichiers VHDL correctement écrits car ils seront compilés à ce stade. Cette étape ressemble à l'étape 1 sauf qu'on ne crée plus un modèle de périphérique on l'enregistre.
- Harware → Create or Import Peripheral puis Next
- cocher "Import existing peripheral" puis Next
- cocher "to an XPS Project" puis Next
- donner un nom et choisir une version (on choisira bien sûr myleds8) puis Next puis "yes" sur boite de dialogue
- cocher "HDL Source File" puis Next
- cocher "Use existing peripheral Analysis Order (*.pao)" et chercher votre fichier pao dans le répertoire "data" de votre périphérique puis Next
- ne rien changer puis Next : c’est cette étape qui compile tout votre périphérique
- Cocher PLBV46 Slave (SPLB) puis Next
- cliquer 6 fois sur Next en décochant l'interruption au passage
- puis "Finish"
- cliquer 6 fois sur Next en décochant l'interruption au passage
- Cocher PLBV46 Slave (SPLB) puis Next
- ne rien changer puis Next : c’est cette étape qui compile tout votre périphérique
- cocher "Use existing peripheral Analysis Order (*.pao)" et chercher votre fichier pao dans le répertoire "data" de votre périphérique puis Next
- cocher "HDL Source File" puis Next
- donner un nom et choisir une version (on choisira bien sûr myleds8) puis Next puis "yes" sur boite de dialogue
- cocher "to an XPS Project" puis Next
- cocher "Import existing peripheral" puis Next
À ce stade votre périphérique existe bel et bien et peut donc être utilisé dans votre EDK. Il peut être global (utilisable par tous) ou local (utilisable par tous les projets du répertoire courant). Puisque nous avons coché "to an XPS Project", il sera local pour nous.
Détails sur l'étape 4 : connecter le périphérique
[modifier | modifier le wikicode]Pour connecter votre périphérique, vous le cherchez dans l'onglet "IP Catalog" (à gauche) et "Project Local pcores" et vous double cliquez dessus. Il apparaît alors dans la partie droite de XPS mais il n’est pas encore connecté. Vous allez utiliser les trois onglets pour le faire :
- onglet "Bus Interface" : au-dessous de myleds8_0 apparaît SPLB en non connecté. Connectez-le en "mb_plb".
- onglet "Ports" : chercher leds8 et faire "make external" pour les rendre externes au FPGA. Un nom vous sera donné pour vos sorties. Pour nous c’est "myleds8_0_leds8".
- onglet "Adresses" : choisir 0x84280000 et 1 seule adresse (nous n'avons qu'un seul registre).
Il nous faut maintenant modifier le fichier ucf.
Pour trouver le nom de la sortie véritable à utiliser dans le fichier ucf il vous faudra lire le fichier *.mhs correspondant à votre projet. Pour nous, nous trouvons l'information cherchée au début du fichier :
###############################################################################
PARAMETER VERSION = 2.1.0
PORT fpga_0_RS232_RX_pin = fpga_0_RS232_RX, DIR = I
PORT fpga_0_RS232_TX_pin = fpga_0_RS232_TX, DIR = O
PORT fpga_0_LED_7SEGMENT_GPIO_d_out_pin = fpga_0_LED_7SEGMENT_GPIO_d_out, DIR = O, VEC = [0:11]
PORT fpga_0_Push_Buttons_3Bit_GPIO_in_pin = fpga_0_Push_Buttons_3Bit_GPIO_in, DIR = I, VEC = [0:2]
PORT fpga_0_DIP_Switches_8Bit_GPIO_in_pin = fpga_0_DIP_Switches_8Bit_GPIO_in, DIR = I, VEC = [0:7]
PORT fpga_0_SRAM_Mem_A_pin = fpga_0_SRAM_Mem_A, DIR = O, VEC = [12:29]
PORT fpga_0_SRAM_Mem_DQ_pin = fpga_0_SRAM_Mem_DQ, DIR = IO, VEC = [0:31]
PORT fpga_0_SRAM_Mem_OEN_pin = fpga_0_SRAM_Mem_OEN, DIR = O, VEC = [0:0]
PORT fpga_0_SRAM_Mem_CEN_pin = fpga_0_SRAM_Mem_CEN, DIR = O, VEC = [0:0]
PORT fpga_0_SRAM_Mem_CEN_1_pin = fpga_0_SRAM_Mem_CEN, DIR = O, VEC = [0:0]
PORT fpga_0_SRAM_Mem_WEN_pin = fpga_0_SRAM_Mem_WEN, DIR = O
PORT fpga_0_SRAM_Mem_BEN_pin = fpga_0_SRAM_Mem_BEN, DIR = O, VEC = [0:3]
PORT sys_clk_pin = dcm_clk_s, DIR = I, SIGIS = CLK, CLK_FREQ = 50000000
PORT sys_rst_pin = sys_rst_s, DIR = I, RST_POLARITY = 1, SIGIS = RST
PORT myleds8_0_leds8_pin = myleds8_0_leds8, DIR = O, VEC = [7:0]
PORT ExternalPort_0 = net_ExternalPort_0, DIR = O
qui nous dit que les noms que nous devrons utiliser dans le fichier ucf sont "myleds8_0_leds8_pin" !!!! Nous y avons mis quelques points d'exclamation car ce détail nous a bloqué plusieurs heures. Eh bien oui c’est plus facile si l’on vous dit où trouver l'information !
Nous pouvons maintenant modifier le fichier ucf comme suit :
....
#### Module LEDs_8Bit constraints
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<0> LOC=k12;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<1> LOC=p14;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<2> LOC=l12;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<3> LOC=n14;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<4> LOC=p13;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<5> LOC=n12;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<6> LOC=p12;
#Net fpga_0_LEDs_8Bit_GPIO_d_out_pin<7> LOC=p11;
Net myleds8_0_leds8_pin<0> LOC=k12;
Net myleds8_0_leds8_pin<1> LOC=p14;
Net myleds8_0_leds8_pin<2> LOC=l12;
Net myleds8_0_leds8_pin<3> LOC=n14;
Net myleds8_0_leds8_pin<4> LOC=p13;
Net myleds8_0_leds8_pin<5> LOC=n12;
Net myleds8_0_leds8_pin<6> LOC=p12;
Net myleds8_0_leds8_pin<7> LOC=p11;
....
Reste maintenant à utiliser le périphérique.
Programme C utilisant notre nouveau périphérique
[modifier | modifier le wikicode]Ici notre périphérique est tellement simple qu’il est facile à programmer. Voici un extrait d'un code possible :
// Located in: microblaze_0/include/xparameters.h
#include "xparameters.h"
#include "stdio.h"
#include "xutil.h"
//==== ajouté car non présent par défaut
#include "xio.h"
//====================================================
int main (void) {
/*
* Enable and initialize cache
*/
#if XPAR_MICROBLAZE_0_USE_ICACHE
microblaze_init_icache_range(0, XPAR_MICROBLAZE_0_CACHE_BYTE_SIZE);
microblaze_enable_icache();
#endif
#if XPAR_MICROBLAZE_0_USE_DCACHE
microblaze_init_dcache_range(0, XPAR_MICROBLAZE_0_DCACHE_BYTE_SIZE);
microblaze_enable_dcache();
#endif
//=================================
// Sortie sur nouveau PORT
XIo_Out32(XPAR_MYLEDS8_0_BASEADDR, 0xAA); // doit allumer une led sur deux
print("-- Entering main() --\r\n");
// La suite est coupée
Ce programme utilise directement "XIo_Out32" sans utiliser la librairie qui est faite en même temps que le projet.
Voila maintenant décrite ci-dessus la trame complète pour créer un périphérique. Seule notre imagination et la place dans notre FPGA peut constituer un frein au développements de périphériques avancés : liaison PS/2 ou VGA. Pour votre information, l’ensemble réalisé prend 80% de la place dans un FPGA spartan 3 200. Si l’on veut rester raisonnable, il ne faut plus rien mettre dans le FPGA.
Travail à réaliser
[modifier | modifier le wikicode]Comme d'habitude dans ce livre, ce chapitre sert à l'un des auteurs comme trame de travail pour un projet d'étudiants d'où la présence de cette section.
Projet tutoré
[modifier | modifier le wikicode]On vous demande d'implanter un microBlaze dans une carte spartan 3. Ce FPGA est un peu petit pour cela mais suffira. On prend cette carte car elle dispose de 4 afficheurs sept segments, de leds, d'interrupteurs, de boutons poussoirs et d'une liaison RS232.
On vous demande de réaliser tous les programmes standards en C sur ce genre de périphériques :
chenillars, compteur sur 4 digits, utilisation de la RS232, de la mémoire RAM.
Le périphérique
[modifier | modifier le wikicode]Pour cette partie on changera de carte pour avoir un peu plus de place dans le FPGA. On vous demande de développer un périphérique (sans microBlaze) destiné à un jeu appelé Casse-briques sur écran VGA.
Une seule raquette est mobile verticalement. L'autre est présente mais son déplacement est lié à celui de la balle. Donc le rebond sur cette raquette se fait toujours. Comme c’est le processeur qui gère cette raquette droite qui suit la balle, il faut quand même un port de 8 bits pour la déplacer.
Nous présentons ci-dessus l'écran VGA pour le casse-brique dans l'hypothèse où le déplacement de la raquette liée à la balle est faite par le processeur. Si l’on compare au jeu de Pong du chapitre sur l'ATMega8, il faut 8 bits supplémentaire pour gérer ce nouveau jeu.
Une fois le périphérique réalisé vous devez le connecter au microBlaze.
Connexion du périphérique au microBlaze
[modifier | modifier le wikicode]La connexion de notre périphérique au microBlaze est un peu plus complexe que dans l'exemple précédent. Voici un schéma représentant le travail à réaliser.
Nous n'avons pas réussi à faire fonctionner le périphérique comme indiqué dans la figure ci-dessus. Nous n'avons pas testé toutes les possibilités mais nous n'avons jamais réussi à voir apparaître les sorties ajoutée à l'entité globale pour les rendre externes : tout se passait comme si l'outil de synthèse de périphérique ne voyait qu'un périphérique sans sortie physique (ceci toujours avec la version 9.2 qui est la seule pour laquelle nous possédons une licence) !!!
Nous laissons cette figure même si elle ne fonctionne pas pour éviter que quelqu’un fasse la même erreur que nous et pour montrer que le chemin le plus court pour aller d'un point à un autre n’est pas forcément la ligne droite !!!
La seule expérience que nous avions était celle décrite plus haut où toute la partie logique est entièrement dans le fichier user_logic.vhd et nous avons donc décidé de recommencer comme cela (figure ci-dessous) et le miracle s'est produit.
On peut voir sur la figure que :
- notre IPCore s’appelle "vgacassbriqu"
- notre gestion complète du VGA s’appelle "VGATop". Cette partie est supposée être au point.
- usr_logic est un composant généré automatiquement avec 4 registres 32 bits appelés slv_reg0, slv_reg1, slv_reg2 et slv_reg3. Ce nombre de registres a été fixé à l'étape 1 avec le Wizard.
- le dessin montre comment ces 32 bits sont reliés aux déplacements de la balle et des raquettes, à la gestion des scores et aux murs de briques. Ces connexions sont extrêmement importantes car elles influenceront directement l'écriture des sous-programmes.
Connexion du périphérique et VHDL
[modifier | modifier le wikicode]Voici des extraits VHDL qui montrent le travail réalisé. On ajoute d’abord les sorties au périphérique :
entity vgacassbriqu is
generic
(
-- ADD USER GENERICS BELOW THIS LINE ---------------
--USER generics added here
-- ADD USER GENERICS ABOVE THIS LINE ---------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol parameters, do not add to or delete
C_BASEADDR : std_logic_vector := X"FFFFFFFF";
C_HIGHADDR : std_logic_vector := X"00000000";
C_SPLB_AWIDTH : integer := 32;
C_SPLB_DWIDTH : integer := 128;
C_SPLB_NUM_MASTERS : integer := 8;
C_SPLB_MID_WIDTH : integer := 3;
C_SPLB_NATIVE_DWIDTH : integer := 32;
C_SPLB_P2P : integer := 0;
C_SPLB_SUPPORT_BURSTS : integer := 0;
C_SPLB_SMALLEST_MASTER : integer := 32;
C_SPLB_CLK_PERIOD_PS : integer := 10000;
C_FAMILY : string := "virtex5"
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
--USER ports added here
hsynch,vsynch,red,green,blue : out STD_LOGIC; -- oui oui c’est bien ici que cela se passe
-- ADD USER PORTS ABOVE THIS LINE ------------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol ports, do not add to or delete
SPLB_Clk : in std_logic;
SPLB_Rst : in std_logic;
.....
Toujours dans le même fichier on connecte maintenant :
------------------------------------------
-- instantiate User Logic
------------------------------------------
USER_LOGIC_I : entity vgacassbrik_v1_00_b.user_logic
generic map
(
-- MAP USER GENERICS BELOW THIS LINE ---------------
--USER generics mapped here
-- MAP USER GENERICS ABOVE THIS LINE ---------------
C_SLV_DWIDTH => USER_SLV_DWIDTH,
C_NUM_REG => USER_NUM_REG
)
port map
(
-- MAP USER PORTS BELOW THIS LINE ------------------
--USER ports mapped here
hsynch => hsynch, -- c’est ici que cela se passe
vsynch => vsynch,
red => red,
green => green,
blue => blue,
-- MAP USER PORTS ABOVE THIS LINE ------------------
Bus2IP_Clk => ipif_Bus2IP_Clk,
-- .............
On ajoute ensuite les sorties à l'entité usr_logic
entity user_logic is
generic
(
-- ADD USER GENERICS BELOW THIS LINE ---------------
--USER generics added here
-- ADD USER GENERICS ABOVE THIS LINE ---------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol parameters, do not add to or delete
C_SLV_DWIDTH : integer := 32;
C_NUM_REG : integer := 4
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
--USER ports added here
hsynch,vsynch,red,green,blue : out STD_LOGIC;
-- ADD USER PORTS ABOVE THIS LINE ------------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol ports, do not add to or delete
Bus2IP_Clk : in std_logic;
Et enfin on connecte tout le monde comme ceci :
------------------------------------------
-- Example code to drive IP to Bus signals
------------------------------------------
IP2Bus_Data <= slv_ip2bus_data when slv_read_ack = '1' else
(others => '0');
IP2Bus_WrAck <= slv_write_ack;
IP2Bus_RdAck <= slv_read_ack;
IP2Bus_Error <= '0';
------------------------------------------
-- my own signals
------------------------------------------
x_ball <= slv_reg0(22 to 31);
y_ball <= slv_reg0(6 to 15);
y_raqG <= slv_reg1(24 to 31);
y_raqD <= slv_reg1(8 to 15);
score <= slv_reg2(24 to 31);
mur1 <= slv_reg3(24 to 31);
mur2 <= slv_reg3(8 to 15);
------------------------------------------
-- my own components
------------------------------------------
USER_LOGIC_II : VGAtop
port map (
clk_50 => Bus2IP_Clk,
x_rect => x_ball,
y_rect => y_ball,
y_raquG => y_raqG,
y_raquD => y_raqD,
scoreG => score,
ligne1 => mur1,
ligne2 => mur2,
hsynch => hsynch,
vsynch => vsynch,
red => red,
green => green,
blue => blue
);
end IMP;
Après cela il suffit de connecter le périphérique en ajoutant celui-ci dans notre projet et en ajoutant les sorties VGA dans le fichier ucf :
#### Module VGATop : VGA outputs (spartan 3) Net vgacassbriqu_0_blue_pin LOC=R11; Net vgacassbriqu_0_green_pin LOC=T12; Net vgacassbriqu_0_red_pin LOC=R12; Net vgacassbriqu_0_vsynch_pin LOC=T10; Net vgacassbriqu_0_hsynch_pin LOC=R9;
Lors de la connexion de votre périphérique demandez un espace d'adressage de 16 et non de 4. Vous avez certes 4 registres mais de 4 octets chacun, cela fait donc un espace d'adressage de 16 octets en tout ! On en profite pour rappeler ici que tout multiple de 16, comme espace d'adressage, conviendrait et même simplifierait la logique de décodage.
Solution pour le fichier usr_logic.vhd
[modifier | modifier le wikicode]Le fichier usr_logic.vhd est généré automatiquement lors de la création du périphérique. Mais il est modifié comme suit.
------------------------------------------------------------------------------
-- user_logic.vhd - entity/architecture pair
------------------------------------------------------------------------------
--
-- ***************************************************************************
-- ** Copyright (c) 1995-2007 Xilinx, Inc. All rights reserved. **
-- ** **
-- ** Xilinx, Inc. **
-- ** XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" **
-- ** AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND **
-- ** SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, **
-- ** OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, **
-- ** APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION **
-- ** THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, **
-- ** AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE **
-- ** FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY **
-- ** WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE **
-- ** IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR **
-- ** REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF **
-- ** INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS **
-- ** FOR A PARTICULAR PURPOSE. **
-- ** **
-- ***************************************************************************
--
------------------------------------------------------------------------------
-- Filename: user_logic.vhd
-- Version: 1.00.b
-- Description: User logic.
-- Date: Wed Dec 14 13:57:18 2011 (by Create and Import Peripheral Wizard)
-- VHDL Standard: VHDL'93
------------------------------------------------------------------------------
-- Naming Conventions:
-- active low signals: "*_n"
-- clock signals: "clk", "clk_div#", "clk_#x"
-- reset signals: "rst", "rst_n"
-- generics: "C_*"
-- user defined types: "*_TYPE"
-- state machine next state: "*_ns"
-- state machine current state: "*_cs"
-- combinatorial signals: "*_com"
-- pipelined or register delay signals: "*_d#"
-- counter signals: "*cnt*"
-- clock enable signals: "*_ce"
-- internal version of output port: "*_i"
-- device pins: "*_pin"
-- ports: "- Names begin with Uppercase"
-- processes: "*_PROCESS"
-- component instantiations: "<ENTITY_>I_<#|FUNC>"
------------------------------------------------------------------------------
-- DO NOT EDIT BELOW THIS LINE --------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library proc_common_v2_00_a;
use proc_common_v2_00_a.proc_common_pkg.all;
-- DO NOT EDIT ABOVE THIS LINE --------------------
--USER libraries added here
------------------------------------------------------------------------------
-- Entity section
------------------------------------------------------------------------------
-- Definition of Generics:
-- C_SLV_DWIDTH -- Slave interface data bus width
-- C_NUM_REG -- Number of software accessible registers
--
-- Definition of Ports:
-- Bus2IP_Clk -- Bus to IP clock
-- Bus2IP_Reset -- Bus to IP reset
-- Bus2IP_Data -- Bus to IP data bus
-- Bus2IP_BE -- Bus to IP byte enables
-- Bus2IP_RdCE -- Bus to IP read chip enable
-- Bus2IP_WrCE -- Bus to IP write chip enable
-- IP2Bus_Data -- IP to Bus data bus
-- IP2Bus_RdAck -- IP to Bus read transfer acknowledgement
-- IP2Bus_WrAck -- IP to Bus write transfer acknowledgement
-- IP2Bus_Error -- IP to Bus error response
------------------------------------------------------------------------------
entity user_logic is
generic
(
-- ADD USER GENERICS BELOW THIS LINE ---------------
--USER generics added here
-- ADD USER GENERICS ABOVE THIS LINE ---------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol parameters, do not add to or delete
C_SLV_DWIDTH : integer := 32;
C_NUM_REG : integer := 4
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
--USER ports added here
hsynch,vsynch,red,green,blue : out STD_LOGIC;
-- ADD USER PORTS ABOVE THIS LINE ------------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol ports, do not add to or delete
Bus2IP_Clk : in std_logic;
Bus2IP_Reset : in std_logic;
Bus2IP_Data : in std_logic_vector(0 to C_SLV_DWIDTH-1);
Bus2IP_BE : in std_logic_vector(0 to C_SLV_DWIDTH/8-1);
Bus2IP_RdCE : in std_logic_vector(0 to C_NUM_REG-1);
Bus2IP_WrCE : in std_logic_vector(0 to C_NUM_REG-1);
IP2Bus_Data : out std_logic_vector(0 to C_SLV_DWIDTH-1);
IP2Bus_RdAck : out std_logic;
IP2Bus_WrAck : out std_logic;
IP2Bus_Error : out std_logic
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
attribute SIGIS : string;
attribute SIGIS of Bus2IP_Clk : signal is "CLK";
attribute SIGIS of Bus2IP_Reset : signal is "RST";
end entity user_logic;
------------------------------------------------------------------------------
-- Architecture section
------------------------------------------------------------------------------
architecture IMP of user_logic is
--USER signal declarations added here, as needed for user logic
signal x_ball,y_ball : std_logic_vector(9 downto 0);
signal y_raqG,y_raqD,score,mur1,mur2 : std_logic_vector(7 downto 0);
------------------------------------------
-- Signals for user logic slave model s/w accessible register example
------------------------------------------
signal slv_reg0 : std_logic_vector(0 to C_SLV_DWIDTH-1);
signal slv_reg1 : std_logic_vector(0 to C_SLV_DWIDTH-1);
signal slv_reg2 : std_logic_vector(0 to C_SLV_DWIDTH-1);
signal slv_reg3 : std_logic_vector(0 to C_SLV_DWIDTH-1);
signal slv_reg_write_sel : std_logic_vector(0 to 3);
signal slv_reg_read_sel : std_logic_vector(0 to 3);
signal slv_ip2bus_data : std_logic_vector(0 to C_SLV_DWIDTH-1);
signal slv_read_ack : std_logic;
signal slv_write_ack : std_logic;
COMPONENT VGAtop IS
PORT (clk_50 : in STD_LOGIC; -- horloge {{unité|50|MHz}}
-- coordonnées de la balle
x_rect, y_rect: IN STD_LOGIC_VECTOR(9 DOWNTO 0);
-- oordonnées des deux raquettes
y_raquG, y_raquD: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- valeurs à afficher sur les scores : 8 bits sur deux digits
scoreG : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- lignes de briques verticales (deux seulement)
ligne1,ligne2 : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- en sortie nos cinq signaux VGA
hsynch,vsynch,red,green,blue : out STD_LOGIC);
END component;
begin
--USER logic implementation added here
------------------------------------------
-- Example code to read/write user logic slave model s/w accessible registers
--
-- Note:
-- The example code presented here is to show you one way of reading/writing
-- software accessible registers implemented in the user logic slave model.
-- Each bit of the Bus2IP_WrCE/Bus2IP_RdCE signals is configured to correspond
-- to one software accessible register by the top level template. For example,
-- if you have four 32 bit software accessible registers in the user logic,
-- you are basically operating on the following memory mapped registers:
--
-- Bus2IP_WrCE/Bus2IP_RdCE Memory Mapped Register
-- "1000" C_BASEADDR + 0x0
-- "0100" C_BASEADDR + 0x4
-- "0010" C_BASEADDR + 0x8
-- "0001" C_BASEADDR + 0xC
--
------------------------------------------
slv_reg_write_sel <= Bus2IP_WrCE(0 to 3);
slv_reg_read_sel <= Bus2IP_RdCE(0 to 3);
slv_write_ack <= Bus2IP_WrCE(0) or Bus2IP_WrCE(1) or Bus2IP_WrCE(2) or Bus2IP_WrCE(3);
slv_read_ack <= Bus2IP_RdCE(0) or Bus2IP_RdCE(1) or Bus2IP_RdCE(2) or Bus2IP_RdCE(3);
-- implement slave model software accessible register(s)
SLAVE_REG_WRITE_PROC : process( Bus2IP_Clk ) is
begin
if Bus2IP_Clk'event and Bus2IP_Clk = '1' then
if Bus2IP_Reset = '1' then
slv_reg0 <= (others => '0');
slv_reg1 <= (others => '0');
slv_reg2 <= (others => '0');
slv_reg3 <= (others => '0');
else
case slv_reg_write_sel is
when "1000" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg0(byte_index*8 to byte_index*8+7) <= Bus2IP_Data(byte_index*8 to byte_index*8+7);
end if;
end loop;
when "0100" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg1(byte_index*8 to byte_index*8+7) <= Bus2IP_Data(byte_index*8 to byte_index*8+7);
end if;
end loop;
when "0010" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg2(byte_index*8 to byte_index*8+7) <= Bus2IP_Data(byte_index*8 to byte_index*8+7);
end if;
end loop;
when "0001" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg3(byte_index*8 to byte_index*8+7) <= Bus2IP_Data(byte_index*8 to byte_index*8+7);
end if;
end loop;
when others => null;
end case;
end if;
end if;
end process SLAVE_REG_WRITE_PROC;
-- implement slave model software accessible register(s) read mux
SLAVE_REG_READ_PROC : process( slv_reg_read_sel, slv_reg0, slv_reg1, slv_reg2, slv_reg3 ) is
begin
case slv_reg_read_sel is
when "1000" => slv_ip2bus_data <= slv_reg0;
when "0100" => slv_ip2bus_data <= slv_reg1;
when "0010" => slv_ip2bus_data <= slv_reg2;
when "0001" => slv_ip2bus_data <= slv_reg3;
when others => slv_ip2bus_data <= (others => '0');
end case;
end process SLAVE_REG_READ_PROC;
------------------------------------------
-- Example code to drive IP to Bus signals
------------------------------------------
IP2Bus_Data <= slv_ip2bus_data when slv_read_ack = '1' else
(others => '0');
IP2Bus_WrAck <= slv_write_ack;
IP2Bus_RdAck <= slv_read_ack;
IP2Bus_Error <= '0';
------------------------------------------
-- my own signals
------------------------------------------
x_ball <= slv_reg0(22 to 31);
y_ball <= slv_reg0(6 to 15);
y_raqG <= slv_reg1(24 to 31);
y_raqD <= slv_reg1(8 to 15);
score <= slv_reg2(24 to 31);
mur1 <= slv_reg3(24 to 31);
mur2 <= slv_reg3(8 to 15);
------------------------------------------
-- my own components
------------------------------------------
USER_LOGIC_II : VGAtop
port map (
clk_50 => Bus2IP_Clk,
x_rect => x_ball,
y_rect => y_ball,
y_raquG => y_raqG,
y_raquD => y_raqD,
scoreG => score,
ligne1 => mur1,
ligne2 => mur2,
hsynch => hsynch,
vsynch => vsynch,
red => red,
green => green,
blue => blue
);
end IMP;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
-- 22th November 2011
-- Casse-briques avec deux lignes asymétriques
--entité VGA globale
ENTITY VGAtop IS
PORT (clk_50 : in STD_LOGIC; -- horloge {{unité|50|MHz}}
-- coordonnées de la balle
x_rect, y_rect: IN STD_LOGIC_VECTOR(9 DOWNTO 0);
-- oordonnées des deux raquettes
y_raquG, y_raquD: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- valeurs à afficher sur les scores : 8 bits sur deux digits
scoreG : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- lignes de briques verticales (deux seulement)
ligne1,ligne2 : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
-- en sortie nos cinq signaux VGA
hsynch,vsynch,red,green,blue : out STD_LOGIC);
END VGAtop;
ARCHITECTURE atop of VGAtop is
COMPONENT VGA_SYNC IS -- ***** composant présenté en exercice 1 **********
PORT(clock_25Mhz : IN STD_LOGIC;
-- c’est ce composant qui gère les signaux de synchronisation
horiz_sync_out, vert_sync_out : OUT STD_LOGIC;
-- et qui sait où est le point dessiné sur l'écran
pixel_row, pixel_column: OUT STD_LOGIC_VECTOR(9 DOWNTO 0));
END COMPONENT;
COMPONENT rect IS PORT( --ancien rectangle avant les generic
row,col,x_rec,y_rec,delta_x,delta_y :in STD_LOGIC_VECTOR(9 DOWNTO 0);
colorRGB : in STD_LOGIC_VECTOR(2 DOWNTO 0);
red1,green1,blue1 : out std_logic);
END component;
COMPONENT aff7seg IS --un afficheur complet 7 segments
generic (gx, gy, gdx, gdy : natural);
port(e_7seg : in std_logic_vector(3 downto 0);
segx, segy : in std_logic_vector(9 downto 0);
Sseg_red, Sseg_green, Sseg_blue : out std_logic);
END COMPONENT;
COMPONENT ligne8briques is
generic (gx, gy, gdx, gdy : natural); --gx, gy = position ; gdx, gdy = tailles
port(e_8 : in std_logic_vector(7 downto 0); -- valeur à affichier
segx, segy : in std_logic_vector(9 downto 0);-- où en est le balayage de l'écran ?
Sseg_red, Sseg_green, Sseg_blue : out std_logic); -- signaux de couleurs
END COMPONENT;
signal clk_25,sred,sgreen,sgreen3,sblue3,sblue5,sred5,sgreen5,sred6,sblue6,sgreen6,sred7,sblue7,sgreen7,
sred3,sblue,sred1,sgreen1,sblue1,sred2,sgreen2,sblue2,sblue4,sgreen4,sred4 : std_logic;
signal srow,scol : STD_LOGIC_VECTOR(9 DOWNTO 0);
--signal s_aff1,s_aff2 : std_logic_vector(7 downto 0);
begin -- comme VGA_SYNC nécessite {{unité|25|MHz}}, on divise nos {{unité|50|MHz}} par deux
process(clk_50) begin
if clk_50'event and clk_50='1' then
clk_25 <= not clk_25; --clk_25 est à {{unité|25|MHz}} comme son nom l'indique...
end if;
end process;
i1:vga_sync port map(clock_25Mhz =>clk_25,horiz_sync_out=>hsynch,vert_sync_out=>vsynch,
pixel_row=>srow,pixel_column=>scol);
balle:rect port map(row=>srow,col=>scol,red1=>sred,green1=>sgreen,blue1=>sblue,colorRGB=>"111",
delta_x=>"0000001010",delta_y=>"0000001100",
x_rec => x_rect, y_rec => y_rect);
bord:rect port map(row=>srow,col=>scol,red1=>sred3,green1=>sgreen3,blue1=>sblue3,colorRGB=>"111",
delta_x=>"1010000000",delta_y=>"0000001000",
x_rec => "0000000000", y_rec => "0110100110");
-- bord_score:rect port map(row=>srow,col=>scol,red1=>sred8,green1=>sgreen8,blue1=>sblue8,colorRGB=>"001",
-- delta_x=>"0000010100",delta_y=>"0000000110",
-- x_rec => "0100101110", y_rec => "0111000000");
raquetteG:rect port map(row=>srow,col=>scol,red1=>sred1,green1=>sgreen1,blue1=>sblue1,colorRGB=>"100",
delta_x=>"0000001010",delta_y=>"0000111010",
x_rec => "0000010110", y_rec(8 downto 1) => y_raquG, y_rec(9)=>'0',y_rec(0)=>'0');
raquetteD:rect port map(row=>srow,col=>scol,red1=>sred2,green1=>sgreen2,blue1=>sblue2,colorRGB=>"100",
delta_x=>"0000001010",delta_y=>"0000111010",
x_rec => "1001001000", y_rec(8 downto 1) => y_raquD,y_rec(9)=>'0',y_rec(0)=>'0');
affich0:aff7seg generic map(gx=>140,gy=>435,gdx=>40,gdy=>40)
port map(e_7seg=>scoreG(7 downto 4), segx=>scol,segy=>srow,Sseg_red=>sred4,
Sseg_blue=>sblue4,Sseg_green=>sgreen4);
affich1:aff7seg generic map(gx=>190,gy=>435,gdx=>40,gdy=>40)
port map(e_7seg=>scoreG(3 downto 0),segx=>scol,segy=>srow,Sseg_red=>sred5,
Sseg_blue=>sblue5,Sseg_green=>sgreen5);
line1:ligne8briques generic map(gx=>420,gy=>0,gdx=>20,gdy=>422)
port map(e_8=>ligne1,segx=>scol,segy=>srow,Sseg_red=>sred6,
Sseg_blue=>sblue6,Sseg_green=>sgreen6);
line2:ligne8briques generic map(gx=>440,gy=>0,gdx=>20,gdy=>422)
port map(e_8=>ligne2,segx=>scol,segy=>srow,Sseg_red=>sred7,
Sseg_blue=>sblue7,Sseg_green=>sgreen7);
red <= sred or sred1 or sred2 or sred3 or sred4 or sred5 or sred6 or sred7;
green <= sgreen or sgreen1 or sgreen2 or sgreen3 or sgreen4 or sgreen5 or sgreen6 or sgreen7;
blue <= sblue or sblue1 or sblue2 or sblue3 or sblue4 or sblue5 or sblue6 or sblue7;
end atop;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
--use ieee.numeric_std.all;
ENTITY rect IS PORT( --ancien rectangle
row,col,x_rec,y_rec,delta_x,delta_y :in STD_LOGIC_VECTOR(9 DOWNTO 0);
colorRGB : in STD_LOGIC_VECTOR(2 DOWNTO 0);
red1,green1,blue1 : out std_logic);
END rect;
ARCHITECTURE arect of rect is begin
PROCESS(row,col,x_rec,y_rec) BEGIN
if row > y_rec and row < y_rec+delta_y then
if col >x_rec and col < x_rec+delta_x then
red1 <= colorRGB(2);
green1 <= colorRGB(1);
blue1 <= colorRGB(0);
else
red1 <= '0';
green1 <= '0';
blue1 <= '0';
end if;
else
red1 <= '0';
green1 <= '0';
blue1 <= '0';
end if;
end process;
end arect;
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
-- nouveau rectangle avec les generic
ENTITY rect_generic is
generic(x,y,dx,dy : natural);
port(vgax, vgay : in std_logic_vector(9 downto 0);
RGB : in std_logic_vector(2 downto 0);
e_rect : in std_logic;
s_red, s_blue, s_green : out std_logic);
END rect_generic;
ARCHITECTURE arect_gen OF rect_generic IS
BEGIN
PROCESS (vgax, vgay, e_rect)
BEGIN
IF vgax>x AND vgax<x+dx THEN
IF vgay>y and vgay<y+dy THEN
IF e_rect = '1' THEN
s_red <= RGB(0);
s_green <= RGB(1);
s_blue <= RGB(2);
ELSE
s_red <= '0';
s_green <= '0';
s_blue <= '0';
END IF;
ELSE
s_red <= '0';
s_green <= '0';
s_blue <= '0';
END IF;
ELSE
s_red <= '0';
s_green <= '0';
s_blue <= '0';
END IF;
END PROCESS;
END arect_gen;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
-- transcodeur BCD/7 segments
-- ATTENTION à l’ordre étrange des segments, mais ceci est transparent...
entity trans is
port (
e_trans: in STD_LOGIC_VECTOR (3 downto 0); -- binary input nybble to be decoded
s_trans: out STD_LOGIC_VECTOR (6 downto 0) -- decoded nybble connected the seven segment display
);
end trans;
architecture seg7_trans of trans is
begin
process (e_trans)
begin
CASE e_trans is -- segments : dcegbfa
when "0000" => s_trans <= "1110111";
when "0001" => s_trans <= "0100100";
when "0010" => s_trans <= "1011101";
when "0011" => s_trans <= "1101101";
when "0100" => s_trans <= "0101110";
when "0101" => s_trans <= "1101011";
when "0110" => s_trans <= "1111011";
when "0111" => s_trans <= "0100101";
when "1000" => s_trans <= "1111111";
when "1001" => s_trans <= "1101111";
when others => s_trans <= "0000000";
end CASE;
end process;
end seg7_trans;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
-- afficheur complet sept segments
entity aff7seg is
generic (gx, gy, gdx, gdy : natural); --gx, gy = position ; gdx, gdy = tailles
port(e_7seg : in std_logic_vector(3 downto 0); -- valeur à affichier
segx, segy : in std_logic_vector(9 downto 0);-- où en est le balayage de l'écran ?
Sseg_red, Sseg_green, Sseg_blue : out std_logic); -- signaux de couleurs
end aff7seg;
architecture a_aff of aff7seg is
component rect_generic
generic(x,y,dx,dy : natural);
port(vgax, vgay : in std_logic_vector(9 downto 0);
RGB : in std_logic_vector(2 downto 0);
e_rect : in std_logic;
s_red, s_blue, s_green : out std_logic);
end component;
component trans
port(e_trans : in std_logic_vector(3 downto 0);
s_trans : out std_logic_vector(6 downto 0));
end component;
signal sig_aff_red, sig_aff_green, sig_aff_blue, seg_trans : std_logic_vector(6 downto 0);
begin
i1 : rect_generic generic map (x=>gx+(10*gdx)/55, y=> gy, dx =>(35*gdx)/55, dy=>gdy/10)
port map (RGB=>"100", e_rect=>seg_trans(0), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(0), s_green=>sig_aff_green(0), s_blue=>sig_aff_blue(0));
i2 : rect_generic generic map (x=>gx, y=> gy+(gdy/10), dx =>(10*gdx)/55, dy=>(35*gdy)/100)
port map (RGB=>"100", e_rect=>seg_trans(1), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(1), s_green=>sig_aff_green(1), s_blue=>sig_aff_blue(1));
i3 : rect_generic generic map (x=>gx+(9*gdx)/11, y=> gy+(gdy/10), dx =>(10*gdx)/55, dy=>(35*gdy)/100)
port map (RGB=>"100", e_rect=>seg_trans(2), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(2), s_green=>sig_aff_green(2), s_blue=>sig_aff_blue(2));
i4 : rect_generic generic map (x=>gx+(10*gdx)/55, y=> gy+(gdy*9)/20, dx =>(35*gdx)/55, dy=>gdy/10)
port map (RGB=>"100", e_rect=>seg_trans(3), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(3), s_green=>sig_aff_green(3), s_blue=>sig_aff_blue(3));
i5 : rect_generic generic map (x=>gx, y=> gy+(gdy*55)/100, dx =>(10*gdx)/55, dy=>(35*gdy)/100)
port map (RGB=>"100", e_rect=>seg_trans(4), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(4), s_green=>sig_aff_green(4), s_blue=>sig_aff_blue(4));
i6 : rect_generic generic map (x=>gx+(9*gdx)/11, y=> gy+(55*gdy)/100, dx =>(10*gdx)/55, dy=>(35*gdy)/100)
port map (RGB=>"100", e_rect=>seg_trans(5), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(5), s_green=>sig_aff_green(5), s_blue=>sig_aff_blue(5));
i7 : rect_generic generic map (x=>gx+(10*gdx)/55, y=> gy+(gdy*9)/10, dx =>(35*gdx)/55, dy=>gdy/10)
port map (RGB=>"100", e_rect=>seg_trans(6), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(6), s_green=>sig_aff_green(6), s_blue=>sig_aff_blue(6));
i8 : trans port map(e_trans=>e_7seg, s_trans=>seg_trans);
Sseg_red<= sig_aff_red(0) or sig_aff_red(1) or sig_aff_red(2) or sig_aff_red(3) or sig_aff_red(4)
or sig_aff_red(5) or sig_aff_red(6);
Sseg_green<= sig_aff_green(0) or sig_aff_green(1) or sig_aff_green(2) or sig_aff_green(3) or sig_aff_green(4)
or sig_aff_green(5) or sig_aff_green(6);
Sseg_blue<= sig_aff_blue(0) or sig_aff_blue(1) or sig_aff_blue(2) or sig_aff_blue(3) or sig_aff_blue(4)
or sig_aff_blue(5) or sig_aff_blue(6);
end a_aff;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
-- on a ajouté pixel_row et pixel column pour la suite
ENTITY VGA_SYNC IS
PORT( clock_25Mhz : IN STD_LOGIC;
horiz_sync_out, vert_sync_out : OUT STD_LOGIC;
pixel_row, pixel_column: OUT STD_LOGIC_VECTOR(9 DOWNTO 0));
END VGA_SYNC;
ARCHITECTURE aVGA_SYNC OF VGA_SYNC IS
SIGNAL horiz_sync, vert_sync : STD_LOGIC;
SIGNAL h_count, v_count :STD_LOGIC_VECTOR(9 DOWNTO 0);
BEGIN
-- Horiz_sync ------------------------------------__________--------
-- H_count 0 640 659 755 799
-- Bloc compteur du haut et gauche de la figure sur front montant
gestion_H_Count:PROCESS(clock_25Mhz) BEGIN
IF(clock_25Mhz'EVENT) AND (clock_25Mhz='1') THEN
IF (h_count = 799) THEN
h_count <= (others =>'0');
ELSE
h_count <= h_count + 1;
END IF;
END IF;
END PROCESS;
gestion_Horiz_sync: PROCESS(clock_25Mhz,h_count) BEGIN
--Bloc comparateur du haut et à droite de la figure synchronisé sur front descendants
IF(clock_25Mhz'EVENT) AND (clock_25Mhz='0') THEN
IF (h_count <= 755) AND (h_count >= 659) THEN
horiz_sync <= '0';
ELSE
horiz_sync <= '1';
END IF;
END IF;
END PROCESS;
-- Vert_sync -----------------------------------------------_______------------
-- V_count 0 480 493-494 524
-- Bloc compteur en bas à gauche de la figure
gestion_V_Count: PROCESS(clock_25Mhz,h_count) BEGIN
IF(clock_25Mhz'EVENT) AND (clock_25Mhz='1') THEN
IF (v_count >= 524) AND (h_count >= 699) THEN
v_count <= (others =>'0');
ELSIF (h_count = 699) THEN
v_count <= v_count + 1;
END IF;
END IF;
END PROCESS;
gestion_Vertical_sync:PROCESS(clock_25Mhz,v_count) BEGIN
IF(clock_25Mhz'EVENT) AND (clock_25Mhz='0') THEN
--Bloc comparateur du bas et à droite de la figure synchronisé sur front descendants
IF (v_count <= 494) AND (v_count >= 493) THEN
vert_sync <= '0';
ELSE
vert_sync <= '1';
END IF;
END IF;
END PROCESS;
pixel_column <= h_count;
pixel_row <= v_count;
horiz_sync_out <= horiz_sync;
vert_sync_out <= vert_sync;
END aVGA_SYNC;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
-- afficheur complet sept segments
entity ligne8briques is
generic (gx, gy, gdx, gdy : natural); --gx, gy = position ; gdx, gdy = tailles
port(e_8 : in std_logic_vector(7 downto 0); -- valeur à affichier
segx, segy : in std_logic_vector(9 downto 0);-- où en est le balayage de l'écran ?
Sseg_red, Sseg_green, Sseg_blue : out std_logic); -- signaux de couleurs
end ligne8briques;
architecture a_ligne8briques of ligne8briques is
component rect_generic
generic(x,y,dx,dy : natural);
port(vgax, vgay : in std_logic_vector(9 downto 0);
RGB : in std_logic_vector(2 downto 0);
e_rect : in std_logic;
s_red, s_blue, s_green : out std_logic);
end component;
signal sig_aff_red, sig_aff_green, sig_aff_blue : std_logic_vector(7 downto 0);
begin
i1 : rect_generic generic map (x=>gx+1, y=> gy+1, dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(0), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(0), s_green=>sig_aff_green(0), s_blue=>sig_aff_blue(0));
i2 : rect_generic generic map (x=>gx+1, y=> gy+1+((gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(1), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(1), s_green=>sig_aff_green(1), s_blue=>sig_aff_blue(1));
i3 : rect_generic generic map (x=>gx+1, y=> gy+1+(2*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(2), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(2), s_green=>sig_aff_green(2), s_blue=>sig_aff_blue(2));
i4 : rect_generic generic map (x=>gx+1, y=> gy+1+(3*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(3), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(3), s_green=>sig_aff_green(3), s_blue=>sig_aff_blue(3));
i5 : rect_generic generic map (x=>gx+1, y=> gy+1+(4*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(4), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(4), s_green=>sig_aff_green(4), s_blue=>sig_aff_blue(4));
i6 : rect_generic generic map (x=>gx+1, y=> gy+1+(5*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(5), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(5), s_green=>sig_aff_green(5), s_blue=>sig_aff_blue(5));
i7 : rect_generic generic map (x=>gx+1, y=> gy+1+(6*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(6), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(6), s_green=>sig_aff_green(6), s_blue=>sig_aff_blue(6));
i8 : rect_generic generic map (x=>gx+1, y=> gy+1+(7*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy)/8-2)
port map (RGB=>"100", e_rect=>e_8(7), vgax=>segx, vgay=>segy,
s_red=>sig_aff_red(7), s_green=>sig_aff_green(7), s_blue=>sig_aff_blue(7));
Sseg_red<= sig_aff_red(0) or sig_aff_red(1) or sig_aff_red(2) or sig_aff_red(3) or sig_aff_red(4)
or sig_aff_red(5) or sig_aff_red(6) or sig_aff_red(7);
Sseg_green<= sig_aff_green(0) or sig_aff_green(1) or sig_aff_green(2) or sig_aff_green(3) or sig_aff_green(4)
or sig_aff_green(5) or sig_aff_green(6) or sig_aff_green(7);
Sseg_blue<= sig_aff_blue(0) or sig_aff_blue(1) or sig_aff_blue(2) or sig_aff_blue(3) or sig_aff_blue(4)
or sig_aff_blue(5) or sig_aff_blue(6) or sig_aff_blue(7);
end a_ligne8briques;
Solution pour le fichier vgacassbrik.vhd
[modifier | modifier le wikicode]Vgacassbrik est le nom de mon périphérique. Ainsi ce fichier est généré automatiquement mais modifié comme suit.
------------------------------------------------------------------------------
-- vgacassbrik.vhd - entity/architecture pair
------------------------------------------------------------------------------
-- IMPORTANT:
-- DO NOT MODIFY THIS FILE EXCEPT IN THE DESIGNATED SECTIONS.
--
-- SEARCH FOR --USER TO DETERMINE WHERE CHANGES ARE ALLOWED.
--
-- TYPICALLY, THE ONLY ACCEPTABLE CHANGES INVOLVE ADDING NEW
-- PORTS AND GENERICS THAT GET PASSED THROUGH TO THE INSTANTIATION
-- OF THE USER_LOGIC ENTITY.
------------------------------------------------------------------------------
--
-- ***************************************************************************
-- ** Copyright (c) 1995-2007 Xilinx, Inc. All rights reserved. **
-- ** **
-- ** Xilinx, Inc. **
-- ** XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" **
-- ** AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND **
-- ** SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, **
-- ** OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, **
-- ** APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION **
-- ** THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, **
-- ** AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE **
-- ** FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY **
-- ** WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE **
-- ** IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR **
-- ** REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF **
-- ** INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS **
-- ** FOR A PARTICULAR PURPOSE. **
-- ** **
-- ***************************************************************************
--
------------------------------------------------------------------------------
-- Filename: vgacassbrik.vhd
-- Version: 1.00.b
-- Description: Top level design, instantiates library components and user logic.
-- Date: Wed Dec 14 13:57:18 2011 (by Create and Import Peripheral Wizard)
-- VHDL Standard: VHDL'93
------------------------------------------------------------------------------
-- Naming Conventions:
-- active low signals: "*_n"
-- clock signals: "clk", "clk_div#", "clk_#x"
-- reset signals: "rst", "rst_n"
-- generics: "C_*"
-- user defined types: "*_TYPE"
-- state machine next state: "*_ns"
-- state machine current state: "*_cs"
-- combinatorial signals: "*_com"
-- pipelined or register delay signals: "*_d#"
-- counter signals: "*cnt*"
-- clock enable signals: "*_ce"
-- internal version of output port: "*_i"
-- device pins: "*_pin"
-- ports: "- Names begin with Uppercase"
-- processes: "*_PROCESS"
-- component instantiations: "<ENTITY_>I_<#|FUNC>"
------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library proc_common_v2_00_a;
use proc_common_v2_00_a.proc_common_pkg.all;
use proc_common_v2_00_a.ipif_pkg.all;
library plbv46_slave_single_v1_00_a;
use plbv46_slave_single_v1_00_a.plbv46_slave_single;
library vgacassbrik_v1_00_b;
use vgacassbrik_v1_00_b.user_logic;
------------------------------------------------------------------------------
-- Entity section
------------------------------------------------------------------------------
-- Definition of Generics:
-- C_BASEADDR -- PLBv46 slave: base address
-- C_HIGHADDR -- PLBv46 slave: high address
-- C_SPLB_AWIDTH -- PLBv46 slave: address bus width
-- C_SPLB_DWIDTH -- PLBv46 slave: data bus width
-- C_SPLB_NUM_MASTERS -- PLBv46 slave: Number of masters
-- C_SPLB_MID_WIDTH -- PLBv46 slave: master ID bus width
-- C_SPLB_NATIVE_DWIDTH -- PLBv46 slave: internal native data bus width
-- C_SPLB_P2P -- PLBv46 slave: point to point interconnect scheme
-- C_SPLB_SUPPORT_BURSTS -- PLBv46 slave: support bursts
-- C_SPLB_SMALLEST_MASTER -- PLBv46 slave: width of the smallest master
-- C_SPLB_CLK_PERIOD_PS -- PLBv46 slave: bus clock in picoseconds
-- C_FAMILY -- Xilinx FPGA family
--
-- Definition of Ports:
-- SPLB_Clk -- PLB main bus clock
-- SPLB_Rst -- PLB main bus reset
-- PLB_ABus -- PLB address bus
-- PLB_UABus -- PLB upper address bus
-- PLB_PAValid -- PLB primary address valid indicator
-- PLB_SAValid -- PLB secondary address valid indicator
-- PLB_rdPrim -- PLB secondary to primary read request indicator
-- PLB_wrPrim -- PLB secondary to primary write request indicator
-- PLB_masterID -- PLB current master identifier
-- PLB_abort -- PLB abort request indicator
-- PLB_busLock -- PLB bus lock
-- PLB_RNW -- PLB read/not write
-- PLB_BE -- PLB byte enables
-- PLB_MSize -- PLB master data bus size
-- PLB_size -- PLB transfer size
-- PLB_type -- PLB transfer type
-- PLB_lockErr -- PLB lock error indicator
-- PLB_wrDBus -- PLB write data bus
-- PLB_wrBurst -- PLB burst write transfer indicator
-- PLB_rdBurst -- PLB burst read transfer indicator
-- PLB_wrPendReq -- PLB write pending bus request indicator
-- PLB_rdPendReq -- PLB read pending bus request indicator
-- PLB_wrPendPri -- PLB write pending request priority
-- PLB_rdPendPri -- PLB read pending request priority
-- PLB_reqPri -- PLB current request priority
-- PLB_TAttribute -- PLB transfer attribute
-- Sl_addrAck -- Slave address acknowledge
-- Sl_SSize -- Slave data bus size
-- Sl_wait -- Slave wait indicator
-- Sl_rearbitrate -- Slave re-arbitrate bus indicator
-- Sl_wrDAck -- Slave write data acknowledge
-- Sl_wrComp -- Slave write transfer complete indicator
-- Sl_wrBTerm -- Slave terminate write burst transfer
-- Sl_rdDBus -- Slave read data bus
-- Sl_rdWdAddr -- Slave read word address
-- Sl_rdDAck -- Slave read data acknowledge
-- Sl_rdComp -- Slave read transfer complete indicator
-- Sl_rdBTerm -- Slave terminate read burst transfer
-- Sl_MBusy -- Slave busy indicator
-- Sl_MWrErr -- Slave write error indicator
-- Sl_MRdErr -- Slave read error indicator
-- Sl_MIRQ -- Slave interrupt indicator
------------------------------------------------------------------------------
entity vgacassbrik is
generic
(
-- ADD USER GENERICS BELOW THIS LINE ---------------
--USER generics added here
-- ADD USER GENERICS ABOVE THIS LINE ---------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol parameters, do not add to or delete
C_BASEADDR : std_logic_vector := X"FFFFFFFF";
C_HIGHADDR : std_logic_vector := X"00000000";
C_SPLB_AWIDTH : integer := 32;
C_SPLB_DWIDTH : integer := 128;
C_SPLB_NUM_MASTERS : integer := 8;
C_SPLB_MID_WIDTH : integer := 3;
C_SPLB_NATIVE_DWIDTH : integer := 32;
C_SPLB_P2P : integer := 0;
C_SPLB_SUPPORT_BURSTS : integer := 0;
C_SPLB_SMALLEST_MASTER : integer := 32;
C_SPLB_CLK_PERIOD_PS : integer := 10000;
C_FAMILY : string := "virtex5"
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
--USER ports added here
hsynch,vsynch,red,green,blue : out STD_LOGIC;
-- ADD USER PORTS ABOVE THIS LINE ------------------
-- DO NOT EDIT BELOW THIS LINE ---------------------
-- Bus protocol ports, do not add to or delete
SPLB_Clk : in std_logic;
SPLB_Rst : in std_logic;
PLB_ABus : in std_logic_vector(0 to 31);
PLB_UABus : in std_logic_vector(0 to 31);
PLB_PAValid : in std_logic;
PLB_SAValid : in std_logic;
PLB_rdPrim : in std_logic;
PLB_wrPrim : in std_logic;
PLB_masterID : in std_logic_vector(0 to C_SPLB_MID_WIDTH-1);
PLB_abort : in std_logic;
PLB_busLock : in std_logic;
PLB_RNW : in std_logic;
PLB_BE : in std_logic_vector(0 to C_SPLB_DWIDTH/8-1);
PLB_MSize : in std_logic_vector(0 to 1);
PLB_size : in std_logic_vector(0 to 3);
PLB_type : in std_logic_vector(0 to 2);
PLB_lockErr : in std_logic;
PLB_wrDBus : in std_logic_vector(0 to C_SPLB_DWIDTH-1);
PLB_wrBurst : in std_logic;
PLB_rdBurst : in std_logic;
PLB_wrPendReq : in std_logic;
PLB_rdPendReq : in std_logic;
PLB_wrPendPri : in std_logic_vector(0 to 1);
PLB_rdPendPri : in std_logic_vector(0 to 1);
PLB_reqPri : in std_logic_vector(0 to 1);
PLB_TAttribute : in std_logic_vector(0 to 15);
Sl_addrAck : out std_logic;
Sl_SSize : out std_logic_vector(0 to 1);
Sl_wait : out std_logic;
Sl_rearbitrate : out std_logic;
Sl_wrDAck : out std_logic;
Sl_wrComp : out std_logic;
Sl_wrBTerm : out std_logic;
Sl_rdDBus : out std_logic_vector(0 to C_SPLB_DWIDTH-1);
Sl_rdWdAddr : out std_logic_vector(0 to 3);
Sl_rdDAck : out std_logic;
Sl_rdComp : out std_logic;
Sl_rdBTerm : out std_logic;
Sl_MBusy : out std_logic_vector(0 to C_SPLB_NUM_MASTERS-1);
Sl_MWrErr : out std_logic_vector(0 to C_SPLB_NUM_MASTERS-1);
Sl_MRdErr : out std_logic_vector(0 to C_SPLB_NUM_MASTERS-1);
Sl_MIRQ : out std_logic_vector(0 to C_SPLB_NUM_MASTERS-1)
-- DO NOT EDIT ABOVE THIS LINE ---------------------
);
attribute SIGIS : string;
attribute SIGIS of SPLB_Clk : signal is "CLK";
attribute SIGIS of SPLB_Rst : signal is "RST";
end entity vgacassbrik;
------------------------------------------------------------------------------
-- Architecture section
------------------------------------------------------------------------------
architecture IMP of vgacassbrik is
------------------------------------------
-- Array of base/high address pairs for each address range
------------------------------------------
constant ZERO_ADDR_PAD : std_logic_vector(0 to 31) := (others => '0');
constant USER_SLV_BASEADDR : std_logic_vector := C_BASEADDR;
constant USER_SLV_HIGHADDR : std_logic_vector := C_HIGHADDR;
constant IPIF_ARD_ADDR_RANGE_ARRAY : SLV64_ARRAY_TYPE :=
(
ZERO_ADDR_PAD & USER_SLV_BASEADDR, -- user logic slave space base address
ZERO_ADDR_PAD & USER_SLV_HIGHADDR -- user logic slave space high address
);
------------------------------------------
-- Array of desired number of chip enables for each address range
------------------------------------------
constant USER_SLV_NUM_REG : integer := 4;
constant USER_NUM_REG : integer := USER_SLV_NUM_REG;
constant IPIF_ARD_NUM_CE_ARRAY : INTEGER_ARRAY_TYPE :=
(
0 => pad_power2(USER_SLV_NUM_REG) -- number of ce for user logic slave space
);
------------------------------------------
-- Ratio of bus clock to core clock (for use in dual clock systems)
-- 1 = ratio is 1:1
-- 2 = ratio is 2:1
------------------------------------------
constant IPIF_BUS2CORE_CLK_RATIO : integer := 1;
------------------------------------------
-- Width of the slave data bus (32 only)
------------------------------------------
constant USER_SLV_DWIDTH : integer := C_SPLB_NATIVE_DWIDTH;
constant IPIF_SLV_DWIDTH : integer := C_SPLB_NATIVE_DWIDTH;
------------------------------------------
-- Index for CS/CE
------------------------------------------
constant USER_SLV_CS_INDEX : integer := 0;
constant USER_SLV_CE_INDEX : integer := calc_start_ce_index(IPIF_ARD_NUM_CE_ARRAY, USER_SLV_CS_INDEX);
constant USER_CE_INDEX : integer := USER_SLV_CE_INDEX;
------------------------------------------
-- IP Interconnect (IPIC) signal declarations
------------------------------------------
signal ipif_Bus2IP_Clk : std_logic;
signal ipif_Bus2IP_Reset : std_logic;
signal ipif_IP2Bus_Data : std_logic_vector(0 to IPIF_SLV_DWIDTH-1);
signal ipif_IP2Bus_WrAck : std_logic;
signal ipif_IP2Bus_RdAck : std_logic;
signal ipif_IP2Bus_Error : std_logic;
signal ipif_Bus2IP_Addr : std_logic_vector(0 to C_SPLB_AWIDTH-1);
signal ipif_Bus2IP_Data : std_logic_vector(0 to IPIF_SLV_DWIDTH-1);
signal ipif_Bus2IP_RNW : std_logic;
signal ipif_Bus2IP_BE : std_logic_vector(0 to IPIF_SLV_DWIDTH/8-1);
signal ipif_Bus2IP_CS : std_logic_vector(0 to ((IPIF_ARD_ADDR_RANGE_ARRAY'length)/2)-1);
signal ipif_Bus2IP_RdCE : std_logic_vector(0 to calc_num_ce(IPIF_ARD_NUM_CE_ARRAY)-1);
signal ipif_Bus2IP_WrCE : std_logic_vector(0 to calc_num_ce(IPIF_ARD_NUM_CE_ARRAY)-1);
signal user_Bus2IP_RdCE : std_logic_vector(0 to USER_NUM_REG-1);
signal user_Bus2IP_WrCE : std_logic_vector(0 to USER_NUM_REG-1);
signal user_IP2Bus_Data : std_logic_vector(0 to USER_SLV_DWIDTH-1);
signal user_IP2Bus_RdAck : std_logic;
signal user_IP2Bus_WrAck : std_logic;
signal user_IP2Bus_Error : std_logic;
begin
------------------------------------------
-- instantiate plbv46_slave_single
------------------------------------------
PLBV46_SLAVE_SINGLE_I : entity plbv46_slave_single_v1_00_a.plbv46_slave_single
generic map
(
C_ARD_ADDR_RANGE_ARRAY => IPIF_ARD_ADDR_RANGE_ARRAY,
C_ARD_NUM_CE_ARRAY => IPIF_ARD_NUM_CE_ARRAY,
C_SPLB_P2P => C_SPLB_P2P,
C_BUS2CORE_CLK_RATIO => IPIF_BUS2CORE_CLK_RATIO,
C_SPLB_MID_WIDTH => C_SPLB_MID_WIDTH,
C_SPLB_NUM_MASTERS => C_SPLB_NUM_MASTERS,
C_SPLB_AWIDTH => C_SPLB_AWIDTH,
C_SPLB_DWIDTH => C_SPLB_DWIDTH,
C_SIPIF_DWIDTH => IPIF_SLV_DWIDTH,
C_FAMILY => C_FAMILY
)
port map
(
SPLB_Clk => SPLB_Clk,
SPLB_Rst => SPLB_Rst,
PLB_ABus => PLB_ABus,
PLB_UABus => PLB_UABus,
PLB_PAValid => PLB_PAValid,
PLB_SAValid => PLB_SAValid,
PLB_rdPrim => PLB_rdPrim,
PLB_wrPrim => PLB_wrPrim,
PLB_masterID => PLB_masterID,
PLB_abort => PLB_abort,
PLB_busLock => PLB_busLock,
PLB_RNW => PLB_RNW,
PLB_BE => PLB_BE,
PLB_MSize => PLB_MSize,
PLB_size => PLB_size,
PLB_type => PLB_type,
PLB_lockErr => PLB_lockErr,
PLB_wrDBus => PLB_wrDBus,
PLB_wrBurst => PLB_wrBurst,
PLB_rdBurst => PLB_rdBurst,
PLB_wrPendReq => PLB_wrPendReq,
PLB_rdPendReq => PLB_rdPendReq,
PLB_wrPendPri => PLB_wrPendPri,
PLB_rdPendPri => PLB_rdPendPri,
PLB_reqPri => PLB_reqPri,
PLB_TAttribute => PLB_TAttribute,
Sl_addrAck => Sl_addrAck,
Sl_SSize => Sl_SSize,
Sl_wait => Sl_wait,
Sl_rearbitrate => Sl_rearbitrate,
Sl_wrDAck => Sl_wrDAck,
Sl_wrComp => Sl_wrComp,
Sl_wrBTerm => Sl_wrBTerm,
Sl_rdDBus => Sl_rdDBus,
Sl_rdWdAddr => Sl_rdWdAddr,
Sl_rdDAck => Sl_rdDAck,
Sl_rdComp => Sl_rdComp,
Sl_rdBTerm => Sl_rdBTerm,
Sl_MBusy => Sl_MBusy,
Sl_MWrErr => Sl_MWrErr,
Sl_MRdErr => Sl_MRdErr,
Sl_MIRQ => Sl_MIRQ,
Bus2IP_Clk => ipif_Bus2IP_Clk,
Bus2IP_Reset => ipif_Bus2IP_Reset,
IP2Bus_Data => ipif_IP2Bus_Data,
IP2Bus_WrAck => ipif_IP2Bus_WrAck,
IP2Bus_RdAck => ipif_IP2Bus_RdAck,
IP2Bus_Error => ipif_IP2Bus_Error,
Bus2IP_Addr => ipif_Bus2IP_Addr,
Bus2IP_Data => ipif_Bus2IP_Data,
Bus2IP_RNW => ipif_Bus2IP_RNW,
Bus2IP_BE => ipif_Bus2IP_BE,
Bus2IP_CS => ipif_Bus2IP_CS,
Bus2IP_RdCE => ipif_Bus2IP_RdCE,
Bus2IP_WrCE => ipif_Bus2IP_WrCE
);
------------------------------------------
-- instantiate User Logic
------------------------------------------
USER_LOGIC_I : entity vgacassbrik_v1_00_b.user_logic
generic map
(
-- MAP USER GENERICS BELOW THIS LINE ---------------
--USER generics mapped here
-- MAP USER GENERICS ABOVE THIS LINE ---------------
C_SLV_DWIDTH => USER_SLV_DWIDTH,
C_NUM_REG => USER_NUM_REG
)
port map
(
-- MAP USER PORTS BELOW THIS LINE ------------------
--USER ports mapped here
hsynch => hsynch,
vsynch => vsynch,
red => red,
green => green,
blue => blue,
-- MAP USER PORTS ABOVE THIS LINE ------------------
Bus2IP_Clk => ipif_Bus2IP_Clk,
Bus2IP_Reset => ipif_Bus2IP_Reset,
Bus2IP_Data => ipif_Bus2IP_Data,
Bus2IP_BE => ipif_Bus2IP_BE,
Bus2IP_RdCE => user_Bus2IP_RdCE,
Bus2IP_WrCE => user_Bus2IP_WrCE,
IP2Bus_Data => user_IP2Bus_Data,
IP2Bus_RdAck => user_IP2Bus_RdAck,
IP2Bus_WrAck => user_IP2Bus_WrAck,
IP2Bus_Error => user_IP2Bus_Error
);
------------------------------------------
-- connect internal signals
------------------------------------------
ipif_IP2Bus_Data <= user_IP2Bus_Data;
ipif_IP2Bus_WrAck <= user_IP2Bus_WrAck;
ipif_IP2Bus_RdAck <= user_IP2Bus_RdAck;
ipif_IP2Bus_Error <= user_IP2Bus_Error;
user_Bus2IP_RdCE <= ipif_Bus2IP_RdCE(USER_CE_INDEX to USER_CE_INDEX+USER_NUM_REG-1);
user_Bus2IP_WrCE <= ipif_Bus2IP_WrCE(USER_CE_INDEX to USER_CE_INDEX+USER_NUM_REG-1);
end IMP;
Développement du logiciel
[modifier | modifier le wikicode]Il vous faut maintenant développer le logiciel pour réaliser le jeu demandé.
La section Partie Logicielle d'un autre projet vous propose un cahier des charges que nous ne reproduisons pas ici pour éviter les doublons.
Voici en attendant un petit bout de programme C qui fait bouger la balle :
#include "xparameters.h"
#include "xio.h"
int main()
{ Xuint32 i,posXY;
// deplacement de la balle
posXY = 0x00100010;
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR,posXY);
while(1) {
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR,posXY);
posXY++;
posXY += 0x00010000;
// if ((posXY & 0x000003FF)>600) posXY &= 0xFFFFFC00;
// si dépassement coordonnée balle en bas on revient en 0
if ((posXY & 0x03FF0000)>0x019A0000) posXY &= 0xFC00FFFF;
for (i=0;i<100000;i++); // attente OK
// déplacement des deux raquettes de 16 pixels vers le bas
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+4, 0x000F000F);
// Affichage de 99 sur les deux afficheurs sept segments
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+8, 0x00000099);
// Affichage des deux murs de brique
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+0xC, 0x00FF00FF);
}
return 0;
}
La boucle d'attente fonctionne correctement : elle n'a pas été optimisée par le compilateur.
Nos étudiants utilisent une version 9.2 sous XP sous MAC et pour une raison que nous ignorons la boucle d'attente a été optimisée !!! Nous avons été obligé d'ajouter une instruction de sortie de PORT "XIo_Out32" pour éviter cette optimisation !!!
Plusieurs essais de compilations, avec aussi la version 9.2, mais avec un vrai XP n'ont jamais donné d'optimisation pour nous. Les mystères de l'informatique sont vraiment impénétrables.
Le développement du logiciel est un défi en lui-même car il doit tenir sur 16 ko. Pour une architecture 32 bits, 16 ko ce n'est vraiment pas beaucoup !
Solution de la partie logicielle
[modifier | modifier le wikicode]La solution présentée ci-dessous gère un certain nombre de niveaux, le changement de pente de trajectoire et semble tenir dans un peu plus de 4 ko. Il y a certainement encore bien des choses à développer d'un point de vue logiciel mais ce sera fait un peu plus tard.
// Welcome to Xilinx Platform Studio SDK !
//
// This is an automatically created source file to help you get started.
// To add more files, navigate to File → New → File
// You may delete this file if you want to use only other files for your project.
//
// Fichier d:\MBProject\SDK_projects\demoCB\main.c pour l'auteur
#include "xparameters.h"
#include "xio.h"
// Constantes liées au matériel (voir dans VHDL)
// Mes étudiants, par exemple, ont mis les deux murs plus à gauche ce qui change ces constantes
//************** mur1
#define GX1 420
#define GY 0
#define GDX 20
#define GDY 422
//************** mur2
#define GX2 440
void setXY(Xuint16 x,Xuint16 y);
struct s_brique {
Xuint16 xdeb,ydeb,xfin,yfin;
} tabmur1[8]={{GX1-10,GY,GX1+GDX,(GDY-GY)/8},{GX1-10,(GDY-GY)/8 -12,GX1+GDX,2*(GDY-GY)/8},{GX1-10,2*(GDY-GY)/8 -12,GX1+GDX,3*(GDY-GY)/8},
{GX1-10,3*(GDY-GY)/8 -12,GX1+GDX,4*(GDY-GY)/8},{GX1-10,4*(GDY-GY)/8 -12,GX1+GDX,5*(GDY-GY)/8},{GX1-10,5*(GDY-GY)/8 -12,GX1+GDX,6*(GDY-GY)/8},
{GX1-10,6*(GDY-GY)/8 -12,GX1+GDX,7*(GDY-GY)/8},{GX1-10,7*(GDY-GY)/8 -12,GX1+GDX,(GDY-GY)}},
tabmur2[8]={{GX2-10,GY,GX2+GDX,(GDY-GY)/8},{GX2-10,(GDY-GY)/8 -12,GX2+GDX,2*(GDY-GY)/8},{GX2-10,2*(GDY-GY)/8 -12,GX2+GDX,3*(GDY-GY)/8},
{GX2-10,3*(GDY-GY)/8 -12,GX2+GDX,4*(GDY-GY)/8},{GX2-10,4*(GDY-GY)/8 -12,GX2+GDX,5*(GDY-GY)/8},{GX2-10,5*(GDY-GY)/8 -12,GX2+GDX,6*(GDY-GY)/8},
{GX2-10,6*(GDY-GY)/8 -12,GX2+GDX,7*(GDY-GY)/8},{GX2-10,7*(GDY-GY)/8 -12,GX2+GDX,(GDY-GY)}};
int main()
{ Xuint32 i,posXY,yraquD;
Xuint16 posX,posY,posRaqu_16;
Xuint8 raqD_y=0,raqG_y=0,scoreG=0,dizG=0,level=1,line1=0,line2=0,random=0;
Xint8 deltaX=1,deltaY=1,delta_raqG_y=1;
Xuint32 DeuxMurs; // interrupteurs et murs
Xuint8 swi;
// int err = (dx>dy ? dx : -dy)/2, e2; montre comment calculer err
Xint16 dx=10,dy=10,err=5,e2; //Pour Bresenham
line1 = 0xFF; // car niveau 1
DeuxMurs = line2;
DeuxMurs = (DeuxMurs << 16) + line1;
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+0xC, DeuxMurs);
while(1) {
// Position initiale de la balle
posX=320;
posY=300;
setXY(posX,posY);
deltaX=-1;
setXY(posX,posY);
while (!XIo_In8(XPAR_DIP_SWITCHES_8BIT_BASEADDR+3)) random++ ; //attente du départ
dy = random & 0x0F;
if (dy < 5) dy=5; //pente pseudo-aléatoire depend seulement du départ
while (posX>30 && (line1 || line2)) { //equation booléenne pas facile à trouver
random++;
posRaqu_16=raqD_y<<1;
if (posX>=574) { // le rebond est automatique
//Ne pas remettre le test ci-dessous semble poser problème de temps en temps
if (deltaX>0) deltaX = -deltaX; // rebond sur raquette droite
dy = random & 0x0F;
if (dy < 5) dy=5;
}
posRaqu_16=raqG_y<<1; //rebond sur raquette gauche ?
if ((posX<=32) && (posY<posRaqu_16+58) && (posY+10>posRaqu_16) && (deltaX<0)) {
deltaX= -deltaX; // rebond sur raquette gauche
if (delta_raqG_y) { // delta_raqG_y!=0 on change la pente
if ((delta_raqG_y >0 && deltaY >0) || (delta_raqG_y <0 && deltaY <0)) {
dy+=5; if (dy > 15) dy=15; // ne peut dépasser 15
} else {
dy-=5; if (dy < 5) dy=5; // ne peut dépasser 5
}
}
}
//posX=posX+deltaX; // Retiré pour Bresenham
// rebond sur ligne du bas
if ((posY>=417) && (deltaY>0)) deltaY= -deltaY;
// rebond sur haut de l'écran
if ((posY<=10) && (deltaY<0)) deltaY= -deltaY;
//*********** calcul du rebond sur mur1
if (posX==GX1-10 && level > 0 && line1 != 0) // rebond avec effacement
for(i=0;i<8;i++)
if (posY>=tabmur1[i].ydeb && posY <= tabmur1[i].yfin && (line1 & 1<<i) && deltaX >0) {
deltaX = -deltaX; // rebond
line1 &= ~(1<<i); // on éteint la brique
}
if (posX==GX1+GDX && level > 0) // rebond sans effacement
for(i=0;i<8;i++)
if (posY>=tabmur1[i].ydeb && posY <= tabmur1[i].yfin && (line1 & 1<<i) && deltaX <0) {
deltaX = -deltaX; // rebond
if (level < 3)
line1 &= ~(1<<i); // on efface la brique
}
if (posX > (GX1-10) && posX < (GX1+GDX) && level > 0) { // possibilité de rebond vertical sur brique
if( deltaY > 0) { // rebond par le haut
for(i=1;i<8;i++) //pas brique du haut
if (posY == tabmur1[i].ydeb && (line1 & 1<<i) ) { //tab[i].ydeb contient déjà le -12
deltaY = -deltaY; // rebond
if (level < 3)
line1 &= ~(1<<i); // on efface la brique
}
} else //autrement change de signe sans arret
// rebond par le bas
for(i=0;i<7;i++) //pas brique du bas
if (posY == tabmur1[i].yfin && (line1 & 1<<i) ) {
deltaY = -deltaY; // rebond
if (level < 3)
line1 &= ~(1<<i); // on efface la brique
}
}
//*********** calcul du rebond sur mur2
if (posX==GX2-10 && level >= 5 && line2 != 0) // rebond avec effacement systématique
for(i=0;i<8;i++)
if (posY>=tabmur2[i].ydeb && posY <= tabmur2[i].yfin && (line2 & 1<<i) && deltaX >0) {
deltaX = -deltaX; // rebond
line2 &= ~(1<<i); // on éteint la brique
}
if (posX==GX2+GDX && level >= 5) // rebond sans effacement
for(i=0;i<8;i++)
if (posY>=tabmur2[i].ydeb && posY <= tabmur2[i].yfin && (line2 & 1<<i) && deltaX <0) {
deltaX = -deltaX; // rebond
if (level ==5)
line2 &= ~(1<<i); // on efface la brique seulement pour level 5
}
if (posX > (GX2-10) && posX < (GX2+GDX) && level >= 5) { // possibilité de rebond vertical sur brique
if( deltaY > 0) { // rebond par le haut
for(i=1;i<8;i++) //pas brique du haut
if (posY == tabmur2[i].ydeb && (line2 & 1<<i) ) { //tabmur2[i].ydeb contient déjà le -12
deltaY = -deltaY; // rebond
if (level ==5)
line2 &= ~(1<<i); // on efface la brique seulement pour level 5
}
} else //autrement change de signe sans arret
// rebond par le bas
for(i=0;i<7;i++) //pas brique du bas
if (posY == tabmur2[i].yfin && (line2 & 1<<i) ) {
deltaY = -deltaY; // rebond
if (level == 5)
line2 &= ~(1<<i); // on efface la brique
}
}
//*********** Début de Brensenham
e2 = err;
if (e2 >-dx) { err -= dy; posX += deltaX; }
if (e2 < dy) { err += dx; posY += deltaY; }
// posY=posY+deltaY;
setXY(posX,posY);
//*********** fin de Bresenham
// gestion des raquettes 2bits pour monter descendre
delta_raqG_y=0;
// lecture des PushButton
swi = XIo_In8(XPAR_DIP_SWITCHES_8BIT_BASEADDR+3);
// pour vérification on sort sur affich 7 segments
// XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+8, swi);
if ((swi & 0x01)==0x01) if (raqG_y<182) delta_raqG_y=1;
if ((swi & 0x02)==0x02) if (raqG_y>0) delta_raqG_y=-1;
raqG_y = raqG_y + delta_raqG_y;
raqD_y = posY>>1;
if (raqD_y>182) raqD_y=182;
// déplacement des deux raquettes là où il faut
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+4,(raqD_y<<16)+raqG_y );
// mise à jour du mur
// Affichage éventuel des deux murs de brique
DeuxMurs = line2;
DeuxMurs = (DeuxMurs << 16) + line1;
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+0xC, DeuxMurs);
// attente passive
for (i=0;i<50000;i++); // attente OK
} // while (posX>30&& (line1 || line2)) : fin des niveaux
// on change de niveau de jeu
if (posX>30) {// a retirer pour des tests
level++;
// tempo -=20;
}
if (level== 3) line1 = 0xFF; // mur 1
if (level == 2||level== 4||level==6) line1 = 0xAA;
if (level >= 5) {
line2 = 0xFF; // ligne2
line1 = 0xFF; // ligne1
}
// utilisation afficheur pour level
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+8, level);
// affichage des murs sur l'écran
DeuxMurs = line2;
DeuxMurs = (DeuxMurs << 16) + line1;
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+0xC, DeuxMurs);
} // while(1)
return 0;
}
// Positionnement de la balle
void setXY(Xuint16 x,Xuint16 y){
Xuint32 posXY;
posXY=y;
posXY=(posXY<<16)+x;
XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR,posXY);
}
Nous avons regroupé le calcul de rebond sur les deux murs de briques dans un seul sous-programme dans un autre projet, ce qui n'a pas été fait ici. Par contre le lecteur attentif aura certainement remarqué la présence d'offset dans les instructions "XIo_Out32". Cela est dû à un manque de savoir faire de l'auteur : le SDK utilisait le mauvais xparameters.h et nous n'avons pas trouvé pourquoi ! Il est donc très probable que pour réaliser ce projet par vous-même, vous soyez amener à retirer un certain nombre d'offset.
Les étudiants ont ajouté un timer lors de l'élaboration de leur partie matérielle, mais comme vous pouvez le voir, ce timer n’est pas utilisé ici.
Conclusion
[modifier | modifier le wikicode]Puisque ce projet a été réalisé avec des étudiants de niveau 14, il nous semble important de donner nos impressions. Nous avons en effet classé ce chapitre en niveau 16 !
Les deux étudiants qui ont réalisé ce projet avaient un bon niveau (dans les dix premiers de la promotion). Ainsi la partie matérielle a été réalisée dans les temps (en moins de 30 heures). Il leur restait un peu plus de 30 heures pour réaliser la partie logicielle et force est de constater que ce laps de temps est largement insuffisant pour des étudiants de L2. Une autre façon de dire les choses est que le programme donné en correction dans la section précédente est celui de l'enseignant tuteur. Les étudiants ont réussi à faire bouger la raquette gauche avec des interrupteurs de la carte F.P.G.A., à faire bouger l'autre raquette en fonction de la balle pour avoir un rebond systématique. Les rebonds sur les parois haute et basse sont aussi gérés, ains que l’utilisation de l’Algorithme de tracé de segment de Bresenham pour la trajectoire de balle. Mais pas de gestion des deux murs de briques.
Voir aussi la conclusion du projet identique avec ATMega8.
Vous voila donc arrivé au terme de votre première réalisation d'un processeur 32 bits avec son périphérique associé.
Perspectives
[modifier | modifier le wikicode]Il existe un LogiCORE IP (appelé MicroBlaze Micro Controller System (MCS)) gratuit chez Xilinx. Sa documentation doit être facile à trouver (ds865_microblaze_mcs.pdf). Sa particularité est que sa mise en œuvre ne nécessite pas XPS, autrement dit, peut être réalisée à partir du Webpack gratuit pour nos étudiants. Ses entrées sorties sont extensibles un peu à la façon du picoBlaze (adresse, données et write/readStrobe), il possède la RS232 et des sources d'interruptions. Nous ignorons cependant à partir de quelle version du Webpack ce logicore est disponible. Après quelques lectures sur Internet, il semblerait que ce soit à partir de la version 13.4 que ce logicore soit disponible. Nous espérons avoir l’occasion de le mettre en œuvre prochainement.
Nous avons découvert récemment (Mai 2012) que l'interface PLB mise en œuvre dans ce chapitre est obsolète, ou en tout cas le deviendra bientôt ! La nouvelle interface s’appelle AXI system et nécessitera évidemment des modifications pour le travail d'interface de notre périphérique graphique présenté dans les sections précédentes. Nous attendons que les choses se stabilisent un peu avant de nous lancer dans son utilisation. Sa documentation peut être trouvée dans "AXI Reference Guide".
Voir aussi
[modifier | modifier le wikicode]- Lisez la partie MicroBlaze de cet index en anglais, qui cible une carte Spartan 6
- (Xilinx forum) Microblaze GPIO interfaces to internal logic
- MicroBlaze MCS sur carte Mojo
- aeMB (clone libre du Microblaze, hébergé sur OpenCores.org)
- mblite (autre clone libre du Microblaze, hébergé sur OpenCores.org)
- MB-Lite+, implementé en VHDL, LGPL license
- OpenFire (encore un clone libre du Microblaze, écrit par Stephen Craven hébergé sur OpenCores.org)