Arthur Garreau 8 oct 2015 19:12:21 CEST - GNU Free Documentation License

remarques :

  • Le document peut présenter des erreurs ou inexactitudes.
  • Les éléments mis entre crochets sont optionnels, ils peuvent être omis sans qu’il y ait d’erreur de syntaxe.

Sommaire

Introduction et Définition

Avant de commencer rien de mieux qu’une petite définition wikipédia pour se rappeler ce qu’est le VHDL :

VHDL est un langage de description de matériel destiné à représenter le comportement ainsi que l’architecture d’un système électronique numérique. Son nom complet est VHSIC1 Hardware Description Language.

Le VHDL est standardisé par IEEE (Institue of Electrical and Electronics Engineers).
Le VHDL n’est pas un langage de programmation, son objectif est de décrire le comportement de matériel et de l’implémenter sur des composants programmables qui n’exécutent pas d’instructions.

Le VHDL est composé de :

  • commentaires
  • délimiteurs
  • identificateurs
  • expressions
  • mots-clés
  • littéraux (objets : signaux, ports, variables, constantes) (ex : 67 est un entier, 0 est un bit, “chaine” est une chaine de caractères …)
  • VHDL est insensible à la casse, mais généralement on écrit les mots réservés en MAJUSCULE

Structure du langage

Il existe “5 unités de compilation” permettant de décrire des composants :

  • l’entité : description du composant en terme d’entrées et de sorties, comme une boîte noire
  • l’architecture : décrit l’intérieur du composant (de l’entité)
  • la déclaration de paquetage : un paquetage est une collection d’objets réutilisables (constantes, types, composants, procédures)
  • le corps de paquetage
  • la configuration indiquant quelle architecture utiliser pour chaque entité.

résumé : Chaque composant est réprésenté par une entité (boîte noire), son fonctionnement est décrit dans une architecture. Chaque entité peut avoir plusieurs architectures.
Un ou plusieurs composants peuvent composer un paquetage réutilisable dans d’autres programmes VHDL.

Style de description

Il existe 3 types de description pour décrire un système matériel. Tous les styles peuvent être utilisés dans une même programme VHDL, cependant les styles n’acceptent pas les mêmes instructions :

  • le style comportemental permet de décrire une fonctionnalité complexe de manière algorithmique comme dans un langage de programmation en y ajoutant la dimension temporelle. Les instructions sont “exécutées” séquentiellement. On utilise le concept de proccesus.
  • le style flot de données permet de décrire les chemins de données et les équations logiques sur ces données. Les instructions sont concurrentes (parallèles).
  • le style structurel permet de décrire un système sous la forme d’une interconnexion de composants (paquetage) vus comme des boîtes noires.

Bibliothèques

Les bibliothèques sont déclarées avec le mot clé library, elle dépendent des outils utilisés.

LIBRARY nom_bibliothèque;

Elle contiennent des paquetages que l’on déclare vouloir utiliser avec le mot clé use

use BIBLIOTHEQUE.PAQUETAGE.all;

La bibliothèque par défaut est WORK. WORK est aussi le nom symbolique de la bibliothèque dans laquelle sont stockés les résultats.

La bibliothèque STD est une bibliothèque standard fournie avec le langage, elle contient des définitions des types et des fonctions de base (integer; bit,…).

Par défaut, les outils considère que l’on utilise les bibliothèques STD et WORK. Il y a donc implicitement :

library STD;
library WORK;

(Remarque : library signifie bibliothèque et non librairie : bookstore en anglais).

Les paquetages de la bibliotèques IEEE

En général, on utilise la bibliothèque IEEE qui permet l’utilisation des fonctions logiques et normalisées (notamment std_logic).

En résumé, on pourra être amené à faire quelque chose comme :

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_textio.all;
use IEEE.std_logic_arith.all;
use IEEE.numeric_bit.all;
use IEEE.numeric_std.all;
use IEEE.std_logic_signed.all;
use IEEE.std_logic_unsigned.all;
use IEEE.math_real.all;
use IEEE.math_complex.all;

En réalité, on utilise principalement trois de ces paquetages :

  • std_logic_1164 définit les types, les fonctions de conversion, les opérateurs logiques et les fonctions de recherches de fronts rising_edge() et falling_edge().
  • numeric_bit définit les opérateurs arithmétiques agissant sur des bit_vector interprétés comme des nombres entiers.
  • numeric_std définit les opérateurs arithmétiques agissant sur des std_logic_vector interprétés comme des nombres entiers.

Les commentaires

En vhdl pour écrire des commentaires on utilise deux tirets : --

Les types

Le VHDL est un langage typé où il est obligatoire de spécifier le type des objets/signaux utilisés.

