TD5 VHDL et CAO

Un livre de Wikilivres.
Aller à : navigation, rechercher
TD4 VHDL Logique Sequentielle << Conception et VHDL >> TD6 VHDL et Simulation

Nous allons dans ce chapitre explorer les liens entre VHDL et la Conception assistée par ordinateur (C.A.O. ou plus simplement CAO) puis passer en revue des exemples utilisant des séquenceurs.

Génération automatique de Netlist[modifier | modifier le wikicode]

La majorité des logiciels de schéma modernes (frontal des logiciels de CAO) ont pour objectif de réaliser une liste des connexions électriques que l'on appellera netlist dans la suite de ce document. Ces netlists ont en général un format propriétaire mais certains outils sont capables générer ces netlists en VHDL. Il est ainsi possible à partir d'un schéma, de générer un circuit imprimé ou de choisir la logique programmable en utilisant des FPGAs ou ASICs.

La base du travail de l'électronicien reste donc toujours de rassembler des composants électroniques ayant une certaine fonctionnalité pour réaliser une nouvelle fonctionnalité. Nous allons présenter dans ce chapitre un certain nombre d'exemples.

Décomposer une synthèse complexe[modifier | modifier le wikicode]

Lorsque l'on doit réaliser un circuit complexe, il convient en général de le décomposer le problème en deux parties : la partie de commande (ou séquenceur) et la partie opérative ou chemin de données. Si cette décomposition est classique en automatisme, elle demande une certaine expérience ici car à la différence de l'automatisme la partie opérative est elle-même composée d'un ou plusieurs circuits électroniques.

Disons pour simplifier que le niveau simple correspond à ce que l'on fait en schéma logique traditionnel : utiliser des composants simples existants et les assembler puis ajouter un séquenceur. C'est ce niveau qui va nous intéresser maintenant dans le cas très particulier où le chemin de données est réalisé par deux compteurs.

Utilisations de compteurs[modifier | modifier le wikicode]

Le problème que l'on présente comme trame de travail dans cette section sera appelé compteur de passages dans toute la suite de ce chapitre. Il est décrit un tout petit peu plus loin.

Le chemin de données est constitué par un ou plusieurs compteurs. On lui ajoute une partie de commande spécifiée par un ou plusieurs diagrammes d'évolutions. Une exemple de réalisation pratique est détaillé ci-contre sous forme schématique.

Le compteur de passages, son séquenceur à gauche et ses compteurs à droite

Il s'agit d'un compteur de passages : deux capteurs séparés appelés Droite et Gauche font incrémenter les compteurs si des personnes (des voitures, des canettes de bière ou de tout autres objets fruit de votre imagination débordante) rentrent dans une pièce en passant d'abord par Droite puis par Gauche. Si elles sortent elles passent d'abord devant Gauche puis devant Droite et l'on décrémente les compteurs. C'est un cahier des charges pas très compliqué mais qui va nous occuper un certain temps.

Ici le chemin de données est réalisée en circuit TTL classiques et la partie commande est une GAL 20V8. Si vous disposez de FPGAs, il est possible de mettre à la fois la partie commande et le chemin de données dans ce circuit.

Remarque :

Si vous réalisez cet ensemble dans un FPGA, le compilateur doit vous donner au moins un avertissement : l'horloge est réalisée par une partie combinatoire ce qui est une pratique déconseillée.

Il est possible de contourner le problème mentionné en remarque, en s'arrangeant pour que la partie commande fonctionne sur front montant d'une horloge et la partie opérative sur front descendant de cette même horloge. Le compteur possédera alors une entrée de validation pour compter, que la partie commande mettra à un ou pas, pour autoriser un comptage/décomptage. Cette technique pourra être utilisée dès que la partie commande et la partie opératives sont toutes deux séquentielles. Cette façon de faire n'est pas considérée par tout le monde comme une bonne méthode ; un méthode consistant à utiliser des fronts d'horloge identiques pour les deux parties est explorée dans un cours de la WIKIVERSITE, et particulièrement au chapitre 4, tandis qu'une méthode utilisant la même horloge mais avec une sensibilité aux fronts différente est explorée dans la section suivante.


link={{{link}}}Attention !

