Expressions

Valeurs littérales

Valeur de type bit ou std_logic

La valeur d’un bit s’écrit entre apostrophes. Les valeurs disponibles sont les suivantes :

bit std_logic Signification
'U' L’état d’un signal non initialisé
'0' '0' La valeur binaire 0
'1' '1' La valeur binaire 1
'X' Une valeur indéterminée (conflit entre '0' et '1')
'Z' L’état haute impédance en logique à trois états
'L' La valeur binaire 0 faible (à travers une résistance de tirage pull-down)
'H' La valeur binaire 1 faible (à travers une résistance de tirage pull-up)
'W' Une valeur indéterminée faible (conflit entre 'L' et 'H')
'-' Une valeur indifférente

Valeur de type bit_vector ou std_logic_vector

La valeur d’un vecteur de bits s’écrit entre guillemets. L’ordre des bits entre les guillemets correspond à l’ordre des indices dans le type vecteur utilisé. La base par défaut est le binaire :

signal a : std_logic_vector(0 to 7);
signal b : std_logic_vector(7 downto 0);
...
a <= "11001000"; -- a(0) <= '1'; a(1) <= '1'; a(2) <= '0'; ...
b <= "11001000"; -- b(7) <= '1'; b(6) <= '1'; b(5) <= '0'; ...

Il est également possible d’écrire un vecteur en hexadécimal en ajoutant la lettre « x » avant le premier guillemet. La taille du vecteur doit être multiple de 4 :

signal b : std_logic_vector(7 downto 0);
...
b <= x"C8";

Valeur de type boolean

Le type boolean définit les valeurs true (vrai) et false (faux).

Valeur entière

Les valeurs entières s’écrivent par défaut en décimal :

signal value : integer range -40000000 to 39999999;
...
value <= 10427123;

Pour améliorer la lisibilité des grands nombres, il est possible d’insérer des caractères « _ » entre les chiffres sans que cela modifie la valeur totale :

signal value : integer range -40_000_000 to 39_999_999;
...
value <= 10_427_123;

Une valeur entière peut être écrite dans une autre base en respectant la syntaxe suivante, où base est un littéral entier écrit en décimal :

base#valeur#

Dans l’exemple ci-dessous, la valeur 10 427 123 a été écrite en hexadécimal (9F1AF3) :

value <= 16#9F1AF3#;

Tableaux

Valeur de type tableau

On peut écrire directement une valeur de type tableau comme une liste d’expressions entre parenthèses et séparées par des virgules :

(expression, ... expression)

La notation par association indique explicitement les indices (à gauche de chaque flèche) et les valeurs (à droite de chaque flèche) qui leurs sont affectées. Le mot-clé others permet d’affecter une valeur à tous les indices qui n’ont pas été mentionnés précédemment :

(expression => expression, ... others => expression)

Exemples :

type color_vec_t is array(1 to 3) of integer range 0 to 255;

constant PINK      : color_vec_t := (255, 192, 203);
constant TURQUOISE : color_vec_t := (1 => 64, 2 => 224, 3 => 208);
constant RED       : color_vec_t := (1 => 255, others => 0); -- (255, 0, 0)
constant GREEN     : color_vec_t := (2 => 255, others => 0); -- (0, 255, 0)
constant BLUE      : color_vec_t := (3 => 255, others => 0); -- (0, 0, 255)
constant WHITE     : color_vec_t := (others => 255);         -- (255, 255, 255)
constant BLACK     : color_vec_t := (others => 0);           -- (0, 0, 0)

Accès à un élément de tableau

L’accès à un élément de tableau s’effectue en indiquant la liste des indices entre parenthèses et séparés par des virgules.

nom du tableau(expression, ... expression)

L’accès à un sous-tableau (une tranche) est possible en indiquant des intervalles à la place des indices. Dans l’exemple ci-dessous, nous séparons un vecteur de 32 bits en quatre vecteurs de 8 bits :

signal c :        : std_logic_vector(31 downto 0);
signal r, v, b, a : std_logic_vector(7 downto 0);
...
r <= c(31 downto 24);
v <= c(23 downto 16);
b <= c(15 downto 8);
a <= c(7 downto 0);

