Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/TP 2
Les Travaux Pratiques que nous présentons sont liés au matériel utilisé. En ce qui nous concerne nous utilisons du matériel XILINX (Spartan 3). Comme vous pourrez cependant le constater, la rédaction de ce TP est relativement indépendante du matériel. Beaucoup d'autres cartes proposent une connexion PS/2 : seuls le nom des broches et la façon de les nommer changent avec le matériel.
Cette série (TP 1 à TP 4) a été réalisée avec des étudiants de deuxième année (L2) sur une période de sept semaines à raison d'une heure et demi par semaine. Les exercices demandés jusqu'en 2008, sont ceux qui ont un corrigé maintenant, et seuls 10% des étudiants sont allés jusqu'au bout (sans le corrigé bien entendu). Ayant maintenant publié les corrigés, il m'a fallu ajouter des exercices non corrigés et même un quatrième TP. Ainsi, en 2010, un binome sur douze a terminé le TP3. Il faut dire que des restrictions budgétaires ont fait passer nos enseignements de 7 à 6 semaines.
L'objectif de ce TP divisé en quatre parties est de lire le scancode envoyé par un clavier, de l'afficher sur plusieurs afficheurs sept segments et pour finir, d'embarquer un processeur qui utilisera ces scancodes. Le protocole PS/2 implanté ici est très loin d’être complet.
TP1 : afficheurs 7 segments
modifierPour visualiser nos futures informations fournies par un clavier, nous avons besoin dans un premier temps de travailler sur l’affichage d'informations. Nous allons utiliser pour cela des afficheurs 7 segments présents sur la carte.
Décodeur 7 segments simple
modifierPour permettre de prendre contact avec la maquette d'essai on va commencer par résoudre un problème simple : utiliser 4 interrupteurs en entrée et afficher en hexadécimal la valeur correspondante sur un des quatre afficheurs sept segments.
Tout ce qui est spécifique au fabricant Xilinx, au composant Spartan3 et à la carte d'évaluation Digilent sera mis dans une boîte déroulante comme ci-dessous partir de maintenant dans ce document. Cela concerne essentiellement les fichiers ucf. Ces fichiers sont destinés à faire une correspondance entre les signaux logiques gérés par le langage VHDL, et les broches physiques du FPGA utilisé. Pour d'autres FPGA et environnements de programmation les fichiers ucf seront certainement remplacés par d'autres types de fichiers utilisant une autre syntaxe. Il vous faut lire la documentation appropriée.
net "entrees<3>" loc="h13"; net "entrees<2>" loc="h14"; net "entrees<1>" loc="g12"; net "entrees<0>" loc="f12";Pour information les quatre autres interrupteurs de notre carte d'évaluation sont en J14, J13, K14 et K13.
Ce problème à résoudre correspond à une entité (entity) VHDL :
entity tp1 is
port (
entrees : in std_logic_vector(3 downto 0); -- 4 inters en entree
s7segs : out std_logic_vector(6 downto 0); -- 7 segments en sorties
aff : out std_logic_vector(3 downto 0)); -- 4 selecteurs d'afficheur
end tp1;
On peut remarquer que :
- les quatre interrupteurs d'entrées sont regroupés dans une variable que l’on appelle entrees
- les sept segments à afficher sont regroupés dans une variable nommée s7segs
- la sélection des afficheurs est regroupée dans une variable nommée aff
La compréhension de ces choix nécessite une compréhension des afficheurs. Ils sont multiplexés à l'aide d'un signal par afficheur. L'affichage se fait ainsi avec 7 signaux (les segments à allumer) qui sont actifs à l'état bas. Cela veut simplement dire que les afficheurs 7 segments sont à anode commune.
net "aff<0>" loc="d14"; net "aff<1>" loc="g14"; net "aff<2>" loc="f14"; net "aff<3>" loc="e13";
La figure ci-dessous présente le travail à réaliser.
Tous les schémas présenteront le travail à faire sous forme fonctionnelle : quelles sont les entrées, quelles sont les sorties ? Le bloc à réaliser en VHDL sera toujours coloré en bleu clair dans cette série de Travaux Pratiques.
L'entité VHDL complète correspondante à la figure ci-dessus sera donc :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity tp1 is
port (
entrees : in std_logic_vector(3 downto 0); -- 4 inters en entree
s7segs : out std_logic_vector(6 downto 0); -- 7 segments en sorties
aff : out std_logic_vector(3 downto 0)); -- 4 selecteurs d'afficheur
end tp1;
qu’il vous faudra compléter avec son architecture correspondante. Il s'git naturellement d'un problème purement combinatoire qui peut être résolu avec un style "with select when" écrit directement à partir d'une table de vérité.
library IEEE;
use IEEE.std_logic_1164.all;
entity seg7 is
port (
entrees : in std_logic_vector(3 downto 0); -- 4 inters en entree
s7segs : out std_logic_vector(6 downto 0); -- 7 segments en sorties
aff : out std_logic_vector(3 downto 0) -- 4 selecteurs d'afficheur
);
end seg7;
architecture seg7_arch of seg7 is
begin
process (entrees)
begin
CASE entrees is -- abcdefg
when "0000" => s7segs <= "0000001";
when "0001" => s7segs <= "1001111";
when "0010" => s7segs <= "0010010";
when "0011" => s7segs <= "0000110";
when "0100" => s7segs <= "1001100";
when "0101" => s7segs <= "0100100";
when "0110" => s7segs <= "0100000";
when "0111" => s7segs <= "0001111";
when "1000" => s7segs <= "0000000";
when "1001" => s7segs <= "0000100";
when "1010" => s7segs <= "0001000";
when "1011" => s7segs <= "1100000";
when "1100" => s7segs <= "0110001";
when "1101" => s7segs <= "1000010";
when "1110" => s7segs <= "0110000";
when "1111" => s7segs <= "0111000";
when others => s7segs <= "1111110";
end CASE;
end process;
-- pour sélectionner un seul afficheur :
aff <= "1110";
end seg7_arch;
Décodeur 8 bits vers deux afficheurs avec sa correction
modifierOn utilisera que les deux afficheurs poids faibles. Réaliser un ensemble permettant d'afficher le contenu de 8 bits sur deux afficheurs en hexadécimal. Ici il vous faudra forcément une horloge pour afficher tantôt le poids faible tantôt le poids fort (l'afficheur est validé par un 0).
Indications : On pourra réaliser l’ensemble comme indiqué dans la figure ci-dessous. Il est important de comprendre que cette figure vous donne l'architecture de votre programme VHDL : on utilisera la programmation structurelle. Ce style consiste à assembler des composants : cela est expliqué dans la section Assembler des composants du WIKIBOOK Conception et VHDL.
Un compteur 20 bits est destiné à réaliser un signal "lent" (Q19) à partir de l'horloge 50 Mhz de la carte. Un mutliplexeur permet d'envoyer alternativement les quatre bits de poids fort et les quatre bits de poids faible sur le décodeur sept segments. Ceci doit être synchronisé avec le signal qui sélectionne l’afficheur 7 segments sur lequel on affiche. Comme il n'y a que deux afficheurs, le bit Q19 inversé/non inversé suffit pour alterner cette sélection d'afficheur : quand on envoie le poids faible on sélectionne l'afficheur droit etc. Le décodeur sept segments de l'exercice 1 peut être utilisé encore ici. Le travail à réaliser peut être caractérisé par l'entité (entity) suivante :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity tp1 is
port (
clk : in std_logic; --horloge ajoutée pour demultiplexage
entrees : in std_logic_vector(7 downto 0); -- 8 interrupteurs en entrée
s7segs : out std_logic_vector(6 downto 0); -- affichage 7 segments
aff : out std_logic_vector(3 downto 0)); -- sélection des afficheurs
end tp1;
qu’il vous suffit de compléter par son architecture correspondante. Avant de dérouler la solution, essayez de réfléchir par vous-même.
Voici sans autres explications une solution du TP1 :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity tp1 is
port (
clk : in std_logic;
entrees : in std_logic_vector(7 downto 0);
s7segs : out std_logic_vector(6 downto 0);
aff : out std_logic_vector(3 downto 0));
end tp1;
architecture atp1 of tp1 is
component lent --fabrication d'une horloge lente
port(horloge : in std_logic;
h_lente : out std_logic);
end component;
component multiplexeur --on envoie pds faible ou pds fort
port(sel : in std_logic; --entrée de sélection
entreeFort : in std_logic_vector(3 downto 0);
entreeFaible : in std_logic_vector(3 downto 0);
affichage : out std_logic_vector(3 downto 0)); --sortie générale
end component;
component seg7 --transcodeur 7 segments
port (
binary: in STD_LOGIC_VECTOR (3 downto 0); --entrée sur {{Unité|4|bits}}
hex: out STD_LOGIC_VECTOR (6 downto 0) -- sortie sur 7 segments
);
end component;
signal horl_1 : std_logic;
signal fil_aff : std_logic_vector(3 downto 0);
begin
i1:lent port map(horloge => clk,h_lente => horl_1 );
-- demultiplexage des afficheurs
aff(0) <= not horl_1;
aff(1) <= horl_1;
aff(2) <= '1'; -- éteindre le 3° afficheur
aff(3) <= '1'; -- éteindre le 4° afficheur
i2:multiplexeur port map(entreeFort=>entrees(7 downto 4),entreeFaible => entrees(3 downto 0),
affichage=> fil_aff,sel=>horl_1);
i3:seg7 port map(binary=>fil_aff,hex=>s7segs);
end atp1;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity lent is
port(horloge : in std_logic;
h_lente : out std_logic);
end lent;
architecture alent of lent is
signal compteur : std_logic_vector(19 downto 0);
begin
--division de l'horloge (environ {{Unité|50|{{abréviation|Hz|hertz}}}})
process(horloge) begin
if(horloge'event and horloge='1') then
compteur <= compteur + 1;
end if;
end process;
h_lente <= compteur(19);
end alent;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity multiplexeur is
port(sel : in std_logic;
entreeFort : in std_logic_vector(3 downto 0);
entreeFaible : in std_logic_vector(3 downto 0);
affichage : out std_logic_vector(3 downto 0));
end multiplexeur;
architecture amux of multiplexeur is
begin
WITH sel SELECT
affichage <= entreeFort WHEN '0',
entreeFaible WHEN OTHERS;
end amux;
library IEEE;
use IEEE.std_logic_1164.all;
entity seg7 is
port (
binary: in STD_LOGIC_VECTOR (3 downto 0); -- binary input nybble to be decoded
hex: out STD_LOGIC_VECTOR (6 downto 0) -- decoded nybble connected the seven segment display
);
end seg7;
architecture seg7_arch of seg7 is
begin
process (binary)
begin
CASE binary is -- abcdefg
when "0000" => hex <= "0000001";
when "0001" => hex <= "1001111";
when "0010" => hex <= "0010010";
when "0011" => hex <= "0000110";
when "0100" => hex <= "1001100";
when "0101" => hex <= "0100100";
when "0110" => hex <= "0100000";
when "0111" => hex <= "0001111";
when "1000" => hex <= "0000000";
when "1001" => hex <= "0000100";
when "1010" => hex <= "0001000";
when "1011" => hex <= "1100000";
when "1100" => hex <= "0110001";
when "1101" => hex <= "1000010";
when "1110" => hex <= "0110000";
when "1111" => hex <= "0111000";
when others => hex <= "1111110";
end CASE;
end process;
end seg7_arch;
Et voici le fichier ucf :
#horloge net "clk" loc="t9"; #nos 8 entrées sur interrupteurs net "entrees<7>" loc="k13"; net "entrees<6>" loc="k14"; net "entrees<5>" loc="j13"; net "entrees<4>" loc="j14"; net "entrees<3>" loc="h13"; net "entrees<2>" loc="h14"; net "entrees<1>" loc="g12"; net "entrees<0>" loc="f12"; # sortie 7 segments net "s7segs<6>" loc="e14"; net "s7segs<5>" loc="g13"; net "s7segs<4>" loc="n15"; net "s7segs<3>" loc="p15"; net "s7segs<2>" loc="r16"; net "s7segs<1>" loc="f13"; net "s7segs<0>" loc="n16"; # sélection afficheur net "aff<0>" loc="d14"; net "aff<1>" loc="g14"; net "aff<2>" loc ="f14"; net "aff<3>" loc ="e13";
Décodeur 16 bits vers quatre afficheurs sans sa correction
modifierOn vous demande d'étendre l’affichage à 16 bits. Vous allez réaliser un registre à décalage sur 4 bits avec un '0' qui se décale en décalage circulaire. Ce registre sera commandé par l'horloge lente naturellement. Ces quatre signaux seront utilisés pour la sortie "aff", c'est-à-dire la sélection des afficheurs.
Il vous faut prévoir une initialisation (synchrone ou asynchrone) de votre registre pour avoir au début "1110" par exemple, avec une entrée spécifique d'initialisation.
La sortie de ce registre à décalage commandera un multiplexeur pour choisir les quatre bits à afficher.
Voici un schéma de principe :
Notre horloge générale est de 50 Mhz. Ainsi, contrairement au schéma, il est bon de prendre q17 en lieu et place de q19 comme horloge lente... autrement votre œil percevra les clignotements des afficheurs. Nous mettrons la figure à jour un de ces jours ! |
L'entité VHDL correspondant au schéma est :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity tp1b is
port (
clk : in std_logic; --horloge ajoutée pour demultiplexage
reset : std_logic; --reset pour le registre à décalage
entrees : in std_logic_vector(15 downto 0); -- 8 interrupteurs en entrée + 8...
s7segs : out std_logic_vector(6 downto 0); -- affichage 7 segments
aff : out std_logic_vector(3 downto 0)); -- sélection des afficheurs
end tp1b;
Le registre à décalage se fait facilement avec l'opérateur de concaténation. Quelque chose comme
-- reset asynchrone
if reset = '1' then
qs <= "1110";
elsif clk'event and clk ='1' then
qs <= qs(0) & qs(3 downto 1); --decalage circulaire droite
end if;
devrait convenir. On vous laisse trouver le process autour, l'entité les signaux ...
net "entrees<3>" loc="h13"; net "entrees<2>" loc="h14"; net "entrees<1>" loc="g12"; net "entrees<0>" loc="f12";
Pour information les quatre autres interrupteurs de notre carte d'évaluation sont en J14, J13, K14 et K13.
Les boutons poussoirs sont en M13, M14, L13 et L14 qui servira de reset pour le registre à décalage.Puisque j'utilise cette page avec mes étudiants, je ne donne pas les solutions complètes.
En résumé, votre travail consiste à implanter les composants suivants :
component lent
port(horloge : in std_logic;
h_lente : out std_logic);
end component;
component multiplexeur
port(sel : in std_logic_vector(3 downto 0);
entree_16_bits : in std_logic_vector(15 downto 0);
affichage : out std_logic_vector(3 downto 0));
end component;
component seg7
port (
binary: in STD_LOGIC_VECTOR (3 downto 0);
hex: out STD_LOGIC_VECTOR (6 downto 0)
);
end component;
COMPONENT CircularRightShift IS
PORT( reset,clk : IN std_logic;
q : out STD_LOGIC_VECTOR (3 downto 0));
END COMPONENT;
Certains sont utilisés dans TP1.
Je vous donne le fichier ucf utilisé pour les tests :
#horloge générale net "Clk" loc="T9"; #reset net "reset" loc="L14"; #nos 8 entrées sur interrupteurs net "entrees<7>" loc="k13"; net "entrees<6>" loc="k14"; net "entrees<5>" loc="j13"; net "entrees<4>" loc="j14"; net "entrees<3>" loc="h13"; net "entrees<2>" loc="h14"; net "entrees<1>" loc="g12"; net "entrees<0>" loc="f12"; #on poursuit avec boutons poussoirs net "entrees<11>" loc="M13"; net "entrees<14>" loc="M14"; net "entrees<15>" loc="L13"; # 7 segments des afficheurs net "sorties<6>" loc="e14"; net "sorties<5>" loc="g13"; net "sorties<4>" loc="n15"; net "sorties<3>" loc="p15"; net "sorties<2>" loc="r16"; net "sorties<1>" loc="f13"; net "sorties<0>" loc="n16"; # sélection des afficheurs net "aff<1>" loc="G14"; net "aff<0>" loc="D14"; net "aff<2>" loc="F14"; net "aff<3>" loc="E13";
Voir aussi
modifier- Afficheurs sept segments
- PS/2
- clavier
- Logique combinatoire
- Logique séquentielle
- Conception et VHDL
- scancode (en)
- PS/2 et clavier (en)
- PS/2 et souris (en)
- PS/2, clavier et souris (en)