Systèmes électroniques
Fermer ×

VHDL avancé

Transtypage et sous-type

Le transtypage

Il réalise la conversion d'un type vers un autre type.

Exemple :
On veut utliser un type de la taille d'un octet (0 à 255). Pour cela on va déclarer ce nouveau type octet
type octet is range 0 to 255;
signal cpt : integer;
signal valeur : octet;
Le passage de l'entier à l'octet peut utiliser la transtypage :
octet <= octet(cpt);

Le sous-type

Le sous-type permet de déclarer un type qui est inclus dans un autre type, qui le rend compatible avec le type dans lequel il est inclus.

Exemple avec l'octet de 0 à 255
subtype octet is integer range 0 to 255;
signal cpt : octet;

Les fonctions et transtypages

Ces fonctions sont groupées dans le paquetage ieee.numeric_std

Les fonctions

Les transtypages

Structures logicielles

Les variables

Elles permettent de stocker des résultats intermédiaires ou encore être utilisées dans des boucles qui permettent de construire un circuit en répétant une structure élémentaire de ce circuit.

Les variables n'ont pas de réprésentation physique et ont un comportement uniquement séquentiel et non concurrent.

Elle sont déclarées avec le mot clé variable

Procédures

C'est un sous-programme qui ne possède pas de valeur de retour. Une procédure est déclarée dans une architecture ou une autre procédure ou une autre fonction. Les procédures n'ont pas de réprésentations physiques.

Syntaxe :

procedure (liste des parametres) is
-- déclaration des variables locales
begin
-- traitement
end procedure;

La liste des paramètres contient les paramètres séparés par un point virgule avec la syntaxe :
categorie nom : sens type := valeur par défaut

  • categorie : signal ou variable
  • nom : identifiant du paramètre
  • sens : in pour un paramètre d'entrée, out pour un paramètre de sortie, inout pour un paramètre en entrée et en sortie
  • type : type du paramètres parmi tous les types de données existants.
Voir le code du compteur en utilisant une procédure et la simulation
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- contient les fonctions de conversion de type
use IEEE.NUMERIC_STD.ALL;
entity compteur is
Port ( clk : in STD_LOGIC;
         reset : in STD_LOGIC;
         en : in STD_LOGIC;
         tc : out STD_LOGIC;
         Q : out STD_LOGIC_VECTOR(3 downto 0));

end compteur;

architecture arch_compteur of compteur is
-- sous type quartet sur 4 bits
subtype quartet is natural range 0 to 15;
-- procédure qui ajoute 1 au signal de type quartet
procedure incrementer(signal c : inout quartet) is
begin
   c <= c + 1;
end procedure;
-- signal qui utilise le sous-type défini
signal cpt : quartet;
constant Nmax : natural := 9 ;
begin
-- conversion du quartet en vecteur
   Q <= std_logic_vector(to_unsigned(cpt,4));
   tc <= '1' when cpt = Nmax else '0';
   comptage: process(clk,reset)
   begin
      if reset='0' then
         cpt <= 0; -- cpt dérive d'entier
      elsif rising_edge(clk) and en ='1' then
         if cpt < Nmax then
            incrementer(cpt);
         else   
            cpt <= 0; -- cpt dérive d'entier
         end if;           
      end if;
   end process comptage;
end arch_compteur;

natural est un entier positif

La procédure ajoute 1 au paramètre qui est un paramètre utilisé en entrée et sortie, ce qui fait que la modification réalisée dans la procédure sera transmise au processus.

cpt n'est plus un vecteur mais un entier, ceci explique l'utilisation de la valeur 0 au lieu du vecteur de 0.

La sortie Q est un vecteur, il faut convertir l'entier cpt en vecteur. Il n'existe pas de fonction de conversion directe, il faut donc utiliser deux conversions, la premier vers un non signé et la deuxième vers un vecteur.

Fonctions

C'est un sous-programme qui possède une valeur de retour. Une fonction est déclarée dans une architecture ou une autre procédure ou une autre fonction. Les fonctions n'ont pas de réprésentations physiques.

