Préambule

Objectifs

Passons maintenant aux descriptions comportementales. Dans ce type de descriptions, il y a plusieurs niveaux

Nous allons ici étudier les principales descriptions : 


Les objectifs de ce chapitre sont de comprendre :

Plan du chapitre


Back to Top

INSTRUCTIONS CONCURRENTES ET SEQUENTIELLES

Comme tout langage de description de matériel, le VHDL décrit des structures par assemblage d'instructions concurrentes dont l'ordre d'écriture n'a aucune importance, contrairement aux instructions séquentielles qui sont exécutées les unes après les autres, comme c'est la cas du C.

VHDL offre cependant la possibilité d'utiliser des instructions séquentielles, plus naturelles pour l'homme, par le biais de processus process. Les processus peuvent avoir leurs propres variables locales variable

Les objets manipulés par les instructions concurrentes sont les signaux signal qui disposent chacun d'un échéancier de façon à effectuer une simulation d'instructions concurrentes sur une machine séquentielle (l'ordinateur).

Il existe 3 principales instructions concurrentes :

  1. Les processus, qui offrent la possibilité d'utiliser des instructions séquentielles.
  2. Les instanciations de composants (étudiées aux chapitre précédent à propos du VHDL structurel)
  3. les affectations concurrentes de signaux, qui peuvent être conditionnelles

Il existe également les assertions et les procédures concurrentes qui ont la même syntaxe que les assertions et procédures séquentielles (étudiées en fin de ce chapitre) mais utilisées en dehors  des processus.


Back to Top

Processus et synchronisation

 Les différentes tâches d'un programme vhdl s'exécutent en parallèle les unes des autres. Ces tâches sont appelées processus. Toutes les instructions concurrentes sont en fait des processus mais la déclaration explicite de processus par le mot clé process permet de construire sa propre instruction par le biais d'instruction séquentielles internes au processus. Un processus peut avoir des variables locales. Le fonctionnement du processus est régi par les règles suivantes :

  1. Un processus est une boucle infinie , lorsqu'il arrive à la fin du code, il reprend automatiquement au début 
  2. Un processus doit être sensible des points d'arrêt de façon à le synchroniser.  La synchronisation est donc indiquée par un point d'arrêt qui est  évènement particulier. Il existe 2 types de points d'arrêts :
  3. Les variables sont internes au processus et sont affectées immédiatement, contrairement aux signaux qui eux ne sont pas affectés directement mais par le biais de leur échéancier qui est mis à jour en fin de processus avec la nouvelle valeur et le temps d'affectation qui correspond à un delta-cycle après le signal ayant réveillé le processus.
Synchronisation des processus par WAIT
L'instruction WAIT permet de mettre des points d'arrêt dans le corps du processus. La syntaxe de l'instruction est la suivante :
wait [on S1,S2,...] [until CONDITION] [for DUREE]
S1 et S2 sont des signaux, CONDITION est une expression générant un booléen, et DUREE est le temps physique d'attente.

L'instruction WAIT n'est pas synthétisable avec la condition de durée. Elle est très utile pour les testbench pour générer précisément des formes de vecteurs d'entrée.
L'exemple suivant génère une trame de 10 impulsions espacées de 100 microsecondes.
trame : process -- le processus peut avoir une étiquette, ici "trame"
  -- il n'y a pas de liste de sensibilité donc il faut des "wait"
	pulse <= '0'; -- pulse est à 0 en début de trame
for i in 0 to 9 loop -- on génère 10 impulsions larges de 2 périodes d'horloge
	wait until clk'event and clk='1';
	pulse <= '1';
  wait until clk'event and clk='1'
	pulse <= '0';
end loop;
	wait for 100 us; -- après 100 us on reprend le process donc pulse va repasser à 0
end process;

Exercice :

Ecrivez un processus permettant de générer un train d'impulsions toutes les microsecondes : pas d'impulsion, puis une impulsions de100 ns puis une impulsions de 200ns, jusqu'à 1 microseconde comme le montre la figure ci-dessous.

train d'impulsion

Afficher la réponse

Synchronisation des processus par liste de sensibilité
La liste des signaux réveillant le processus est indiquée dans la liste de sensibilité suivant cette syntaxe : process(liste de sensibilité). Cette  méthode est tout à fait équivalente à celle consistant à utiliser un wait on liste à la fin du corps du processus.
L' exemple suivant permet de coder la logique combinatoire.
process(x,y)
begin
if x='1' then
	z<=y;
else
	z<='0';
end if;
end process;
--
De quelle fonction s'agit-il ? Afficher la réponse

Notez la présence dans la liste de sensibilité des entrées uniquement.

Attention un processus correspondra à de la logique combinatoire seulement si :

Si ces règles ne sont pas respectées, le processus est séquentiel.

Exercices :

Pour fiabiliser le processus séquentiel , nous avons vu qu'il était fortement recommandé de fonctionner en mode synchrone, c'est à dire avec l'utiliation d'un horloge qui échantillonne le calcul. Autrement dit un processus séquentiel synchrone a une liste de sensibilité qui se réduit simplement à l'horloge. Les signaux codés dans ce processus seront donc des sorties de bascule D.
S'il est impératif d'initialiser les bascules (cas des machines à états), le reset asynchrone peut être employé. Dans ce cas il faut le rajouter dans la liste de sensibilité car il est prioritaire sur l'horloge quand il est actif. La syntaxe générique d'un bloc de logique séquentiel est donc la suivante:

process(clk, n_reset)
--
begin
if n_reset='0' then
	sortie <= (others=>'0'); -- tous les bits de sortie sont initialisés à 0 par le reset
elsif clk'event and clk='1' then
	liste d'instructions codant "sortie"
end if;
end process;
--

Exercice



Back to Top

SIGNAUX vs variables

Les signaux sont équivalents à des variables globales assurant les communications entre processus. Ils sont équivalents à des équipotentielles si le code est synthétisable. Les signaux ne sont pas mis à jour tout de suite mais à la fin du processus avec un delta-cycle de retard par rapport au signal ayant déclenché le processus. Les signaux sont affectés avec l'instruction <= qui se dit aussi "reçoit"  plutôt que "égal" car le signal va recevoir cette valeur en fin de processus avec un delta-cycle de retard.   

Les variables sont locales à chaque processus et sont mises à jour immédiatement.  Elles sont très utiles pour effectuer un codage séquentiel classique comme avec le langage C. Les variables sont déclarées juste avant le corps du processus et sont affectées avec l'instruction d'affectation immédiate := de façon à bien ne pas confondre avec l'instruction <= "reçoit" pour les signaux. Les variables gardent leur valeur quand le processus est terminé.

Dans l'exemple suivant, a,b,c sont des signaux et x une variable. Enfin de processus, a et b vont prendre la valeur a+1 après un delta-cycle alors que c prendre la valeur a après un delta-cycle.

process (a)
variable x : std_logic;
begin
	x := a+1;
	a <= a+1;
	b <= x;
	c <= a;
end if;
end process;
--

Exercice: Ecrivez un processus qui effectue la multiplication de 2 signaux a et b. Si le résultat de la multiplication dépasse le seuil c alors la sortie prend la valeur c. Afficher la réponse]


