Nous avons déjà réalisé un certain nombre de circuits numériques combinatoires. Si on y regarde de plus prés, on observe que les portes logiques sont caractérisées par des temps de propagation : un événement sur une entrée peut créer un événement sur la sortie après ce temps de propagation. Ces temps de propagation se cumulent au fur et à mesure que l'on avance dans le circuit. Il existe une période d'instabilité sur les fils de sortie où les résultats produits sont faux.
Il est souhaitable sur des circuits et en particulier sur des FPGA de se référencer dans le temps afin de synchroniser des étapes de calcul où l'on est certain d'avoir eu le temps de produire des résultats corrects. Un circuit logique séquentiel synchrone incorpore une horloge qui sert à enclencher les actions. Le changement de l'état logique de la sortie est commandé par une horloge. La combinaison des états logiques des entrées présentes et de l'état logique de la sortie ne sera prise en considération qu'à la suite d'un changement du signal de l'horloge sur front montant ou front descendant.
Une entité de mémorisation (un registre) est associé au circuit combinatoire afin de garantir le signal de sortie stable entre deux tops d'horloge. Les registres que l'on trouvera dans nos circuits ont une triple fonction :
Mémorisation : stocker une valeur qui sera utilisée plus tard. Ceci impose que l’écriture dans les registres soit conditionnelle et non systématique à chaque top..
Synchronisation : assurer que toutes les données d’un même opérateur combinatoire sont simultanément disponibles indépendamment du nombre de portes logiques traversées.
Stabilisation : garantir que les signaux d’entrée des opérateurs combinatoires sont stables pendant toute la période.
Voici un schéma générique pour la construction d'un système synchrone stable:
L'horloge permet de régler la cadence des impulsions en synchronisant les actions du circuit logique. Elle génère des impulsions de durée fixe. Cette durée est appelée période et elle est mesurée en secondes. La fréquence de ces impulsions est définie comme étant l'inverse de la période et elle se mesure en hertz (Hz). Idéalement les impulsions générées par l'horloge ont la forme d'une onde carrée.
Il existe des circuits asynchrones, je n'en parlerai pas dans ce cours. Par contre nous allons regarder comment définir une unité de stockage en VHDL, les codes proposés sont tous reconnus par VIVADO, un tutorial Xilinx existe sur le sujet.
Ce circuit mémorise un signal avec un bistable. En électronique, bistable se dit d'un signal ayant deux états logiques stables: 0 et 1. Le passage d'un état à un autre ne peut s'opérer qu'à la suite d'une action extérieure.
Le latch peut être utilisé sous Vivado mais ce'n'est pas recommandé de l'utiliser, parfois il peut être générer à votre insu à partir d'une mauvaise programmation VHDL. Voici le message type que VIVADO vous produira un warning
Néanmoins voici le code VHDL qui le produit, afin de ne pas le faire ;-). Il n'y a pas de clock, mais un signal En ( Enable) qui valide la mémorisation à tout moment! Le process est sensible à tout changement d'état de la donnée D et du signal Enable.
library IEEE;
use IEEE.Std_Logic_1164.all;
entity Latch is
port (D, En : in Std_Logic;
Q : out Std_Logic);
end Latch;
architecture RTL of Latch is
begin
L1: process(D, En)
begin
if En=‘1’ then
Q <= D;
end if;
end process;
end RTL;
Mêmes effets avec un when else qui reprend la sortie en entrée!
Q <= D when En=‘1’ else Q;
Dans les 2 cas Vivado reconnait un latch et synthétise avec le circuit ld de sa bibliothèque. Quand vous voyez des ld dans votre circuit, il faut mieux revoir votre code VHDL!
Les bascules sont elles, synchronisées par le front montant ou descendant d'une horloge. Vivado reconnait une Horloge non pas par son nom mais par son utilisation dans un process. La donnée n'est pas dans la liste de sensibilité, le process n'est sensible qu'au changement d'état de la clock. Voici la bascule D:
library IEEE;
use IEEE.Std_Logic_1164.all;
entity flipflop is
port (D, clk : in Std_Logic;
Q : out Std_Logic);
end flipflop;
architecture RTL of flipflop is
begin
process (clk)
begin
if (clk'event and clk='1') then -- rising_edge(clk) fait "à peu prés" la même chose proposé ds la bilio IEEE
Q <= D;
end if ;
end process ;
end RTL;
Vivado identifie un bascule FD, le petit triangle identifie l'entrée qui doit être une clock!
Une bascule D permet de « mémoriser » 1 bit
L’écriture d’une nouvelle valeur a lieu lors du front montant du signal C mais rien n'est instantané:
L’entrée D doit être stable un peu avant (Tsu) et un peu après (Th) le front
La sortie Q change de valeur au plus une fois par cycle après le front (Ta)
Il est souvent intéressant de pouvoir forcer la valeur de sortie à '0', on appelle çà un reset. Il existe deux types de bascule, reset synchrone et asynchrone (ou clear) . La différence vient de la prise en compte ou non du reset dans la liste de sensibilité et de l’imbrication de vos IF.
Proposez deux codes vhdl qui implémentent ces deux stratégies. Vous devriez obtenir les deux RTL view suivantes sous Vivado. Vous pouvez créer un nouveau projet exercice pour vos tests.
Synchrone
Asynchrone
Dans certains cas, il est utile de pouvoir sélectionner une flipflop/bascule par un signal (enable). En particulier si vous connectez une même donnée à deux bascules distinctes, avec le signal enable une seule des deux pourrait effectivement la stocker au top d'horloge: intéressant pour choisir un registre destination dans un processeur....Dans le schéma précédent une adresse de registre est décodée par un décodeur 3x8 qui valide un fil sur les 8 en fonction du numéro du registre sélectionné. Coté VHDL, le enable n'est pas présent dans la liste de sensibilité. Voila comment gérer une écriture dans une bascule sous condition de sélection:
process (clk)
begin
if (clk’event and clk=’1’) then
if (enable=’1’) then
Q <= D ;
end if ;
end if ;
end process ;
Vivado reconnait une bascule FDE appelé registre avec CE pour Chip Enable.
Pour la lecture, il existe des solutions à base de multiplexeurs assez simple à mettre en oeuvre (cf ce schéma). Il faut alors que le nombre d’entrée soit raisonnable et connu au moment du design. On utilise également la notion de logique trois états ou Tri-state. Avec ce mécanisme on peut connecter plusieurs sorties sur le même signal, il suffit d'en avoir un seul qui n'est pas dans "le troisième état". Voici un exemple de montage en sortie, ici un seul registre est sélectionné pour chaque bus SRC1 et SRC2 en fonction d'un numéro de sélection, ce numéro est en général explicite dans instruction assembleur en cours d'exécution, il s'agit des champs registre source de l’instruction ( ex ADD R1, R2) :
Pour obtenir un tel montage sur votre FPGA, le code VHDL est assez simple, le type std_logic vous propose la valeur 'Z' pour représenter ce troisième état.
library IEEE;
use IEEE.std_logic_1164.all;
entity tristate is
port ( D1, D2 , en1, en2 : in std_logic ;
Q : out std_logic);
end tristate ;
architecture EG of tristate is
begin
Q <= D1 when en1 = '1' else 'Z' ;
Q <= D2 when en2 = '1' else 'Z' ;
end EG ;
Vivado reconnait ce code et utilise un tristate , ce composant "laisse passer ou non le signal" et est actif à l'état bas. En changeant dans ce code les 'Z' par des '0' on obtiendrait lors de la synthèse par viavado un critical warning CRITICAL WARNING: [Synth 8-3352] multi-driven net Q_OBUF with 1st driver pin 'i_1/O': la synthèse est alors impossible. Vous pouvez bien entendu utiliser cette notion de tri state pour un std_logic_vector avec un "ZZZZZ...ZZ". Voici le RTL shematic que j'ai obtenu avec la version correcte:
Voici un code vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity C16b is
port (
clk,raz :in std_logic;
qs : out std_logic_vector(15 downto 0));
end C16b;
architecture aC16b of C16b is
signal q : std_logic_vector(15 downto 0);
begin
qs <= q;
process(clk)
begin
if clk'event and clk='1' then
if raz='1' then
q<=(others=>'0');
else
q<=q+1;
end if;
end if;
end process;
end aC16b;
Ce circuit travaille sur quel front de l'horloge?
Le signal raz est-il synchrone ou asynchrone?
Pourquoi utiliser cette librairie? use ieee.std_logic_unsigned.all;
Quelle type de bascule Vivado va-t-il générer?
Peut-on déplacer dans le code la ligne qs <= q; et où peut-on la mettre?
D'après vous que signifie (others=>'0')? par quoi peut-on le remplacer?
Générez le RTL Schematic avec Vivado, est-ce conforme à votre attente?
Lancez le simulateur afin de vérifier le comportement de votre composant. Il faudra cette fois faire un Force Clock sur le signal clk , avancez d'une période de 10 ns après avoir placer '1' sur raz, puis lancez la simulation sur quelques us avec le raz mis à '0'. Vous devriez observer le retour du compteur à la valeur x"0000" une fois arrivé à x"FFFF".
Ajoutez un signal enable, de telle sorte que la sortie qs n'est valide que si enable ='1', elle est en tri state sinon. Reconstruire le RTL Schematic et relancez la simulation en jouant avec le signal enable. Comment se comporte le compteur?