Syntaxe :

function (liste des parametres) return type is
-- déclaration des variables locales
-- et de la valeur renvoyée par return
begin
-- traitement
return retour;
end function;

La liste des paramètres contient les paramètres séparés par un point virgule avec la syntaxe :
categorie nom : sens type := valeur par défaut

  • categorie : signal ou variable
  • nom : identifiant du paramètre
  • sens : in pour un paramètre d'entrée, out pour un paramètre de sortie, inout pour un paramètre en entrée et en sortie
  • type : type du paramètres parmi tous les types de données existants.
Voir le code du compteur en utilisant une fonction et la simulation
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- contient les fonctions de conversion de type
use IEEE.NUMERIC_STD.ALL;
entity compteur is
Port ( clk : in STD_LOGIC;
         reset : in STD_LOGIC;
         en : in STD_LOGIC;
         tc : out STD_LOGIC;
         Q : out STD_LOGIC_VECTOR(3 downto 0));

end compteur;

architecture arch_compteur of compteur is
-- sous type quartet sur 4 bits
subtype quartet is natural range 0 to 15;
-- procédure qui ajoute 1 au signal de type quartet
function incrementer(signal c : in quartet) return quartet is
variable valeur : quartet;
begin
   valeur := c + 1;
   return valeur;
end function;
-- signal qui utilise le sous-type défini
signal cpt : quartet;
constant Nmax : natural := 9 ;
begin
-- conversion du quartet en vecteur
   Q <= std_logic_vector(to_unsigned(cpt,4));
   tc <= '1' when cpt = Nmax else '0';
   comptage: process(clk,reset)
   begin
      if reset='0' then
         cpt <= 0; -- cpt dérive d'entier
      elsif rising_edge(clk) and en ='1' then
         if cpt < Nmax then
            cpt <= incrementer(cpt);
         else   
            cpt <= 0; -- cpt dérive d'entier
         end if;           
      end if;
   end process comptage;
end arch_compteur;

Les paquetages

Un paquetage permet de déclarer un ou plusieurs type de données avec les procédures et fonctions de traitement de ces nouveaux types ainsi que les fonctions de conversions

Un paquetage est composé de deux blocs qui sont l'entête qui contient les déclarations et le corps qui contient les codes des procedures et fonctions

Entête

package nom is
−− declarations
end nom;

Corps

package body nom is
−− codage des procédures et fonctions
end nom;

On propose d'écrire un paquetage qui contient la définition du type quartet ainsi que la procédure d'incrémentation et la fonction de conversion d'un quartet en vecteur de bits.

On ajoutera à ce paquetage la surcharge de l'opérateur + pour le quartet. La surcharge de l'opérateur + est simplement une fonction de nom "+" qu'il faut déclarer dans l'entête du paquetage et définir dans le corps du paquetage.

Voir le code du paquetage de gestion du type octet
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

package quartet_pkg is
-- définition du type quartet
   type quartet is range 0 to 15;
-- définition du type vecteur de 4 bits
   subtype stdvec4 is std_logic_vector(3 downto 0);
-- procédure qui incrémente le quartet
   procedure incrementer(signal c : inout quartet);
-- surcharge de l'opérateur + pour l'octet
   function "+"(a,b:quartet) return quartet;   
-- fonction de conversion d'un quartet en vecteur de bits
   function octet_to_stdlogicvector(signal c : in quartet) return stdvec4;
end quartet_pkg;

-- incrémenter un quartet
-- paramètre en entrée et en sortie : c de type quartet
package body quartet_pkg is
   procedure incrementer(signal c : inout quartet) is
   begin
      c <= c + 1;
   end procedure;
   
-- surcharge de l'opérateur + pour le quartet
   function "+"(a,b:quartet) return quartet is
      variable c: quartet;
   begin
      c := quartet(integer(a) + integer(b));
      return c;
   end function;   