Le niveau de cette section sur la décomposition en chemin de données et séquenceur est relativement élevé. Plutôt que de la terminer ici, j'ai décidé de poursuivre ce travail dans un Cours WIKIVERSITE sur VHDL dans le chapitre VHDL et machines à états algorithmiques.

Nous allons maintenant plutôt nous concentrer sur les cas où la décomposition en deux parties est immédiate et donc va de soi.

Conceptions autour de séquenceurs[modifier | modifier le wikicode]

La série d'exemples donnée ci-dessous a comme objectif de préparer le lecteur à des assemblages de composants autour de divers séquenceurs. Les schémas sont donnés pour expliquer ce que l'on veut faire, mais il vous faudra faire un effort de concentration pour bien les comprendre. Parfois on donnera quelques indications VHDL mais il nous semble bien plus important à ce niveau de raisonner sur les schémas et de bien comprendre le rôle du séquenceur.

Le compteur de passages revu et corrigé[modifier | modifier le wikicode]

Le compteur de passages déjà présenté, est un dispositif destiné à compter des personnes qui entrent dans un pièce, sachant qu'elles ont la possibilité de sortir. Il est naturellement possible de compter des voitures qui entrent et sortent d'un parking avec le même principe.

Un ensemble de deux capteurs infrarouge perçoit le passage d'une personne et en fonction de son sens de passage incrémente ou décrémente un compteur qui décodé affiche le nombre de personnes sur deux afficheurs 7 segments. C'est ce que l'on a déjà nommé compteur de passage et que l'on continuera à nommer ainsi.

Nous avons eu l'occasion d'exprimer des doutes sur la création d'une horloge par le séquenceur. Si l'on veut éviter cela il nous faut une architecture du type :

CmptPassage2.png

'

Remarque : on notera sur cette figure que l'horloge du séquenceur et l'horloge du chemin de données (compteur/décompteur) est la même, mais que ces deux parties sont sensibles à des fronts d'horloge différents (à cause de l'inverseur en bas). Cet inverseur peut être retiré, ce qui est d'ailleurs considéré comme une bonne pratique dans les FPGA, mais une mauvaise pratique si on le garde.

La partie notée compteur/décompteur est en fait un peu plus complexe qu'un simple compteur/décompteur puisqu'elle sort directement sur deux afficheurs sept segments. Cela signifie qu'elle comporte un décodeur, mais ces détails ne nous intéressent pas pour l'instant. Par contre il est important de savoir que :

  • "en" est une entrée de validation (enable en anglais) : lorsque en=1 les fronts d'horloge font bouger le compteur
  • "du" est un diminutif de "down/up" que l'on traduit par comptage (up) et décomptage (down). Pour information, nous prendrons la convention suivante du=1 on compte (un compteur BCD cascadable est présenté dans un autre chapitre).

Il nous faudra comme composants :

  • deux compteurs-décompteurs décimaux (cascadés) ainsi que la logique d'affichage capable de transformer la valeur BCD des deux compteurs en affichage sept segments.
  • une logique de contrôle pour détecter le sens de passage

Seule la logique de contrôle est spécifiée à l'aide d'un graphe d'évolution.

Travail sur le compteur de passages[modifier | modifier le wikicode]

  • Que se passe-t-il si Droite et gauche arrivent en même temps ? Modifier le graphe d'évolution pour gérer seulement le cas Droite='1'&Gauche='0' d'une part et Gauche='1'&Droite='0' d'autre part.
  • Chercher le graphe d'états correspondant au problème à partir du graphe d'évolution donné.
  • Écrire les équations de récurrence faisant intervenir l'entrée d'initialisation "Init".
  • On cherche maintenant à réaliser ce même séquenceur mais en donnant la possibilité aux personnes de faire demi-tour entre les deux capteurs (c'est à dire de changer d'avis).
  • Montrer qu'un graphe d'état de 9 états est nécessaire pour gérer ce problème. Écrire les équations de récurrence ou implanter avec un "case when" puis tester.

Éléments d'implantation en VHDL[modifier | modifier le wikicode]

Nous allons donner un certain nombre d'indications pour une implantation complète dans un FPGA dans cette section. Les compteurs ne seront donc plus externes et devront être décrit et câblés en VHDL.

Commençons donc par le compteur BCD cascadable

library IEEE;                -- On inclus la librairie IEEE
use IEEE.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity compteurbcd is port (
      clk :in std_logic;
      en : in std_logic;
      init : in std_logic;
      enout : out std_logic; -- appelée rco en TD3
      s: out std_logic_vector(3 downto 0);
      du: in std_logic
    );