Back to Top

Structures de contrôle

Dans les processus, il est possible d'utiliser des structures de contrôle similaires à celles du C :


Instruction IF  

L' instructions IF reposent sur le test d'une  condition qui génère un booléen. Si celui ci est "TRUE"  l'instruction qui suit est exécutée

Syntaxe du IF:

if condition1 then instruction;
[elsif condition2 then instruction;]
...
[else instruction;]
end if;

Pour coder un processus combinatoire, l'utilisation du ELSE est obligatoire de façon à traiter toutes les combinaisons (sinon il y a mémorisation donc c'est da la logqiue séquentielle)

Instruction CASE
L' instructions CASE reposent sur le test d'un signal ou d'une variable. En fonction de la valeur, une instruction spécifique est exécutée

Syntaxe du CASE :

case A is
  when -7 =>     B := 10;
                 C :='0'
  when -3 =>     B :=15;
                 C :='0';
  when others => B :=2;
                 C :='1';
end case;

Pour coder un processus combinatoire, l'utilisation du when others est obligatoire de façon à traiter toutes les combinaisons (sinon il y a mémorisation donc c'est da la logqiue séquentielle)

Instruction de boucle
L1: for i in 0 to 10 loop
    ...
    L2: loop
        ...
        L3 : while non _stop_L3 loop
            ...
            exit L2 when stop_L2;
      next L3 when suite_L3;
            if stop_L1 then
                    exit L1;
            end if;
        end loop L3;
    end loop L2;