-- conversion d'un quartet en vecteur de bits
-- paramètre en entrée : c de type quartet
-- valeir de retour : vecteur de 4 bits
   function octet_to_stdlogicvector(signal c : in quartet) return stdvec4 is
      variable s : std_logic_vector(3 downto 0);
      variable ci : natural;
   begin
-- conversion du quartet en entier positif
      ci := natural(c);
-- conversion d'un entier positif en vecteur de 4 bits
      s := std_logic_vector(to_unsigned(ci,4));
-- valeur de retour
      return s;
   end function;
end quartet_pkg;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- contient les fonctions de conversion de type
use IEEE.NUMERIC_STD.ALL;
-- inclusion du paquetage situé dans le même répertoire
use WORK.quartet_pkg.all;

entity compteur is
Port ( clk : in STD_LOGIC;
         reset : in STD_LOGIC;
         en : in STD_LOGIC;
         tc : out STD_LOGIC;
         Q : out STD_LOGIC_VECTOR(3 downto 0));
end compteur;

architecture arch_compteur of compteur is
-- signal qui utilise le sous-type défini
signal cpt : quartet;
constant Nmax : quartet := 9 ;
begin
-- conversion du quartet en vecteur
   Q <= octet_to_stdlogicvector(cpt);
   tc <= '1' when cpt = Nmax else '0';
   comptage: process(clk,reset)
   begin
      if reset='0' then
         cpt <= 0; -- cpt dérive d'entier
      elsif rising_edge(clk) and en ='1' then
         if cpt < Nmax then
            cpt <= cpt + 1 ; -- utilisation + pour quartet
         else   
            cpt <= 0; -- cpt dérive d'entier
         end if;           
      end if;
   end process comptage;
end arch_compteur;

La duplication de circuit ou répétition spatiale

Cette structure de contrôle permet de créer un circuit en répétant une structure. Elle s'applique en dehors du processus.

etiquette:for variable in liste generate
begin
-- traitement
end generate etiquette;
  • etiquette n'est pas obligatoire mais conseillé
  • liste est la suite des valeurs prises par variable et peut être de la forme :
    valeurmin to valeurmax
    valeurmax downto valeurmin
    ou encore l'attribut range d'un vecteur
Voir le code du décodeur utilisant cette répétition et la simulation
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- package qui permet d'écrire e = i
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- decodeur 3 vers 8
entity decodeur is
port ( e : in STD_LOGIC_VECTOR(2 downto 0);
      S : out STD_LOGIC_VECTOR(0 to 7));
end decodeur;

architecture architecture_decodeur of decodeur is
begin
-- i varie entre 0 et 7
   circuit: for i in 0 to 7 generate
   begin
-- le bit i de la sortie est affecté à 1
-- si l'entrée e est égal à la valeur i
      S(i) <= '1' when e = i else '0' ;
   end generate;
end architecture_decodeur;

Les autres structures de contoles

Celles-ci s'appliquent à l'intérieur des processus.

etiquette:for variable in liste loop
begin
-- traitement
end loop etiquette;
etiquette:loop
begin
-- traitement
end loop etiquette;
etiquette:while expression booléenne VRAIE loop
begin
-- traitement
end loop etiquette;

Les structures de contrôle dans les boucles

La généricité

Un composant générique est un composant qui est paramétrable, comme par exemple un décodeur de n vers 2n, ou encore un diviseur de fréquence par N

L'entité comprend, en plus, une instruction generic, la syntaxe de l'entité d'un composant générique est :

entity nom is
  generic (liste des parametres)
  map (liste des signaux)
end nom;

La liste des paramètres, séparés par une virgule, respecte la syntaxe :
nom : type := valeur par défaut;

Voir le code du décodeur générique et la simulation
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- package qui permet d'écrire e = i
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- decodeur 3 vers 8
entity decodeur is
generic ( n : integer := 3);
port ( e : in STD_LOGIC_VECTOR(n-1 downto 0);
      S : out STD_LOGIC_VECTOR(0 to 2**n-1));
end decodeur;

architecture architecture_decodeur of decodeur is
begin
-- i varie entre 0 et 2^n-1
   circuit: for i in s'range generate
   begin