Types structurés

Valeur de type structuré

Comme pour les tableaux, on peut écrire une valeur d’un type structuré comme une liste d’expressions entre parenthèses et séparées par des virgules :

(expression, ... expression)

La notation par association indique explicitement les noms des champs (à gauche de chaque flèche) et les valeurs (à droite de chaque flèche) qui leur sont affectées. Le mot-clé others permet d’affecter une valeur à tous les champs qui n’ont pas été mentionnés précédemment, à condition qu’ils soient tous de même type :

(nom => expression, ... others => expression)

Exemple :

type color_rec_t is record
    rouge, vert, bleu : integer range 0 to 255;
end record;

constant PINK      : color_rec_t := (255, 192, 203);
constant TURQUOISE : color_rec_t := (rouge  => 64,  vert   => 224, bleu => 208);
constant RED       : color_rec_t := (rouge  => 255, others => 0);
constant GREEN     : color_rec_t := (vert   => 255, others => 0);
constant BLUE      : color_rec_t := (bleu   => 255, others => 0);
constant WHITE     : color_rec_t := (others => 255);
constant BLACK     : color_rec_t := (others => 0);

Accès à un champ d’une valeur de type structuré

L’accès à un champ s’effectue à l’aide d’un point comme ceci :

nom de la donnée.nom du champ

Exemple :

signal c         : color_rec_t;
signal luminance : integer range 0 to 765;
...
luminance <= c.red + c.green + c.blue;

Opérateurs

Les expressions sont évaluées de la gauche vers la droite. Les opérateurs de plus forte priorité sont appliqués en premier. Il est possible d’ajouter des parenthèses autour des sous-expressions lorsque c’est nécessaire pour des questions de priorité, ou pour améliorer la lisibilité.

Dans le tableau ci-dessous, nous avons classé les opérateurs les plus courants par priorité décroissante. Les opérateurs situés sur la même ligne on la même priorité.

Catégorie Opérateurs
Autres opérateurs **, not
Opérateurs multiplicatifs *, /, mod, rem
Signes +, -
Opérateurs additifs +, -, &
Opérateurs relationnels =, /=, <, <=, >, >=
Opérateurs logiques and, or, xor, nand, nor, xnor

Cette liste n’est pas exhaustive. Nous n’avons représenté que les principaux opérateurs pris en charge par les outils de synthèse.

Opérateurs logiques

Les opérateurs ci-dessous s’appliquent aux booléens et aux valeurs logiques (bit, bit_vector, std_logic et std_logic_vector).

Opérateur Signification
not Négation
and Et
or Ou (inclusif)
xor Ou exclusif
nand Non-et
nor Non-ou
xnor Non-ou exclusif

À l’exception de not, qui a une priorité plus élevée que les autres, il n’est pas possible de mélanger des opérateurs logiques dans une même expression. Dans l’exemple ci-dessous, les parenthèses sont obligatoires :

y <= (a and b) or (c and d);

Opérateurs arithmétiques

Les opérateurs ci-dessous s’appliquent aux entiers et aux types vecteurs signed et unsigned.

Opérateur Signification
+ Signe positif ou addition
- Signe négatif ou soustraction
* Multiplication
/ Division
mod Modulo
rem Reste
** Puissance

Pour synthétiser un circuit, les opérateurs /, mod et rem sont soumis à des restrictions.

Opérateurs relationnels

Les opérateurs suivants produisent un résultat de type boolean.

Opérateur Signification
= Est égal à…
/= Est différent de…
< Est strictement inférieur à…
<= Est inférieur ou égal à…
> Est strictement supérieur à…
>= Est supérieur ou égal à…

Ne pas confondre l’opérateur d’affectation de signal <= avec l’opérateur de comparaison « Est inférieur ou égal à… ».

Opérateur de concaténation de tableaux

L’opérateur & sert à concaténer des tableaux. Il ne faut pas le confondre avec l’opérateur logique and :