end loop  L1;

Il faut noter :
  1. Les indices de boucles (ici i pour L1) ne sont pas à déclarer
  2. Les étiquettes de boucles (L1,L2,L3) sont optionnelles
  3. L'excution peut être altérée avec les clauses next et exit

 Exemple : la boucle loop de cet exemple permet de compter le nombre de bits à 1 d'un signal.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--
entity nb_un is
	port
	(
	a : in unsigned(7 downto 0);
	s : out  unsigned(3 downto 0)
);
end entity;
--
architecture rtl of nb_un is
begin
process(a)
variable x : unsigned (3 downto 0);
begin
x:= (others => '0');
for i in 0 to 7 loop
	x := x + ('0' & a(i));
end loop;
s <= x;
end process;
end rtl;
Le paquetage IEEE.numeric.std permet d'avoir accès aux fonction arithmétiques sur les types vecteurs non-signés unsigned ou signés signed.

Remarquez que l'addition se fait en concaténant chaque bit avec '0' car l'opérateur + du paquetage n'opère pas sur un bit simple.

 Exercice : Ecrivez un processus calculant la parité d'un signal sur n bits.

Afficher la réponse]


 




Back to Top

Affectations CONCURRENTES DE SIGNAUX

Ces affectations sont des instructions concurrentes au même titre que les processus et les instanciations de composants. Le codage comportemental d'une architecture repose sur l'utilisation  de ces 3 types d'instructions concurrentes qui peuvent apparaître dans n'importe quel ordre.
Une instruction concurrente est équivalente à un processus et correspond à un raccourci d'écriture pour éviter le verbosité du processus.
Exemple :

Architecture avec processus Architecture avec affectation concurrente
architecture arc of adder is
begin
process(A,B,Cin)
begin
   S <= A xor B xor Cin;
architecture arc of adder is
begin
    S <= A xor B xor Cin;
end arc;

Affectation concurrente conditionnelle


Il existe également des instruction concurrentes conditionnelles  permettant d'effectuer des raccourcis d'écriture pour remplacer des processus simples à base de  IF et CASE
Exemples :

Architecture avec processus Architecture avec affectation concurrente
architecture arc of adder is
begin
process(A,B,Cin)
  begin
  if A = '0' then
    S <= B xor Cin;
  elsif B = '0' then
    S <= not(Cin);
  else
    S <= Cin;
  end if;
end process;
end arc;
architecture arc of adder is
begin
S <= B xor Cin when A = '0' else
     not Cin when B = '0' else
     Cin;
architecture arc of MUX is
begin
process(SEL)
begin
  case SEL is
    when 0 =>
      sortie <= A;
    when 1 =>
      sortie <= B;
    when 2 =>
      sortie <= C;
    when others =>
      sortie <= D;
  end case;
end process;
end arc;
architecture arc of MUX is
begin
with SEL select
  sortie <= A when 0,
            B when 1,
            C when 2,
            D when others;
end arc;


Pour les fonctions combinatoires, il est souvent plus prudent d'utiliser les affectations concurrentes plutôt que les processus où il est possible d'omettre un signal d'entrée dans la liste de sensibilité.
Il faut toutefois avoir un chemin de contrôle exhaustif et toujours avoir la clause else ou when others pour ne rien omettre.