-- le bit i de la sortie est affecté à 1
-- si l'entrée e est égal à la valeur i
      S(i) <= '1' when e = i else '0' ;
   end generate;
end architecture_decodeur;

Les valeurs obtenues en simulation confirment que le système fonctionne toujours.

Voir le code du compteur et du diviseur générique et la simulation
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
-- fonctions mathématiques
use ieee.math_real.all;
-- diviseur par clkdiv
-- frequence tc = frequence clk / clkdiv
entity diviseur is
   Generic(clkdiv : integer := 2);
   Port ( clk : in STD_LOGIC;
         reset : in STD_LOGIC;
         tc : out STD_LOGIC);
end diviseur;

architecture architecture_diviseur of diviseur is
-- calcul du nombre de bits en fonction de la valeur maximale
   constant Nbits : natural := integer(ceil(log2(real(clkdiv+1))));
   signal cptdiv : std_logic_vector(Nbits downto 0);

begin
-- processus de compptage de 0 à clkdiv-1
-- pou diviser par clkdiv
   comptage: process(clk)
   begin
      if reset = '0' then
         cptdiv <= (others => '0');
      elsif rising_edge(clk) then
         if cptdiv < clkdiv-1 then
            cptdiv <= cptdiv + 1;
         else   
            cptdiv <= (others => '0');
         end if;
      end if;
   end process comptage;

   retenue: process(cptdiv)
   begin
      if cptdiv=clkdiv-1 then
         tc <= '1';
      else
         tc <= '0';
      end if;      
   end process retenue;

end architecture_diviseur;

On doit réaliser un diviseur de fréquence par clkdiv, cette valeur doit être représentée sur n bits avec la relation : clkdiv ≤ 2n-1. Mathématiquement, il s'agit de prendre le n ≥ log2(clkdiv+1). Cela se calcule en utilisant les fonctions mathématiques su paquetage math_real avec la relation :
N b i t s = log 2 ( c l k d i v + 1 )

Le reste du code est un compteur avec sortie de retenue sans sortie de comptage.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- compteur synchrone décimal
-- reset asynchrone
-- validation synchrone
-- sortie de retenue
entity compteur is
Port ( clk : in STD_LOGIC;
       reset : in STD_LOGIC;
       en : in STD_LOGIC;
       tc : out STD_LOGIC;
       Q : out STD_LOGIC_VECTOR(3 downto 0));
end compteur;

architecture arch_compteur of compteur is
-- declaration du composant diviseur
   component diviseur is
      Generic(clkdiv : integer := 2);
      Port ( clk : in STD_LOGIC;
             reset : in STD_LOGIC;
             tc : out STD_LOGIC);
   end component;

   signal endiv : STD_LOGIC ;
   signal cpt : STD_LOGIC_VECTOR (3 downto 0);
   constant Nmax : integer := 9 ;
begin
-- instanciation physique du composant diviseur
   chipdiviseur : diviseur generic map (clkdiv => 4)
                           port map( clk => clk, reset => reset,
                                     tc => endiv );
   Q <= cpt;
   tc <= '1' when cpt = Nmax else '0';
   comptage: process(clk,reset)
   begin
      if reset='0' then
         cpt <= (others => '0');
-- compteur synchronisé sur le diviseur avec endiv
      elsif rising_edge(clk) and en ='1'and endiv = '1' then
         if cpt < Nmax then
            cpt <= cpt + 1;
         else   
            cpt <= (others => '0');
         end if;           
      end if;
   end process comptage;
end arch_compteur;

La division de fréquence est précisée au moment de l'instanciation du composant et non dans la déclaration.

La période du compteur en sortie est bien 4 fois la période de l'horloge clk. On a bien un compteur avec division de fréquence par 4.

La gestion des fichiers

Affichage de messages d'erreur

Les structure assert et report permettent d'afficher des commentaires pendant la simulation

report seul affiche un message pendant la simulation

assert expression booléenne report message severity niveau

