TD2 VHDL et logique programmable
L'objectif de ce chapitre est d'introduire rapidement les notions matérielles pour les circuits simples programmables ou PLA. Les documentations de ces circuits peuvent encore être appréhendées par des schémas logiques simples, mais ce ne sera plus le cas si l'on désire s'intéresser aux Complex Programmable Logic Devices ou aux FPGA. Nous aborderons aussi quelques notions liées aux FPGA en fin de chapitre. En ce qui nous concerne, seules ces notions sont enseignées maintenant.
= Généralités =
Nous commençons par présenter les conventions de la représentation des circuits programmables : regardez l'image ci-dessous avec à gauche la convention schématique habituelle et à droite la nouvelle convention.
On voit sur cette image que les entrées des portes ET sont, par convention, ramenées sur un fil. Pour connaître le nombre d'entrées des portes ET ainsi dessinées, il vous faut compter le nombre d'intersections avec des fils perpendiculaires et de diviser par deux. Par exemple sur l'image, le fil d'entrée (vertical) de la porte ET de gauche rencontre 6 fils (horizontaux), ce qui signifie que la porte ET comporte trois entrées (ce qui correspond bien au schéma de gauche). Une division par deux est réalisée car en général il arrive la variable et son complément.
Nous avons utilisé ici la même convention pour le OU logique qui suit le ET. Mais, en général, pour les circuits simples, on dessine toutes les entrées des OU comme ce sera fait dans les schémas internes décrits plus loin dans ce chapitre. 
Les croix indiquent les liaisons sur des fils qui se croisent. Cette convention n'est utile qu'avec les circuits programmables simples (PLD, PAL et PLA) 
Remarque : un réseau PLA ne contient que rarement tous les "minterms" (termes produits) contrairement à une mémoire. Dans l'exemple de la figure ci-dessus, le fait que le OU ne contient que 4 entrées veut dire que seulement quatre termes produits sont réalisables simultanément. Pourtant, avec trois entrées, il existe huit termes produits.
Correspondance entre les variables logiques et les broches physiques.
Lors de la programmation de composants, il nous faut établir une correspondance entre les variables logiques déclarées dans l'entité et les broches physiques d'un composant. Il existe plusieurs techniques pour cela :
ATTRIBUTE pin_numbers of mydesign:ENTITY IS
 "x:1 y:2 clk:3 a(0):4 ";