Back to Top

Affectations et délais

VHDL permet de spécifier des délais dans les affectations. 

Il existe deux types d'affectations avec délai :


Le type inertiel permet de "filtrer" les variations de X trop courtes par rapport au délai de la transaction. Par exemple si x est à 1 pendant 1ns, l'affectation x <= 3 after 2 ns; ne changera pas la forme d'onde de x qui restera à 0. Contrairement au mode inertiel , le type transport n'opère pas de  "réjection des parasites". Il respecte les temps de propagation mais est certainement moins réaliste que le mode inertiel.



 


Back to Top

ASSERTIONS

Les assertions permettent de vérifier une propriété et générer un message d'erreur si cette propriété n'est pas vérifiée.
La syntaxe est la suivante :
assert CONDITION
[report MESSAGE]
[severity NIVEAU];

CONDITION =  condition générant un booleén. Cette condition doit être vraie pour que rien ne se passe
MESSAGE = chaîne de caractères renseignant le fait que la condition n'a pas été remplie
NIVEAU = niveau d'erreur. Ilen existe 4 prédifinis : NOTE,WARNING, ERROR, FAILURE

Les assertions peuvent font aprtie du domaine séquentiel (dans un processus) ou du domaine concurrent

exemple :


assert NOW < 10 ms -- NOW est une fonction renvoyant le temps physique
    report "Fin de simulation"
    severity failure



Back to Top

SOUS-PROGRAMMES

IL existe 2 types de sous-programmes:

  1. Les fonctions
  2. Les procédures

Les sous-programmes font partie du domaine séquentiel et ont la même utilité que pour les autres langages de programmation : regrouper les instructions qu'on utilise souvent. Les procédures font peuvent être appelées aussi bien dans le domaine séquentiel que concurrent
La différence entre fonctions et procédures sont les suivantes:

fonction procédure
paramètres d'appels non modifiable (entrées) les sorties sont modifiables
valeur retournée oui non
syntaxe function F(A: unsigned(3 downto 0))
return std_logic is
...
begin
...
end function F;
procedure P(A: in unsigned(3 downto 0))
            S : out std_logic ) is
...
begin
...
end procedure P;
 
Par rapport à la fonction, La procédure permet d'avoir plusieurs sorties, mais à condition de déclarer les entrées et les sorties comme dans une entité ou composant.  

Exemples :

Fonction de calcul de minimum et maximum en procédure.

procedure MinMax( a,b : in unigned (7 downto 0);
min : out unsigned(7 downto 0);
max : out unsigned(7 downto 0)) is
begin
if (a < b) then
min <= a;
max <= b;
else
min <= b;
max <= a;

end if;
end procedure MinMax;


MinMax(x, y, z, t);

Il faut 2 fonctions min et max pour avoir léquivalent en fonction. Voici l'exemple de min :

Exercices :

function min (a,b : unsigned(7 downto 0)
return unsigned(7 downto 0) is 
varaiable min : unsigned(7 downto0);
begin
if (a < b) then
min := a;
else
min := b;
end if;
return min;
endfunction

...

z := min(x, y);

Trouvez le code de la fonction comptant le nombre de bits à 1 dans un nombre de 8 bits.





Back to Top

En résumé

Ce chapitre vous a présenté les façons de décrire la fonctionnalité des processus, c'est-à-dire leur description comportementale.

Les descriptions comportementales ne s'opposent pas aux description structurelles. Elle se complètent : on adopte une description structurelle pour séparer un bloc en sous-systèmes simples, qui eux seront décrits comportementalement.


Les processus s'exécutent en parallèle les uns des autres, mais leur déroulement interne est séquentiel. Ils peuvent utiliser la plupart des structures de contrôle du C.

Les processus always s'exécutent en boucle, et nécessitent donc un point d'arrêt pour que le temps puisse s'écouler. Il en existe trois :

L'objectif du prochain chapitre est de faire la différence entre les constructions synthétisables, et celles spécifiques à la simulation.
.

Back to Top