x <= "1100";
y <= "0101";
z <= x & y;   -- z vaut "11000101"
t <= x and y; -- t vaut "0100"

Intervalles

Les intervalles sont utilisés dans différentes situations :

Un intervalle peut prendre l’une des formes suivantes selon que les indices sont parcourus dans le sens croissant (to) ou décroissant (downto) :

expression to expression
expression downto expression

Pour un type tableau ou un objet de type tableau, l’attribut range retourne l’intervalle de définition des indices. L’attribut reverse_range retourne le même intervalle parcouru dans l’ordre inverse.

Dans l’exemple ci-dessous, le type signed_byte_t a le même intervalle d’indices que le type byte_t (7 downto 0). Le signal n est de type entier compris entre 0 et 7;

subtype byte_t        is std_logic_vector(7 downto 0);
subtype signed_byte_t is signed(byte_t'range);

signal s : signed_byte_t;
signal n : integer range s'reverse_range;

Appel de fonction

L’appel de fonction s’écrit en indiquant le nom de la fonction, suivi de la liste des arguments entre parenthèses et séparés par des virgules.

nom de la fonction(expression, ... expression)

La notation par association fait correspondre chaque nom de paramètre avec sa valeur :

nom de la fonction(nom de paramètre => expression, ... nom de paramètre => expression)

Typage des expressions

Expressions qualifiées

Une expression qualifiée permet de lever une ambiguïté sur le type du résultat d’une expression. Elle s’écrit de la manière suivante :

nom de type'(expression)
nom de type'valeur de type tableau
nom de type'valeur de type structuré

Par exemple, à première vue, il n’est pas possible de savoir si "0011" est de type bit_vector ou std_logic_vector. De même, l’expression (50, 10, 30) peut aussi bien être de type color_vec_t que de type color_rec_t, ou encore de n’importe quel autre type tableau d’entiers. Lorsque c’est nécessaire, on peut écrire, par exemple :

std_logic_vector'("0011")
color_rec_t'(50, 10, 30)

Dans l’exemple ci-dessous, le tableau (r, v, b) n’est pas affecté à un signal ou une variable. Son type ne peut pas être identifié avec certitude.

type color_enum_t is (NOIR, ROUGE, VERT, BLEU, JAUNE, MAGENTA, CYAN, BLANC);

signal c       : color_enum_t;
signal r, v, b : std_logic;
...
with (r, v, b) select
    c <= NOIR    when "000",
         ROUGE   when "100",
         VERT    when "010",
         BLEU    when "001",
         JAUNE   when "110",
         MAGENTA when "101",
         CYAN    when "011",
         BLANC   when others;

On peut alors écrire :

with std_logic_vector'(r, v, b) select
    ...

Conversion de type

Une conversion de type permet de traiter une valeur d’un certain type comme si elle était d’un autre type, à condition que ces deux types soient compatibles. En VHDL, la conversion de type ressemble à un appel de fonction dans lequel, à la place d’un nom de fonction, on indique le nom du type de destination :

nom de type(expression)

Par exemple, le paquetage numeric_std définit les types signed et unsigned comme des sous-types de std_logic_vector. Les opérations arithmétiques définies sur les types signed et unsigned ne sont pas applicables directement aux valeurs de type std_logic_vector, pour lesquelles on ne sait pas s’il faut appliquer les règles de calcul pour des nombres signés ou non signés. Dans l’exemple ci-dessous :

  1. L’opérateur + n’est pas défini pour le type std_logic_vector.
  2. On force l’utilisation de l’opérateur + pour le type signed, mais son résultat est de type signed et ne peut pas être affecté à s qui est de type std_logic_vector.
  3. On convertit le résultat de l’addition vers le type std_logic_vector avant de l’affecter à s.
signal a, b, s : std_logic_vector(7 downto 0);
...
s <= a + b;                                  -- Incorrect (1)
s <= signed(a) + signed(b);                  -- Incorrect (2)
s <= std_logic_vector(signed(a) + signed(b)) -- Correct   (3)

Fonctions de conversion

Une fonction de conversion réalise une transformation d’une valeur d’un certain type en une valeur d’un autre type incompatible avec le premier.

Par exemple, le type signed et le type integer sont incompatibles : le premier est un type tableau tandis que le second est défini comme un intervalle de valeurs. Pour effectuer des conversions entre vecteurs et entiers, ou entre vecteurs de même nature, le paquetage numeric_std définit les fonctions suivantes :

Fonction Type de x Type du résultat
to_integer(x) signed ou unsigned integer
to_signed(x, n) integer signed(n - 1 downto 0)
to_unsigned(x, n) integer unsigned(n - 1 downto 0)
resize(x, n) signed ou unsigned signed(n - 1 downto 0) ou
unsigned(n - 1 downto 0)

Attributs

Les attributs permettent d’obtenir des informations supplémentaires sur un élément. La syntaxe générale d’un attribut est :

objet'nom de l'attribut
objet'nom de l'attribut(expression)

Dans cette section, nous nous intéressons uniquement aux attributs qui sont acceptés par les outils de synthèse.

Attributs sur les types scalaires

Les types scalaires sont les types entiers et les types énumérés. Pour un type scalaire T, VHDL définit les attributs suivants :

Attribut Type du résultat Valeur
T'left T La valeur la plus à gauche de son intervalle de définition, ou la première valeur pour un type énuméré.
T'right T La valeur la plus à droite de son intervalle de définition, ou la dernière valeur pour un type énuméré.
T'low T La plus petite valeur de ce type.
T'high T La plus grande valeur de ce type.
T'ascending boolean true si l’intervalle des valeurs est croissant, false s’il est décroissant.
T'pos(x) integer La position de la valeur x.
T'val(n) T La valeur située en position n.
T'succ(x) T La valeur immédiatement supérieure à x.
T'pred(x) T La valeur immédiatement inférieure à x.
T'leftof(x) T La valeur située à gauche de x.
T'rightof(x) T La valeur située à droite de x.

Comme les types entiers peuvent être définis avec des intervalles croissants (to) ou décroissants (downto), ces attributs doivent distinguer deux systèmes de classement selon que l’on s’intéresse aux valeurs (low, high, pred, succ) ou à l’ordre dans lequel le type est défini (left, right, leftof, rightof).

Les attributs pos et val permettent d’effectuer des conversions entre types énumérés et entiers :

type color_enum_t is (BLACK, RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE);

signal c1, c2 : color_enum_t;
signal n1, n2 : natural range 0 to 7;
...
n1 <= color_enum_t'pos(c1):
c2 <= color_enum_t'val(n2);

Attributs sur les tableaux

Cest attributs s’appliquent aussi bien aux types tableaux qu’aux valeurs de type tableau. Pour les tableaux multidimensionnels, la plupart des attributs acceptent un paramètre n qui indique le numéro de la dimension (1 pour la dimension la plus à gauche). Ce paramètre peut être omis pour les tableaux à une dimension :

type vec3d_t is array(0 to 2) of integer;

-- Les deux écritures suivantes sont équivalentes
constant M : positive := vec3d_t'length;
constant N : positive := vec3d_t'length(1);
Attribut Type du résultat Valeur
A'left(n) integer L’indice le plus à gauche pour la dimension n.
A'right(n) integer L’indice le plus à droite pour la dimension n.
A'low(n) integer L’indice le plus petit pour la dimension n.
A'high(n) integer L’indice le plus grand pour la dimension n.
A'ascending(n) boolean true si l’intervalle des indices pour la dimension n est croissant, false sinon.
A'length(n) integer La taille du tableau pour la dimension n.
A'range(n) integer L’intervalle des indices pour la dimension n.
A'reverse_range(n) integer L’intervalle des indices pour la dimension n, parcouru dans le sens inverse.
A'element Type Le type des éléments du tableau (VHDL-2002 ou versions ultérieures).

Attributs sur les signaux

La plupart des attributs disponibles pour les signaux ne sont utilisables qu’en simulation. Le seul attribut reconnu par les outils de synthèse est :

Attribut Type du résultat Valeur
s'event boolean true si s vient de changer de valeur, false sinon.