avant de fermer la déclaration d'entité (qui s'appelle dans ce cas "mydesign"). Cette façon de faire est très peu portable.
Nomenclature des circuits programmables.
Les circuits programmables ont des noms dans lesquels au moins deux chiffres apparaissent séparés par une ou plusieurs lettres.
Remarque : sachant que la représentation des entrées des portes ET se fait toujours par un seul fil horizontal, le premier chiffre multiplié par deux indiquera toujours le nombre de fil vertical rencontrés par ce fil horizontal.
Appliquons tout ceci à la PAL 16R4 présentée ci-dessous :
Son nom indique que l'on 4 sorties séquentielles, ce qui n'est pas difficile à remarquer. Ces sorties sont complétées par 4 sorties combinatoires.
Les 64 termes produits indiqués sur la figure sont répartis à égalité sur les 8 sorties, ce qui indique que les OU ont tous 8 entrées.
Exercice 1.
Remarque : La PAL étant une 16R8, le schéma ci-dessus est incomplet : il manque 7 sorties et aussi 28 fils verticaux (2 x 16 moins les 4 que l'on a).
Indication : transformez votre schéma en équation de récurrence. Ce qui se trouve avant Qout dans le schéma original est une commande trois états sans inversion. Quand vous essayez d'implanter votre équation partez de la sortie physique de votre PAL.
. 
Notre équation de récurrence est donc transformée en : 
formula_1 car formula_2
Ce qui donne le schéma suivant :
Pour changer de champ d'application voici un exercice concernant l'arithmétique élémentaire.
Exercice 2 (PAL 16X4 spécialisé pour arithmétique).
On vous présente le contenu partiel d'une PAL 16X4 destinée à réaliser des opérations arithmétiques.
Remarque : La PAL étant une 16X4, le schéma ci-dessus est incomplet : il manque 6 sorties et aussi 24 fils verticaux (2 x 16 moins les 8 que l'on a).
1°) La cellule de base d'un PAL destiné à l'arithmétique est donnée ci-dessus.
Repérer et exprimer les valeurs logiques des fils internes 8, 9, 10 et 11 en fonction de A1 et B1.
A quoi sert I ?
2°) On cherche à utiliser ce genre de cellule pour réaliser une addition 1 bit : 
formula_3. On rappelle les équations de C1 et A1+
C1 = (A1 xor B1).C0 + A1.B1
A1+ = A1 xor B1 xor C0
En déduire les équations de S1=formula_4 (sous forme de ou exclusifs et/ou fonctions identités) et de /C1 (sous forme disjonctive).
3°) Calculs intermédiaires : calculer les produits (fonctions ET logique) (8).(9), (9).(11), (9).(10), (8).(11), (8).(9).(11) et (8).(10).(11) et montrer qu'ils interviennent tous dans les équations précédentes de S1 et de /C1.
4°) Dessiner les fusibles non grillés sur la figure ci-dessus.
Nous allons maintenant nous intéresser aux nouvelles catégories de composants programmables, ceux qui utilisent des cellules "versatiles" (configurables) et que l'on appelle parfois GAL.
= La GAL 20V8 =
On présente ci-dessous le schéma partiel du circuit de la GAL 20V8 (24 broches dont 8 sorties configurables).
On peut distinguer 13 entrées véritables notées de I1 à I13. Chaque sortie est notée de I/O0 à I/O7. La notation laisse penser, à juste titre, que si l'on manque d'entrée, on peut transformer des sorties en entrées. Si vous n'avez pas d'horloge (en logique combinatoire par exemple), vous pouvez aussi utiliser la broche CLK/I0 en entrée.
Chaque sortie est constituée par une cellule configurable à l'aide de bits internes. Le schéma de cette cellule configurable est donné ci-dessous :
On peut y distinguer des multiplexeurs, commandés par les bits SG1 et SL0x (où x est le numéro de la sortie correspondante), un ou exclusif commandé par un bit SL1x. L'objectif de ce ou exclusif est de complémenter ou pas la sortie du ou logique qui le précède. 
Un bit global est forcément identique pour les 8 cellules configurables. Un bit local est dépendant de la macrocellule, et c'est pour cela qu'il est terminé par un x.
Pour terminer sur ce composant on donne un schéma interne partiel où l'on reconnaît ces cellules configurables :
Remarque : La PAL étant une 20V8, le schéma ci-dessus est incomplet : il manque 4 sorties et aussi 16 fils verticaux (2 x 20 moins les 24 que l'on a).
= La GAL 22V10 =
La GAL 22V10 est plus récente que la 20V8. On pourrait penser que la cellule configurable de ce composant est plus complexe que celle de la 20V8, puisque plus moderne, mais il n'en est rien. Nous commençons par présenter cette cellule configurable :
On distingue sur ce schéma une simplification sur les entrées du OU logique à l'aide de pointillés. Cela est dû au fait que le nombre de ET logique arrivant sur le OU logique est variable suivant la cellule configurable : les broches 14 et 23 ont en 8, les broches 15 et 22 ont en 10, les broches 21 et 16 ont en 12, les broches 17 et 20 ont en 14 et enfin les broches 18 et 19 ont en 16. Le nombre de multiplexeurs a franchement diminué (en taille comme en nombre) ce qui facilite sa compréhension.
Voici un exemple de composant 22V10 :
Le brochage de ce composant est donné par la figure ci-dessous :
On présente maintenant un schéma interne partiel de ce composant :
Remarque : La PAL étant une 22V10, le schéma ci-dessus est incomplet : il manque 6 sorties et aussi 28 fils verticaux (2 x 22 moins les 16 que l'on a).
Le schéma interne complet est complexe et peut être simplifié comme sur la figure ci-dessous :
Exercice 3.
La polarité de chaque cellule configurable peut être programmée individuellement pour être inversée ou non. D'autre part la sortie peut être combinatoire ou séquentielle.
1°) Lequel des deux bits bits (S0 et S1) détermine la polarité ?
2°) Dessinez les quatre circuits équivalents à la cellule configurable.
= Les CPLD =
Un CPLD peut être défini comme un assemblage de PLDs de type 22V10. Nous en donnons la structure ci-dessous :
Tous les "Logic Blocs" sont à peu près équivalents à des PALs 22V10. Comme on peut le voir, ces PALs sont connectées par le bloc "Programmable Interconnect". Dans cette architecture une implantation ne nécessite pas de placements et routages mais seulement des interconnexions.
= Les FPGA =
Les FPGA sont des composants plus complexes encore que les CPLD. Nous allons examiner quelques unes de leurs particularités à travers des exemples d'implantation.
Les LUTs.
Les LUTs sont des éléments importants des FPGA. Nous allons essayer de les manipuler en VHDL.
Présentation des LUTs.
Une table de vérité de trois entrées peut être représentée par un nombre 8 bits que l'on convertit en hexadécimal. Soit donc la table de vérité suivante (trois entrées notées e0, e1 et e2, une sortie notée s) :
Vous pouvez synthétiser la table de vérité à l'aide d'un seul nombre sur 8 bits (poids faible en haut) :
La valeur hexadécimale 56 (notée X"56" en VHDL) est la valeur avec laquelle il faudra initialiser votre LUT avec un composant lut3.
Pour 4 entrées (ce qui est notre cas), on utilise une lut4 avec 4 chiffres hexadécimaux.
Utiliser des LUTs en VHDL.
Cette section est malheureusement spécifique au fabricant Xilinx.
Un exemple est donné maintenant :
library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 
library unisim; 
use unisim.vcomponents.all;
ENTITY transcodeur IS PORT(
 e : in STD_LOGIC_VECTOR(3 DOWNTO 0); -- 4 entrées
 s : out STD_LOGIC_VECTOR(6 DOWNTO 0)); -- 7 sorties
