Notions avancées de VHDL

Attributs, fonctions, packages, procédures, subtypes et pipeline — pour aller plus loin en VHDL.

Les attributs

Les attributs permettent de récupérer des informations sur des éléments VHDL. Par exemple, là où Python utiliserait max(tableau), VHDL utilise tableau'HIGH.

Il existe plusieurs familles d'attributs selon le type de l'objet ciblé :

-- Attributs pour les types ARRAY ou scalaires
X'HIGH   -- élément le plus grand / borne maximale
X'LOW    -- élément le plus petit / borne minimale
X'LEFT   -- élément de gauche / borne de gauche
X'RIGHT  -- élément de droite / borne de droite
 
-- Attributs pour les objets dans un ARRAY ou SUBTYPE
x'RANGE         -- range de x
x'REVERSE_RANGE -- range de x dans l'ordre inverse
x'LENGTH        -- x'HIGH - x'LOW + 1 (integer)
 
-- Attributs pour SIGNAL
x'EVENT      -- TRUE si x change d'état
x'ACTIVE     -- TRUE si x a changé durant le dernier intervalle
x'LAST_EVENT -- valeur temporelle depuis le dernier changement de x
x'LAST_VALUE -- dernière valeur de x
 
-- Ces attributs créent un nouveau SIGNAL
x'DELAYED(t)    -- signal du type de x retardé par t
x'STABLE(t)     -- TRUE si x n'est pas modifié pendant le temps t
x'QUIET(t)      -- TRUE si x n'est pas modifié pendant un temps t
x'TRANSACTION   -- bascule lorsque x change d'état

La casse

En VHDL, il n'y a pas de casse. Le compilateur ne distingue pas les majuscules des minuscules. Signal, SIGNAL et signal sont équivalents.


Les fonctions

Une fonction est un bloc d'instructions qui retourne une valeur. Elle doit contenir au moins une instruction return, et ne peut contenir que des instructions séquentielles — pas de process, pas de wait, pas d'affectation de signal.

-- Déclaration (dans l'architecture ou un package) :
FUNCTION function_name
  {(parameter_list)}
RETURN type_name;
 
-- Corps de la fonction :
function function_name
  {(parameter_list)}
return type_name is
begin
  [instructions]
end [function_name];

Exemple — fonction MAX :

function MAX (
  A : in integer;
  B : in integer
) return integer is
begin
  if (A > B) then
    return A;
  else
    return B;
  end if;
end MAX;

Les packages

Un package regroupe des déclarations partagées : types, constantes, composants, fonctions, procédures. Pensez-y comme une bibliothèque réutilisable.

-- Déclaration du package :
package package_name is
  [type_declaration]
  [subtype_declaration]
  [constant_declaration]
  [component_declaration]
end [package_name];
 
-- Corps du package (implémentation) :
package body package_name is
  [type_declaration]
  [subtype_declaration]
  [constant_declaration]
  [subprogram_declaration]
end [package_name];

Les procédures

Une procédure ressemble à une fonction, mais elle ne retourne pas de valeur (return absent). Elle peut modifier directement des variables ou signaux de l'architecture. Elle peut contenir des wait, sauf si elle est appelée depuis un process ou une fonction.

-- Déclaration :
procedure procedure_name
  {(parameter_list)};
 
-- Corps :
procedure procedure_name
  {(parameter_list)}
begin
  [instructions]
end [procedure_name];

Les subtypes

Un subtype est un type contraint dérivé d'un autre type. Il permet de restreindre la plage de valeurs autorisées.

type integer is range -2147483647 to 2147483647;
subtype absolu is integer range 0 to 2147483647;

Pipeline

Le pipelining consiste à diviser un calcul complexe en étapes successives, chacune enregistrée dans une bascule. Cela augmente la fréquence maximale d'horloge au prix d'une légère latence.

Exemple — calcul Y = A×B - C sans pipeline :

process(i_clk, i_reset)
begin
  if (i_reset = '1') then
    o_y <= 0;
  elsif rising_edge(i_clk) then
    o_y <= i_a * i_b + i_c;
  end if;
end process;

Même calcul avec pipeline (2 étages) :

process(i_clk, i_reset)
begin
  if (i_reset = '1') then
    r_mul <= 0;
    o_y   <= 0;
  elsif rising_edge(i_clk) then
    r_mul <= i_a * i_b;     -- étage 1 : multiplication
    o_y   <= r_mul + i_c;   -- étage 2 : addition
  end if;
end process;

Physiquement, le code sans pipeline produit un multiplicateur et un additionneur chaînés. Le code pipeliné insère une bascule D entre les deux opérations pour mémoriser le résultat intermédiaire r_mul.

Avantage : le chemin critique est plus court → fréquence d'horloge plus élevée. Inconvénient : latence de 1 cycle supplémentaire entre l'entrée et la sortie.

L'important à retenir : lorsque vos designs deviennent complexes, divisez les calculs en plusieurs process ou fonctions. Le pipeline est dans cet esprit d'un code plus pur et plus efficace.