Les instructions concurrentes doivent être placées dans le corps d’une architecture. Elles s’exécutent en parallèle et communiquent à travers des signaux. L’ordre dans lequel les instructions concurrentes sont écrites n’a pas d’influence sur le comportement de l’architecture.
Une affectation concurrente de signal calcule le résultat d’une expression et affecte la valeur obtenue au signal de destination. Cette instruction est exécutée à chaque fois que l’un des signaux dont elle dépend change de valeur.
nom de signal <= expression;
Ici, nom de signal
peut également désigner un
élément de tableau
ou un champ d’une valeur structurée.
En VHDL, le symbole <=
peut avoir deux significations :
a <= b
signifie « le signal a
reçoit la valeur de b
».a <= b
signifie « la valeur de a
est inférieure ou égale à la valeur de b
».Par exemple, l’instruction ci-dessous se lit :
« a
reçoit '1'
si la valeur de b
est inférieure ou égale à la valeur de c
, sinon a
reçoit '0'
».
a <= '1' when b <= c else '0';
L’affectation concurrente conditionnelle choisit l’expression à évaluer
parmi une liste d’expressions associées à des conditions.
Le signal de destination reçoit le résultat de la première expression
dont la condition est vraie.
Si aucune condition n’est vraie, la clause else
finale est évaluée.
nom de signal <= expression when condition
else expression when condition
...
else expression;
Les conditions sont des expressions dont le résultat doit être de type boolean
.
D’un point de vue syntaxique, la clause else
finale est facultative.
En pratique, nous considérerons son absence comme une erreur de conception pour les raisons évoquées dans la section Bascules et registres.
L’affectation concurrente avec sélection choisit l’expression à évaluer parmi une liste d’expressions, en fonction de la valeur d’une expression de référence.
with expression select
nom de signal <= expression when valeur,
expression when valeur | ... valeur,
...
expression when others;
Après chaque mot-clé when
, il est possible d’indiquer une valeur seule ou
une liste de valeurs séparées par des barres verticales (|
, à ne pas confondre avec l’opérateur or
).
La clause when others
est facultative : elle permet de traiter tous les cas non couverts dans les
clauses when
précédentes.
Les clauses when
doivent être mutuellement exclusives
(on ne doit pas trouver deux fois la même valeur) et exhaustives
(toutes les valeurs possibles du signal de référence doivent être couvertes).
Un processus est une instruction concurrente qui contient un bloc d’instructions séquentielles. Cela signifie que les instructions situées dans le corps d’un processus s’exécutent l’une après l’autre dans l’ordre indiqué.
nom du processus : process(nom de signal, ... nom de signal)
déclaration
...
déclaration
begin
instruction séquentielle
...
instruction séquentielle
end process nom du processus;
Le nom du processus est facultatif.
Les déclarations situées avant le mot-clé begin
sont locales
au processus.
On pourra y trouver des déclarations de variables, de constantes, de types,
mais pas de signaux.
La liste des signaux entre parenthèses est la liste de sensibilité du processus. À chaque fois que l’un de ces signaux change de valeur, le processus s’exécute à nouveau.
À partir du standard VHDL-2008, ont peut utiliser le mot-clé all
comme liste de sensibilité à la place d’une liste de noms de signaux.
Ce remplacement est toujours possible pour les processus qui décrivent des circuits combinatoires. Les outils de développement sont aujourd’hui capables de déterminer la liste de sensibilité automatiquement.
process(all) -- équivalent à process(x, y)
begin
if x < y then
min <= x;
max <= y;
else
min <= y;
max <= x;
end if;
end process;
Pour des circuits séquentiels, la façon dont le corps du processus est écrit
peut être incompatible avec l’utilisation du mot-clé all
.
Par exemple, le processus ci-dessous est une description possible d’une
bascule D. Il s’exécute à chaque fois que clk
change, et copie d
dans q
si clk
vaut '1'
.
process(clk)
begin
if clk = '1' then
q <= d;
end if;
end process;
En remplaçant la liste de sensibilité par all
, l’instruction
q <= d
serait aussi exécutée à chaque modification de n’importe quel autre signal
pendant la durée où clk
vaut '1'
.
Un version correcte consiste à modifier la condition pour détecter les changements de clk
:
process(all)
begin
if rising_edge(clk) then
q <= d;
end if;
end process;
Une instruction d’instanciation crée un exemplaire (une instance) d’une entité en associant des valeurs à ses paramètres génériques, et en connectant des signaux à ses ports.
Cette instruction a été introduite dans le standard VHDL-93. Si vous utilisez un outil qui n’accepte que le VHDL-87, vous devrez utiliser une instanciation de composant.
Selon la présence ou non de paramètres génériques dans l’entité à instancier, l’instruction d’instanciation peut se présenter sous l’une des formes suivantes :
nom de l'instance : entity nom de bibliothèque.nom d'entité(nom d'architecture)
port map(associations de ports);
nom de l'instance : entity nom de bibliothèque.nom d'entité(nom d'architecture)
generic map(associations de paramètres)
port map(associations de ports);
Le « ;
» indique la fin de l’instruction.
Il n’y a pas de « ;
» entre les clauses port map()
et generic map()
.
Dans la définition ci-dessus :
nom de l'instance
est un nom que vous choisissez de donner à la nouvelle instance.nom de bibliothèque
est le nom de la bibliothèque qui contient l’entité et l’architecture à instancier.
Par défaut, la bibliothèque de travail s’appelle work
.nom d'entité
est le nom de l’entité où sont déclarées les entrées/sorties de l’instance à créer.nom d'architecture
est le nom de l’architecture qui décrit le comportement ou la structure de l’instance à créer.
Cette architecture doit être associée à l’entité choisie.
S’il n’existe qu’une architecture pour l’entité à instancier, on peut omettre complètement le nom d’architecture.La clause generic map()
affecte une valeur à chaque paramètre générique de
l’entité instanciée.
La clause port map()
établit les connexions entre les ports de l’instance
et des signaux ou des valeurs.
Les associations de paramètres et de ports peuvent se présenter sous deux formes. Dans la notation par position, on donne simplement une liste d’expressions ou de noms de signaux qui sont associés aux paramètres et aux ports de l’instance en respectant l’ordre de leurs déclarations :
generic map(expression statique, ... expression statique)
port map(nom de signal ou expression statique, ... nom de signal ou expression statique)
Dans la notation par association, on indique explicitement les noms des paramètres ou des ports (à gauche des flèches) et les expressions ou signaux qui leur sont associés (à droite des flèches) :
generic map(
nom de paramètre => expression statique,
...
nom de paramètre => expression statique
)
port map(
nom de port => nom de signal ou expression statique,
...
nom de port => nom de signal ou expression statique
)
Une expression statique est une expression dont la valeur peut être précalculée au moment de la construction du circuit. Une telle expression contient typiquement des valeurs littérales et des constantes, mais pas de signaux.
À partir du standard VHDL-2008, une clause port map()
peut contenir des
expressions faisant intervenir des signaux.
Dans une association de port, on peut utiliser le mot-clé open
pour indiquer
qu’un port n’est pas connecté.
Voici un exemple inspiré de l’activité Instanciation :
entity CounterModN is
generic(
N : positive
);
port(
clk_i, reset_i, inc_i : in std_logic;
value_o : out integer range 0 to N - 1;
cycle_o : out std_logic
);
end CounterModN;
entity CounterDemo is
port(
clk_i : in std_logic;
btn_center_i : in std_logic;
...
);
end CounterDemo;
architecture Structural of CounterDemo is
signal sec_inc : std_logic;
signal sec_value : integer range 0 to 15;
...
begin
-- Instanciation avec notation par position.
div_counter_inst : entity work.CounterModN(Behavioral)
generic map(100e6)
port map(clk_i, '0', '1', open, sec_inc);
-- Instanciation avec notation par association.
sec_counter_inst : entity work.CounterModN(Behavioral)
generic map(
N => 16
)
port map(
clk_i => clk_i,
reset_i => btn_center_i,
inc_i => sec_inc,
value_o => sec_value,
cycle_o => open
);
...
end Structural;
La flèche =>
signifie « est relié à ».
Ne pas confondre :
<=
) ;port map
) ou d’un paramètre générique (generic map
)
dans une instanciation, toujours représentée par une flèche vers la droite (=>
)
quel que soit le sens de circulation des données.L’instanciation de composant est plus générale, mais également plus verbeuse car elle nécessite de déclarer un composant avec la même interface que l’entité à instancier. Nous ne détaillerons pas ici les raisons qui pourraient vous amener à la choisir, mais nous la mentionnons car elle figure encore dans de nombreux exemples respectant le standard VHDL-87, et elle est parfois imposée par les outils de développement. En pratique, dans des projets de taille modeste, l’instanciation d’entité est plus naturelle.
Selon la présence ou non de paramètres génériques dans le composant à instancier, l’instruction d’instanciation peut se présenter sous l’une des formes suivantes :
nom de l'instance : nom de composant
port map(associations de ports);
nom de l'instance : nom de composant
generic map(associations de paramètres)
port map(associations de ports);
L’instruction if...generate
permet d’ajouter ou de retirer un groupe d’instructions
concurrentes d’une architecture en fonction de la valeur d’une condition.
La condition doit être une expression statique qui retourne un booléen.
label : if condition generate
instruction concurrente
...
instruction concurrente
end generate label;
Ne pas confondre l’instruction if...generate
avec l’instruction if
ou l’affectation conditionnelle.
Ces dernières agissent sur le comportement du circuit, en lui permettant de prendre des décisions en fonction de conditions qui peuvent varier au cours du fonctionnement.
L’instruction if...generate
agit sur la structure du circuit.
Au moment de sa construction, un circuit différent sera réalisé selon que
la condition est vraie ou fausse.
L’exemple ci-dessous décrit un circuit qui additionne la valeur absolue de ses opérandes.
a_i
et b_i
sont définis comme toujours positifs ou nuls (MIN
≥ 0),
l’architecture ne contiendra qu’une addition.MIN
< 0),
l’architecture contiendra également des instructions pour calculer leur valeur absolue.entity AddAbs is
generic(
MIN, MAX : integer
);
port(
a_i, b_i : in integer range MIN to MAX;
r_o : out integer range 0 to 2 * MAX
);
end AddAbs;
architecture Behavioral of AddAbs is
signal abs_a, abs_b : integer range 0 to MAX;
begin
pos_gen : if MIN >= 0 generate
abs_a <= a_i;
abs_b <= b_i;
end generate pos_gen;
neg_gen : if MIN < 0 generate
abs_a <= a_i when a_i >= 0 else -a_i;
abs_b <= b_i when b_i >= 0 else -b_i;
end generate neg_gen;
r_o <= abs_a + abs_b;
end Behavioral;
À partir du standard VHDL-2008, l’instruction if...generate
accepte les clauses elsif...generate
et else generate
sur le modèle de l’instruction if
.
abs_gen : if MIN >= 0 generate
abs_a <= a_i;
abs_b <= b_i;
else generate
abs_a <= a_i when a_i >= 0 else -a_i;
abs_b <= b_i when b_i >= 0 else -b_i;
end generate abs_gen;
VHDL-2008 définit également un instruction case...generate
sur le modèle de l’instruction case
.
L’instruction for...generate
permet de construire une architecture par
répétition d’un groupe d’instructions concurrentes.
label : for nom du compteur in intervalle generate
instruction concurrente
...
instruction concurrente
end generate label;
L’exemple suivant décrit un circuit additionneur sur N
bits réalisé
avec N
additionneurs sur 1 bit :
entity BitAdder is
port(
a_i, b_i, c_i : in std_logic;
s_o, c_o : out std_logic
);
end BitAdder;
entity VectorAdder is
generic(
N : positive
)
port(
a_i, b_i : in std_logic_vector(N - 1 downto 0);
c_i : in std_logic;
s_o : out std_logic_vector(N downto 0)
);
end VectorAdder;
architecture Structural of VectorAdder is
signal c : std_logic_vector(0 to N);
begin
c(0) <= c_i;
add_gen : for k in 0 to N - 1 generate
add_inst : entity work.BitAdder
port map(
a_i => a_i(k),
b_i => b_i(k),
c_i => c(k),
s_o => s_o(k),
c_o => c(k + 1)
);
end generate add_gen;
s_o(N) <= c(N);
end Structural;
La boucle for...generate
ci-dessus construit instances
distinctes de l’entité BitAdder
.
L’utilisation de cette boucle impose la création d’un
signal c
de type vecteur qui rassemble les retenues en entrée et en sortie
de chaque instance.
Ne pas confondre l’instruction concurrente for...generate
et la boucle for
.
L’instruction for...generate
est une instruction concurrente qui contient
d’autres instructions concurrentes. Elle agit sur la structure du circuit
en construisant exemplaires d’un même groupe d’instructions.
La boucle for
est une instruction
séquentielle qui contient d’autres instructions séquentielles. Elle agit sur
le comportement du circuit en décrivant une séquence répétitive d’opérations.