END transcodeur;
ARCHITECTURE atranscodeur OF transcodeur IS BEGIN
i1 : LUT4 
 generic map (INIT => X"EAAA") 
 port map( I0 => e(0), 
 I1 => e(1), 
 I2 => e(2), 
 I3 => e(3), 
 O => s(0) );
Cet exemple vous montre comment on câble une LUT4 en VHDL (port map) et comment on l'initialise (generic map). Le câblage de ce composant est correct mais pas son initialisation puisqu'on vous demande de la calculer plus loin.
Les deux lignes library ... et use ... sont à ajouter avant toute entité qui utilise une LUT en plus bien sûr de "library ieee;".
Exercice 4.
Réaliser le schéma correspondant au "port map" de l'exemple ci-dessus dans le composant "transcodeur" (deux rectangles, un appelé "transcodeur" et un appelé "LUT4"). 
Compléter ce schéma avec d'éventuels pointillés en montrant que sept LUTs seront nécessaires pour réaliser le transcodeur complet.
Réaliser une table de vérité complète du décodage demandé.
En déduire les 7 valeurs hexadécimales d'initialisation des LUTs.
Réaliser l'architecture complète de "transcodeur".
RAM et FPGA.
La nomination de la mémoire dans un FPGA n'est pas très simple : s'agit-il de RAM ou de ROM ? La documentation parle de RAM mais comme il est possible de l'initialiser lors de son écriture en VHDL, on peut facilement l'utiliser comme une ROM. C'est naturellement ce que l'on fait lorsque l'on implante un processeur dans le FPGA pour lui donner un programme à exécuter (voir ).
Il est possible de réaliser des protocoles pour charger cette mémoire à partir des fichiers HEX (Intel) ou binaire comme ce sera développé dans le chapitre du livre précédemment cité.
La BRAM ou bloc RAM.
Les mémoires sont difficiles à utiliser en VHDL car la façon pour les implémenter dépend du fondeur de FPGA. Ainsi la façon de faire présentée dans cette section n'est vraie que pour les FPGA Xilinx.
Si vous voulez que votre outil de synthèse infère une RAM/ROM il vous faut écrire votre RAM/ROM avec un style spécifique. Nous présentons ici un exemple sur lequel nous nous appuierons dans la suite : ROM avec sortie Registered 
-- ROMs Using Block RAM Resources. 
-- VHDL code for a ROM with registered output (template 1) 
library ieee; 
use ieee.std_logic_1164.all; 
use ieee.std_logic_unsigned.all; 
entity rams_21a is 
 port (clk : in std_logic; 
 en : in std_logic; 
 addr : in std_logic_vector(5 downto 0); 
 data : out std_logic_vector(19 downto 0)); 
