Very High Speed Integrated Circuit Hardware Description Language/Interfaces VGA et PS2
Nous allons dans ce chapitre nous intéresser aux interfaces VGA (Video Graphics Array) et PS/2 (Personnal System/2). Les cartes d'évaluations modernes ont pratiquement toutes ces deux interfaces directement montées et exploitables.
La partie concernant l'interface VGA sera émaillée de nombreux exemples destinés à appréhender quelques techniques modernes de réalisations de jeux vidéo. On partira de simples dessins de rectangles pour arriver aux jeux vidéo nécessitant un décor de fond sur lequel des sprites mobiles viennent se déplacer.
Interface VGA
modifierL'interface VGA est intéressante car elle montre comment réaliser des signaux calibrés en temps à l'aide des compteurs. Un connecteur VGA montre que l'interface est complexe puisqu'elle gère 15 signaux différents. En ce qui nous concerne, nous allons nous intéresser à 5 signaux seulement définis dans la section suivante.
Les signaux à construire
modifierLa gestion d'un écran VGA se fait à l'aide de cinq signaux : trois pour les couleurs et deux pour la synchronisation. Dans un premier temps nous n'allons pas nous occuper des signaux de couleurs, autrement dit on ne s'intéressera qu'aux deux signaux de synchronisation.
Les signaux de synchronisation à générer sont décrits dans la figure ci-après pour une résolution de 640 x 480. Ils sont appelés "hsynch" pour la synchronisation horizontale et "vsynch" pour la synchronisation verticale. Ils sont simples et nécessitent seulement deux compteurs pour être réalisés.
Dans cette figure, nous présentons en haut les trois signaux rouge, vert et bleu colorés. Ils sont identiques par commodité, mais ce n'est absolument pas le cas en général. Les signaux auxquels on s'intéresse en premier lieu, sont en-dessous et très simple. Repérez-les : "hsynch" et "vsynch".
Nous pouvons donc réaliser les deux signaux hsynch et vsynch à l'aide de l'architecture ci-dessous :
Dans cette figure, les éléments séquentiels sont représentés en bleu turquoise et les éléments combinatoires en vert. Les comparateurs sont des éléments combinatoires en principe. Mais comme on peut le voir ici, on les synchronise sur des fronts d'horloge descendants. C'est du combinatoire mais synchronisé. Le programme VHDL capable de générer les signaux de synchronisation est donné un peu plus loin (solution de l'exercice 1). Le code VHDL correspondant permet simplement de synchroniser l'écran VGA mais ne dessine rien à l'intérieur.
Exercice 1
modifierÉcrire un programme VHDL qui implante l'architecture ci-dessus.
Les valeurs que l’on donne dans le programme sont légèrement différentes de celles du dessin, mais cela n'a pas beaucoup d'importance. Si vous voulez comprendre d'où viennent ces valeurs, je vous conseille de lire cet exercice et sa solution.
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 656 751 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 <= 751) AND (h_count >= 656) THEN --***** corrigée le 6 mars 2012
horiz_sync <= '0';
ELSE
horiz_sync <= '1';
END IF;
END IF;
END PROCESS;
-- Vert_sync -----------------------------------------------_______------------
-- V_count 0 480 490-491 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 <= 491) AND (v_count >= 490) THEN --***** corrigé le 17 mai 2012
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;
On a donné ici que les quatre "process" qui réalisent nos quatre blocs.
Une correction importante de ce fichier a été réalisée (Mars 2012) : 755 a été remplacé par 751 et 659 par 656.
Voyons maintenant comment dessiner un rectangle.
Dessiner un rectangle
modifierOn cherche à dessiner un rectangle dont la position est donnée par des entrées externes. Sa taille est fixée une fois pour toute, seule sa position peut changer. Regardez la figure ci-dessous et vous verrez une partie combinatoire ajoutée, dans laquelle on entre les positions du rectangle sur 10 bits (ici x_rect et y_rect). Tout dessin sur l'écran se trouvera dans ce composant. Les coordonnées x_rect et y_rect seront à terme, fournies par un composant externe qui pour nous sera un processeur softcore.
Dans ce schéma on a représenté en gris le composant de l'exercice 1. Le dessin explique de lui-même pourquoi on a ajouté dans la correction de l'exercice 1 des sorties appelées "pixel_row" et "pixel_column". Elles sont directement connectées au bloc combinatoire que l’on a ajouté à droite. Ce bloc permet de dessiner n’importe quoi à partir de la valeur des compteurs (justement "pixel_row" et "pixel_column") et ici des coordonnées d'une position d'un rectangle. Si les entrées "x_rect" et "y_rect" sont remplacées par d'autres données on peut imaginer dessiner n’importe quoi sur l'écran.
Tout dessin sur écran VGA consistera à ajouter une partie logique combinatoire (ou y ressemblant, nous pensons à de la mémoire en écrivant cela). Cette partie combinatoire aura forcément comme entrées les valeurs des deux compteurs notées "pixel_row" et "pixel_column" et d'autres entrées remplaçant ou complétant "x_rect" et "y_rect". Ces entrées devront, d'une manière ou une autre, donner des informations sur le type de dessin envisagé. Ses sorties seront naturellement des données sur les couleurs : signal du vert, du rouge et du bleu.
Prenez toujours le temps de réfléchir à l'aspect fonctionnel des choses : quelles sont les entrées, quelles sont les sorties et que doit faire tel ou tel composant ? Cette réflexion peut prendre du temps, mais elle est absolument nécessaire pour une compréhension approfondie.
Exercice 2
modifierOn dispose d'un composant combinatoire rect capable de dessiner un rectangle décrit en VHDL par son entité :
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
ENTITY rect IS PORT(
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;
Sa couleur est fixée par son entrée coloRGB, sa position par x_rect et y_rect. Sa taille est fixée par delta_x et delta_y. Le code correspondant à son architecture est :
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;
1°) Écrire le programme VHDL qui permet de dessiner un rectangle de taille 100x100 aux coordonnées 100 suivant x et 100 suivant y. On dispose pour cela d'une horloge à 50 MHz appelée clk_50. On réalise donc l'assemblage de la figure ci-dessus pour donner l'entité :
ENTITY VGAtop IS
PORT (clk_50 : in STD_LOGIC;
hsynch,vsynch,red,green,blue : out STD_LOGIC);
END VGAtop;
2°) Modifier le programme précédant pour qu’il dessine une balle et deux raquettes. La position de la balle sera donnée par des entrées externes mais les positions X des raquettes seront fixées.
ENTITY VGAtop IS
PORT (clk_50 : in STD_LOGIC;
x_rect, y_rect: IN STD_LOGIC_VECTOR(9 DOWNTO 0);
y_raquG, y_raquD: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
hsynch,vsynch,red,green,blue : out STD_LOGIC);
END VGAtop;
Remarquez par la même occasion que l’on a choisi les positions verticales des raquettes sur seulement 8 bits. Économie économie !
Pour éviter des solutions trop longues, on commence par donner la partie VHDL commune aux deux questions :
library IEEE;
use IEEE.STD_LOGIC_1164.all;
ENTITY VGAtop IS
PORT (clk_50 : in STD_LOGIC;
-- x_rect, y_rect: IN STD_LOGIC_VECTOR(9 DOWNTO 0); -- retirer commentaire pour question 2
-- y_raquG, y_raquD: IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- retirer commentaire pour question 2
hsynch,vsynch,red,green,blue : out STD_LOGIC);
END VGAtop;
ARCHITECTURE atop of VGAtop is
COMPONENT VGA_SYNC IS -- entité et architecture dans l'exercice 1
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 COMPONENT;
COMPONENT rect IS PORT( -- entité et architecture dans l'énoncé
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;
-- ********* A compléter ici ************
end atop;
Remarquez qu’il faut retirer des commentaires pour la question 2°)
Chercher ensuite la partie à compléter. C'est cette partie que nous donnons comme correction.
1°) Puisque 100 est notre chiffre pour la question, il nous faut d’abord le convertir en binaire soit : 0001100100 Mettez la partie VHDL ci-dessous dans la partie à compléter et vous aurez votre rectangle.
signal clk_25 : std_logic;
signal srow,scol : STD_LOGIC_VECTOR(9 DOWNTO 0);
begin
process(clk_50) begin --réalisation de l'horloge 25 MHz
if clk_50'event and clk_50='1' then
clk_25 <= not clk_25;
end if;
end process;
-- on assemble notre composant gris
i1:vga_sync port map(clock_25Mhz =>clk_25, horiz_sync_out=>hsynch,
vert_sync_out=>vsynch, pixel_row=>srow, pixel_column=>scol);
-- on assemble maintenant le composant rect
rectangle:rect port map(row=>srow, col=>scol, red1=>red, green1=>green,
blue1=>blue, colorRGB=>"001",
delta_x=>"0001100100",delta_y=>"0001100100", -- 100 et 100
x_rec => "0001100100", y_rec => "0001100100");
2°) C'est un peu plus complexe pour la balle et les deux raquettes. Il faut en effet utiliser des signaux intermédiaire pour chaque composant rectangle et faire un OU final.
N'oubliez pas de retirer les commentaires de l'entité VGATop avant d'y insérer ce code :
signal clk_25,sred,sgreen,sblue,sred1,sgreen1,sblue1,sred2,sgreen2,sblue2 : std_logic;
signal srow,scol : STD_LOGIC_VECTOR(9 DOWNTO 0);
begin
process(clk_50) begin --réalisation de l'horloge 25 MHz
if clk_50'event and clk_50='1' then
clk_25 <= not clk_25;
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);
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');
red <= sred or sred1 or sred2;
green <= sgreen or sgreen1 or sgreen2;
blue <= sblue or sblue1 or sblue2;
Pour ne pas rester cantonné au fondeur Xilinx, nous allons maintenant nous intéresser au même problème avec Altera.
Dessiner un rectangle sur DE2-115 (Altera)
modifierVoici un exemple de tracé de rectangle destiné à la carte DE2-115 d'Altera. La grande différence avec ce qui a été fait jusqu'à présent est l'apparition des signaux VGA_BLANK, VGA_SYNC et VGA_CLK en sortie. Ils sont liés d'une manière ou une autre au fait que cette carte utilise des convertisseurs numériques analogiques pour les signaux de couleur.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
ENTITY VGA_pong IS
PORT (clk_50 : in STD_LOGIC;
-- x_rect, y_rect: IN STD_LOGIC_VECTOR(9 DOWNTO 0); -- retirer commentaire pour question 2
-- y_raquG, y_raquD: IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- retirer commentaire pour question 2
VGA_BLANK, VGA_SYNC,VGA_CLK: out STD_LOGIC;
hsynch,vsynch,red,green,blue : out STD_LOGIC);
END VGA_pong;
ARCHITECTURE atop of VGA_pong is
COMPONENT VGA_SYNCHRO IS
PORT( clock_50Mhz : IN STD_LOGIC;
horiz_sync_out, vert_sync_out : OUT STD_LOGIC;
VGA_BLANK, VGA_SYNC,VGA_CLK,RED,GREEN,BLUE, video_on : out STD_LOGIC;
pixel_row, pixel_column: OUT STD_LOGIC_VECTOR(9 DOWNTO 0));
END COMPONENT;
COMPONENT rect IS PORT(
row,col,x_rec,y_rec,delta_x,delta_y :in STD_LOGIC_VECTOR(9 DOWNTO 0); ----- on dit les entrees------
colorRGB : in STD_LOGIC_VECTOR(2 DOWNTO 0);
video_on : in std_logic;
red1,green1,blue1 : out std_logic); -----on dit les sorties ------
END COMPONENT;
signal s_row, s_col : STD_LOGIC_VECTOR(9 DOWNTO 0);
signal s_video_on : std_logic;
BEGIN
-------------------- liaison dans l'entite pong---------------
il: VGA_SYNCHRO port map(
clock_50Mhz => clk_50,
pixel_row => s_row,
pixel_column => s_col,
video_on => s_video_on,
horiz_sync_out => hsynch,
vert_sync_out => vsynch,
VGA_BLANK => VGA_BLANK,
VGA_SYNC => VGA_SYNC,
VGA_CLK=> VGA_CLK);
------------------liaison du rect----------------------------
i2: rect port map(
row => s_row,
col => s_col,
x_rec => "0001100100",
y_rec => "0001100100",
delta_x => "0001100100",
delta_y => "0001100100",
colorRGB => "001",
video_on => s_video_on,
red1 => RED,
green1 => GREEN,
blue1 => BLUE );
---------------------------------------------------------------
END aTop;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use IEEE.STD_LOGIC_1164.all;
-- on a ajouté pixel_row et pixel column pour la suite
ENTITY VGA_SYNCHRO IS
PORT( clock_50Mhz : IN STD_LOGIC;
horiz_sync_out, vert_sync_out : OUT STD_LOGIC;
VGA_BLANK, VGA_SYNC,VGA_CLK,RED,GREEN,BLUE, video_on : out STD_LOGIC;
pixel_row, pixel_column: OUT STD_LOGIC_VECTOR(9 DOWNTO 0));
END VGA_SYNCHRO;
ARCHITECTURE aVGA_SYNC OF VGA_SYNCHRO IS
SIGNAL horiz_sync, vert_sync,clock_25Mhz : STD_LOGIC;
SIGNAL h_count, v_count :STD_LOGIC_VECTOR(9 DOWNTO 0);
BEGIN
div2:process(clock_50Mhz) begin
if rising_edge(clock_50Mhz) then
clock_25Mhz <= not clock_25Mhz;
end if;
end process;
-- Horiz_sync ------------------------------------__________--------
-- H_count 0 640 656 751 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 <= 751) AND (h_count >= 656) THEN --***** corrigée le 6 mars 2012
horiz_sync <= '0';
ELSE
horiz_sync <= '1';
END IF;
END IF;
END PROCESS;
-- Vert_sync -----------------------------------------------_______------------
-- V_count 0 480 490-491 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 <= 491) AND (v_count >= 490) THEN --***** corrigé le 17 mai 2012
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;
VGA_BLANK <= '1';
VGA_SYNC <= '0';
VGA_CLK <= clock_25MHz;
video_on <= '1' when ((h_count < 640) and (v_count < 480)) else
'0';
END aVGA_SYNC;
------------------------------Entity of rect-------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use IEEE.STD_LOGIC_1164.all;
ENTITY rect IS PORT(
row,col,x_rec,y_rec,delta_x,delta_y :in STD_LOGIC_VECTOR(9 DOWNTO 0); ----- on dit les entrees------
colorRGB : in STD_LOGIC_VECTOR(2 DOWNTO 0);
video_on : in std_logic;
red1,green1,blue1 : out std_logic); -----on dit les sorties ------
END rect;
----------------architecture of rect -------------------------------
ARCHITECTURE arect of rect is begin
PROCESS(row,col,x_rec,y_rec,video_on) BEGIN
if video_on = '1' then
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);-- and video_on;
green1 <= colorRGB(1);-- and video_on;
blue1 <= colorRGB(0);-- and video_on;
else
red1 <= '0';
green1 <= '0';
blue1 <= '0';
end if;
else
red1 <= '0';
green1 <= '0';
blue1 <= '0';
end if;
else -- video_on = '0'
red1 <= '0';
green1 <= '0';
blue1 <= '0';
end if;
end process;
end arect;
Les contraintes pour la carte DE2-115 sont présentées sous forme de fichier csv. Ce genre de fichier peut facilement s'importer dans Quartus.
# Quartus II 64-Bit Version 15.0.0 Build 145 04/22/2015 SJ Web Edition # File: /home/royer001/Bureau/pong.csv # Generated on: Fri Oct 23 09:03:45 2015 # Note: The column header names should not be changed if you wish to import this .csv file into the Quartus II software. To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved clk_50,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, blue,Output,PIN_D12,8,B8_N0,3.3-V LVTTL, VGA_BLANK,Output,PIN_F11,8,B8_N1,3.3-V LVTTL, VGA_CLK,Output,PIN_A12,8,B8_N0,3.3-V LVTTL, green,Output,PIN_C9,8,B8_N1,3.3-V LVTTL, hsynch,Output,PIN_G13,8,B8_N0,3.3-V LVTTL, red,Output,PIN_H10,8,B8_N1,3.3-V LVTTL, VGA_SYNC,Output,PIN_C10,8,B8_N0,3.3-V LVTTL, vsynch,Output,PIN_C13,8,B8_N0,3.3-V LVTTL,
Dessiner un ensemble de rectangles
modifierOn désire toujours rester dans le domaine du dessin de rectangles, mais en cherchant à dessiner des objets plus complexes, comme des afficheurs sept segments. Si l’on désire en plus pouvoir modifier la taille de ces objets, il faut concevoir l’ensemble avec intelligence. En effet modifier la taille implique un ensemble de calculs. Ces calculs ne doivent en aucun cas être réalisés par le VHDL mais par le compilateur avant implantation. Pour se faire on dispose d'une possibilité avec le generic, et voici comment cela fonctionne.
Soit le programme VHDL suivant :
ENTITY rect_generic is
generic(x,y,dx,dy : natural); --c'est ici qu’il y a du nouveau
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;
Ses seules grandes différences sont l'apparition dans l'entité de quatre valeurs generic : x, et y pour la position et dx, dy pour la taille ainsi que l'entrée "e_rect" destinée à choisir un affichage ou non du rectangle. L'utilisation d'un tel rectangle se fera toujours par un "port map", mais il faudra lui ajouter un "generic map".
Exercice 3
modifierQuestion 1
modifierÉcrire un programme VHDL réalisant un afficheur sept segments à partir du rectangle générique ci-dessus. L'ensemble devra être caractérisé par une position en x et en y ainsi qu'une taille. L'entrée se fera sur 4 bits.
La solution présentée a été réalisée par des étudiants. Ainsi l’ordre choisi pour les segments est un peu étrange, mais cette solution a été testée avec succès.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity aff is
generic (gx, gy, gdx, gdy : natural); -- gx,gy = positions, gdx, gdy = tailles
port(e_7seg : in std_logic_vector(3 downto 0);
-- entrées compteurs générées par VGA synchr
segx, segy : in std_logic_vector(9 downto 0);
Sseg_red, Sseg_green, Sseg_blue : out std_logic);
end aff;
architecture a_aff of aff 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;
Tous les calculs réalisés dans les "generic map" (multiplications et divisions) ne sont pas réalisés par les ressources du FPGA mais par les ressources de votre PC : c’est le compilateur VHDL qui fait le travail.
Question 2
modifierRéaliser un ensemble capable d'afficher deux raquettes, une balle, un rectangle de séparation en bas de l'écran et un affichage de deux scores sur deux digits. Vous remarquerez sur la figure ci-dessous le nombre impressionnant de fils d'entrées regroupés en bus... d'où la question : que va-t-on bien pouvoir faire de tous ces fils ? Ils seront reliés à un processeur par l'intermédiaire de PORTs tout simplement :
Voici la figure correspondante :
On montre sur la figure ci-dessus ce que l’on cherche à réaliser. Encore une fois il est important de se poser devant celle-ci et de bien regarder quelles sont les entrées et quelles sont les sorties ? Les sorties sont les 5 signaux VGA non représentés qui en final vont faire le dessin ci-dessus, c'est-à-dire deux raquettes, une balle et quatre afficheurs de scores. Pour cadencer tout cela il faut une horloge, encore une fois non présentée. Toutes les entrées sont pour changer les positions, de la balle et des raquettes, et pour fournir des nombres à afficher sur les scores.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
--entité VGA globale
ENTITY VGAtop IS
PORT (clk_50 : in STD_LOGIC; -- horloge 50MHz
-- 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 : {{Unité|8|bits}} sur deux digits
scoreG, scoreD: 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 aff 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;
signal clk_25,sred,sgreen,sgreen3,sblue3,sblue5,sred5,sgreen5,sred6,sblue6,sgreen6,sred7,sblue7,sgreen7,
sred3,sred8,sgreen8,sblue8,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 25MHz, on divise nos 50MHz par deux
process(clk_50) begin
if clk_50'event and clk_50='1' then
clk_25 <= not clk_25; --clk_25 est à 25MHz 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=>"1111111111",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:aff generic map(gx=>97,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:aff generic map(gx=>168,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);
affich2:aff generic map(gx=>417,gy=>435,gdx=>40,gdy=>40)
port map(e_7seg=>scoreD(7 downto 4),segx=>scol,segy=>srow,Sseg_red=>sred6,
Sseg_blue=>sblue6,Sseg_green=>sgreen6);
affich3:aff generic map(gx=>488,gy=>435,gdx=>40,gdy=>40)
port map(e_7seg=>scoreD(3 downto 0),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 or sred8;
green <= sgreen or sgreen1 or sgreen2 or sgreen3 or sgreen4 or sgreen5 or sgreen6 or sgreen7 or sgreen8;
blue <= sblue or sblue1 or sblue2 or sblue3 or sblue4 or sblue5 or sblue6 or sblue7 or sblue8;
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 aff 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 aff;
architecture a_aff of aff 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;
Question 3
modifierModifier l'exemple de la question 2 pour réaliser la figure ci-dessous.
Vu le nombre d'entrées de ce composants il ne peut qu'être utilisé avec un processeur embarqué :
- un ATMega8 embarqué l'utilise dans un projet étudiant décrit plus loin dans ce livre .
Le code ci-dessous n’est pas complet : il vous faudra chercher le reste dans les corrections précédentes de ce chapitre. Remarquez que l'afficheur sept segments a été renommé Aff7seg (au lieu de Aff en question 2). Nous pensons que cela sera plus compréhensible comme cela.
Le code donné dessine les murs de briques légèrement sur la droite (c'est volontaire) et les briques sont bleues.
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 50MHz
-- 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 : {{Unité|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 25MHz, on divise nos 50MHz par deux
process(clk_50) begin
if clk_50'event and clk_50='1' then
clk_25 <= not clk_25; --clk_25 est à 25MHz 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;
-- afficheur complet d'un mur vertical de briques
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;
Nous allons maintenant nous intéresser à un autre problème : comment afficher des caractères sur un écran vidéo ?
Dessiner des caractères sur un écran
modifierNous allons nous intéresser dans cette section à l’affichage de caractères sur un écran V.G.A.. Depuis les débuts de l'informatique on utilise une ROM où l’on stocke les formes des caractères pour faire ce travail. Nous allons donc commencer à présenter la ROM de caractères que nous allons utiliser avec un passage obligé à un peu de terminologie.
Nous allons utiliser dans la suite des composants spécifiques à Xilinx. C'est embêtant pour la portabilité mais nous laissons des références au lecteur lui permettant de retrouver les projets libres contenant des ROM génériques.
On appellera dans la suite :
- RAMB16 une RAM/ROM de 16 kbits
- RAMB16_S9 une RAMB16 avec 9 bits de données (2 ko). Le neuvième bit est un bit de parité non utilisé pour nos besoins.
- RAMB16_S1 une RAMB16 avec 1 bit de données (16 kbits).
ROM/RAM de caractères
modifierNos investigations sur le sujet nous ont donné l’occasion d’utiliser deux ROMs différentes :
- celle de P. P. Chu associée à son livre "FPGA prototyping by VHDL Examples"
- celle d'un projet libre chez Opencores appelé Yet Another VGA.
Après divers essais nous avons décidé d’utiliser la deuxième car elle donne de plus beaux résultats pour un grossissement de 2x2... mais comme on dit les goûts et les couleurs...
La mise en œuvre de mémoires programmes pour nos processeurs nous a appris à aborder les mémoires avec précautions. Tous les F.P.G.A. modernes comportent des zones réservées à ces mémoires : on les appelle des BRAM. Si une option de compilation correcte manque, une synthèse utilisant ce que l’on appelle des mémoires distribuées sera réalisée. Ce qui est en général très inefficace. Pour éviter tout problème, nous avons décidé d’utiliser les modèles Xilinx qui forcent la synthèse avec les BRAM. Mais cela revient à changer le code source car ces modèles BRAMs ont leurs propres méthodes d'initialisation et malheureusement ce nouveau code est beaucoup moins portable ! Nous allons quand même publier le code correspondant ici, mais il vous faudra l'adapter pour les autres fondeurs comme Altera (voir remarque ci-dessous) :
library IEEE;
use IEEE.STD_LOGIC_1164.all;
Library UNISIM;
use UNISIM.vcomponents.all;
-- RAMB16_S9: Virtex-II/II-Pro, Spartan-3/3E 16kx1 Single-Port RAM
-- Xilinx HDL Libraries Guide, version 10.1.2
entity font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(10 downto 0);
data: out std_logic_vector(7 downto 0)
);
end font_rom;
architecture arch of font_rom is
begin
RAMB16_S9_inst : RAMB16_S9
generic map (
INIT => X"000", -- Value of output RAM registers at startup
SRVAL => X"000", -- Ouput value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
INITP_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_02=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_03=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
INIT_00=>X"000000FF0000FF0000FF0000FF00000000000000000000000000000000000000",
INIT_01=>X"0000242424242424242424242424000000000000FF0000FF0000FF0000FF0000",
INIT_02=>X"0000929292929292929292929292000000004949494949494949494949490000",
INIT_03=>X"0000AAAAAAAAAAAAAAAAAAAAAAAA000000005555555555555555555555550000",
INIT_04=>X"0000F3FCF3FCF3FCF3FCF3FCF3FC00000000FF00FF00FF00FF00FF00FF000000",
INIT_05=>X"00000C030C030C030C030C030C0300000000CF3FCF3FCF3FCF3FCF3FCF3F0000",
INIT_06=>X"00000066666666000066666666000000000030C030C030C030C030C030C00000",
INIT_07=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FF99999999FFFF99999999FF0000",
INIT_08=>X"0000000000000000FFFFFFFFFFFF00000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_09=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FFFFFFFFFFFF0000000000000000",
INIT_0a=>X"0000007E42424242424242427E0000000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_0b=>X"000024492449244924492449244900000000FF81818181818181818181FF0000",
INIT_0c=>X"0000499249924992499249924992000000002492249224922492249224920000",
INIT_0d=>X"0000AA55AA55AA55AA55AA55AA550000000055AA55AA55AA55AA55AA55AA0000",
INIT_0e=>X"0000DB6DDB6DDB6DDB6DDB6DDB6D00000000DBB6DBB6DBB6DBB6DBB6DBB60000",
INIT_0f=>X"0000FFFFFFFFFFFFFFFFFFFFFFFF00000000B66DB66DB66DB66DB66DB66D0000",
-- Address 4096 to 8191
INIT_10=>X"0000001000001010101010101010000000000000000000000000000000000000",
INIT_11=>X"0000004444FE4444444444FE4444000000000000000000000044444444440000",
INIT_12=>X"0000000C12924C2010086492906000000000007C921212127C909090927C0000",
INIT_13=>X"000000000000000000101010101000000000007A84848A507090888848300000",
INIT_14=>X"0000001008080404040404080810000000000010202040404040402020100000",
INIT_15=>X"0000000010101010FE101010100000000000009292545438FE38545492920000",
INIT_16=>X"0000000000000000FE0000000000000000000020100808000000000000000000",
INIT_17=>X"0000000000804020100804020000000000000000001818000000000000000000",
INIT_18=>X"0000003810101010101010503010000000000038448282A2928A828244380000",
INIT_19=>X"0000007C820202027C020202827C0000000000FE808080807C020202827C0000",
INIT_1a=>X"0000007C820202027C80808080FE00000000001C080808FE8888482818080000",
INIT_1b=>X"00000038101010101008040202FE00000000007C828282827C808080807E0000",
INIT_1c=>X"000000FC020202027C828282827C00000000007C828282827C828282827C0000",
INIT_1d=>X"0000002010080800000018180000000000000000001818000000181800000000",
INIT_1e=>X"0000000000FE0000000000FE000000000000000000020C30C0300C0200000000",
INIT_1f=>X"0000001000101008040282824438000000000000008060180618608000000000",
-- Address 8192 to 12287
INIT_20=>X"00000082828244447C442828281000000000003C42809EA2A29E828244380000",
INIT_21=>X"0000007C8280808080808080827C0000000000FC82828284F884828282FC0000",
INIT_22=>X"000000FE80808080FC80808080FE0000000000F8848482828282848488F00000",
INIT_23=>X"0000007C828282829E808080827C00000000008080808080FC80808080FE0000",
INIT_24=>X"0000003810101010101010101038000000000082828282827C82828282820000",
INIT_25=>X"0000008282848488F088848482820000000000708888080808080808081C0000",
INIT_26=>X"000000828282829292AAAAAAC6820000000000FE808080808080808080800000",
INIT_27=>X"0000007C8282828282828282827C000000000082868A8A8A92A2A2A2C2820000",
INIT_28=>X"0000007A848AB28282828282827C00000000008080808080FC828282827C0000",
INIT_29=>X"0000007C820202027C808080827C000000000082848890A0FC828282827C0000",
INIT_2a=>X"0000007C82828282828282828282000000000010101010101010101092FE0000",
INIT_2b=>X"00000082C6AAAAAA929282828282000000000010102828284444448282820000",
INIT_2c=>X"0000001010101010282844448282000000000082824444283828444482820000",
INIT_2d=>X"00000038202020202020202020380000000000FE824040203808040482FE0000",
INIT_2e=>X"0000003808080808080808080838000000000000000204081020408000000000",
INIT_2f=>X"000000FE00000000000000000000000000000000000000000082442810000000",
-- Address 12288 to 16383
INIT_30=>X"0000003AC6828282C63A00000000000000000000000000000008101020200000",
INIT_31=>X"0000003CC2808080C23C000000000000000000B8C6828282C6B8808080800000",
INIT_32=>X"00000038C680FC82C6380000000000000000003AC6828282C63A020202020000",
INIT_33=>X"00000038C6027E82C638000000000000000000808080808080F88080423C0000",
INIT_34=>X"0000000C1210101010100000100000000000008282828282C6B8808080800000",
INIT_35=>X"000000828488B0C0B88680808000000000000070880404040404000004000000",
INIT_36=>X"0000009292929292D2AC0000000000000000000E102020202020202020200000",
INIT_37=>X"00000038C6828282C6380000000000000000008282828282C6B8000000000000",
INIT_38=>X"0000000202027E82C63A000000000000000000808080FC82C6B8000000000000",
INIT_39=>X"0000007C82027E80827C0000000000000000008080808080C6B8000000000000",
INIT_3a=>X"0000003AC682828282820000000000000000003C4280808080F8808080800000",
INIT_3b=>X"0000006C92929292928200000000000000000010284482828282000000000000",
INIT_3c=>X"0000003008083C42820000000000000000000082443828448200000000000000",
INIT_3d=>X"00000010202020408040202020100000000000FE40300804FE00000000000000",
INIT_3e=>X"0000001008080804020408080810000000000010101010101010101010100000",
INIT_3f=>X"00000000000000000000000000000000000000000000000C9260000000000000")
port map (
DO => data, -- 8-bit Data Output
DOP => open,
ADDR => ADDR, -- 11-bit Address Input
CLK => CLK, -- Clock
DI => "00000000", -- 8-bit Data Input
DIP => "0",
EN => '1', -- RAM Enable Input
SSR => '0', -- Synchronous Set/Reset Input
WE => '0' -- Write Enable Input
);
-- End of RAMB16_S9_inst instantiation
end arch;
Ce code nécessite la remarque suivante :
Pour tout autre fondeur que Xilinx, partez plutôt de la ROM originale publiée dans le projet Yet Another VGA chez Opencores. Elle est beaucoup plus portable que le code ci-dessus.
Pour la petite histoire la ROM ci-dessus a été obtenue à partir du fichier d'extension xdl généré à partir la ROM originale par une compilation dans l'ISE. Ce fichier a été ouvert par hasard, et nous a évité un travail long et fastidieux, comme quoi, la curiosité n’est pas toujours un vilain défaut !
Un travail correctement réalisé devrait utiliser une RAMB16_S1 plutôt qu'une RAMB16_S9 : cela éviterait le multiplexeur de sortie ! (Ne vous inquiétez pas si vous ne voyez pas de quel multiplexeur nous parlons puisqu'on va le retirer). Voici la BRAM correspondante, que nous utiliserons dans la section suivante.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
Library UNISIM;
use UNISIM.vcomponents.all;
-- RAMB16_S1: Virtex-II/II-Pro, Spartan-3/3E 16kx1 Single-Port RAM
-- Xilinx HDL Libraries Guide, version 10.1.2
entity font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic_vector(0 downto 0)
);
end font_rom;
architecture arch of font_rom is
begin
RAMB16_S1_inst : RAMB16_S1
generic map (
INIT => X"0", -- Value of output RAM registers at startup
SRVAL => X"0", -- Ouput value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
INIT_00=>X"000000FF0000FF0000FF0000FF00000000000000000000000000000000000000",
INIT_01=>X"0000242424242424242424242424000000000000FF0000FF0000FF0000FF0000",
INIT_02=>X"0000929292929292929292929292000000004949494949494949494949490000",
INIT_03=>X"0000AAAAAAAAAAAAAAAAAAAAAAAA000000005555555555555555555555550000",
INIT_04=>X"0000F3FCF3FCF3FCF3FCF3FCF3FC00000000FF00FF00FF00FF00FF00FF000000",
INIT_05=>X"00000C030C030C030C030C030C0300000000CF3FCF3FCF3FCF3FCF3FCF3F0000",
INIT_06=>X"00000066666666000066666666000000000030C030C030C030C030C030C00000",
INIT_07=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FF99999999FFFF99999999FF0000",
INIT_08=>X"0000000000000000FFFFFFFFFFFF00000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_09=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FFFFFFFFFFFF0000000000000000",
INIT_0a=>X"0000007E42424242424242427E0000000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_0b=>X"000024492449244924492449244900000000FF81818181818181818181FF0000",
INIT_0c=>X"0000499249924992499249924992000000002492249224922492249224920000",
INIT_0d=>X"0000AA55AA55AA55AA55AA55AA550000000055AA55AA55AA55AA55AA55AA0000",
INIT_0e=>X"0000DB6DDB6DDB6DDB6DDB6DDB6D00000000DBB6DBB6DBB6DBB6DBB6DBB60000",
INIT_0f=>X"0000FFFFFFFFFFFFFFFFFFFFFFFF00000000B66DB66DB66DB66DB66DB66D0000",
-- Address 4096 to 8191
INIT_10=>X"0000001000001010101010101010000000000000000000000000000000000000",
INIT_11=>X"0000004444FE4444444444FE4444000000000000000000000044444444440000",
INIT_12=>X"0000000C12924C2010086492906000000000007C921212127C909090927C0000",
INIT_13=>X"000000000000000000101010101000000000007A84848A507090888848300000",
INIT_14=>X"0000001008080404040404080810000000000010202040404040402020100000",
INIT_15=>X"0000000010101010FE101010100000000000009292545438FE38545492920000",
INIT_16=>X"0000000000000000FE0000000000000000000020100808000000000000000000",
INIT_17=>X"0000000000804020100804020000000000000000001818000000000000000000",
INIT_18=>X"0000003810101010101010503010000000000038448282A2928A828244380000",
INIT_19=>X"0000007C820202027C020202827C0000000000FE808080807C020202827C0000",
INIT_1a=>X"0000007C820202027C80808080FE00000000001C080808FE8888482818080000",
INIT_1b=>X"00000038101010101008040202FE00000000007C828282827C808080807E0000",
INIT_1c=>X"000000FC020202027C828282827C00000000007C828282827C828282827C0000",
INIT_1d=>X"0000002010080800000018180000000000000000001818000000181800000000",
INIT_1e=>X"0000000000FE0000000000FE000000000000000000020C30C0300C0200000000",
INIT_1f=>X"0000001000101008040282824438000000000000008060180618608000000000",
-- Address 8192 to 12287
INIT_20=>X"00000082828244447C442828281000000000003C42809EA2A29E828244380000",
INIT_21=>X"0000007C8280808080808080827C0000000000FC82828284F884828282FC0000",
INIT_22=>X"000000FE80808080FC80808080FE0000000000F8848482828282848488F00000",
INIT_23=>X"0000007C828282829E808080827C00000000008080808080FC80808080FE0000",
INIT_24=>X"0000003810101010101010101038000000000082828282827C82828282820000",
INIT_25=>X"0000008282848488F088848482820000000000708888080808080808081C0000",
INIT_26=>X"000000828282829292AAAAAAC6820000000000FE808080808080808080800000",
INIT_27=>X"0000007C8282828282828282827C000000000082868A8A8A92A2A2A2C2820000",
INIT_28=>X"0000007A848AB28282828282827C00000000008080808080FC828282827C0000",
INIT_29=>X"0000007C820202027C808080827C000000000082848890A0FC828282827C0000",
INIT_2a=>X"0000007C82828282828282828282000000000010101010101010101092FE0000",
INIT_2b=>X"00000082C6AAAAAA929282828282000000000010102828284444448282820000",
INIT_2c=>X"0000001010101010282844448282000000000082824444283828444482820000",
INIT_2d=>X"00000038202020202020202020380000000000FE824040203808040482FE0000",
INIT_2e=>X"0000003808080808080808080838000000000000000204081020408000000000",
INIT_2f=>X"000000FE00000000000000000000000000000000000000000082442810000000",
-- Address 12288 to 16383
INIT_30=>X"0000003AC6828282C63A00000000000000000000000000000008101020200000",
INIT_31=>X"0000003CC2808080C23C000000000000000000B8C6828282C6B8808080800000",
INIT_32=>X"00000038C680FC82C6380000000000000000003AC6828282C63A020202020000",
INIT_33=>X"00000038C6027E82C638000000000000000000808080808080F88080423C0000",
INIT_34=>X"0000000C1210101010100000100000000000008282828282C6B8808080800000",
INIT_35=>X"000000828488B0C0B88680808000000000000070880404040404000004000000",
INIT_36=>X"0000009292929292D2AC0000000000000000000E102020202020202020200000",
INIT_37=>X"00000038C6828282C6380000000000000000008282828282C6B8000000000000",
INIT_38=>X"0000000202027E82C63A000000000000000000808080FC82C6B8000000000000",
INIT_39=>X"0000007C82027E80827C0000000000000000008080808080C6B8000000000000",
INIT_3a=>X"0000003AC682828282820000000000000000003C4280808080F8808080800000",
INIT_3b=>X"0000006C92929292928200000000000000000010284482828282000000000000",
INIT_3c=>X"0000003008083C42820000000000000000000082443828448200000000000000",
INIT_3d=>X"00000010202020408040202020100000000000FE40300804FE00000000000000",
INIT_3e=>X"0000001008080804020408080810000000000010101010101010101010100000",
INIT_3f=>X"00000000000000000000000000000000000000000000000C9260000000000000")
port map (
DO => data, -- 1-bit Data Output
ADDR => ADDR, -- 14-bit Address Input
CLK => CLK, -- Clock
DI => "0", -- 1-bit Data Input
EN => '1', -- RAM Enable Input
SSR => '0', -- Synchronous Set/Reset Input
WE => '0' -- Write Enable Input
);
-- End of RAMB16_S1_inst instantiation
end arch;
Cette RAM sera modifiée lorsque l’on s'intéressera au jeu du pacman un peu plus loin.
Utilisation de la BRAM
modifierS'il est important de posséder une BRAM de caractères il est encore plus important d’en voir le contenu sur un écran VGA. C'est à ce travail que nous allons nous consacrer maintenant.
Cahier des charges simplifié pour commencer
modifierAfin de visualiser le contenu de notre ROM de caractères, nous désirons dessiner 4 lignes de 32 caractères dans le coin gauche de l'écran. Voici en image ce que cela donne. On a représenté les caractères non pas par leur contenu mais par des pavés numérotés.
Regardez attentivement cette figure : elle vous explique comment retrouver les numéros des pavés dessinés en haut à gauche de l'écran VGA. Ces numéros sont importants car ils représentent les codes ASCII des caractères que l’on dessinera. Puisque le dessin présente des pavés numérotés de 0 à 127, cela confirme que notre ROM de caractères contient 128 caractères.
- ce qui est au-dessus de l'écran représente quelques valeurs binaires spécifiques de pixel_x (ou pixel_column). Avec un peu d'imagination vous devinez comment ce compteur varie quand on se déplace vers la droite...
- ce qui est à gauche de l'écran représente quelques valeurs binaires spécifiques de pixel_y (ou pixel_row). Avec un peu d'imagination vous devinez comment ce compteur varie quand on se déplace vers le bas...
- le registre de 7 bits en bas est composée d'une partie bleue tirée de pixel_y (2 bits) et d'une partie jaune tirée de pixel_x (4 bits)
- si vous avez compris vous verrez assez facilement qu’à droite du pavé 31 il y a un pavé 0 non représentée car on désire ne pas le voir. Ce que nous essayons d'expliquer ici, c’est que la mise bout à bout de pixel_y(5:4) et pixel_x(7:3) donne un numéro au pavé à droite pavé 31. Un autre mécanisme sera nécessaire pour effacer ce pavé !
Principe de réalisation
modifierNous allons maintenant chercher une architecture générale capable de dessiner ces caractères. La voici en image :
L'inverseur est uniquement présent pour éviter de dessiner les caractères à l’envers (réflexion selon un axe vertical au milieu du caractère). On pourrait le supprimer en initialisant autrement la ROM, ce que nous n'avons pas le courage de faire.
Tout circuit de dessin de caractères à l'écran se conçoit en deux parties :
- Le circuit de génération de caractères est réalisé comme expliqué en section précédente : concaténation d'une partie de pixel_y avec une partie de pixel_x. Tout travail sur l’affichage de caractères commence par là. On rappelle que la simple concaténation utilisée génère automatiquement des pavés en dehors des zones : pavé 0 à droite du pavé 31.
- Le circuit combinatoire qui évite le dessin en dehors de la plage des 128 caractères n’est pas représenté dans la figure ci-dessus. Il a donc pour objectif d'empêcher l’affichage du pavé 0 qui est à droite du pavé 31.
Nous pouvons maintenant passer au code source.
Solution
modifierVoici le programme VHDL complet qui réalise ceci :
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 font_test_top is
port(
clk, reset: in std_logic;
hsync, vsync: out std_logic;
rgb: out std_logic_vector(2 downto 0)
);
end font_test_top;
architecture arch of font_test_top is
COMPONENT 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 COMPONENT;
COMPONENT font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic
);
end component;
signal pixel_x, pixel_y: std_logic_vector(9 downto 0);
signal clk25,text_bit_on,video_on,font_bit: std_logic;
signal rgb_reg, rgb_next: std_logic_vector(2 downto 0);
signal char_addr: std_logic_vector(6 downto 0);
signal rom_addr: std_logic_vector(13 downto 0);
begin
process(clk) begin
if rising_edge(clk) then
clk25 <= NOT clk25;
end if;
end process;
-- Circuit de synchronisation VGA
vga_sync_unit: vga_sync
port map(clock_25Mhz=>clk25, horiz_sync_out=>hsync,
vert_sync_out=>vsync,-- video_on=>video_on,
pixel_column=>pixel_x, pixel_row=>pixel_y);
-- calcul de l'adressage de la ROM
--char_addr <= pixel_y(5 downto 4) & pixel_x(7 downto 3);
char_addr <= pixel_y(6 downto 5) & pixel_x(8 downto 4);
--rom_addr <= char_addr & pixel_y(3 downto 0) & not pixel_x(2 downto 0);
rom_addr <= char_addr & pixel_y(4 downto 1) & not pixel_x(3 downto 1);
-- instantiate font ROM
font_gen_unit: font_rom
port map(clk=>clk, addr => rom_addr, data =>font_bit);
-- doit-on dessiner sur ecran VGA ?
video_on <=
'1' when (pixel_x<640) and (pixel_y<480) else
'0';
-- doit-on dessiner sur zone caractères ?
text_bit_on <=
-- '1' when (pixel_x(9 downto 8)="00") and (pixel_y(9 downto 6)="0000") else
'1' when (pixel_x(9)='0') and (pixel_y(9 downto 7)="000") else
'0';
-- sortie rgb
process (video_on,text_bit_on)
begin
if (video_on='0') then
rgb <= "000";
elsif text_bit_on='1' then rgb <= '0' & font_bit & '0'; --vert
else
rgb <= "000";
end if;
end process;
end arch;
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
-- ********* voir exercice 1
library IEEE;
use IEEE.STD_LOGIC_1164.all;
Library UNISIM;
use UNISIM.vcomponents.all;
-- RAMB16_S1: Virtex-II/II-Pro, Spartan-3/3E 16kx1 Single-Port RAM
-- Xilinx HDL Libraries Guide, version 10.1.2
entity font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic_vector(0 downto 0)
);
end font_rom;
architecture arch of font_rom is
-- ******** voir RAM1B6_S1 plus haut
Vous avez trois lignes (utilisez les commentaires pour cela) à changer pour retirer le zoom :
- char_addr <= pixel_y(6 downto 5) & pixel_x(8 downto 4); à remplacer par char_addr <= pixel_y(5 downto 4) & pixel_x(7 downto 3);
- rom_addr <= char_addr & pixel_y(4 downto 1) & not pixel_x(3 downto 1); à remplacer par rom_addr <= char_addr & pixel_y(3 downto 0) & not pixel_x(2 downto 0);
- '1' when (pixel_x(9)='0') and (pixel_y(9 downto 7)="000") else à remplacer par '1' when (pixel_x(9 downto 8)="00") and (pixel_y(9 downto 6)="0000") else
Avant de passer à la suite nous vous proposons un exercice d'application qui représente une petite modification par rapport à ce que l’on vient de présenter. Le résoudre par vous-même vous montrera que vous avez bien compris la génération de caractères.
Exercice 4
modifierPuisque notre but, même s'il n’est pas encore avoué, est de remplacer la partie basse de la question 3 de l'exercice 3 par un ensemble de caractères, nous vous proposons le cahier des charges suivant :
- dessiner 32 caractères en bas de l'écran représentés par leur numéros dans le schéma ci-dessous
- utiliser encore le zoom 2x2
On rappelle que ce qui sera affiché à l'écran n’est pas le numéro des pavés mais les caractères dont le code ASCII est le numéro de pavé, soit ici la ligne : @ABCDEFGHIJKLMNOPQRST[\]^_
Les questions qu’il nous faut résoudre sont donc :
- Comment générer les numéros des pavés à partir de pixel_x et de pixel_y ?
- La méthode simple utilisée dans l'exemple précédent (concaténation d'une partie de pixel_x avec une partie de pixel_y) fonctionne-t-elle encore ?
Nous nous sommes aperçu après coup que si l’on continue à paver l'écran les numéros des pavés tombent justement sur 64, 65, 66, ... pour la ligne du bas. Ainsi la génération des numéros de pavés à partir de pixel_x et pixel_y reste identique à ce que l’on a déjà fait. Par contre pour ne garder que l’affichage de cette ligne du bas, il faut changer la génération de "text_bit_on" qui, rappelons-le, est responsable de l'extinction des caractères non voulus.
-- partie VHDL a changer
text_bit_on <=
'1' when (pixel_x(9)='0') and (pixel_y(9 downto 7)="000") else
'0';
en
-- partie VHDL changée
text_bit_on <=
'1' when (pixel_x(9)='0') and (pixel_y(9 downto 6)="0111") else -- pour exo4
'0';
Afficher un texte quelconque
modifierJusqu'à présent les numéros des pavés à afficher étaient successifs. Mais qu'en est-il si l’on désire afficher une chaîne de caractères fixe quelconque ?
Ce problème est généralement résolu en utilisant une deuxième mémoire contenant la liste des codes ASCII à afficher. Puisque vous avez maintenant un peu l'habitude de la terminologie Xilinx vous devinez qu'une RAMB16_S9 sera nécessaire.
Il est facile de décomposer ce problème en deux parties :
- générer des pavés de la figure de l'exercice 4 portant les numéros 0, 1, ..., 31, 32, 33, .. 39 pour le bas de l'écran (au lieu des 64 à 95 dans la figure).
- utiliser les numéros précédemment générés comme adresse de la mémoire qui contient les codes ASCII.
Nous allons donc commencer par essayer de générer des pavés numérotés à partir de 0. Ils formeront les adresses de la RAM des caractères à afficher. Si l'adresse se nomme addr_pave (pour adresse pavé ou numéro pavé) on réalise simplement :
addr_pave <= pixel_y(6) & pixel_x(9 downto 4);
Quand pixel_x et pixel_y s'incrémentent addr_pave donne le numéro de chaque pavé. Voyez-vous correctement que la dernière ligne (de caractères sur l'écran VGA) commence par le numéro 64 et s'arrête à 103 ?
Voila, nous sommes prêts à ajouter la RAMB16_S9. Avant cela il nous faut pointer une petite subtilité qu’il nous faudra aussi résoudre.
Le fait d’utiliser deux mémoires synchrones retarde l'arrivée du pixel à afficher par rapport aux compteurs pixel_x et pixel_y. Chaque caractère est donc affiché avec un pixel de retard si l’on ne corrige pas cela.
Exercice 5
modifierRéaliser l’ensemble capable d'afficher la chaine de caractères "GEII-TROYES SCORE : 0000 - LEVEL : 00" tout en bas de l'écran. En zoom 2x2, il y a juste la place. Il vous faudra correctement initialiser la RAMB16 et surtout au bon endroit.
Voici un schéma de principe qui explique ce que l’on cherche à réaliser :
Notez la présence de deux buffers (en jaune pâle) destinés à synchroniser le dessin à l'écran avec le balayage, sachant que l’on utilise deux mémoires synchrones. Pourquoi deux ? Parce qu'on utilise le 50 MHz alors que les compteurs de balayages sont en 25 MHz. Cela mériterait certainement bien d'autres explications, mais nous remettons celles-ci à plus tard.
Voici maintenant la solution complète en VHDL.
Nous donnons sans plus de commentaire le programme complet. Essayez de trouver comment est géré le retard de la recherche évoqué en remarque ci-dessus.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
Library UNISIM;
use UNISIM.vcomponents.all;
--use ieee.numeric_std.all;
entity font_test_top is
port(
clk, reset: in std_logic;
hsync, vsync: out std_logic;
rgb: out std_logic_vector(2 downto 0)
);
end font_test_top;
architecture arch of font_test_top is
-- composant de l'exercice 1
COMPONENT 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 COMPONENT;
-- voir : Notre RAMB16_S1 de caractères (spécifique à Xilinx) pour le contenu de font_rom
COMPONENT font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic
);
end component;
signal pixel_x, pixel_y: std_logic_vector(9 downto 0);
signal pixel_x1_reg,pixel_x2_reg,pixel_y1_reg,pixel_y2_reg: std_logic_vector(9 downto 0);
signal clk25,text_bit_on,video_on,font_bit: std_logic;
signal rgb_reg, rgb_next: std_logic_vector(2 downto 0);
signal char_addr,addr_pave: std_logic_vector(6 downto 0);
signal rom_addr: std_logic_vector(13 downto 0);
begin
process(clk) begin
if rising_edge(clk) then
clk25 <= NOT clk25;
pixel_x1_reg <= pixel_x; -- 2 clock delay
pixel_x2_reg <= pixel_x1_reg;
pixel_y1_reg <= pixel_y;
pixel_y2_reg <= pixel_y1_reg;
end if;
end process;
-- Circuit de synchronisation VGA
vga_sync_unit: vga_sync
port map(clock_25Mhz=>clk25, horiz_sync_out=>hsync,
vert_sync_out=>vsync,-- video_on=>video_on,
pixel_column=>pixel_x, pixel_row=>pixel_y);
-- calcul de l'adressage de la ROM
--char_addr <= pixel_y(5 downto 4) & pixel_x(7 downto 3);
-- char_addr <= pixel_y(6 downto 5) & pixel_x(8 downto 4); --OK pour exo4
--rom_addr <= char_addr & pixel_y(3 downto 0) & not pixel_x(2 downto 0);
rom_addr <= char_addr & pixel_y2_reg(4 downto 1) & not pixel_x2_reg(3 downto 1);
-- instantiate font ROM
font_gen_unit: font_rom
port map(clk=>clk, addr => rom_addr, data =>font_bit);
-- doit-on dessiner sur ecran VGA ?
video_on <=
'1' when (pixel_x<640) and (pixel_y<480) else
'0';
-- doit-on dessiner sur zone caractères ?
text_bit_on <=
-- '1' when (pixel_x(9 downto 8)="00") and (pixel_y(9 downto 6)="0000") else
-- '1' when (pixel_x(9)='0') and (pixel_y(9 downto 7)="000") else
-- '1' when (pixel_x(9)='0') and (pixel_y(9 downto 6)="0111") else -- pour exo4
'1' when (pixel_y(9 downto 6)="0111") else
'0';
-- sortie rgb
process (video_on,text_bit_on)
begin
if (video_on='0') then
rgb <= "000";
elsif text_bit_on='1' then rgb <= '0' & font_bit & '0'; --vert
else
rgb <= "000";
end if;
end process;
RAMB16_S9_inst : RAMB16_S9 --RAM qui contient le texte à afficher
generic map (
INIT => X"000", -- Value of output RAM registers at startup
SRVAL => X"000", -- Ouput value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
INITP_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_02=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_03=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
INIT_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02=>X"56454C202D2030303030203A2045524F43532020205345594F52542D49494547",
INIT_03=>X"000000000000000000000000000000000000000000002020203030203A204C45",
INIT_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 4096 to 8191
INIT_10=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1d=>X"00000000000000000000000000000000000000000000000000000000000000000",
INIT_1e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 8192 to 12287
INIT_20=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_23=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_24=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_25=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_26=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_27=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_28=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_29=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 12288 to 16383
INIT_30=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_31=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_32=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_33=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_35=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_36=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_37=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_38=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3f=>X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DO(6 downto 0) => char_addr, -- 7-bit Data Output
DO(7)=> open,
DOP => open,
ADDR(6 downto 0) => addr_pave, -- 7-bit Address Input
ADDR(10 downto 7) => "0000",
CLK => CLK, -- Clock
DI => "00000000", -- 8-bit Data Input
DIP => "0",
EN => '1', -- RAM Enable Input
SSR => '0', -- Synchronous Set/Reset Input
WE => '0' -- Write Enable Input
);
addr_pave <= pixel_y(6) & pixel_x(9 downto 4);
end arch;
Question subsidiaire : comment changer le code source précédent pour que le numéro du pavé de la dernière ligne commence par zéro (au lieu de 64) pour que le texte à afficher se trouve en adresse 0.
Le minimum de changement est :
addr_pave <= '0' & pixel_x(9 downto 4);
Remarquez qu'avec cette modification toute la colonne gauche contient un ensemble de pavés numéro 0... et qu'en fait toutes les colonnes contiennent le même numéro de pavé ! C'est
text_bit_on <=
'1' when (pixel_y(9 downto 6)="0111") else
'0';
qui gère la zone d'affichage : ici la dernière ligne.
Conclusion
modifierCes essais nous ont montré un léger problème dans notre module de synchronisation VGA car les caractères les plus à gauches ne sont dessinés que partiellement. Jusqu'à présent nous avons toujours mis cela sur le compte des écrans mais le problème est qu'avec les fichiers sources de P. P. Chu associés à son livre, on n'a pas ce problème !
Nous avons donc corrigé notre fichier de synchronisation dans l'exercice 1 pour éviter ce problème.
Mélanger dessins et caractères
modifierArrivé ici vous êtes capable d’une part de dessiner des rectangles et d’autre part d'afficher des caractères. Nous allons nous intéresser au mélange des deux maintenant.
Exercice 6
modifierReprendre la question 3 de l'exercice 3 en retirant toute la partie basse composée de deux afficheurs sept segments et en la remplaçant par le texte de la section précédente. À ce point, vous n'avez aucune idée sur la façon qu’il vous faudra mettre en œuvre pour afficher le bon niveau (level en anglais). Il vous faudra donc retirer l'entrée correspondante. Elle est d'ailleurs improprement appelée score alors qu'en réalité elle est utilisée pour afficher le niveau (voir le projet étudiant correspondant et le programme C de la correction).
Nous donnons en vrac une solution qui fonctionne mais qui mériterait d’être commentée et épurée.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
Library UNISIM;
use UNISIM.vcomponents.all;
ENTITY VGAtop IS
PORT (clk_50 : in STD_LOGIC; -- horloge 50MHz
-- 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);
-- 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 arch of VGAtop is
COMPONENT 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 COMPONENT;
COMPONENT font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic
);
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 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 pixel_x, pixel_y: std_logic_vector(9 downto 0);
signal pixel_x1_reg,pixel_x2_reg,pixel_y1_reg,pixel_y2_reg: std_logic_vector(9 downto 0);
signal clk25,text_bit_on,video_on,font_bit: std_logic;
signal rgb_reg,rgb : std_logic_vector(2 downto 0);
signal char_addr,addr_pave: std_logic_vector(6 downto 0);
signal rom_addr: std_logic_vector(13 downto 0);
signal sgreen,sgreen3,sblue3,sblue5,sred5,sgreen5,sred6,sblue6,sgreen6,sred7,sblue7,sgreen7,
sred3,sblue,sred1,sgreen1,sblue1,sred2,sgreen2,sblue2,sblue4,sgreen4,sred4,sred : std_logic;
begin
process(clk_50) begin
if rising_edge(clk_50) then
clk25 <= NOT clk25;
pixel_x1_reg <= pixel_x; -- 2 clock delay
pixel_x2_reg <= pixel_x1_reg;
pixel_y1_reg <= pixel_y;
pixel_y2_reg <= pixel_y1_reg;
end if;
end process;
-- Circuit de synchronisation VGA
vga_sync_unit: vga_sync
port map(clock_25Mhz=>clk25, horiz_sync_out=>hsynch,
vert_sync_out=>vsynch,-- video_on=>video_on,
pixel_column=>pixel_x, pixel_row=>pixel_y);
-- gestion du texte
rom_addr <= char_addr & pixel_y2_reg(4 downto 1) & not pixel_x2_reg(3 downto 1);
font_gen_unit: font_rom
port map(clk=>clk_50, addr => rom_addr, data =>font_bit);
-- doit-on dessiner du texte sur notre écran VGA ?
video_on <=
'1' when (pixel_x<640) and (pixel_y<480) else
'0';
-- doit-on dessiner sur zone caractères ?
text_bit_on <=
'1' when (pixel_y(9 downto 6)="0111") else
'0';
-- sortie rgb
process (video_on,text_bit_on)
begin
if (video_on='0') then
rgb <= "000";
elsif text_bit_on='1' then rgb <= '0' & font_bit & '0'; --vert
else
rgb <= "000";
end if;
end process;
RAMB16_S9_inst : RAMB16_S9 --RAM qui contient le texte à afficher
generic map (
INIT => X"000", -- Value of output RAM registers at startup
SRVAL => X"000", -- Ouput value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
INITP_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_02=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_03=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
INIT_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02=>X"56454C202D2030303030203A2045524F43532020205345594F52542D49494547",
INIT_03=>X"000000000000000000000000000000000000000000002020203030203A204C45",
INIT_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 4096 to 8191
INIT_10=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1d=>X"00000000000000000000000000000000000000000000000000000000000000000",
INIT_1e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 8192 to 12287
INIT_20=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_23=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_24=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_25=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_26=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_27=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_28=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_29=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 12288 to 16383
INIT_30=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_31=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_32=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_33=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_35=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_36=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_37=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_38=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3f=>X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DO(6 downto 0) => char_addr, -- 7-bit Data Output
DO(7)=> open,
DOP => open,
ADDR(6 downto 0) => addr_pave, -- 7-bit Address Input
ADDR(10 downto 7) => "0000",
CLK => CLK_50, -- Clock
DI => "00000000", -- 8-bit Data Input
DIP => "0",
EN => '1', -- RAM Enable Input
SSR => '0', -- Synchronous Set/Reset Input
WE => '0' -- Write Enable Input
);
addr_pave <= pixel_y(6) & pixel_x(9 downto 4);
--******** fin gestion de l’affichage du texte *****
-- ******* debut de l’affichage graphique *********
balle:rect port map(row=>pixel_y,col=>pixel_x,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=>pixel_y,col=>pixel_x,red1=>sred3,green1=>sgreen3,blue1=>sblue3,colorRGB=>"111",
delta_x=>"1010000000",delta_y=>"0000001000",
x_rec => "0000000000", y_rec => "0110100110");
raquetteG:rect port map(row=>pixel_y,col=>pixel_x,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=>pixel_y,col=>pixel_x,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');
line1:ligne8briques generic map(gx=>420,gy=>0,gdx=>20,gdy=>422)
port map(e_8=>ligne1,segx=>pixel_x,segy=>pixel_y,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=>pixel_x,segy=>pixel_y,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 or rgb(2);
green <= sgreen or sgreen1 or sgreen2 or sgreen3 or sgreen4 or sgreen5 or sgreen6 or sgreen7 or rgb(1);
blue <= sblue or sblue1 or sblue2 or sblue3 or sblue4 or sblue5 or sblue6 or sblue7 or rgb(0);
end arch;
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 <= 751) AND (h_count >= 656) 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;
Library UNISIM;
use UNISIM.vcomponents.all;
-- RAMB16_S1: Virtex-II/II-Pro, Spartan-3/3E 16kx1 Single-Port RAM
-- Xilinx HDL Libraries Guide, version 10.1.2
entity font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic_vector(0 downto 0)
);
end font_rom;
architecture arch of font_rom is
begin
RAMB16_S1_inst : RAMB16_S1
generic map (
INIT => X"0", -- Value of output RAM registers at startup
SRVAL => X"0", -- Ouput value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
INIT_00=>X"000000FF0000FF0000FF0000FF00000000000000000000000000000000000000",
INIT_01=>X"0000242424242424242424242424000000000000FF0000FF0000FF0000FF0000",
INIT_02=>X"0000929292929292929292929292000000004949494949494949494949490000",
INIT_03=>X"0000AAAAAAAAAAAAAAAAAAAAAAAA000000005555555555555555555555550000",
INIT_04=>X"0000F3FCF3FCF3FCF3FCF3FCF3FC00000000FF00FF00FF00FF00FF00FF000000",
INIT_05=>X"00000C030C030C030C030C030C0300000000CF3FCF3FCF3FCF3FCF3FCF3F0000",
INIT_06=>X"00000066666666000066666666000000000030C030C030C030C030C030C00000",
INIT_07=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FF99999999FFFF99999999FF0000",
INIT_08=>X"0000000000000000FFFFFFFFFFFF00000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_09=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FFFFFFFFFFFF0000000000000000",
INIT_0a=>X"0000007E42424242424242427E0000000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_0b=>X"000024492449244924492449244900000000FF81818181818181818181FF0000",
INIT_0c=>X"0000499249924992499249924992000000002492249224922492249224920000",
INIT_0d=>X"0000AA55AA55AA55AA55AA55AA550000000055AA55AA55AA55AA55AA55AA0000",
INIT_0e=>X"0000DB6DDB6DDB6DDB6DDB6DDB6D00000000DBB6DBB6DBB6DBB6DBB6DBB60000",
INIT_0f=>X"0000FFFFFFFFFFFFFFFFFFFFFFFF00000000B66DB66DB66DB66DB66DB66D0000",
-- Address 4096 to 8191
INIT_10=>X"0000001000001010101010101010000000000000000000000000000000000000",
INIT_11=>X"0000004444FE4444444444FE4444000000000000000000000044444444440000",
INIT_12=>X"0000000C12924C2010086492906000000000007C921212127C909090927C0000",
INIT_13=>X"000000000000000000101010101000000000007A84848A507090888848300000",
INIT_14=>X"0000001008080404040404080810000000000010202040404040402020100000",
INIT_15=>X"0000000010101010FE101010100000000000009292545438FE38545492920000",
INIT_16=>X"0000000000000000FE0000000000000000000020100808000000000000000000",
INIT_17=>X"0000000000804020100804020000000000000000001818000000000000000000",
INIT_18=>X"0000003810101010101010503010000000000038448282A2928A828244380000",
INIT_19=>X"0000007C820202027C020202827C0000000000FE808080807C020202827C0000",
INIT_1a=>X"0000007C820202027C80808080FE00000000001C080808FE8888482818080000",
INIT_1b=>X"00000038101010101008040202FE00000000007C828282827C808080807E0000",
INIT_1c=>X"000000FC020202027C828282827C00000000007C828282827C828282827C0000",
INIT_1d=>X"0000002010080800000018180000000000000000001818000000181800000000",
INIT_1e=>X"0000000000FE0000000000FE000000000000000000020C30C0300C0200000000",
INIT_1f=>X"0000001000101008040282824438000000000000008060180618608000000000",
-- Address 8192 to 12287
INIT_20=>X"00000082828244447C442828281000000000003C42809EA2A29E828244380000",
INIT_21=>X"0000007C8280808080808080827C0000000000FC82828284F884828282FC0000",
INIT_22=>X"000000FE80808080FC80808080FE0000000000F8848482828282848488F00000",
INIT_23=>X"0000007C828282829E808080827C00000000008080808080FC80808080FE0000",
INIT_24=>X"0000003810101010101010101038000000000082828282827C82828282820000",
INIT_25=>X"0000008282848488F088848482820000000000708888080808080808081C0000",
INIT_26=>X"000000828282829292AAAAAAC6820000000000FE808080808080808080800000",
INIT_27=>X"0000007C8282828282828282827C000000000082868A8A8A92A2A2A2C2820000",
INIT_28=>X"0000007A848AB28282828282827C00000000008080808080FC828282827C0000",
INIT_29=>X"0000007C820202027C808080827C000000000082848890A0FC828282827C0000",
INIT_2a=>X"0000007C82828282828282828282000000000010101010101010101092FE0000",
INIT_2b=>X"00000082C6AAAAAA929282828282000000000010102828284444448282820000",
INIT_2c=>X"0000001010101010282844448282000000000082824444283828444482820000",
INIT_2d=>X"00000038202020202020202020380000000000FE824040203808040482FE0000",
INIT_2e=>X"0000003808080808080808080838000000000000000204081020408000000000",
INIT_2f=>X"000000FE00000000000000000000000000000000000000000082442810000000",
-- Address 12288 to 16383
INIT_30=>X"0000003AC6828282C63A00000000000000000000000000000008101020200000",
INIT_31=>X"0000003CC2808080C23C000000000000000000B8C6828282C6B8808080800000",
INIT_32=>X"00000038C680FC82C6380000000000000000003AC6828282C63A020202020000",
INIT_33=>X"00000038C6027E82C638000000000000000000808080808080F88080423C0000",
INIT_34=>X"0000000C1210101010100000100000000000008282828282C6B8808080800000",
INIT_35=>X"000000828488B0C0B88680808000000000000070880404040404000004000000",
INIT_36=>X"0000009292929292D2AC0000000000000000000E102020202020202020200000",
INIT_37=>X"00000038C6828282C6380000000000000000008282828282C6B8000000000000",
INIT_38=>X"0000000202027E82C63A000000000000000000808080FC82C6B8000000000000",
INIT_39=>X"0000007C82027E80827C0000000000000000008080808080C6B8000000000000",
INIT_3a=>X"0000003AC682828282820000000000000000003C4280808080F8808080800000",
INIT_3b=>X"0000006C92929292928200000000000000000010284482828282000000000000",
INIT_3c=>X"0000003008083C42820000000000000000000082443828448200000000000000",
INIT_3d=>X"00000010202020408040202020100000000000FE40300804FE00000000000000",
INIT_3e=>X"0000001008080804020408080810000000000010101010101010101010100000",
INIT_3f=>X"00000000000000000000000000000000000000000000000C9260000000000000")
port map (
DO => data, -- 1-bit Data Output
ADDR => ADDR, -- 14-bit Address Input
CLK => CLK, -- Clock
DI => "0", -- 1-bit Data Input
EN => '1', -- RAM Enable Input
SSR => '0', -- Synchronous Set/Reset Input
WE => '0' -- Write Enable Input
);
-- End of RAMB16_S1_inst instantiation
end arch;
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;
-- affichage complet mur 8 briques
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+((gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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+(2*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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+(3*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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+(4*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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+(5*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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+(6*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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+(7*(gdy-gy)/8), dx =>gdx-2, dy=>(gdy-gy-2)/8)
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;
Nous allons mettre toutes ces connaissances acquises à la lecture de ces sections à profit pour réaliser un pacman. Cela nous permettra de changer un peu de domaine de jeu pour laisser tomber les raquettes.
Pacman
modifierLes techniques graphiques utilisées dans le jeu de pacman sont universelles. Elles n'ont cependant pas été utilisées jusqu'à présent même si le jeu de Casse-briques aurait très bien pu les mettre en œuvre (ce qui sera d'ailleurs essayé dans des futurs projets).
Ces techniques peuvent être résumées comme ceci :
- vous disposez d'un fond d'écran qui joue le rôle de décor. Traditionnellement il est réalisé avec un pavage dans lequel sont dessinés diverses briques de taille constante. Ce décor est en principe fixe. Cette technique est complètement identique au dessin de caractères déjà évoquée dans ce chapitre.
- vous disposez d'un ou plusieurs objets mobiles que l’on appelle sprite (terme anglo-saxon qui ne semble pas avoir été traduit. Ces sprites ont une partie transparente et une partie opaque.
Si l’on reprend le jeu de casse-briques, l’affichage du score et des briques constitue le décor et la (ou les) raquette(s) ainsi que la balle constituent les sprites. Jusqu'à maintenant nous avons utilisé la technique de dessin de rectangles pour les sprites. Le décor quant à lui était un mix : rectangles pour les briques et pavés pour les scores (à partir de 2012).
Construction du décor
modifierNous allons utiliser la technique des dessins de caractères pour construire le décor. Puisque nous disposons d'une ROM de caractères 8x16 (horizontal x vertical), nous allons l’utiliser telle quelle. Une utilisation de briques 16x16 aurait certainement été bien mieux, esthétiquement parlant, mais nous remettons cela à plus tard, dans un futur projet.
Le décor est changeant : les Pac-gommes disposées tout au long du labyrinthe sont mangées par pacman et devront donc disparaitre. Conséquence, le décor doit avoir une image en RAM. Comme déjà indiqué, la première chose à faire dans ce cas précis, est de choisir le nombre et la taille des pavés numérotés que l’on va disposer à l'écran. Ces numéros (de pavé) constitueront l'adresse de la dite RAM.
Construction des numéros de pavés
modifierNous allons tout reprendre les explications depuis le début. Il nous faut donc construire un nouveau pavage. Nous allons choisir des pavés 16x32 (horizontal x vertical). On y placera à l'intérieur des pavés de taille 8x16 qui seront donc grossis d'un facteur 2.
Nous rappelons dans cette figure comment il est simple de former les numéros de pavés à l'aide d'une simple concaténation. Ce faisant, les numéros des pavés dépassent la taille de l'écran vers la droite puisqu’il vont jusqu'à 63 pour la première ligne. Ceci est visible dès la seconde ligne qui commence par le numéro 64.
Ces numéros de pavés vont nous servir à adresser une mémoire qui contiendra donc l’ensemble du décor. Le fait que certains des pavés ne soient pas visibles nous fait perdre de la place dans la mémoire, mais ceci est compensé par la facilité de réaliser les adresses (nous n'insisterons jamais assez, par simple concaténation).
Construction des pavés de base du décor
modifierÀ part le texte qui sera affiché en bas de l'écran, nous n'avons pas besoin de beaucoup de pavés pour fabriquer un labyrinthe. Les voici présentés :
Il faut savoir que nous avons décidé de ne pas coder les couleurs dans la mémoire. Les couleurs de cette figures sont celles que nous avons choisies mais elles seront obtenues par un circuit combinatoire assez complexe. Les numéros entre parenthèses représentent l'adresse où se trouve le dessin du caractère.
Cette figure permet de comprendre le début de l'initialisation de la mémoire :
--*********** début du contenu de la RAMB16_S1
-- rien d'intéressant / Noir complet
INIT_00=>X"00000000183C7F78786F3C180000000000000000000000000000000000000000",
--rien d'intéressant ici
INIT_01=>X"00000000183C6E7E7E7E3C180000000000000000FF0000FF0000FF0000FF0000",
-- fruit / pac-gomme
INIT_02=>X"0000000000183C7E7E3C180000000000000000000000183C3C18000000000000",
--brique / rien d'intéressant
INIT_03=>X"FFD5ABD5ABD5ABD5ABD5ABD5ABD5ABFF00005555555555555555555555550000",
Cette mémoire est commandée par une RAMB16_S9 qui sera destinée à réaliser le labyrinthe complet.
Exercice 7
modifierÀ partir du contenu de la RAMB16_S9, pouvez-vous dessiner ce qui sera reproduit sur votre écran VGA ?
-- Address 0 to 4095
INIT_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02=>X"0707070707070707070707070707070707070707000707070707070707070700",
INIT_03=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_04=>X"0004000400040004000400040004000400040004000400040004000400040700",
INIT_05=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_06=>X"0407040704070407040704070407070707070700000707070404070400040700",
INIT_07=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_08=>X"0407040704070400040704000407000000000000000000070407070707040700",
INIT_09=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_0a=>X"0407040404070407040704070407000000000000000000070407070407040000",
INIT_0b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0c=>X"0404040704070400040704000407070707070707070707070400000400040700",
INIT_0d=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_0e=>X"0407040704000407040704070004000400040004000400040704040704070700",
INIT_0f=>X"0000000000000000000000000000000000000000000000000000000000000007",
-- Address 4096 to 8191
INIT_10=>X"0404050404070707040404040704070704070407070004000407040004040700",
INIT_11=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_12=>X"0407040704070407040704070407040004000407040704070407070407040000",
INIT_13=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14=>X"0404040400040004000400040004000400040004000400040004000400040700",
INIT_15=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_16=>X"0707070707070707070707070707070707070700070707070707070707070700",
INIT_17=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_18=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1c=>X"56454C202D203030303030203A2045524F435320205345594F52542D49494547",
INIT_1d=>X"000000000000000000000000000000000000000000002020203030203A204C45",
INIT_1e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1f=>X"0000000000000000000000000000000000000000000000000000000000000000",
Une chaîne de caractères est écrite en bas de l'écran. Pouvez-vous la donner en vous aidant des codes ASCII contenu dans cette mémoire.
Construction et gestion des sprites
modifierLes sprites sont des parties mobiles. Leur dessin sera stocké lui aussi dans la ROM de caractères. Étant donné qu'un sprite se superpose au décor qui lui aussi utilise la ROM de caractères, cela indique qu’à un certain moment cette ROM de caractères peut être accédée deux fois simultanément ! pour le décor du fond et pour le sprite ! Il en résulte immédiatement qu’il nous faudra ajouter un port à la ROM de caractères. D'autre part, il nous faudra gérer deux sprites : le pacman et l'ennemi et choisir une priorité puisque les mémoires contenant plus de deux PORTs n'existent pas.
Exercice 8
modifierNous donnons le contenu de la RAMB16_S1 qui gère les sprites :
-- Address 0 to 4095
--??/blanc
INIT_00=>X"00000000183C7F78786F3C180000000000000000000000000000000000000000",
--INIT_01=>X"0000242424242424242424242424000000000000FF0000FF0000FF0000FF0000",
INIT_01=>X"00000000183C6E7E7E7E3C180000000000000000FF0000FF0000FF0000FF0000",
-- fruit/pac-gomme
INIT_02=>X"0000000000183C7E7E3C180000000000000000000000183C3C18000000000000",
--brique/??
INIT_03=>X"FFD5ABD5ABD5ABD5ABD5ABD5ABD5ABFF00005555555555555555555555550000",
--pacman gauche/droite
INIT_04=>X"00000000183CFE1E1EF63C1800000000000000003078FEF0F0DE783000000000",
--pacman down/up
INIT_05=>X"00000024243C6E7E7E7E3C180000000000000000183C7E7E7E6E3C2424000000",
--pacman fermé gauche/droite
INIT_06=>X"00000000183C7E7E7E763C1800000000000000003078FCFCFCDC783000000000",
-- pacman fermé down/up
INIT_07=>X"00000000183C6E7E7E7E3C180000000000000000183C7E7E7E6E3C1800000000",
-- ennemi fantome/ennemi esprit
INIT_08=>X"000000003C243C007E5A7E0000000000000000AAFFC3C3FF7E5A7E3C00000000",
Pouvez-vous dessiner sur une feuille ce que cela donnera sur un écran VGA ?
Affichage des sprites
modifierPour gérer les sprites il nous faut prévoir les entrées correspondantes dans l'entité. La voici présentée :
entity pacman is
port(
clk, reset: in std_logic;
-- pacman's positions
pacman_x,pacman_y : in std_logic_vector(9 downto 0);
-- numéro du sprite pacman utilisé (huit possibles)
pacman_sprite_number : in std_logic_vector(2 downto 0);
-- enemy's positions
enemy_x,enemy_y : in std_logic_vector(9 downto 0);
-- numéro du sprite ennemi utilisé (deux possibles)
enemy_sprite_number : in std_logic;
hsync, vsync: out std_logic;
rgb: out std_logic_vector(2 downto 0)
);
end pacman;
On y voit apparaître les coordonnées du pacman sur 10 bits et le numéro du sprite car le pacman est représenté bouche ouverte ou fermée et allant vers la droite, la gauche, le haut et le bas. Cela fait en tout 8 sprites différents d'où les trois bits "pacman_sprite_number". Le fait que les coordonnées soient sur 10 bits indique qu’il peut être affiché n’importe où sur l'écran. Si l’on désire interfacer ces coordonnées avec un processeur 8 bits, on peut se limiter à 8 bits et relier directement les deux bits de poids faibles à 0.
Le programme complet
modifierVoici le programme complet avec sprite et décor.
library ieee;
use ieee.std_logic_1164.all;
entity topTest is
port(
clk: in std_logic;
-- switchs
swi : in std_logic_vector(7 downto 0);
-- push buttons
pbi : in std_logic_vector(3 downto 0);
hsync, vsync: out std_logic;
rgb: out std_logic_vector(2 downto 0)
);
end topTest;
architecture atopTest of topTest is
component pacman is
port(
clk : in std_logic;
-- pacman's positions
pacman_x,pacman_y : in std_logic_vector(9 downto 0);
-- numéro du sprite pacman utilisé (huit possibles)
pacman_sprite_number : in std_logic_vector(2 downto 0);
-- enemy's positions
enemy_x,enemy_y : in std_logic_vector(9 downto 0);
-- numéro du sprite ennemi utilisé (deux possibles)
enemy_sprite_number : in std_logic;
-- Sortir un PORT de RAMB16_S9_S9
-- gestion sans la parité
ADDRB : in std_logic_vector(10 downto 0); -- Port B 11-bit Address Input
DOB : out std_logic_vector(7 downto 0); -- Port B 8-bit Data Output
DIB : in std_logic_vector(7 downto 0); -- Port B 8-bit Data Input
-- CLKB : in std_logic; -- Port B Clock
ENB,SSRB,WEB : in std_logic; -- PortB RAM Enable Input,
-- Synchronous Set/Reset Input,Write Enable Input
hsync, vsync: out std_logic;
rgb: out std_logic_vector(2 downto 0)
);
end component;
begin
ic1:pacman port map(clk => clk,
pacman_x(9 downto 4) => "000000",pacman_x(3 downto 2) => swi(4 downto 3),pacman_x(1 downto 0) =>"00",
pacman_y(9) => '0',pacman_y(8 downto 7) => swi(6 downto 5),pacman_y(6 downto 0) =>"0000000",
pacman_sprite_number => swi(2 downto 0),
enemy_sprite_number => swi(7),
enemy_x(9) => '0',enemy_x(8 downto 7) => pbi(1 downto 0),enemy_x(6 downto 0) =>"0000000",
enemy_y(9) => '0',enemy_y(8 downto 7) => pbi(3 downto 2),enemy_y(6 downto 0) =>"0000000",
ADDRB => "00000000000",
DIB => "00000000",
DOB => open,
-- CLKB => clk,
ENB => '0',
SSRB => '0',
WEB => '0',
hsync => hsync,
vsync => vsync,
rgb => rgb);
end atopTest;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
Library UNISIM;
use UNISIM.vcomponents.all;
--use ieee.numeric_std.all;
entity pacman is
port(
clk : in std_logic;
-- pacman's positions
pacman_x,pacman_y : in std_logic_vector(9 downto 0);
-- numéro du sprite pacman utilisé (huit possibles)
pacman_sprite_number : in std_logic_vector(2 downto 0);
-- enemy's positions
enemy_x,enemy_y : in std_logic_vector(9 downto 0);
-- numéro du sprite ennemi utilisé (deux possibles)
enemy_sprite_number : in std_logic;
-- Sortir un PORT de RAMB16_S9_S9
-- gestion sans la parité
ADDRB : in std_logic_vector(10 downto 0); -- Port B 11-bit Address Input
DOB : out std_logic_vector(7 downto 0); -- Port B 8-bit Data Output
DIB : in std_logic_vector(7 downto 0); -- Port B 8-bit Data Input
-- CLKB : in std_logic; -- Port B Clock
ENB,SSRB,WEB : in std_logic; -- PortB RAM Enable Input,
-- Synchronous Set/Reset Input,Write Enable Input
-- gestion du VGA
hsync, vsync: out std_logic;
rgb: out std_logic_vector(2 downto 0)
);
end pacman;
architecture arch of pacman is
-- composant de l'exercice 1 de ce chapitre à ajouter à ce programme
COMPONENT 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 COMPONENT;
-- voir : Notre RAMB16_S1 de caractères (spécifique à Xilinx) pour le contenu de font_rom
COMPONENT font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic;
addrB: in std_logic_vector(13 downto 0);
dataB: out std_logic
);
end component;
signal pixel_x, pixel_y: std_logic_vector(9 downto 0);
signal pixel_x1_reg,pixel_x2_reg,pixel_y1_reg,pixel_y2_reg: std_logic_vector(9 downto 0);
signal clk25,text_bit_on,video_on,font_bit,sprite_pacman_on,Data_sprite: std_logic;
signal sprite_enemy_on,sprite_enemy_on_b,sprite_pacman_on_b : std_logic;
signal selection : std_logic_vector(1 downto 0);
signal rgb_reg, rgb_next: std_logic_vector(2 downto 0);
signal char_addr,char_addrb: std_logic_vector(6 downto 0);
signal addr_pave: std_logic_vector(9 downto 0);
signal rom_addr,sprite_rom_addr: std_logic_vector(13 downto 0);
signal addrpacx, addrpacy, addrenemyx, addrenemyy : std_logic_vector(9 downto 0);
begin
process(clk) begin
if rising_edge(clk) then
clk25 <= NOT clk25;
pixel_x1_reg <= pixel_x; -- 2 clock delay
-- pixel_x2_reg <= pixel_x1_reg;
pixel_y1_reg <= pixel_y;
-- pixel_y2_reg <= pixel_y1_reg;
char_addrb <= char_addr; -- pour le calcul des couleurs
sprite_pacman_on_b <= sprite_pacman_on;
sprite_enemy_on_b <= sprite_enemy_on;
end if;
end process;
-- Circuit de synchronisation VGA
vga_sync_unit: vga_sync
port map(clock_25Mhz=>clk25, horiz_sync_out=>hsync,
vert_sync_out=>vsync,-- video_on=>video_on,
pixel_column=>pixel_x, pixel_row=>pixel_y);
-- calcul des numéros du pavage de l'écran
-- relié à RAMB16_S9 pour sortir char_addr (voir port map plus loin)
addr_pave <= pixel_y(8 downto 5) & pixel_x(9 downto 4);
-- calcul de l'adressage de la ROM
--utilise char_addr qui sort de la RAMB16_S9 et pixel_y et pixel_x retardés
-- semble OK conforme aux espérances
rom_addr <= char_addr & pixel_y1_reg(4 downto 1) & not pixel_x1_reg(3 downto 1);
-- doit-on dessiner le sprite pacman ?
sprite_pacman_on <=
'1' when (pixel_x1_reg >= pacman_x) and (pixel_x1_reg < pacman_x + 16)
and (pixel_y1_reg >= pacman_y) and (pixel_y1_reg < pacman_y + 32) else
'0';
-- doit-on dessiner le sprite ennemi ?
sprite_enemy_on <=
'1' when (pixel_x1_reg >= enemy_x) and (pixel_x1_reg < enemy_x + 16)
and (pixel_y1_reg >= enemy_y) and (pixel_y1_reg < enemy_y + 32) else
'0';
selection <= sprite_pacman_on & sprite_enemy_on;
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ajouté par gilles pour test positionnnement dynamique du pacman et de ennemi
-- positionnement du pacman et de ennemi a n’importe quelle position dans l'ecran
-- on calcule des coordonnées relatives qui sont des " delta_x " et " delta_y " pour avoir directement l'adresse
-- en memoire du pixel a dessiner pour l’objet que l’on veut placer
-- addrpacx , etc ne sont peut être pas tres bien choisis comme noms de signaux ... !
-- position de l’objet à partir du coin en haut a gauche
-- NB !! la soustraction va fournir des resultats complètement incoherents a chaque fois que le pixel courant
-- du balayage temps reel (pixel_x) ne sera pas dans la zone de l’objet a dessiner .... mais ce n’est pas grave car dans ces
-- cas là le bit "sprite_pacman_on " et ''idem'' avec " sprite_enemy_on " sera à 0
addrpacx <= pixel_x1_reg - pacman_x;
addrpacy <= pixel_y1_reg - pacman_y;
addrenemyx <= pixel_x1_reg - enemy_x;
addrenemyy <= pixel_y1_reg - enemy_y;
-- fin d'ajout par gilles
with selection select -- multiplexage des sprites
sprite_rom_addr <= "00000000000000" when "00",
-- calcul de l'adressage de la ROM pour le sprite pacman
-- (16*8*enemy_sprite_number+debut_sprite_enemy)
("001000" & enemy_sprite_number & addrenemyy(4 downto 1) &
not addrenemyx(3 downto 1)) when "01",
-- calcul de l'adressage de la ROM pour le sprite pacman
-- (16*8*pacman_sprite_number+(debut_sprite_pacman=4x32x8))
("0001" & pacman_sprite_number & addrpacy(4 downto 1) &
not addrpacx(3 downto 1)) when others; --+1024
-- instantiate font ROM
font_gen_unit: font_rom
port map(clk=>clk, addr => rom_addr, data =>font_bit,addrB=>sprite_rom_addr,dataB=>Data_sprite);
-- doit-on dessiner sur ecran VGA ?
video_on <=
'1' when (pixel_x<640) and (pixel_y<480) else
'0';
-- doit-on dessiner sur zone caractères ? oui complet pour le moment
text_bit_on <= video_on; -- car ici tout le décor représente du texte sur tout l'écran
-- gestion des couleurs : sortie rgb est compliquée car on ne stocke pas d'info couleur dans la ROM de caractères
rgb <= "000" when video_on='0' else
'0' & Data_sprite & Data_sprite when sprite_pacman_on_b='1' and Data_sprite='1' else
Data_sprite & enemy_sprite_number & Data_sprite when sprite_enemy_on_b='1' and Data_sprite='1' else
'0' & font_bit & '0' when pixel_y>400 else --440 !!!
font_bit & font_bit & '0' when char_addrb = 7 else
font_bit & font_bit & font_bit when char_addrb = 4 else
"00" & font_bit when char_addrb = 5 else
"000";
-- Il faut ajouter un PORT si l’on veut qu'un processeur puisse y intervenir
RAMB16_S9_inst : RAMB16_S9_S9 --RAM qui contient le décor à afficher
generic map (
INIT_A => X"000", -- Value of output RAM registers at startup
INIT_B => X"000", -- Value of output RAM registers at startup
SRVAL_A => X"000", -- Ouput value upon SSR assertion
SRVAL_B => X"000", -- Ouput value upon SSR assertion
WRITE_MODE_A => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
WRITE_MODE_B => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
SIM_COLLISION_CHECK => "ALL", -- "NONE", "WARNING", "GENERATE_X_ONLY", "ALL"
INITP_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_02=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_03=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INITP_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
INIT_00=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_03=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_04=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 4096 to 8191
INIT_10=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1a=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1c=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1d=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1f=>X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 8192 to 12287
INIT_20=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22=>X"0707070707070707070707070707070707070707000707070707070707070700",
INIT_23=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_24=>X"0004000400040004000400040004000400040004000400040004000400040700",
INIT_25=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_26=>X"0407040704070407040704070407070707070700000707070404070400040700",
INIT_27=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_28=>X"0407040704070400040704000407000000000000000000070407070707040700",
INIT_29=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_2a=>X"0407040404070407040704070407000000000000000000070407070407040000",
INIT_2b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2c=>X"0404040704070400040704000407070707070707070707070400000400040700",
INIT_2d=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_2e=>X"0407040704000407040704070004000400040004000400040704040704070700",
INIT_2f=>X"0000000000000000000000000000000000000000000000000000000000000007",
-- Address 12288 to 16383
INIT_30=>X"0404050404070707040404040704070704070407070004000407040004040700",
INIT_31=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_32=>X"0407040704070407040704070407040004000407040704070407070407040000",
INIT_33=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34=>X"0404040400040004000400040004000400040004000400040004000400040700",
INIT_35=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_36=>X"0707070707070707070707070707070707070700070707070707070707070700",
INIT_37=>X"0000000000000000000000000000000000000000000000000000000000000007",
INIT_38=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3a=>X"00000000000000000000000000000000000011100F0E0D0C0B0A090800000000",
INIT_3b=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3c=>X"56454C202D203030303030203A2045524F435320205345594F52542D49494547",
INIT_3d=>X"000000000000000000000000000000000000000000002020203030203A204C45",
INIT_3e=>X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3f=>X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DOA(6 downto 0) => char_addr, -- 7-bit Data Output
DOA(7)=> open,
DOPA => open,
ADDRA(9 downto 0) => addr_pave, -- 10-bit Address Input
ADDRA(10) => '1', -- en haut de la mémoire
CLKA => CLK, -- Clock
DIA => "00000000", -- 8-bit Data Input
DIPA => "0",
ENA => '1', -- RAM Enable Input
SSRA => '0', -- Synchronous Set/Reset Input
WEA => '0', -- Write Enable Input
ADDRB => ADDRB,
DOB => DOB,
DIB => DIB,
CLKB => CLK, -- Clock
DIPB => "0", --pas de gestion de parité
DOPB => open,--toujours pas de gestion de parité
ENB => ENB,
SSRB => SSRB,
WEB => WEB
);
end arch;
library IEEE;
use IEEE.STD_LOGIC_1164.all;
Library UNISIM;
use UNISIM.vcomponents.all;
-- RAMB16_S1: Virtex-II/II-Pro, Spartan-3/3E 16kx1 Single-Port RAM
-- Xilinx HDL Libraries Guide, version 10.1.2
entity font_rom is
port(
clk: in std_logic;
addr: in std_logic_vector(13 downto 0);
data: out std_logic_vector(0 downto 0);
addrB: in std_logic_vector(13 downto 0);
dataB: out std_logic_vector(0 downto 0)
);
end font_rom;
architecture arch of font_rom is
begin
RAMB16_S1_inst : RAMB16_S1_S1
generic map (
INIT_A => X"0", -- Value of output RAM registers at startup
INIT_B => X"0", -- Value of output RAM registers at startup
SRVAL_A => X"0", -- Ouput value upon SSR assertion
SRVAL_B => X"0", -- Ouput value upon SSR assertion
WRITE_MODE_A => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
WRITE_MODE_B => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
SIM_COLLISION_CHECK => "ALL", -- "NONE", "WARNING", "GENERATE_X_ONLY", "ALL"
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 4095
--??/blanc
INIT_00=>X"00000000183C7F78786F3C180000000000000000000000000000000000000000",
--INIT_01=>X"0000242424242424242424242424000000000000FF0000FF0000FF0000FF0000",
INIT_01=>X"00000000183C6E7E7E7E3C180000000000000000FF0000FF0000FF0000FF0000",
-- fruit/pac-gomme
INIT_02=>X"0000000000183C7E7E3C180000000000000000000000183C3C18000000000000",
--brique/??
INIT_03=>X"FFD5ABD5ABD5ABD5ABD5ABD5ABD5ABFF00005555555555555555555555550000",
--pacman gauche/droite
INIT_04=>X"00000000183CFE1E1EF63C1800000000000000003078FEF0F0DE783000000000",
--pacman down/up
INIT_05=>X"00000024243C6E7E7E7E3C180000000000000000183C7E7E7E6E3C2424000000",
--pacman fermé gauche/droite
INIT_06=>X"00000000183C7E7E7E763C1800000000000000003078FCFCFCDC783000000000",
-- pacman fermé down/up
INIT_07=>X"00000000183C6E7E7E7E3C180000000000000000183C7E7E7E6E3C1800000000",
-- ennemi fantome/ouvert
INIT_08=>X"000000003C243C007E5A7E0000000000000000AAFFC3C3FF7E5A7E3C00000000",
INIT_09=>X"00000F0F0F0F0F0F0F0F0F0F0F0F00000000FFFFFFFFFFFF0000000000000000",
INIT_0a=>X"0000007E42424242424242427E0000000000F0F0F0F0F0F0F0F0F0F0F0F00000",
INIT_0b=>X"000024492449244924492449244900000000FF81818181818181818181FF0000",
INIT_0c=>X"0000499249924992499249924992000000002492249224922492249224920000",
INIT_0d=>X"0000AA55AA55AA55AA55AA55AA550000000055AA55AA55AA55AA55AA55AA0000",
INIT_0e=>X"0000DB6DDB6DDB6DDB6DDB6DDB6D00000000DBB6DBB6DBB6DBB6DBB6DBB60000",
INIT_0f=>X"0000FFFFFFFFFFFFFFFFFFFFFFFF00000000B66DB66DB66DB66DB66DB66D0000",
-- Address 4096 to 8191
INIT_10=>X"0000001000001010101010101010000000000000000000000000000000000000",
INIT_11=>X"0000004444FE4444444444FE4444000000000000000000000044444444440000",
INIT_12=>X"0000000C12924C2010086492906000000000007C921212127C909090927C0000",
INIT_13=>X"000000000000000000101010101000000000007A84848A507090888848300000",
INIT_14=>X"0000001008080404040404080810000000000010202040404040402020100000",
INIT_15=>X"0000000010101010FE101010100000000000009292545438FE38545492920000",
INIT_16=>X"0000000000000000FE0000000000000000000020100808000000000000000000",
INIT_17=>X"0000000000804020100804020000000000000000001818000000000000000000",
INIT_18=>X"0000003810101010101010503010000000000038448282A2928A828244380000",
INIT_19=>X"0000007C820202027C020202827C0000000000FE808080807C020202827C0000",
INIT_1a=>X"0000007C820202027C80808080FE00000000001C080808FE8888482818080000",
INIT_1b=>X"00000038101010101008040202FE00000000007C828282827C808080807E0000",
INIT_1c=>X"000000FC020202027C828282827C00000000007C828282827C828282827C0000",
INIT_1d=>X"0000002010080800000018180000000000000000001818000000181800000000",
INIT_1e=>X"0000000000FE0000000000FE000000000000000000020C30C0300C0200000000",
INIT_1f=>X"0000001000101008040282824438000000000000008060180618608000000000",
-- Address 8192 to 12287
INIT_20=>X"00000082828244447C442828281000000000003C42809EA2A29E828244380000",
INIT_21=>X"0000007C8280808080808080827C0000000000FC82828284F884828282FC0000",
INIT_22=>X"000000FE80808080FC80808080FE0000000000F8848482828282848488F00000",
INIT_23=>X"0000007C828282829E808080827C00000000008080808080FC80808080FE0000",
INIT_24=>X"0000003810101010101010101038000000000082828282827C82828282820000",
INIT_25=>X"0000008282848488F088848482820000000000708888080808080808081C0000",
INIT_26=>X"000000828282829292AAAAAAC6820000000000FE808080808080808080800000",
INIT_27=>X"0000007C8282828282828282827C000000000082868A8A8A92A2A2A2C2820000",
INIT_28=>X"0000007A848AB28282828282827C00000000008080808080FC828282827C0000",
INIT_29=>X"0000007C820202027C808080827C000000000082848890A0FC828282827C0000",
INIT_2a=>X"0000007C82828282828282828282000000000010101010101010101092FE0000",
INIT_2b=>X"00000082C6AAAAAA929282828282000000000010102828284444448282820000",
INIT_2c=>X"0000001010101010282844448282000000000082824444283828444482820000",
INIT_2d=>X"00000038202020202020202020380000000000FE824040203808040482FE0000",
INIT_2e=>X"0000003808080808080808080838000000000000000204081020408000000000",
INIT_2f=>X"000000FE00000000000000000000000000000000000000000082442810000000",
-- Address 12288 to 16383
INIT_30=>X"0000003AC6828282C63A00000000000000000000000000000008101020200000",
INIT_31=>X"0000003CC2808080C23C000000000000000000B8C6828282C6B8808080800000",
INIT_32=>X"00000038C680FC82C6380000000000000000003AC6828282C63A020202020000",
INIT_33=>X"00000038C6027E82C638000000000000000000808080808080F88080423C0000",
INIT_34=>X"0000000C1210101010100000100000000000008282828282C6B8808080800000",
INIT_35=>X"000000828488B0C0B88680808000000000000070880404040404000004000000",
INIT_36=>X"0000009292929292D2AC0000000000000000000E102020202020202020200000",
INIT_37=>X"00000038C6828282C6380000000000000000008282828282C6B8000000000000",
INIT_38=>X"0000000202027E82C63A000000000000000000808080FC82C6B8000000000000",
INIT_39=>X"0000007C82027E80827C0000000000000000008080808080C6B8000000000000",
INIT_3a=>X"0000003AC682828282820000000000000000003C4280808080F8808080800000",
INIT_3b=>X"0000006C92929292928200000000000000000010284482828282000000000000",
INIT_3c=>X"0000003008083C42820000000000000000000082443828448200000000000000",
INIT_3d=>X"00000010202020408040202020100000000000FE40300804FE00000000000000",
INIT_3e=>X"0000001008080804020408080810000000000010101010101010101010100000",
INIT_3f=>X"00000000000000000000000000000000000000000000000C9260000000000000")
port map (
DOA => data, -- 1-bit Data Output
ADDRA => ADDR, -- 14-bit Address Input
CLKA => CLK, -- Clock
DIA => "0", -- 1-bit Data Input
ENA => '1', -- RAM Enable Input
SSRA => '0', -- Synchronous Set/Reset Input
WEA => '0', -- Write Enable Input
DOB => dataB, -- 1-bit Data Output
ADDRB => addrB, -- 14-bit Address Input
CLKB => CLK, -- Clock
DIB => "0", -- 1-bit Data Input
ENB => '1', -- RAM Enable Input
SSRB => '0', -- Synchronous Set/Reset Input
WEB => '0' -- Write Enable Input
);
-- End of RAMB16_S1_inst instantiation
end arch;
Ce programme est un peu long, mais nous sommes obligé de le donner quasiment complètement puisque le contenu de la RAMB16_S1 a changé et en plus elle a été transformée en RAMB16_S1_S1.
Le contenu de la RAMB16_S9_S9 qui dessine le fond de l'écran, le décor, est en adresse haute. Certains lecteurs attentifs peuvent se demander pourquoi. La raison est liée à l’utilisation future de cette partie : l'interfacer avec un processeur. Mais nous voulons encore beaucoup plus : utiliser cette RAM comme RAM du processeur. Pour cela il nous faut laisser la partie basse intacte de la RAM car les variables en C iront s'y loger. Nous espérons accéder à cette zone de mémoire haute en C avec des pointeurs... mais ceci sera examiné dans un autre chapitre.
Pour les utilisateurs éventuels de la carte "Nexys3", nous venons de découvrir que le pacman donné ci-dessus ne se compile pas. Ce n’est pas lié à la version de l'ISE 14.5 qui compile correctement quand on cible des spartan3 ou des spartan3E mais se révèle beaucoup plus pointilleux pour une cible spartan6 !!! Ce problème a été réglé dans la ressource nexys3PacmanAtmega16.zip pour la carte Nexys 3.
Ressources
modifierPour une gestion plus avancée des cartes VGA :
- GITHUB:vga_configurable.vhd
- GITHUB:lib.vhd
- Youtube : vidéo en anglais expliquant les synchronisations
Passons maintenant à un tout autre sujet, la lecture d'un clavier PS/2.
Les claviers PS/2
modifierLe protocole PS/2 est un protocole bidirectionnel du périphérique vers l'hôte. Nous limiterons notre discussion ici en considérant le périphérique comme un clavier. Il peut sembler à première vu que la communication entre un clavier et un hôte est mono directionnelle (clavier --> hôte), mais il n'en est rien.
C'est l'hôte qui demande à telle ou telle diode électroluminescente de s'allumer sur le clavier. Quand on appuie sur majuscule, cette information est envoyée à l'ordinateur qui en retour demande au clavier d'allumer sa diode Caps Lock.
Comprendre le protocole PS/2 nécessite ainsi de s'interroger sur les deux sens de communication :
- clavier vers hôte
- hôte vers clavier
Le brochage du port PS/2 définit les signaux conventionnels. Dans ce cours nous ne gardons pas les noms conventionnels pour les deux principaux signaux :
- la broche 5 appelée "Horloge" sera appelée "ps2_clk"
- la broche 1 appelée "Données" sera appelée "ps2_data"
La figure ci-après vous montre un exemple de protocole PS/2 avec ces appellations.
Le protocole PS/2
modifierC'est le système maître qui alimente le périphérique PS/2. Les signaux "ps2_data" et "ps2_clk" sont bidirectionnels et à sortie collecteur ouvert. Une résistance de 10K ohm environ doivent être placées entre ces deux signaux et l'alimentation ce qui garantie un niveau haut hors de toute transaction.
Les octets de commandes et messages sont transmis de façon synchrone et série sur 11 bits ( les 8 bits à transmettre, poids faible en premier, précédés d'un bit start (0) et suivi d'un bit de parité et d'un bit stop (1). Le bit de parité impaire vaut 1 si le nombre total de 1 dans l'octet et le bit de parité lui-même est impair. Durant la transmission, c’est le périphérique qui fournit l'horloge en la positionnant à un niveau bas (front descendant) tandis que l'émetteur (système ou périphérique) place data à un niveau bas pour un bit 0 ou le laisse inactif pour un bit 1.
La fréquence d'horloge est au maximum de 33 kHz mais le plus couramment de 15 kHz.
Entre les transmissions, le bus peut être :
- Idle : CLK et DATA sont tous les deux au niveau haut . Il n y a pas d'activité.
- Inhibit : Le système maintient CLK au niveau bas.
- Request to send : Le maître maintient DATA au niveau bas mais laisse CLK flotter. Il est prêt à émettre.
Chronogrammes de l'échange clavier vers hôte
modifierPuisque c’est le clavier qui est à l'initiative de l'échange des données, c’est lui qui aura la lourde tâche de la fabrication de l'horloge. De l'autre côté, l'hôte échantillonnera les données sur les fronts descendants de cette horloge comme indiqué dans le chronogramme ci-dessous :
On remarque que le clavier envoie d’abord un bit de "start" à '0', puis les données sur 8 bits en commençant par le poids faible, puis un bit de parité suivi d'un bit de stop qui est '1'. Ceci est bien conforme au protocole PS/2 décrit dans la précédente section.
Exercice 7
modifierImaginez une architecture électronique capable de lire le protocole PS/2.
On aura l’occasion d'implanter cette architecture en VHDL.
Sur cette figure, intéressez-vous à la partie droite. En haut un registre à décalage de 11 bits pour recevoir les 11 bits de données qui arrivent directement de "ps2_data". L'horloge de ce registre est un peu complexe car elle passe à travers un anti-rebond. Cette même horloge incrémente un compteur capable de compter jusqu'à 11 (4 bits). Il est suivi d'un comparateur qui compare justement à 11. Puis vient l’unité de contrôle (ou séquenceur) : une fois que le compteur est à 11 on transfert les 8 bits intéressant dans un registre d'affichage, puis on remet le compteur à 0.
Echange hôte vers clavier
modifierÀ continuer
Implémentation
modifierUn ensemble de TPs permet de comprendre le PS2 :
Cette série de TPs construit pas à pas un module capable de réaliser la lecture du PS2.
La fin du TP3 (particulièrement la mise en conformité des horloges) montre des implantations directement utilisables et beaucoup plus compactes que celles construites de toute pièce.
Pour dire les choses autrement, si vous désirez comprendre le PS2 faites les TPs proposés, si vous chercher un module tout prêt à être utilisé allez directement à la mise en conformité des horloges et prenez le code VHDL correspondant.
En ce qui nous concerne, nous avons expérimenté :
- les TPs (qui n'étaient bien sûr pas corrigés à l'époque) avec des étudiants de deuxième année de DUT GEII (L2 = niveau 15)
- un interfaçage entre le module de P.P Chu et le picoBlaze avec des étudiants de branche de l'UTT (L3) énoncé des TPs Printemps 2012
- un interfaçage entre le module de P.P Chu et l'ATMega8 avec des étudiants de branche de l'UTT (L3) énoncé des TPs Printemps 2013
Voir aussi
modifier- D'autres périphériques sont évoqués dans la partie travaux pratiques de ce livre