end entity;

architecture behavior of compteurbcd is
      signal n : std_logic_vector(3 downto 0);
begin
      increment : process(clk) begin
            if clk'event and clk='1' then
                   if init ='1' then
                         n <= (others => '0');
                   elsif en='1' then
                      if du='1' then -- up : on compte
                         if n < 9 then
                           -- ??????????
                         else
                            -- ??????????
                         end if;
                       else -- down : on décompte                  
                         if n > 0 then
                           n <= n - 1 ;
                         else
                           n <= "1001";
                         end if;
                       end if;
                    end if;
                end if;
       end process;
       enableout: process(n,en,du)
       begin
                if du='1' then
                        if en='1' and n=9 then
                                -- ??????????
                        else
                                -- ??????????
                        end if;
                else
                        if en='1' and n=0 then
                                -- ??????????
                        else
                                -- ??????????
                        end if;
                end if;
        end process;
        s <= n;
end behavior;

On a laissé un certain nombre de points d'interrogations à remplir par le lecteur.

Nous allons maintenant nous intéresser au séquenceur.

library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity machine_a_etat is port (
        clk: in std_logic;
                init: in std_logic;
                g: in std_logic;
                d: in std_logic;
                en: out std_logic;
                du: out std_logic
        );
end machine_a_etat;

architecture archi_machine of machine_a_etat is

type state is (S1,S2,S3,S4,S5,S6,S7);
signal next_etat,reg_etat:state;
begin
  -- premier process : met à jour le présent avec le futur
        valide_etat:process(clk)
        begin
                if rising_edge(clk) then
                        if init='1' then
                                reg_etat<=S1;
                        else
                                reg_etat<=next_etat;
                        end if;
                end if;
        end process valide_etat;
  -- deuxième process : calcule l'état futur
        etat_suivant: process (reg_etat,d,g)
        begin
                case reg_etat is
                        when S1=>
                                if d='1' and g='0' then
                                        next_etat<=S2;
                                elsif g='1' and d='0' then
                                        next_etat<=S3;
                                else
                                        next_etat<=S1;
                                end if;
                        when S2=>
                                -- ?????????
                        when S3=>
                                -- ?????????
                        when S4=>next_etat<=S6;

                        when S5=>n-- ?????????

                        when S6=>
                                -- ?????????
                        when S7=>
                                -- ?????????
                        end case;
        end process etat_suivant;
-- partie combinatoire
        en<='0' when (reg_etat=S1 or reg_etat=S3 or reg_etat=S2 or reg_etat=S6 or reg_etat=S7) else '1';
        du<='0' when (reg_etat=S1 or reg_etat=S3 or reg_etat=S5) else '1';
end archi_machine;

où encore une fois on a laissé un peu de travail au lecteur.

Le transcodeur peut être trouvé ailleurs pour un fonctionnement led allumée si entrée correspondante à 0.

En aura-t-on fini un jour avec ce compteur de passages ?[modifier | modifier le wikicode]

Nous allons ajouter une liaison RS232 à ce compteur de passages. Il nous faudra donc un hyperterminal pour voir s'afficher les nombres à l'intérieur. L'intérêt de cet exemple est de présenter un autre séquenceur (que celui du compteur de passage). Nous allons commencer par présenter l'ensemble complet.

Insérer un composant pour gérer la RS232[modifier | modifier le wikicode]

La RS232 est décrite ailleurs (Wikiversité) mais nous tenons à dire avant de continuer qu'il est inutile de comprendre la liaison en question dans tous ses détails pour comprendre la problématique évoquée ici.

Le compteur de passages et RS232 : le principe

Comme tout dessin nous avons suivi un certain nombre de conventions pour le réaliser.

  • le rectangle gris représente ce que nous avons l'intention d'étudier
  • tous les fils de connexions sont volontairement représentés en rouge et certaines connexions ont été omises pour simplifier le dessin. Ces fils seront des signaux dans le programme VHDL.

