Prise en main des outils
1. Spécification d'un incrémenteur
Ce premier exercice est l'occasion de se familiariser avec les outils et de réaliser la première synthèse logique.
Le "circuit" étudié est un incrémenteur.
Sa spécification est immédiate :
- un mot de n bits, nommé y, doit être produit à partir de l'entrée a, de telle sorte que y=a+1 ;
- le circuit est purement combinatoire.
En suivant les recommandations vues en cours,
les types adaptés à la modélisations de circuits sont
ieee.std_logic_1164.std_ulogic et
ieee.std_logic_1164.std_ulogic_vector
(car ils sont non résolus, ce qui prévient les court-circuits dès la simulation).
Les opérations arithmétiques ne sont pas implémentées pour ces types.
Plutôt que de les définir par nous-même, au risque d'introduire des
bugs, nous allons utiliser ceux du paquetage
ieee.numeric_std.
Comme les types
ieee.std_logic_1164.std_logic_vector et
ieee.numeric_std.unsigned sont compatibles,
cette opération est possible.
La description des passerelles entre les types est détaillées ci-dessous,
avec en plus le passage entre le type vectoriel
ieee.numeric_std.unsigned (ou
signed, ce qui ne change rien à la démonstration) et
le type scalaire
std.standard.integer (ou ses sous-types usuels, comme
natural ou
positive).
- ieee.std_logic_1164.std_logic_vector
- ieee.std_logic_1164.std_ulogic_vector
- ieee.numeric_std.unsigned
- std.standard.integer
- De 1 à 2 et vice-versa, un transtypage suffit.
Si a est de type std_logic_vector, std_ulogic_vector( a ) est de type std_ulogic_vector.
- De 2 à 3 et vice-versa, un transtypage suffit également.
- De 3 à 4, il faut utiliser la fonction de conversion ieee.std_logic_1164.to_integer.
De 4 à 3, c'est ieee.std_logic_1164.to_unsigned qui convient.
Ceci nous permet d'être écrire un modèle comportemental pour l'incrémenteur :
inc.vhd.
2. Simulation de l'incrémenteur
Pour simuler, et ainsi valider, l'incrémenteur, un environnement de simulation (souvent appelé
test-bench) doit être créé.
Traditionnellement, celui-ci se découpe en trois parties :
- un processus producteur, qui génère les stimuli adéquats,
- une instance du circuit à tester (le device under test) et
- un processus consommateur, qui récupère et analyse les résultats.
Dans le cas de l'incrémenteur, le test-bench peut être exhaustif.
Nous ne nous privons donc pas de ce luxe rare.
Dans le test-bench suivant (
inc_tb.vhd), les sorties ne sont pas analysées,
mais simplement recopiées sur la sortie standard à l'aide des fonctionnalités du paquetage
std.textio.
On constate que, comme bien souvent, plus que le langage lui-même, c'est la bonne connaissance des bibliothèques qui aide à programmer efficacement.
Pour lancer la simulation, utilisez ce script
BASH :
inc_sim.txt.
Il réalisera toutes les étapes nécessaires (gestion des bibliothèques, compilation et exécution).
Les résultats vous paraissent-ils corrects ?
Plus précisément :
- Pourquoi la sortie est-elle initialement égale à l'entrée ?
Est-ce un problème fonctionnel ou un artefact de simulation ?
Comment l'éviter ?
- Que donne 0xF...F + 1 ?
Pourquoi le simulateur n'émet-il aucun avertissement ?
Si vous souhaitez consulter les chronogrammes de la trace d'exécution du test-bench, décommentez la ligne idoine du script
inc_sim.sh.
3. Synthèse de l'incrémenteur
Notre synthétiseur est
pks_shell.
Contrairement à
vsim,
pks_shell se lance par défaut en ligne de commande.
Pour disposer de l'interface graphique (
GUI ou
Graphical User Interface), tapez
pks_shell -gui &.
L'esperluette (caractère
&) en fin de commande assure que le contrôle revient au
shell du
terminal courant.
Comme son nom l'indique,
pks_shell est un
shell : on peut
- y exécuter les commandes usuelles, telles que ls, cd, rm, date, etc.
- y lancer des programmes externes, comme vim,
- programmer en TCL (Tool Command Langage).
C'est grâce à ce langage que l'on peut dialoguer avec l'outil pks_shell.
Il y a deux types d'actions sur le synthétiseur :
- L'exécution de fonctions (cf. syncomref.pdf)
Une commande utile est help (tapez par exemple help help pour avoir de l'aide sur l'usage de la fonction d'aide).
- La modification de variables globales (cf. synglobref.pdf).
Il sera par exemple utile de postionner la verbosité
(néologisme signifiant abondance verbale) des messages à sa valeur maxiamle :
set_global message_verbosity_level 8.
Pour vérifier que l'affectation s'est passée sans difficulté,
tapez simplement la commande dualeget_global message_verbosity_level.
Toute synthèse se découpe en deux étapes successives :
- une synthèse dans une bibliothèque générique, suivie
- d'une synthèse dans la technologie ciblée.
Les deux commandes du synthétiseur
pks_shell sont :
- do_build_generic et
- do_optimize.
Sachant en plus que
read_vhdl et
write_vhdl permettent de lire un circuit VHDL RTL synthétisable et de sauver une netlist,
nous pouvons écrire le premier script :
inc_syn.tcl.
Pour le lancer d'un coup, tapez
pks_shell -f inc_syn.tcl.
Pour y aller étape par étape, tapez
pks_shell (ou
pks_shell -gui & pour ouvrir une fenêtre),
et collez dans le
shell les commandes les unes après les autres.
Attention à ne pas copier le
quit final, autrement
pks_shell quittera sans crier gare.
4. Analyse de la netlist
Après synthèse, une
netlist nommée
inc_netlist.vhd a été générée (toujours au format VHDL).
Essayez d'extraire le schéma en lisant la netlist (pour les courageux),
ou bien utilisez l'outil de visualisation de
pks_shell lancé en mode GUI.
Si l'on omet le buffer
BFX2, reconnaissez-vous l'incrémenteur ?
On rappelle que les équations booléennes d'un additionneur complet (ou
full-adder)
2 x cout + s = a + b + cin sont :
- s = a XOR b XOR cin et
- cout = a AND b OR cin AND ( a XOR b ).
Ainsi, pour un incrémenteur, l'on a :
- s0 = NOT a0,
- cout0 = a0,
- si = cini XOR ai (i>0) et
- couti = cini AND ai (i>0).
avec
y=s (
cout est interne à l'incrémenteur).
Y-a-t'il eu une simplification ?
Montrez que parmi les équipotentielles internes,
toutes ont un nombre de destinations (aussi appelé
fan-out) égal à un,
sauf pour une.
Laquelle ?
Que lui est-il arrivé ?
Qu'en est-il des entrées ?
Dans le mécanisme de calcul de synthèse de pks_shell, les entrées ne sont pas amplifiées.
Les contraintes extrinsèques, telles que
set_drive_cell et
set_external_delay
affectent simplement le calcul du chemin critique.
En revanche, les sorties, quant à elles, peuvent être amplifiées.
Essayez de comprendre l'impact d'une déclaration de forte charge sur le bus de sortie y :
set_port_capacitance 4 [ find -port -output * ];.
Remarquez les modifications subies par l'entité.
Qu'en est-il en particulier des ports ?
Corriger ce problème en positionnant la variable globale ad hoc :
set_global hdl_vhdl_write_bit_type "std_ulogic";.
De même, le nom de l'entité a été décoré.
La commande set_global hdl_vhdl_write_entity_name "inc" rétablit un nom convenable,
qui prend autrement le nom d'un module (représentation interne à pks_shell des entités analysées).
Le paramètre générique n a disparu de l'entité.
Pour celui-ci, point de salut.
Il faut donc nous résigner à une modification de l'interface du circuit initial.
Toute l'information relative au traitement des fichiers VHDL, en entrée comme en sortie, est disponible dans le guide :
synhdlmod.pdf.
Refaire la synthèse, cette fois-ci en utilisant la technologie 130 nm de STMicroelectronics.
Rappel : la bibliothèque de portes disponibles est common/CORE9GPLL_Worst.lib.
Quelle remarque pouvez-vous formuler quant au choix des portes logiques ?
Pensez au fait que non seulement des full-adders,
mais également des halves-adders,
de capacités de charge (drive strength ou Cmax) variables,
se trouvent dans cette bibliothèque.
Arrivez-vous à retrouver la décomposition booléenne des équations ?