Il existe des types :

  • scalaire
    • entier:
      • integer prédéfini dans le paquetage STD permet de définir des nombres signés sur 32 bits entre -2^-31 et 2^31 - 1. Il existe 2 “sous types” subtype associés à integer :
        • natural
        • positive
        • En général, on spécifie le range pour restreindre le nombre de bits qu’occupe un nombre.
      • Les paquetages numeric_std et numeric_bit définissent deux types correspondant à l’utilisation d’entiers sous forme de tableau de std_logic et bit. Les deux types ont les mêmes noms dans les deux modules, ainsi on ne peut pas utiliser ces deux modules en même temps dans un même programme
        • UNSIGNED : nombre entier non signé (positif)
        • SIGNED : nombre entier signé
    • réel : REAL permet de représenter des nombres entre -1.0E+38 à 1.0E+38. Il n’est pas synthétisable (c-à-d qu’il ne peut pas être représenté en matériel).
    • physique : le VHDL permet de définir des types physiques pour représenter une grandeur physque, comme le temps, la tension… Un type physique est la combinaison d’un type entier et d’un système d’unité.
      • TIME, est le seul type physique prédéfini (cf. remarque 3)
    • énuméré : Un type énuméré est un type défini par une énumération exhaustive. Un signal de ce type prendra une certaine valeur de ce type et rien d’autre.
      • syntaxe : TYPE nom_enum IS (valeur1, valeur2, ...);
      • dans le paquetage STANDARD de STD on trouve :
        • type boolean is (FALSE, TRUE); pour les booléen
        • type bit is ('0', '1'); – noter que ‘0’ et ‘1’ sont des caractères entre apostrophes.
        • type severity_level is (NOTE, WARNING, ERROR, FAILURE);
        • type character is ( NUL, SOH, STX, ETX,..., '0','1', ...); pour les caractères.
      • dans le paquetage STD_LOGIC_1164 de IEEE
        • type std_ulogic is ('U', 'X, 'O', '1', 'Z', 'W', 'L', 'H', '-'); avec : * Au démarrage les signaux sont dans un état inconnu ‘U’. * ‘X’ indique un conflit, le signal est affecté d’un côté à ‘1’ et d’un autre à ‘0’. * ‘0’ et ‘1’ correspondant aux valeurs booleennes du signal. * ‘Z’ correspond à l’état haute ‘impédance”. * ‘W’ est la valeur d’un signal relié à 2 résistances de tirage, une tirant à 0 et l’autre à 1. * ‘H’ et ‘L’ sont des valeurs d’un signal relié respectivement à une résistance de tirage à 1 et à 0. * ‘-‘ est un état indifférent. Utile pour décrire les tables de vérité. * std_logic est un sous-type de std_ulogic et est plus(+) utilisé. Il a un attribut en plus : il est résolu s’il y a plusieurs pilotes/sources (cf. remarque 4). * L’ordre de déclaration est important. Lors de l’initialisation d’un signal de type enuméré std_ulogic, le signal prend la valeur std_ulogic'LEFT (cf. attributs), c’est-à-dire ‘U’.
  • composite
    • tableau (ou array) est une collection d’objets de même type, indexés par des entiers ou des énumérés. On trouve :
      • dans la bibliothèque STD
        • STRING permet de définir les chaînes de caractères. Il est définit comme un tableau d’éléments de type CHARACTER.
        • bit_vector : tableau de bits
      • dans le paquetage STD_LOGIC_1164 de IEEE on trouve :
        • std_logic_vector permet de déclarer des groupements de signaux de type std_logic.
        • std_ulogic_vector : même chose que std_logic_vector mais avec des signaux de type std_ulogic
    • enregistrement permet de définir des collections de valeurs de types différentes. On utilise le mot clé RECORD. Les enregistrements sont très utiles pour les entités dont les ports peuvent être amenés à changer en nombre et en type. Les ports sont alors de type RECORD et ne changent pas. En cas de modification il suffit de modifier le contenu du type RECORD plutôt que de modifier les ports dans les entités
  • fichier : FILE permet l’échange de données entre l’extérieur et le simulateur VHDL. Il est utilisé principalement pour créer des fichiers de test ou TESTBENCH
    • le paquetage TEXTIO de la bibliothèque STD définit un type fichier texte TEXT et des procédures pour accéder aux lignes du fichier et aux chaînes dans la ligne. Pour l’utiliser il est nécessaire de le déclarer en début de fichier : use STD.TEXTIO.ALL;
  • pointeur : Les pointeurs sont peu utilisés en VHDL car on préfère les tableaux indicés qui peuvent être synthétisables, à la différence des pointeurs.
    • ACCESS

FIN TYPES

Remarques

remarque 1

Un sous type subtype permet de déclarer un type héritant des propriétés du type père.
Exemples, déclaration de natural et positive :

subtype natural is integer range 0 to integer'high;
subtype positive is integer range 1 to integer'high;

Notez que

  • range permet d’indiquer l’intervalle
  • 'HIGH indique la plus grande valeur du type INTEGER, c’est un attribut de type

remarque 2

Les types prédéfinis reconnaissent six objets scalaires :

  • Bit : représente un élément binaire avec les valeurs ‘0’ et ‘1’ ;
  • Bit_vector : représente un tableau d’éléments binaires ;
  • Boolean : représente une donnée pouvant être TRUE ou FALSE ;
  • Real : représente un nombre réel ;
  • Integer : est un entier de 32 éléments binaires ;
  • Character : représente un charactère ASCII.

remarque 3

Syntaxe

Définition du type TIME:

type time is range $- to $+  -- l'intervalle dépend de la machine
units
	fs;
	ps = 1000 fs;
	ns = 1000 ps;
	us = 1000 ns;
	Ms = 1000 us;
	sec = 1000 ms;
	min = 60 sec;
	hr = 60 min;
end units;

remarque 4

Un signal multisources doit être associé à une fonction de résolution. C’est un mécanisme de résolution qu’offre VHDL et qui permet de résoudre les conflits susceptibles de survenir et de calculer la valeur résultante de ce conflit (on dit la valeur résolue du signal).

Un signal possédant une fonction de résolution est appelé signal résolu. Il peut donc être la cible de plusieurs affectations différentes.

remarque 5 : exemple lecture et écriture dans un fichier

LECTURE: process
	variable L: line;	-- le type LINE est un pointeur
	file ENTREES: text open READ_MODE is "entrees.dat";	-- fichier spécifié
	variable A: bit_vector(7 downto 0);	 -- variables à lire
	variable B: natural range 0 to 11;
begin
	readline(ENTREES, L);	-- lecture d'une nouvelle ligne dans le fichier
	read(L, A);	-- lecture dans la ligne du 1er symbole => BIT
	VA <= A;	-- utilisation pour la simulation
	read(L, B);	-- lecture dans la ligne du 2ème symbole => entier
	VB <= B;	-- utilisation pour la simulation
	wait for 20 ns;	-- attente de 20 ns;
	end process LECTURE;
--
ECRITURE: process(S)
	variable L: line;
	file SORTIES: text open WRITE_MODE is "sorties.dat";
begin
	write(L, S);	-- écriture de S dans la ligne
	write(L, string'(" à t = "));		-- écriture de texte dans la ligne
	write(L, now);	-- écriture du temps de simulation dans la ligne
	writeline(SORTIES, L); -- écriture de la ligne dans le fichier
end process ECRITURE;

FIN REMARQUES

Les attributs

Les attributs sont des caractéristique (variables ou fonctions) de types ou d’objet qu’il est possible d’utiliser dans le modèle.

Il est possible de créer ces propres attributs. Certains outils de synthèse en tirent profit pour passer des arguments de synthèse.

Syntaxe

nom_objet'nom_attribut

Syntaxe des attributs pour le type ARRAY ou scalaire

  • X'HIGH élément le plus grand ou borne maximale
  • X'LOW élément le plus petit ou borne minimale
  • X'LEFT élément de gauche ou borne de gauche
  • X'RIGHTélément de droite ou borne de droite

Syntaxe des attributs pour les objets déclarés dans un ARRAY ou un SUBTYPE

  • x'RANGE élément range de x
  • x'REVERSE_RANGE élément range de x dans l’ordre inverse
  • x'LENGTH x'HIGH - x’LOW + 1 (integer)

Syntaxe des attributs pour SIGNAL

  • x'EVENT retourne TRUE si x change d’état
  • x'ACTIVE retourne TRUE si x a changé durant le dernier interval
  • x'LAST_EVENT retourne une valeur temporelle depuis le dernier changement de x
  • x'LAST_ACTIVE retourne une valeur temporelle depuis la dernière transition de x
  • x'LAST_VALUE retourne la dernière valeur de x

Ces attributs créent un nouveau SIGNAL

  • x'DELAYED(t) crée un signal du type de x retardé par t
  • x'STABLE(t) retourne TRUE si x n’est pas modifié pendant le temps t
  • x'QUIET(t) crée un signal logique à TRUE si x n’est pas modifié pendant un temps t
  • x'TRANSACTION crée un signal logique qui bascule lorsque x est change d’état

Créer ses propres types, Syntaxe des types, Déclaration de signaux

Maintenant que l’on a une vue d’ensemble sur les (une partie des) types existants. Intéressons nous un peu plus à leur syntaxe de déclaration, pour créer nos propres types et savoir déclarer des objets (signaux,ports…).

On utilise le mot clé TYPE pour définir un type utilisateur auquel on peut assigner un objet. Tous les objets doivent être assignés à un type. Chaque TYPE déclaré doit être unique.

On utilise le mot clé SUBTYPE pour déclarer des sous-types héritant des attributs du type.

Le TYPE énuméré

Syntaxe

TYPE identifier IS enumeration_type_literals;

Exemple

TYPE op_type IS (opadd, opor, opand, opxor);
TYPE letters IS ('A', 'a', 'R', 'r');
TYPE std_ulogic IS ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-');

Le TYPE numérique

Cette définition de type doit être dans une échelle de valeur. La base par défaut est la décimale.

Syntaxe

TYPE identifier IS RANGE implementation_defined;

Exemples

TYPE byte IS RANGE 0 to 255; -- byte varie de 0 à 255
TYPE index IS RANGE 7 DOWNTO 0; -- index varie de 7 à 0
SUBTYPE byte IS INTEGER RANGE 0 TO 255; -- byte est un entier de 0 à 255 sous-type de INTEGER

Le TYPE physique

Cette définition de type représente une quantité physique. La base des unités doit être spécifiée. Les valeurs sont calculées à partir de cette base.

Syntaxe

TYPE identifier IS RANGE implementation_defined;
    UNITS
        declaration_unite_de_base
        [declaration_unites_secondaires]
    END UNITS;

Exemple

TYPE time IS RANGE implementation_defined;
    UNITS
        fs;
        ps = 1000fs;
        ns = 1000fs;
        us = 1000ns;
        ms = 1000us;
        sec = 1000ms;
        min = 60sec;
        hr = 60min;
    END UNITS;

Le TYPE tableau

Le type tableau est un groupement de types identiques. Les tableaux peuvent être multidimentionels. Il existe deux sortes de tableaux :

  • Les tableaux avec contrainte (Constrained Array) : Ces tableaux sont définis avec des dimensions figées (rangées colonnes).
  • Les tableaux sans contrainte (Unconstrained Array) : Ces tableaux sont définis sans préciser les dimensions. Le tableau est donc dynamique. Ces tableaux doivent définir le type de leur index dans leur déclaration. La constrainte de taille est exprimée dans la déclaration de l’objet.

Syntaxe

--vhdl
TYPE nom_tableau IS ARRAY (nombre_elements) OF type_des_elements_du_tableau;

Ici nombre_element représente la taille du tableau dont l’expression dépend du type de tableau :

  • Un tableau peut avoir une taille inconnue, donc non constraint, on utilise l’expression type_index RANGE <>
  • Un tableau peut avoir une taille constrainte, on utilise :
    • MSB downto LSB - bit de poids faible à la case 0, bit de poids fort en tête
    • LSB to MSB - bit de poids fort à la case 0, bit de poids faible en tête
    • MSB = bit de poids fort, LSB = bit de poids faible

Exemples

type bit_vector is array (natural range <>) of bit; -- déclaration d'un tableau de bis de taille inconnue et d'index de type natural

TYPE data_bus IS ARRAY (0 TO 31) OF bit; -- tableau de 32 bit
TYPE bit4 IS ARRAY (3 DOWNTO 0) OF bit; -- tableau de 4 bit : bit4(3) à bit4(0)

TYPE dim2 IS ARRAY (0 TO 7, 0 TO 7) OF bit; -- tableau à deux dimensions

type COULEURS is (BLANC, BLEU, VERT, ROUGE, JAUNE, NOIR, ARC_EN_CIEL);
type PRIX_PEINTURES is array (COULEUR range BLANC to NOIR) of PRIX;

Utilisation

SIGNAL entree : std_logic_vector(N-1 downto 0)
SIGNAL sortie : std_logic_vector(0 to N-1)
  • entree et sortie sont des bus de N signaux de type std_logic accessible globalement ou de manière individuelle.
  • entree(2) permet d’accéder au troisième signal en partant du “signal de poids faible”.
  • sortie(0) permet d’accéder au premier signal en partant de la gauche (bit de poid fort)

le TYPE enregistrement

Les enregistrements permettent de définir des collections d’objets de types différents

Syntaxe

--vhdl
TYPE nom_enregistrement IS RECORD
	obj1 : type1;
	obj1 : type2;
END RECORD;

Exemple

TYPE date IS RECORD
	jour  : natural RANGE 1 TO 31;
	mois  : string;
	annee : integer RANGE 0 TO 4000;
END RECORD;

Fonctions de conversions de types

Il arrive souvent de devoir faire des opérations entre des objets de types différents, or celles-ci (les opérations) requierent que les types soient les mêmes, c’est pourquoi des fonctions de conversions sont définies.

Des fonctions de conversions permettent de passer du type binaire aux types IEEE et réciproquement, ou d’un type IEEE à l’autre :

function To_bit ( s : std_ulogic; xmap : bit := '0') return bit;

function To_bitvector ( s : std_logic_vector ; xmap : bit := '0') return bit_vector;

function To_bitvector ( s : std_ulogic_vector; xmap : bit := '0') return bit_vector;

function To_StdULogic ( b : bit ) return std_ulogic;

function To_StdLogicVector ( b : bit_vector ) return std_logic_vector;

function To_StdLogicVector ( s : std_ulogic_vector ) return std_logic_vector;

function To_StdULogicVector ( b : bit_vector ) return std_ulogic_vector;

Par défaut les fonctions comme To_bit remplacent, au moyen du paramètre xmap, toutes les valeurs autres que ‘1’ et ‘H’ par ‘0’.

Conversion std_numeric vers std : numeric_std_conversion

Les signaux

Les signaux représentent des signaux logiques ou des fils dans un circuit. Les signaux peuvent aussi représenter l’état d’une mémoire. Un signal est un objet qui permet de transporter l’information. On utilise le mot clé signal. On peut les déclarer dans les : entités, architectures et paquetages. Ils peuvent être utilisés en lecture et/ou en écriture.

Syntaxe

SIGNAL sig_name {, sig_name} : signal_type [:=initial_value];

Avec signal_type, un des types vu précedemment.

On assigne une valeur à un signal en suivant ce format :

sig_name <= expression;

Exemple

SIGNAL entree : std_logic;
entree <= '1';

Les ports

Un port est un canal de communication dynamique entre un bloc et son environnement. En pratique les ports sont la plupart du temps utilisés dans les entités et composants pour déclarer des signaux d’interface.

Les signaux “externes” d’un composant sont appelés ports.

Syntaxe :

PORT port_name {, port_name} : mode type [:= default_value];

Il existe 5 modes :

  • in : le port est accessible uniquement en lecture (correspond à une entrée)
  • out : on peut assigner un valeur (écriture), le port ne peut pas être lu
  • inout : lecture et écriture sont autorisées
  • buffer : sortie avec possibilité de lecture
  • linkage : liaison vers une entité non exprimé en termes de sémantique VHDL

Le mode par défaut, s’il n’est pas spécifié est in.

exemple :

PORT entree1, entree2 : in std_logic;

Les entités

Une entité est une description d’un composant en terme d’entrées et de sorties, comme une boîte noire. L’entité ne s’intéresse pas au fonctionnement interne du composant, c’est le rôle de l’architecture.

Pour déclarer une entité, on utilise les mots clés ENTITY ... IS ... END;.

ENTITY nom_entite IS
	[clause de port]
END nom_entite;

Les signaux externes et visibles (entrées, sorties) d’une entité sont décrits avec la fonction port :

port(port_interface_list);
port(signal_name {, signal_name} : mode type [:= valeur par défaut])

exemple

ENTITY nom_entite IS
	port(
		entree1, entree2 : in std_logic;
		sortie : out std_logic;
	);
END nom_entite; -- nom_entite est facultatif ici

les Architectures

L’architecture décrit le fonctionnement d’un circuit.

Elle est toujours associée à une entité. Une entité peut avoir plusieurs architectures.

Les architectures peuvent être de type combinatoires, séquentielles ou regroupant les 2 types de fonctionnement.

Syntaxe

--vhdl
ARCHITECTURE nom_arch OF nom_entite IS
	[subprogram_declaration] -- zone de declaration
	[subprogram_body]
	[type_declarations]
	[constant_declarations]
	[signal_declarations]
BEGIN
	--instructions, process, concurrents
END [nom_arch];

Exemple

--vhdl
ENTITY inverseur IS
	port(
		e : in std_logic;
		s : out std_logic;
		);
END inverseur;

ARCHITECTURE arch OF inverseur IS
	-- dans ce cas pas besoin de déclarer des signaux, types ...
BEGIN
	s <= NOT(e);
END arch;

Les opérateurs

Affectation simple

opérateur : <=

Exemple

--vhdl
C <= '1';

Concaténation

opérateur : &

Exemple

--vhdl
SIGNAL A, B : std_logic_vector(1 downto 0);
SIGNAL S : std_logic_vector(3 downto 0);
-- ...
S <= A & B;

Opérateurs logiques

  • ET AND
  • NON ET NAND
  • OU OR
  • NON OU NOR
  • OU EXCLUSIF XOR
  • NON OU EXCLUSIF XNOR
  • NON NOT
  • Décalage à gauche SLL
  • Décalage à droite SRL
  • Rotation à gauche ROL
  • Rotation à droite ROR

Opérateurs arithmétiques

  • Addition +
  • Soustraction -
  • Multiplication *
  • Division / (opération qui n’est pas toujours supportée)

Opérateurs relationnels

  • Egal =
  • Non Egal /=
  • Inférieur <
  • Inférieur ou Egal <=
  • Supérieur >
  • Supérieur ou Egal >=

Description flot de données, instructions concurentes

Une description flot de données spécifie comment la donnée est transférée de signal à signal sans utiliser d’affectations séquentielles, elle utilise des instructions concurrentes.

Ce type de description est plus naturel au sens où l’on manipule des signaux et indiquons les chemins à suivre.

Instructions concurrentes

Les instructions sont concurrentes (parallèles), c’est-à-dire qu’elles sont évaluées en même temps ; l’ordre d’écriture n’a donc aucune importance.

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 :

  • Les affectations concurrentes de signaux
  • Les instanciations de composants pour la description structurale
  • Les processus qui offrent la possibilités d’utiliser des instructions séquentielles

Affectation conditionnelle (instru concurrentes)

L’affectation conditionnelle permet d’affecter une valeur à un signal en fonction d’une condition.

Syntaxe

--vhdl
nom_signal <= expression WHEN condition
[ELSE expression2 WHEN condition2]
[ELSE expression];

Exemple

S <= "01" WHEN (A=B)
ELSE "10" WHEN (A=C)
ELSE "00";

Affectation sélective

L’affectation sélective permet d’affecter à un signal une valeur selon la valeur d’un autre signal dit signal de sélection.

Syntaxe

WITH signal_de_selection SELECT
	nom_signal <= expression WHEN val_de_selection,
				 [expression2 WHEN val_de_selection2,]
				 [expression3 WHEN OTHERS];
--end with

Exemple

WITH E SELECT
	S <= "11" WHEN "00", -- S prend la valeur 11 quand E vaut 00
		"00" WHEN "01",
		"10" WHEN OTHERS;
--end with

Boucle (concurrentes)

Une instruction generate permet de dupliquer un bloc d’instructions concurrentes un certain nombre de fois, ou de créer un tel bloc si une condition est vérifiée.

Ne Pas Confondre avec FOR LOOP - séquentiel

Syntaxe

Label:FOR indice IN val_min TO val_max GENERATE
	--instructions
END GENERATE;

Exemple

--Q : out std_logic_vector(2 downto 0);
--D : IN std_logic_vector(2 downto 0);
--H : IN std_logic
ET:FOR i IN 0 TO 2 GENERATE
	Q(i) <= D(i) and H;
END GENERATE;

Description comportementale, instructions séquentielles

Pour décrire le comportement d’un composant, le vhdl permet de le faire est des instructions séquentielles (qui “s’exécutent” les unes après les autres). On appelle cela la description comportementale.

Les variables

Les variables sont des objets qui servent à stocker un résultat intermédiaire pour faciliter la construction d’un algorithme séquentiel.

Les variables sont utilisées dans un PROCESS, une PROCEDURE ou une FONCTION et sont affectées immédiatement.

On peut leur donner une valeur initiale. Cette valeur initiale est attribuée à chaque appel de la PROCEDURE ou de la FONCTION. Elles servent à manipuler des variables temporaires/intermédiaires pour faciliter le développement d’un algorithme séquentiel.

Syntaxe de déclaration

--vhdl
variable nom1, nom2 : type_variable [:=expression];
nom:= valeur_compatible_avec_le_type ;

Exemple

--vhdl
variable nom : integer;

Les constantes

Les constantes sont similaires aux variables à la différence que leur valeur ne peut pas changer.

Syntaxe

--vhdl
constant nom1 : type [ := valeur_constante ];

Exemple

constant nom : integer;

Process

Un PROCESS est une déclaration concurrente dans une architecture, dont le but est de contenir des instructions séquentielles.

Les PROCESS contiennet seulement des déclarations séquentielles qui sont évaluées dans l’ordre spécifié. On ne peut pas utiliser d’instructions concurrentes. On ne peut pas déclarer de signaux.

Le fonctionnement d’un PROCESS est régi par les règles suivantes :

  • Un processus est une boucle infine, lorsqu’il arrive à la fin du code, il recommence automatiquement
  • Un processus doit être sensible à des points d’arrêt, il en existe 2 types :
    • Le processus est sensible à une liste de sensibilité, c-à-d à une liste de signaux, qui réveille le processus lorsque la valeur d’un d’eux change
    • Le processus a des intructions d’arrêt WAIT dans sa description interne

Toutes les instructions séquentielles doivent être à l’intérieur d’un PROCESS

Syntaxe

[label]: PROCESS [(sensitive_signal_name {, sensitive_signal_name})]
    [constant_declarations]
    [variable_declarations]
BEGIN
    [sequential_statements]
END PROCESS [label];

Exemple

LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY nor2 IS
    PORT (a,b : IN std_logic:='0';
        qn : OUT std_logic);
END nor2;

ARCHITECTURE proc_behv OF nor2 IS
BEGIN
    ex1: PROCESS(a,b) -- ce PROCESS est décrit avec une liste de sensibilité, quand a ou b change, process est exécuté
    BEGIN
        qn <= a NOR b;
    END PROCESS ex1;

    ex2: PROCESS -- ce PROCESS n'a pas de liste de sensibilité
    BEGIN
        qn <= a NOR b;
        WAIT ON a,b; -- WAIT permet d'attendre un événement sur a ou b
    END PROCESS ex2;
END proc_behv;

Retour sur les signaux

Contrairement aux variables, l’affectation du signal n’a pas un effet immédiat. (cf processus) Ainsi, dans un process, après cinq instructions A<=A+1;, le signal A n’est pas augmenté de 5 mais seulement de 1.

Affectation conditionnelle (séquentielle)

Dans un programme séquentiel (dans un PROCESS), on peut utiliser les structures algorithmiques telles que les conditions :

Syntaxe

IF condition THEN
	--instructions
ELSIF condition2 THEN
	--instructions
ELSE
	--instructions
END IF;

Exemple

PROCESS(H)
BEGIN
	IF(rising_edge(H)) THEN
		Q<=D;
	END IF;
END PROCESS;

Affectation sélective (séquentielle)

(dans un PROCESS)

Syntaxe

CASE expression IS
	WHEN etat1 => instru;
	WHEN etat2 => instru;
	-- ...
	WHEN OTHERS => instru;
END CASE;

Exemple

CASE state IS -- state est la variable du choix
    WHEN "00" => state <= st1;
    WHEN "01" => state <= st2;
    WHEN "10" => state <= st3;
    WHEN "11" => state <= st0;
    WHEN OTHERS => NULL;
END CASE;

Boucle (séquentielle)

Une boucle permet de répéter des instructions séquentielles (dans un PROCESS).

Syntaxe

FOR indice IN val_min TO val_max LOOP
	--instu
END LOOP;

Exemple

PROCESS(H)
BEGIN
	IF(rising_edge(H)) THEN
		FOR I IN 0 TO 6 LOOP
			x(I+1) <= X(I);
		END LOOP;
	END IF;
END PROCESS;

WAIT

WAIT est utilisé dans une déclaration séquentielle de type PROCESS ou PROCEDURE. WAIT remplace une liste de sensibilité pour contrôler l’exécution et la suspension d’un PROCESS. WAIT peut être placé n’importe où dans le PROCESS. Sans WAIT ou sans liste de sensibilité, un PROCESS se boucle indéfiniment.

Il existe trois syntaxes différentes autour de WAIT :

Wait ON

WAIT ON est équivalent à une liste de sensibilité. La liste des signaux suit. Ainsi :

PROCESS (A,B)
BEGIN
	--insru
END PROCESS;

est équivalent à :

PROCESS
BEGIN
	--insru
	WAIT ON A,B;
END PROCESS;

WAIT UNTIL

La condition testée par WAIT UNTIL d’expression booléenne doit parvenir à TRUE pour continuer l’exécution.

PROCESS
BEGIN
    WAIT UNTIL condition;
    --instruc
end PROCESS;

WAIT FOR

Le temps imparti par WAIT FOR spécifie la durée maximale pendant laquelle le PROCESS reste suspendu.

STIMULUS: process
BEGIN
        EN_1 <= '0';
        EN_2 <= '1';
    WAIT FOR 10 ns; -- attend 10ns avant de passer à la ligne suivante
        EN_1 <= '1';
        EN_2 <= '0';
    WAIT FOR 10 ns;
        EN_1 <= '0';
    WAIT; -- attent indéfiniment
END PROCESS STIMULUS;

Gestion du délai

Une affectation de signal avec la commande after modélise un comportement temporel. Il s’agit d’un modèle destiné à la simulation

Exemple

S <= ‘1’, ‘0’ after 5 ns, ‘1’ after 10 ns, ‘0’ after 15 ns;

Les expressions doivent être ordonnées selon un ordre croissant des valeurs des délais.

La valeur du délai de chaque expression est relative au moment de l’évaluation globale de l’affectation et non pas du délai précédent.

Toutes les expressions sont calculées au même moment et les valeurs résultats sont mémorisées dans le pilote du signal.

Fonction

FUNCTION : exécute une suite d’instructions séquentielles sur les paramètres d’entree (constrante ou signaux) et retourne une valeur et une seule.

Syntaxe

FUNCTION nom_fonction [ ( liste de paramètres formels ) ] RETURN nom_du_type IS
	[ déclarations ]
BEGIN
	--instructions séquentielles
END [ nom_fonction ] ;

Exemple

FUNCTION PARITY (X : std_ulogic_vector) RETURN std_ulogic IS
	variable TMP : std_ulogic := '0';
BEGIN
	for J in X'range loop
		TMP := TMP xor X(J);
	end loop; -- works for any size X
	return TMP;
END PARITY;

PROCEDURE

PROCEDURE : idem fonction mais peut retourner plusieurs paramètres de sorties définis dans sa déclaration

Syntaxe

PROCEDURE nom_procedure [ ( liste de paramètres formels ) ] IS [ déclarations ]
BEGIN
	--instructions séquentielles
END [ nom_procedure ] ;

Exemple

PROCEDURE MAX2 (N1,N2:IN INTEGER; N3 : OUT INTEGER) IS
	BEGIN
		IF N1>N2 THEN N3 <= N1;
				 ELSE N3 <= N2;
		END IF;
	END MAX2;

Résumé, instructions concurrentes, séquentielles & types de description

Il y a deux types d’instructions :

  • concurrentes : elles sont évaluées en même temps, elles manipulent des signaux (uniquement). Les principales sont :
    • Les affectations
    • Les processus
    • Les instanciations de composants
    • les fonctions
  • Séquentielles : elles sont “exécutées” l’une après l’autre, elles sont obligatoirement placées dans une instruction concurrente : processus, fonctions, procédures. Elles manipulent des variables, des constantes et des signaux, les signaux ne sont pas affectés immédiatement.

Description structurelle

Méthode de description par assemblage de composants élémentaires. La description structurelle d’un circuit complexe en vhdl présente de nombreux avantages :

  • Une architecture hiérarchique compréhensible : il est plus simple de séparer un circuit en un ensemble de blocs plus petits, ayant des fonctions bien identifiées. Ces blocs pourront alors être décrits sous forme comportementale, ou bien à leur tour être séparés en blocs encore plus simples.
  • Une synthèse logique efficace : la synthèse est un processus lent (en terme de temps de calcul). Plus un bloc est gros et complexe, plus sa synthèse prendra du temps. Il vaut donc mieux travailler sur des blocs plus petits, plus simples à synthétiser, et rassembler le tout à la fin

Principe

  • On définit les composants dans des fichiers séparés (contenant entité, architecture(s)).
  • Un composant a une entité, et une/des architecture(s).
  • Les composants doivent être définis dans un package et compilés dans une bibliothèque.
  • Les bibliothèques sont attachées par une déclaration dans le programme dans lequel on veut les utiliser.
  • On déclare le composant avec COMPONENT
  • On instancie un composant avec port map

Composant (componet)

VHDL permet l’assemblage de “composants” ce qui constitue une description structurelle. Ce composant peut être appelé plusieurs fois dans un même circuit. Pour différencier ces mêmes composants, il est nécessaire de leur donner un nom d’“instance”. L’appel d’un composant se dit aussi “instanciation”.

Pour instancier un composant il est nécessaire de connaître :

  • le prototype du composant (ports d’E/S). On utilise la directive COMPONENT pour ça.
  • A quelle entité et architecture est lié chaque instance de composant. Ce lien peut être connu grâce à l’unité de configuration. La configuration est une unité de compilation optionnelle

Remarque

La déclaration du composant (directive component) est redondante textuellement avec celle de l’entité associée mais permet :

  • Une compilation indépendante entre l’entité associée au composant et le circuit utilisant le composant.
  • La conception descendante. Le composant peut être déclaré avant l’entité associée.

Déclaration

Le mot clé component sert à déclarer le prototype d’interconnexion. La syntaxe est presque identique à celle de l’entité :

COMPONENT nom_composant
	port(liste_ports);
END COMPONENT;

Instanciation

L’instanciation d’un composant se fait dans le corps de l’architecture de cette façon :

nom_instance:nom_composant PORT MAP(liste de connexions);

Exemple

--fichier composant and2
ENTITY and2 IS
	port(
			a : bit;
			b : bit;
			s : bit;
		);
END ENTITY;

ARCHITECTURE arch of and2 IS
BEGIN
	s <= a AND b;
END arch;

---------------------------
--fichier principal
ENTITY and_3 IS
	port(
		e1 : in bit;
		e2 : in bit;
		e3 : in bit;
		 s : out bit;
	);
END ENTITY;

ARCHITECTURE arc OF and_3 IS
	SIGNAL z : bit;
	COMPONENT and2
		port(
			a : bit;
			b : bit;
			s : bit;
		);
	END COMPONENT;

	BEGIN
		inst1 : and2 port map(a=>e1, b=>e2, s=>z);
		inst2 : and2 port map(z, e3, s);
	END arc;

Dans cet exemple , deux instances de composant “and2” sont appelées pour créer une porte ET à 3 entrées.

L’association des ports du composants aux signaux de l’instance se fait à l’aide de la clause `port map.

La syntaxe des associations est soit

  1. par nom où chaque broche du composant est associée à un signal : cas de inst_1
  2. positionnelle où l’ordre des signaux correspond à l’ordre des broches : cas de inst_2

Genericité

Déclaration

Un composant peut être générique en définissant les paramètres qui seront vus comme des constantes à chaque instance de composant. Il est ainsi possible de n’avoir qu’un seul composant pour différentes instances ayant des paramètres différents. Dans la déclaration du composant, la clause generic sert à passer les paramètres au composant. Dans l’exemple suivant, l’entier positif N indique le nombre de bits de l’additionneur.

[à compléter]

Configuration

à faire En attendant http://hdl.telecom-paristech.fr/vhdl_structurel.html#header

sources (notamment)