D'autres parties ont été ajoutée par rapport à l'exercice précédent : un diviseur de fréquence par exemple. Il a une double fonction :

  • ralentir l'horloge du séquenceur pour éviter les rebonds mécaniques des interrupteurs
  • ralentir le comptage BCD. Il n'y a aucune raison physique à cela, mais les compteurs et le séquenceur doivent fonctionner sur la même horloge (avec sensibilité différente)
  • réaliser un signal qui sert à multiplexer les deux afficheurs. Il n'y a en effet qu'une entrée sept segments pour les deux afficheurs et l'afficheur où va finir cette donnée est choisi avec la sortie "selaff".

Construire ce composant[modifier | modifier le wikicode]

Le composant présenté par le rectangle gris est maintenant bâti petit à petit.

Générateur de bauds[modifier | modifier le wikicode]

La liaison série est une liaison sans horloge. Elle est cependant caractérisée par une vitesse de transmission. Nous désirons transmettre à 19200 bauds. Pour cela il nous faut réaliser un signal 16 fois plus rapide que la fréquence correspondante soit 307200 Hz.

Si l'on dispose d'une horloge à 50 MHz jusqu'à combien devra compter le compteur pour réaliser cette fréquence de 307200 Hz ? Combien de bit aura ce compteur ?

Réponse : M=163 et N= 8 bits.

Construction de la liaison série[modifier | modifier le wikicode]

Lire Utiliser les logicores Xilinx pour une réalisation très orientée Xilinx. Un schéma nous rappelle comment tout cela fonctionne :

Logicores Tx.png

Lorsque l'entrée "write_buffer" passe à un, on écrit les données dans "data-in" et la partie matérielle se débrouille alors pour les envoyer sur la RS232.

A ce point nous allons réaliser le schéma ci-dessous :

CmptPassage4.png

Rappelons-nous que ce composant est inséré dans un autre non représenté... et donc que les entrées rouges sont des signaux. La donnée qui est envoyée sur la RS232 est un nombre fixe correspondant au caractère 'A'.

Comment régler votre hyperterminal ?

Réponse  : gtkterm (sous LINUX) est configuré comme ci-dessous (à part peut-être le PORT) :

PORT :/dev/ttyS1, vitesse : 19200, Parité : odd, Bits 8, Bit de stop : 1, contrôle de flux : none

Expliquer pourquoi plusieurs 'A' sont reçus dans l'hyperterminal

Réponse  : parce que le signal "en" est un signal lent et qu'ainsi il resta à un pendant plusieurs périodes de l'horloge du logicore de transmission (qui elle est à 50 MHz).

Connexion au compteur de passages... et hop encore un séquenceur[modifier | modifier le wikicode]

On va donc ajouter un séquenceur qui va agir sur une partie combinatoire que l'on commence à présenter.

Partie combinatoire[modifier | modifier le wikicode]

Le but de la partie combinatoire est de calculer la parité et de gérer ce que l'on envoie suivant deux entrées appelées mux1 et mux2.

Le caractère '0' a comme code ASCII "0110000" sur 7 bits soit "00110000" sur 8 bit. On peut ainsi construire simplement les codes ASCII des chiffres de '0' à '9' : "0011" suivi du chiffre sur 4 bits.

Puisque l'on a choisi une parité impaire (odd en anglais) elle est calculée en inversant un ou exclusif sur tous les 8 bits. Nous pouvons remarquer que le ou exclusif de "0011" (partie constante de l'octet) est 0 et qu'ainsi seuls les 4 derniers bits sont nécessaires pour le calcul de cette parité.

Le séquenceur et sa partie combinatoire

Voici schématisé le travail réalisé par ce composant combinatoire dans la figure ci-contre (partie droite).

Il vous montre comment sont construits les neuf bits à envoyer, comment est calculée la parité et comment le dernier multiplexeur permet de choisir entre une valeur fixe (ici un retour chariot qui nécessitera de choisir dans configuration, CR/LF auto pour le GTKTerm) et une autre valeur.

Séquenceur[modifier | modifier le wikicode]

Le séquenceur met en œuvre le composant combinatoire en positionnant correctement mux1 et mux2. Comme d'habitude, nous présentons le graphe d'évolution de ce séquenceur (figure ci-contre).