Utiliser des diagrammes des temps pour afficher les résultats de la simulation est bien, mais quelques fois, comme en combinatoire, une table de vérité est suffisante. Pour cela, on va maintenant étudier, l'écriture des résultats dans des fichiers textes.

Entrée (clavier) et sortie standard (écran)

Il faut inclure les paquetages std.textio et ieee.std_logic_textio

Affichage écran

Cet affichage de fait en deux étapes :

  1. stocker les messages à afficher dans un buffer de type line.
    write(ligne, donnee, alignement, nombre de colonnes)
    • ligne : variable de type line
    • donnee : chaîne de caractères, vecteur, entier
      • string'("chaîne") : pour une chaine de caractères
      • std_logic_vector'("chaîne de bits") : pour un vecteur
      • integer'image(entier) : pour un entier
      Si on remplace write par owrite, on obtient un affichage en octal, hwrite pour un affichage en hexadécimal
    • alignement : facultatif, il utilise les mots clés left ou right, gauche par défaut
    • nombre de colonnes : facultatif, 0 par défaut
    Attention : cette fonction ne vide pas le buffer.
  2. transférer le contenu du buffer sur la sortie standard avec retour à la ligne, puis vide le buffer
    writeline(output,ligne)
    output est le mot clé qui désigne la sortie standard, c'est à dire l'écran.
    ligne est la variable qui contient l'ensembe des messages à afficher.

Note : tant que writeline n'est pas appelé, les fonctions write concatènent les messages dans le buffer

Le programme ci-contre calcule le nombre de bits nécessaire pour stocker une valeur. La fonction mathématique a déjà été décrite dans l'exemple avec le diviseur.

Le processus se termine toujours par wait pour stoper le programme, car il ne faut oublier que process est une boucle temporelle infinie.

library ieee;
use ieee.std_logic_1164.all;
-- fonctions mathématiques
use ieee.math_real.all;
-- sortie texte
use std.textio.all;
use ieee.std_logic_textio.all;

entity calcul is
end calcul;