end rams_21a; 
architecture syn of rams_21a is 
 type rom_type is array (63 downto 0) of std_logic_vector (19 downto 0); 
 signal ROM : rom_type:= 
 (X"0200A", X"00300", X"08101", X"04000", X"08601", X"0233A", 
 X"00300", X"08602", X"02310", X"0203B", X"08300", X"04002", 
 X"08201", X"00500", X"04001", X"02500", X"00340", X"00241", 
 X"04002", X"08300", X"08201", X"00500", X"08101", X"00602", 
 X"04003", X"0241E", X"00301", X"00102", X"02122", X"02021", 
 X"00301", X"00102", X"02222", X"04001", X"00342", X"0232B", 
 X"00900", X"00302", X"00102", X"04002", X"00900", X"08201", 
 X"02023", X"00303", X"02433", X"00301", X"04004", X"00301", 
 X"00102", X"02137", X"02036", X"00301", X"00102", X"02237", 
 X"04004", X"00304", X"04040", X"02500", X"02500", X"02500", 
 X"0030D", X"02341", X"08201", X"0400D"); 
begin 
 process (clk) 
 begin 
 if (clk'event and clk = '1') then 
 if (en = '1') then 
 data <= ROM(conv_integer(addr)); 
 end if; 
 end if; 
 end process; 
end syn;
Exercice 5 (spécifique Xilinx et carte Spartan3).
Compteur à sortie sept segments réalisé avec une mémoire
Nous allons utiliser un compteur normal, c'est à dire qui compte en binaire sur 4 bits suivi d'une mémoire pour le transcodage pour l'affichage sur un afficheur sept segments.
Dimensionner la ROM nécessaire à votre problème. Calculer les valeurs hexadécimales contenues dans cette ROM.
Commentaires sur l'exercice 5 (spécifique Xilinx).
Votre fichier rapport de compilation doit faire apparaître une ROM : dans "detailed reports" chercher "Synthesizing Unit <rams_21a>", et vous verrez :
Malheureusement, le fait qu'il détecte une ROM à ce point n'est pas suffisant pour être sûr qu'il l'implante tel quel (je veux dire en BRAM). Si vous voulez forcer une BRAM donc, dans la fenêtre "Design", 
On peut voir le résultat en démarrant PlanAhead (un exemple provenant d'un autre projet est montré ci-dessous) après le placement routage complet de l'application (avec un fichier de contraintes .UCF) depuis la même fenêtre "Design" en développant l'arborescence de "Place and Route" et en lançant "Analyze Timing / Floorplan Design (PlanAhead)". 
Les blocs mémoires utilisés sont dessinés en blanc. L'ensemble des blocs disponibles est représenté par les 12 rectangles rouges dont la taille et le nombre dépendent très fortement du type du FPGA utilisé. Nous croyons qu'il est bon de s'habituer à ce genre d'outil pour bien comprendre ce que l'on fait. Il existe même des situations (pas très fréquentes) pour lesquelles l'utilisation de PlanAhead est quasiment obligatoire.
Dans PlanAhead, on ne voit pas forcément le chevelu et les blocs utilisés par l'application. Il faut être sur l'onglet "Device" et cliquer sur les 3 icônes (bande verticale d'icônes à côté du dessin du circuit) "show/hide loc constraints" "show/hide bundle nets" et "show/hide I/O nets".
La RAM distribuée.
La RAM distribuée est plus facile à utiliser : il suffit de prendre l'exercice 5 sans choisir les options pour réaliser une BRAM et c'est tout. Nous présentons ci-dessous un exemple qui utilise de la ROM distribuée.
Cet exemple provient d'un autre . On distingue encore les rectangles rouges représentant les BRAM non utilisés. L'ensemble des blocs utilisés se trouve encore en blanc et cet exemple montre bien pourquoi on appelle cela de la ROM distribuée.