Comme nous l'avons vu, "en" est un signal très lent et le séquenceur attendra que ce signal repasse à zéro pour revenir dans son état initial S0.

  • En S1 Mux1 passe à 1 car on a l'intention d'envoyer les dizaines en premier.
  • En S2, puisque "write_buffer" passe à 1 cela veut dire que l'on écrit dans le buffer
  • En S3 Mux0 passe à 1 car on a l'intention d'envoyer les unités maintenant.
  • En S4, puisque "write_buffer" passe à 1 cela veut dire que l'on écrit dans le buffer
  • En S5 Mux2 passe à 1 pour envoyer le retour chariot (RC dans la suite)
  • En S6 c'est là que l'on envoie véritablement ce RC... puis on reste bloqué en S7 tant que en (signal lent) ne repasse pas à 0.

A ce point ce que nous avons réalisé peut se schématiser ainsi :

CmptPassage6.png

... et seul un décalage de '1' entre la valeur affichée sur les deux digits et la valeur reçue dans l'hyperterminal nous indique une petite erreur.

deuxième séquenceur[modifier | modifier le wikicode]

Le défaut constaté dans la section précédente est dû au fait que "en" arrive trop tôt. En effet, quand il est positionné à 1 le compteur sera incrémenté seulement sur le front suivant (de l'horloge très lente), c'est à dire quand "en" retombe à 0.

Le nouveau séquenceur partira encore avec "en", mais attendra qu'il repasse à 0 avant de commencer à envoyer dans la RS232.

Indication VHDL[modifier | modifier le wikicode]

Le programme VHDL correspondant au schéma ci-dessus aura bien évidemment la structure suivante :

entity Travail_a_realiser is
  port (clk,reset : in std_logic;
        data : in std_logic_vector(7 downto 0);
	  en : in std_logic; --relié au fil en
	  serial_out : out std_logic --sortie série
  );
end  Travail_a_realiser;

architecture Travail_a_realiser of Travail_a_realiser is 
component uart9_tx is
    Port (            data_in : in std_logic_vector(8 downto 0);
                 write_buffer : in std_logic;
                 reset_buffer : in std_logic;
                 en_16_x_baud : in std_logic;
                   serial_out : out std_logic;
                  buffer_full : out std_logic;
             buffer_half_full : out std_logic;
                          clk : in std_logic);
end component;
component mod_m_counter is 
  generic(
    N : integer := 4; -- number of bits
	 M : integer := 10 -- mod-M
  );
  port (
    clk, reset : in std_logic;
    max_tick : out std_logic;
    q : out std_logic_vector(N-1 downto 0)
  );
end component;
component combinatoire is
  port (data_in : in std_logic_vector(7 downto 0);
        mux1, mux2 : in std_logic;
	data_out : out std_logic_vector(8 downto 0)
	);
end component;
component sequenceur is
  port(
    clk,init,en : in std_logic; 
    mux1,mux2,write_buffer : out std_logic -- sorties
  );
end component;

signal  en_tick : std_logic;
signal q : std_logic_vector(8 downto 0);
signal s_data : std_logic_vector(8 downto 0);
signal s_write_buffer, s_mux1, s_mux2 : std_logic;
begin
  -- Set baud rate to 19200 for the UART communications
  -- Requires en_16_x_baud to be 307200Hz which is a single cycle pulse every 163 cycles at 50MHz 
  --
  baud : mod_m_counter 
         generic map(N=>8,M=>163)
	port map(clk => clk,reset =>reset,q=>q,max_tick=>en_tick);
  --sequencer : des que "en" je prépare les trois octets à envoyer et remplit le buffer au fur et à mesure
  sequencer : sequenceur port map( 
                clk => clk,
		init => reset,
                en => en,
		mux1 => s_mux1,
		mux2 => s_mux2,
		write_buffer => s_write_buffer                                         
              );
	comb: combinatoire port map(
                   data_in => data,
                   data_out => s_data,
                   mux1 => s_mux1,
                   mux2 => s_mux2);						 
  
  uart: uart9_tx port map(data_in => s_data,
                          write_buffer => s_write_buffer,
                          reset_buffer => reset,
                          en_16_x_baud => en_tick,
                          serial_out => serial_out,
                          buffer_full => open,
                          buffer_half_full => open,
                          clk => clk); 								  
end Travail_a_realiser;

La réalisation de cet ensemble se fait de manière systématique. Ce qui peut vous surprendre à ce stade, c'est le "compteur modulo" que nous n'avons pas détaillé outre mesure.

Et un petit dernier séquenceur pour la route...[modifier | modifier le wikicode]

Nous allons utiliser un séquenceur dans un tout autre contexte pour terminer cet ensemble d'exemples. L'envoi de données par la RS232 va maintenant servir à remplir une mémoire RAM avec des valeurs prédéterminés. Ces valeurs seront ensuite envoyées sur des LEDs pour réaliser divers chenillards à l'aide d'un compteur.

Travail préliminaire[modifier | modifier le wikicode]

On désire réaliser un ensemble comprenant un diviseur de fréquence (déjà rencontré), un compteur 4 bits binaire possédant une entrée "en" de validation" et une entrée "init" (devinez pourquoi faire ?) et une mémoire.

Préparation[modifier | modifier le wikicode]

Pour éviter le changement des options de compilation (décrit dans une autre section) pour qu'ISE infère correctement une mémoire BRAM (Block RAM) nous allons utiliser un composant faisant partie des librairies Xilinx. Ce composant s'appelle RAM16X8S. Le meilleur moyen d'en trouver la documentation est le suivant :

  • ouvrir une page blanche de schématique (qui ne nous servira pas par la suite en fait)
  • chercher ce composant dans la catégorie mémoire et le dessiner sur la page blanche
  • click droit sur le composant dessiné, puis "Object properties", puis "Symbol Info"

Vous avez en bas de la page de la documentation un exemple d'instanciation de ce composant en VHDL (puis en verilog, ce qui ne nous intéresse pas). Ce que l'on vous demande de faire à ce stade est de comprendre comment cette RAM est initialisée.

  • fermer la page schématique sans enregistrer mais garder la documentation ouverte.

Calculer les valeurs prédéfinies à mettre dans la RAM pour réaliser un chenillard aller et retour d'une LED.

Travail à réaliser[modifier | modifier le wikicode]

Malheureusement le travail décrit ci-dessous est très lié au fabricant Xilinx. Si quelqu'un peu faire un travail similaire chez Altera ce sera sans refus.

On vous demande d'importer le diviseur de fréquence de ce qui a été fait précédemment, de réaliser un compteur sur 4 bits avec entrées "init" et "en".

Ajouter la mémoire RAM16X8S (dépendant de Xilinx) correctement initialisée, sans oublier les lignes :

Library UNISIM;
use UNISIM.vcomponents.all;

avant votre entité "Travail_A_Realiser".

Le schéma de principe est présenté ci-dessous :

Ensemble destiné à réaliser un chenillard

Cet ensemble est donc destiné à réaliser un chenillard visuel. C'est pour cela qu'on utilise un diviseur de fréquence : notre œil ne perçoit rien au-delà de 20Hz). Le compteur est destiné à générer les adresses (de la mémoire RAM) qui sort une suite de valeurs destinées à l'effet visuel.