architecture arch_calcul of calcul is
begin
   calculproc: process
      variable ligne : line;
      -- valeur à utiliser
      variable valeur : natural := 50000000;
      variable nbits : natural ;
      variable Nmax : natural ;
      variable imax : natural ;
   begin
      write(ligne, string'("Calcul du nombre de bits"));
      writeline(output,ligne);
      write(ligne,string'("valeur = "));
      write(ligne,natural'image(valeur));
      writeline(output,ligne);
      nbits := natural(ceil(log2(real(valeur+1))));
      Nmax := 2**nbits - 1;
      write(ligne,string'("nbits = "));
      write(ligne,natural'image(nbits));
      writeline(output,ligne);
      write(ligne,string'("Valeur maximale = "));
      write(ligne,natural'image(Nmax));
      writeline(output,ligne);
      if Nmax >= valeur then
         write(ligne,string'("OK"));
      else
         write(ligne,string'("insuffisant"));
      end if;
      writeline(output,ligne);
      wait; -- fin de processus
   end process calculproc;

end arch_calcul;

Lecture de données depuis le clavier

Dans le programme présédent, il faut compiler pour chaque nouvelle valeur, on propose, ici, de demander à l'utilisateur de saisir la valeur. On va donc utiliser les fonctions de lecture de données à partir de l'entrée standard qui est le clavier qui se fait également en deux étapes

  1. stocker la valeur saisie dans un buffer de type line.
    readline(input,ligne)
    output est le mot clé qui désigne l'entrée standard qui est le clavier
  2. transférer le contenu du buffer dans un variable en la convertissant
    read(ligne,donnee,valide)
    valide, qui n'est pas obligatoire, est un booléen qui indique que le format de la donnée saisie respecte le format attendu.

Le code ci-contre montre la partie du code précédent qui a été modifié pour permettre la saisie de la valeur au clavier

Si la valeur saisie n'est pas un entier, alors on arrête la simulation en affichant un message d'erreur.

   calculproc: process
      variable ligne, clavier : line;
      -- valeur à utiliser
      variable valeur : natural := 10000 ;
      variable nbits : natural ;
      variable Nmax : natural ;
      variable imax : natural ;
      variable conforme : boolean ;
   begin
      write(ligne, string'("Entrer la valeur"));
      writeline(output,ligne);
      readline(input,clavier);
      read(clavier,valeur,conforme);
      assert conforme report "ERREUR de saisie" severity failure;
      write(ligne,string'("valeur = "));
      write(ligne,natural'image(valeur));
      writeline(output,ligne);
      nbits := natural(ceil(log2(real(valeur+1))));
      Nmax := 2**nbits - 1;
      write(ligne,string'("nbits = "));
      write(ligne,natural'image(nbits));
      writeline(output,ligne);
      write(ligne,string'("Valeur maximale = "));
      write(ligne,natural'image(Nmax));
      writeline(output,ligne);
      if Nmax >= valeur then
         write(ligne,string'("OK"));
      else
         write(ligne,string'("insuffisant"));
      end if;
      writeline(output,ligne);
      wait; -- fin de processus
   end process calculproc;

Accès aux fichers

L'accès au fichier utilise un identificateur de fichier (handle) qui est de type file. Un type fichier est défini avec la syntaxe :
type nom du type is file of type de l'élément
le type de l'élément est un type de donnée connu ou bien défini avec la syntaxe type

Il existe un type de fichier prédéfini de type fichier texte : text

Ensuite on déclare une variable avec de type, le nom de la variable est l'indentificateur de fichier qui sera utilisé à la place de output dans writeline et à la place de input dans readline.

La fonction d'ouverture, open, assigne cet identificateur au fichier représenté par son nom. La fonction de fermeture, close, annule cette assignation en vidant les buffers d'échange de données.

L'accès au contenu d'un fichier commence obligatoirement par l'ouverture, effectue l'échange des données, et se termine obligatoirement par la fermeture.

Ouverture

Elle est réalisé avec la fonction :
file_open(statut,idfichier,nom du fichier,mode accès) avec

Fermeture

Elle se fait avec la fonction file_close(idfichier)

Ouverture implicite

Elle se fait avec la déclaration des variables avec la syntaxe
file idfichier : nom du type open mode d'accès is nom du fichier;

Fermeture implicite

Elle se fait automatiquement en fin de processus

Exemple

On utilise les fichiers pour enregister les résultats de simulation sous la forme d'une table de vérité dans un fichier texte. On applique ce principe au décodeur.

Test bench

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use STD.TEXTIO.all;
use IEEE.STD_LOGIC_TEXTIO.all;

entity codeur_tb is
end codeur_tb;

architecture tb_arch of codeur_tb is

procedure entetetobuffer(variable ligne : inout line;variable v : in unsigned;variable c : in string) is
begin
   for i in v'range loop
      write(ligne,HT); -- tabulation
      write(ligne,string'(c));
      write(ligne,integer'image(i));
   end loop;
end procedure;

procedure Lignetobuffer(variable ligne : inout line;variable v : in unsigned) is
begin
   for i in v'range loop
      write(ligne,HT); -- tabulation
      write(ligne,std_logic'(v(i)));
   end loop;
end procedure;

-- declaration composant a simuler : codeur
   component codeur
   port ( e : in STD_LOGIC_VECTOR(0 to 7);
          S: out STD_LOGIC_VECTOR(2 downto 0));
   end component;
-- signaux relies au composant codeur
   signal tbe : STD_LOGIC_VECTOR(0 to 7); -- entree 8 bits
   signal tbS : STD_LOGIC_VECTOR(2 downto 0); -- sortie 3 bits
begin
-- instanciation physique du composant codeur
   code83: codeur port map(e => tbe, S => tbS);
-- process de simulation avec boucle et variable
   simulation: process
      variable entree : unsigned(0 to 7) := b"1000_0000";
      variable sortie : unsigned(2 downto 0);
      file ftable : text open WRITE_MODE is "codeur.txt" ;
      variable ligne : line;
      variable variableentree : string(1 to 1) := "e" ;
      variable variablesortie : string(1 to 1) := "S" ;
   begin
      sortie := unsigned(tbS);
      entetetobuffer(ligne,entree,variableentree);
      write(ligne,HT);
      write(ligne,string'("|"));
      entetetobuffer(ligne,sortie,variablesortie);
      writeline(ftable,ligne);
      boucle : while entree /= 0 loop
         tbe <= std_logic_vector(entree);
         wait for 50 ns;
         Lignetobuffer(ligne,entree);
         write(ligne,HT);
         write(ligne,string'("|"));
         sortie := unsigned(tbS);
         Lignetobuffer(ligne,sortie);
         writeline(ftable,ligne);
         wait for 50 ns;
         entree := entree srl 1;
      end loop boucle;
      wait; -- fin du process de simulation
   end process simulation;
end;

Table de vérité des résultats, contenu de "codeur.txt"

  e0  e1  e2  e3  e4  e5  e6  e7   |  S2  S1  S0
   1   0   0   0   0   0   0   0   |   0   0   0
   0   1   0   0   0   0   0   0   |   0   0   1
   0   0   1   0   0   0   0   0   |   0   1   0
   0   0   0   1   0   0   0   0   |   0   1   1
   0   0   0   0   1   0   0   0   |   1   0   0
   0   0   0   0   0   1   0   0   |   1   0   1
   0   0   0   0   0   0   1   0   |   1   1   0
   0   0   0   0   0   0   0   1   |   1   1   1

Par rapport au testbench classique on utilise un processus et une boucle qui génère les différentes valeurs de l'entrée. Afin de générer les différentes valeurs nécessaires à la vérification du fonctionnement, on utilise un vecteur que l'on décale à droite avec une variable, ce qui fait que dans ce cas on peut utliser l'opérateir srl. On arrête lorsque toutes les entrées valent 0.

Afin qu'il n'y aie pas de décalage temporel à cause de l'utilisation de signaux et variables, on ajoute un délai entre chaque changment et entre changement et écriture dans le fichier.

On a ajouté une ouverture de fichier de type texte implicite en écriture et création, le fichier est détruit à chaque exécution de la simulation.

Pour écrire les vecteurs d'entrée et de sortie bit par bit, on utilise une procédure qui permet d'afficher chaque bit du vecteur séparé par une tabulation.

On utilise également une procédure pour afficher les variables d'entrée et de sortie en entête de fichier. Pour cela on se sert des indices des vecteurs d'entrée et de sortie.

Si on veut encore plus de codes, comme des codes de mémoire, processeur, interface, ... , on peut consulter le site dédié opencores.

Générateur de valeurs binaires en VHDL

Le code VHDL qui suit est celui d'un générateur de valeurs pseudo-aléatoires en utilisant le corps de Galois comme cela a déjà été présenté dans le chapitre logique séquentielle.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity galois is
   Generic ( N : integer := 4;
            polynome : integer := 2#1001#;
            vinit : integer := 2#0000#);
   Port ( clk : in STD_LOGIC;
         reset : in STD_LOGIC;
         en : in STD_LOGIC;
         y : out STD_LOGIC_VECTOR(0 to N-1));
end galois;

architecture arch_galois of galois is
   signal qreg : STD_LOGIC_VECTOR (0 to N-1);
   signal h, yk : STD_LOGIC_VECTOR(0 to N-1);
   signal x : STD_LOGIC;
begin
   h <= conv_std_logic_vector(polynome,N);
   registre: process(clk,reset)
   begin
      if reset = '0' then
         qreg <= conv_std_logic_vector(vinit,N);
      elsif rising_edge(clk) and en = '1' then
         qreg <= x & qreg(0 to N-2);
      else
         qreg <= qreg;
      end if;
   end process registre;
   yk(N-1) <= qreg(N-1) and h(N-1);
   multi: for i in N-2 downto 0 generate
      yk(i) <= yk(i+1) xor (qreg(i) and h(i));
   end generate;
   x <= yk(0);
   y <= qreg;
end arch_galois;
Demander les
Solution [ Voir ]