Modification du contenu de la RAM[modifier | modifier le wikicode]

On désire maintenant s'intéresser à la possibilité de modifier le contenu de cette mémoire à l'aide d'une liaison RS232. Par rapport au TP sur le compteur de passage, la liaison devra être dans l'autre sens : c'est le PC qui maintenant va fournir des valeurs au FPGA qui finiront dans une RAM (le problème des RAM dans les FPGAs est évoqué dans une autre section du chapitre 2). La réalisation de cette liaison se fait pratiquement comme dans la section précédente ("_rx" au lieu de "_tx") :

Logicore Rx.png

Un séquenceur sera responsable dès qu'il y a une donnée reçue ("buffer_full") de lire "data_out" et de l'écrire dans la mémoire puis d'incrémenter un compteur (d'adresses). Nous aurons en final deux compteurs : un pour lire le contenu de la mémoire et un pour écrire. Ils devront donc se partager le bus d'adresse, cela pourra se faire avec un multiplexeur dont l'entrée sera gérée par le séquenceur. Quand le séquenceur sera en train d'écrire dans la mémoire, il devra bloquer le compteur de lecture.

Travail à réaliser[modifier | modifier le wikicode]

Comme habituellement le travail à réaliser vous est donné sous forme schématique. Vous devez l'implanter en VHDL avec des PORT MAP.

Mémoire2.png

Il y a beaucoup de travail, mais si vous respectez les noms des signaux (en rouge) et les noms de chacune des entrées et sorties de chaque bloc, le travail se fait rapidement. Les deux compteurs du haut sont identiques et sont simplement les compteurs de l'exercice précédent de ce document.

Le multiplexeur est classique et déjà présenté, mais vous avez une chance sur deux de le faire fonctionner comme il faut : comme le séquenceur ci-dessous vous l'indique, quand sel = 1 vous lisez e0 pour l'amener sur la sortie du multiplexeur !

SequencerFinal.png

Le plus long à concevoir est indiscutablement le séquenceur. Nous avons un petit peu hésité à le donner, il aurait été intéressant de vous le faire chercher. Son principe est de lancer le remplissage de la mémoire dès que le buffer est plein. Cette technique fonctionne correctement parce que nous avons justement 16 valeurs à envoyer à la RAM. Si vous avez moins de 16 valeurs à envoyer, n'attendez pas que le buffer soit plein car il ne le sera jamais. Vous pouvez commencer à lire dès qu'il y a une valeur dans le buffer, technique qui doit aussi être utilisée dans le cas où il y a plus de 16 valeurs. Dans ces cas, un des problème est de savoir quand la transmission est finie.

Une fois que toute la partie matérielle est en place, il vous faut réaliser un fichier binaire de 16 valeurs pour remplir la RAM. On vous propose le fichier C++ suivant :

/***  tpmemo.cpp  ***/ 
#include <stdio.h> 
#include <string.h> 
char valhex(char aconv){ 
  static char hex[17]="0123456789ABCDEF"; 
  char i; 
  i=0; 
  while(aconv!=hex[i]) i++; 
  return i; 
} 
main() { 
  FILE *sortie; 
  static char hexa[17]="0123456789ABCDEF"; 
  char trame[128],nomfichier[34]; 
  unsigned char nval,i,tab[255]; 
  printf("   ****************************************************"); 
  printf("\n   *****  Conversion valeur -> fichier Intel HEX  *****"); 
  printf("\n   ********  TP programmation de memoires  ************"); 
  printf("\n   ********  Version 0.1 (S. Moutou & Cie)  ***********"); 
  printf("\n   ****************************************************"); 
  printf("\n\nCombien de valeurs voulez-vous entrer ? "); 
  scanf("%d",&nval); 
  for (i=0;i<nval;i++) { 
    printf("%d° valeur (en hexadecimal) : ",i+1); 
    scanf("%x",&tab[i]); 
  } 
  printf ("\nQuel est votre nom de fichier de sauvegarde ? (extension .bin) "); 
  scanf("%s",nomfichier); 
  sortie=fopen(nomfichier,"w"); 
  fwrite(tab,1,16,sortie); 
  fclose(sortie);
}

que l'on compile avec g++ tpmemo.cpp -o tpmemo

On le lance alors avec ./tpmemo :

[smoutou@localhost LO11]$ ./tpmemo 
   **************************************************** 
   *****  Conversion valeur -> fichier Intel HEX  ***** 
   ********  TP programmation de memoires  ************ 
   ********  Version 0.1 (S. Moutou & Cie)  *********** 
   **************************************************** 

Combien de valeurs voulez-vous entrer ? 16 
1° valeur (en hexadecimal) : 81 
2° valeur (en hexadecimal) : 42 
3° valeur (en hexadecimal) : 24 
4° valeur (en hexadecimal) : 18 
5° valeur (en hexadecimal) : FF 
6° valeur (en hexadecimal) : 00 
7° valeur (en hexadecimal) : FF 
8° valeur (en hexadecimal) : 00 
9° valeur (en hexadecimal) : 18 
10° valeur (en hexadecimal) : 24 
11° valeur (en hexadecimal) : 42 
12° valeur (en hexadecimal) : 81 
13° valeur (en hexadecimal) : 00 
14° valeur (en hexadecimal) : FF 
15° valeur (en hexadecimal) : 00 
16° valeur (en hexadecimal) : FF 

Quel est votre nom de fichier de sauvegarde ? (extension .bin) demo1.bin 

Tester avec gtkterm configuré comme ci-dessous :

PORT :/dev/ttyS0, vitesse : 19200, Parité : odd, Bits 8, Bit de stop : 1, contrôle de flux : none

Fichier -> envoi de fichier brut en cherchant votre fichier binaire créé.

Si tout fonctionne, vous obtiendrez un nouveau chenillard.

TD4 VHDL Logique Sequentielle << Conception et VHDL >> TD6 VHDL et Simulation