Logique combinatoire
2023-2024
Cette partie du cours est consacrée à la logique combinatoire. Nous allons aborder deux aspects:
Dans cette première section nous nous intéressons à la construction de portes en logique MOS complémentaire (CMOS). Nous commençons par reprendre l’inverseur, vu dans le cours d’introduction, puis nous présenterons quelques exemples de portes plus complexes. Nous finirons la section par une analyse des performances (la vitesse et la consommation énergétique) de ces portes.
Les transistors MOS peuvent être utilisés comme interrupteurs commandés. Ce modèle n’est valable que si les transistors sont correctement polarisés.
Ainsi, il faut que:
Si ces conditions ne sont pas respectées, le modèle d’interrupteur n’est plus valide.
Pour cela, dans la suite,
Si ces conditions sont respectées, on peut voir les transistors comme des interrupteurs commandés qui par la tension de la grille comme résumé dans les tables suivantes.
nMOS | V_{gs}n |
---|---|
bloqué | < V_T |
passant | > V_T |
nMOS | V_{gs}n |
---|---|
bloqué | >-V_T |
passant | <-V_T |
Comme nous manipulons des signaux transportant une information binaire, les tensions qui nous intéressent sont celles qui correspondent à la grille reliée à la masse ou reliée à l’alimentation.
État logique | V_{g}n | V_{gs}n | nMOS |
---|---|---|---|
0 |
V_{ss} | 0 V < V_T | bloqué |
1 |
V_{dd} | V_{dd}> V_T | passant |
État logique | V_{g}p | V_{gs}p | pMOS |
---|---|---|---|
0 |
V_{ss} | -V_{dd} < V_T | passant |
1 |
V_{dd} | 0 V > -V_T | bloqué |
La porte CMOS la plus simple
En mode statique: pas de courant entre l’alimentation et la masse
Cette porte est la porte CMOS la plus simple que nous pouvons construire. Elle est composée de deux transistors:
NOTE V_{ss} est généralement la référence de tension du circuit (la masse, gnd).
Examinons le comportement de cette porte quand l’entrée change.
La tension V_i est égale à V_{dd}. C’est la tension appliquée aux grilles des deux transistors.
Comme cette tension est plus grande que la tension de seuil V_T du transistor nMOS celui-ci est passant. La tension entre le drain et la source du transistor nMOS est porche de 0 et la sortie o est ainsi connectée à la tension la plus basse du circuit.
Du côté du transistor pMOS, V_{gs} = 0V. En valeur absolue, elle est inférieure à la tension de seuil du transistor pMOS (|V_{gs}|<V_T) ce qui fait que ce transistor est bloqué.
Un transistor bloqué peut être vu comme un circuit ouvert. Il n’y a donc pas de liaison entre la sortie o et la tension haute v_{dd} du circuit.
La tension sur la sortie est donc égale à V_{ss} ce qui correspond à un niveau logique 0.
La tension V_i est cette fois-ci égale à V_{ss}.
Du côté du transistor nMOS, V_{gs} = 0V < V_{T}, le transistor est bloqué. Alors que du côté du transistor pMOS, |V_{gs} - V_{dd}| > V_{T} et donc ce transistor est passant.
Nous avons donc la sortie o qui est connectée à la tension la plus haute du circuit V_{dd} et donc un état logique haut.
La table suivante résume les deux états:
i | V_{i} | V_{gs}n | V_{gs}p | nMOS | pMOS | V_{o} | o |
---|---|---|---|---|---|---|---|
0 | V_{ss} | 0 V | -V_{dd} | bloqué | passant | V_{dd} | 1 |
1 | V_{dd} | V_{dd} | 0 V | passant | bloqué | 0 V | 0 |
Et si on ne garde que les états logiques:
i | o |
---|---|
0 | 1 |
1 | 0 |
Ce qui est bien la table de vérité d’un inverseur logique (o = \overline{i}).
L’intérêt principal de cette structure, est que dans aucun des deux cas, il n’existe un chemin de conduction qui relirait l’alimentation (V_{dd}) à la masse (V_{ss}). Il n’y a donc pas de consommation électrique (au-delà de fuites liées à l’imperfection de l’état bloqué des transistors) si l’état des entrées ne change pas.
Dans la suite, pour simplifier les raisonnements, nous ne considèrerons les transistors comme des interrupteurs commandés avec le comportement suivant:
Il faudra garder à l’esprit que ceci est valide tant que les niveaux de tension électriques sont corrects par rapport aux seuils des transistors.
S = \overline{\sum\prod e_i}
Dans l’équation précédente, \sum et \prod font référence aux opérateurs booléens (le OU et ET).
Le réseau N, quand il est passant, ne peut que mettre à 0 la sortie, alors que le réseau P ne peut que la mettre à 1. Il faudra s’assurer que les deux réseaux ne sont pas passants simultanément.
Cette généralisation a ses limites, car:
Concernant ce dernier point, quand le nombre de transistors en série (dans le réseau N ou P) augmente, on éloigne ces derniers du potentiel des lignes d’alimentations et les transistors ne se comportent plus comme des interrupteurs. Ceci limite donc le nombre d’entrées que nous pouvons avoir dans une porte CMOS, typiquement à 3 ou 4 entrées maximum.
Ces limites ne sont pas vraiment un problème. Les fonctions logiques sont décomposées en fonctions plus simples, pour lesquelles on peut construire des portes CMOS, qui sont ensuite connectées entre elles.
Dans la suite, quelques exemples de construction de portes CMOS habituelles.
o = \overline{a \cdot b}
Le réseau N \overline{o} = a \cdot b
Le réseau P o = \overline{a} + \overline{b}
En parant de l’équation logique de la porte, on peut exprimer:
Pour la porte NAND, la sortie est à 0 si et seulement si les deux entrées sont à l’état haut.
Ce qui revient à dire que la sortie est connectée à la masse seulement si les deux transistors N commandés par les entrées a et b sont passant. D’où la structure du réseau avec les deux transistors en série.
Pour la porte NAND, la sortie est à 1 si une des deux entrées sont à l’état bas. Ce qui revient à dire que la sortie est connectée à l’alimentation si un des transistors P commandés par les entrées a et b est passant. D’où la structure du réseau avec les deux transistors en parallèle.
La table suivante résume ce raisonnement:
a | b | Tn(a) | Tn(b) | N | Tp(a) | Tp(a) | P | o |
---|---|---|---|---|---|---|---|---|
0 | 0 | bloqué | bloqué | bloqué | passant | passant | passant | 1 |
0 | 1 | bloqué | passant | bloqué | passant | bloqué | passant | 1 |
1 | 0 | passant | bloqué | bloqué | bloqué | passant | passant | 1 |
1 | 1 | passant | passant | passant | bloqué | bloqué | bloqué | 0 |
Notez que d’un point de vue structurel, des transistors disposés en série correspondent à un ET alors que quand ils sont disposés en parallèle, ceci correspond à un OU.
o = \overline{a + b}
Le réseau N \overline{o} = a + b
Le réseau P o = \overline{a} \cdot \overline{b}
Avec le même raisonnement que pour la porte NAND, on obtient une structure duale.
a | b | Tn(a) | Tn(b) | N | Tp(a) | Tp(a) | P | o |
---|---|---|---|---|---|---|---|---|
0 | 0 | bloqué | bloqué | bloqué | passant | passant | passant | 1 |
0 | 1 | bloqué | passant | passant | passant | bloqué | bloqué | 0 |
1 | 0 | passant | bloqué | passant | bloqué | passant | bloqué | 0 |
1 | 1 | passant | passant | passant | bloqué | bloqué | bloqué | 0 |
o = \overline{a \cdot (b1 + b2)}
Le réseau N \overline{o} = a \cdot (b1 + b2)
Le réseau P o = \overline{a} + (\overline{b1} \cdot \overline{b2})
Ici aussi la sortie est inversée et ne dépend que des entrées non inversées. Nous voyons que nous pouvons construire ce type de portes en suivant la même méthodologie.
Si l’équation de la porte n’est pas directement exprimable comme le complément de la somme ou du produit de ses entrées, il faut la décomposer.
\begin{aligned} o & = a \cdot b\\ & = \overline{(\overline{a \cdot b})} \end{aligned}
C’est l’inverse d’un NAND. On combine un NAND et un inverseur.
\begin{aligned} o & = a + b\\ & = \overline{(\overline{a + b})} \end{aligned}
C’est l’inverse d’un NOR On combine un NOR et un inverseur.
Assez simple ici d’exprimer la fonction logique de la porte comme l’inverse d’une porte simple. Parfois c’est un peu plus complexe comme le montre l’exemple suivant.
La sortie dépend des entrées et de l’inverse des entrées. o = a \oplus b = a \cdot \overline{b} + \overline{a} \cdot b
Le complément de la sortie:
\overline{o} = \overline{a \oplus b} = \overline{a} \cdot \overline{b} + a \cdot b
dépend aussi des entrées et de leur complément.
Pour ce type de portes, on a pas le choix, il faut avoir en même temps, les entrées et leur complément.
Il faut pour cela ajouter deux inverseurs qui génèrent les signaux complémentaires. Les sorties de ces portes seront connectées à des signaux internes à la porte.
Pour la porte XOR, dans la figure, nous avons deux inverseurs dont les sorties son na et nb.
\begin{aligned} na & = \overline{a}\\ nb & = \overline{b} \end{aligned}
On peut donc réécrire les équations de la sortie et de la sortie complémentaire comme:
\begin{aligned} o & = a \cdot nb + na \cdot b\\ \overline{o} & = na \cdot nb + a \cdot b \end{aligned}
et en déduire les réseaux N et P correspondant.
Retour à la physique des composants!
Combien de temps pour changer l’état de la sortie d’un inverseur?
Les grilles (entrées des portes CMOS) ne laisse pas passer de courant en régime permanent. Par contre, elles se comportent comme des condensateurs et en régime transitoire, il faut les charger ou décharger pour changer leur état. De plus, les connexions métalliques reliant les portes vont ajouter des capacités parasites.
Pour simplifier le problème, nous pouvons représenter l’ensemble de ces capacités par une capacité équivalente en sortie de la porte. Cette capacité équivalente peut être décomposée en deux parties:
C_L = C_i + C_e
Simplification du problème:
On s’intéressera dans la suite à la décharge de la capacité à travers les transistors nMOS.
Le cas dual est similaire en considérant le transistor pMOS.
t_p = C_L \frac{\Delta V}{I_{DSmax}}
Le courant de décharge est imposé par le transistor nMOS. Comme la tension à l’entrée de la grille est égale à V_{dd}, ce courant est initialement égal au courant de saturation I_{DS max}.
La valeur de ce courant est liée à la tension d’alimentation et à la physique et dimensions du transistor.
I_{DSmax} = K_n \cdot (V_{dd} - V_t)^2
avec,
K_n = \frac{1}{2} \mu_{0N} \cdot C'_{ox} \frac{W_N}{L_N}
Ce qui permet d’exprimer le temps de propagation (avoir un modèle)
t_p = C_L \frac{\Delta V}{I_{DSmax}} = C_L \frac{\frac{V_{dd}}{2}}{K_n\cdot(V_{dd}-V_t)^2}
On peut décomposer ce temps en deux parties:
t_p = t_{p0} + d_{tp} \cdot C_e
avec,
L’énergie nécessaire pour changer l’état de la sortie d’une porte CMOS
L’énergie consommée pour chaque changement d’état:
E = \frac{1}{2} C_L V_{dd}^2
En termes de puissance avec un taux d’activité \alpha:
P \propto \alpha f C_L V_{dd}^2
Considérons la charge à travers le transistor pMOS.
Au niveau de l’alimentation:
L’énergie fournie:
\begin{aligned} E_{pwr} & = \int_0^{\infty} V_{dd} ( C_L \frac{\text{d} v_c}{\text{d}t}) \text{d}t\\ & = \int_0^{V_{dd}} V_{dd} \cdot C_L \cdot \text{d} v_c\\ & = C_L \cdot V_{dd}^2 \end{aligned}
An niveau de la capacité:
L’énergie emmagasinée dans la capacité:
\begin{aligned} E_{c} & = \int_0^{\infty} v_{c} ( C_L \frac{\text{d} v_c}{\text{d}t}) \text{d}t\\ & = \int_0^{V_{dd}} v_{c} \cdot C_L \cdot \text{d} v_c\\ & = \frac{1}{2} \cdot C_L \cdot V_{dd}^2 \end{aligned}
La moitié de l’énergie fournie par l’alimentation a été dissipée par effet joule dans les transistors.
Pour un circuit complexe,
la puissance consommée est de la forme:
P \propto \alpha f C_L V_{dd}^2
On voit que cette puissance est proportionnelle à la fréquence de fonctionnement, et au carré de la tension d’alimentation. La capacité est quant à elle liée à la technologie et taille des transistors.
Les fabricants de circuits (les fondeurs) conçoivent les portes logiques
Elles sont caractérisées:
Ils fournissent des bibliothèques de cellules utilisables sans avoir besoin de descendre au niveau transistor.
Les concepteurs de circuits numériques peuvent se concentrer sur les fonctions logiques avec des modèles plus abstraits.
Voici deux exemples montrant les informations qui sont fournies avec une bibliothèque de cellules précarctérisées.
Ces exemples sont extraits de la documentation de la bibliothèque NanGate/Silvaco OpenCell 45nm Library.
Pour chaque porte, nous aurons au moins des informations concernant:
Ces informations sont fournies sous forme de fichiers informatiques qui seront utilisés par des outils informatiques pour automatiser la conception des circuits logiques.
Pour un concepteur de circuits numériques:
Utilisation de langages informatique pour la description du matériel et d’outils d’automatisation.
On parle d’EDA pour Electronic Design Automation. Ces outils vont piocher dans les bibliothèques précaractérisée pour transformer des représentations abstraites en assemblage de portes puis de transistors.
Ces outils permettent:
SystemVerilog, est un langage informatique pour la description du matériel (HDL).
Un langage de description du matériel (HDL pour Hardware Description Language) est un langage informatique qui sert à décrire la structure et le comportement de blocs électroniques numérique.
Ces langages permettent d’utiliser des outils informatiques pour simuler, analyser et concevoir des blocs matériels numériques.
Ils sont différents d’un langage de programmation dans le fait qu’ils ont comme objectif de concevoir des composants matériels, alors qu’un langage de programmation standard a pour objectif de produire une séquence d’instruction qui sera exécutée par un processeur.
Dans l’industrie, deux familles de langages existent, Verilog/SystemVerilog et VHDL. Bien que les syntaxes soient différentes, les concepts manipulés et les objectifs sont quasi identiques.
Dans ce cours, nous allons vous présenter quelques bases de SystemVerilog pour représenter de la logique combinatoire, puis de la logique séquentielle.
En SystemVerilog, un bloc numérique est représenté par un
module
.
Exemple:
module foo( input logic a,
input logic b,
output logic c );
// ici on trouvera la description du module
endmodule
Les modules sont déclarés avec le mot-clé module
(mot
réservé). Ce module doit avoir un nom (foo
dans l’exemple).
input
.output
.Dans l’exemple, les entrées et sorties sont de type
logic
. Cela signifie qu’elles correspondent à des
entrées/sorties logiques sur 1 bit.
NOTE Tout code SystemVerilog doit appartenir à un module!
Pour des bus sur plus d’un bit, il faut utiliser des
crochets [...]
pour préciser la taille (plus précisément,
on précise les indices de début et de fin du bus).
Par exemple:
input logic [3:0] A
A
est un bus en entrée du module, composé des quatre
bits suivant: a[3]
,a[2]
,a[1]
et
a[0]
. a[3]
étant le bit de poids fort
(msb pour most significant bit ), et a[0]
le poids faible (lsb pour least significant bit).
La valeur transportée par ce bus est par défaut celle d’un nombre entier non signé.
A = \sum_0^3 a[i] \cdot 2^i
En SystemVerilog, on peut décrire un bloc de logique combinatoire par une équation booléenne.
// Exemple: Un buffer (identité)
module BUF (input logic i,
output logic o);
assign o = i;
endmodule
// Exemple: Un inverseur
module INV (input logic i,
output logic o);
assign o = ~i;
endmodule
Le mot clé assign
est obligatoire!
Le mot clé assign
permet d’exprimer le fait que nous
avons une affectation continue (continuous
assignment).
Contrairement à un langage de programmation standard, ici nous
exprimons le fait que le signal logique à gauche de l’affectation (à
gauche du =
) est continument connecté à la
sortie d’une porte logique dont l’équation booléenne est donnée à droite
de l’affectation.
La syntaxe de SystemVerilog est héritée de celle du langage C, les symboles utilisés pour les différents opérateurs sont très similaires.
symbole | fonction |
---|---|
& |
ET bit à bit |
| |
ET bit à bit |
^ |
XOR bit à bit |
~ |
Complément bit à bit |
Au-delà de la logique booléenne pure, il est possible d’utiliser d’autres opérateurs pour des fonctions combinatoires plus complexes.
symbole | fonction |
---|---|
== |
Égaux |
!= |
Différents |
> |
Plus grand que |
< |
Plus petit que |
symbole | fonction |
---|---|
&& |
Et logique |
|| |
Ou logique |
! |
Complément logique |
:? |
Opérateur ternaire |
NOTE Pour les signaux binaires, les opérateurs logiques et les opérateurs bit-à-bit sont équivalents.
symbole | fonction |
---|---|
+ |
Addition |
- |
Soustraction |
* |
Multiplication |
Nous allons utiliser DigitalJS
un outil
en ligne pour l’interprétation et la simulation de code
SystemVerilog.
L’instance hébergée à l’école: https://digital.r2.enst.fr/
DigitalJS est un outil libre, en ligne, permettant d’analyser et simuler du code SystemVerilog. Il fait appel à plusieurs outils libres:
Yosys
un outil de synthèse libre pour transfromer le code
en ensemble de blocs/portes électronique,Verlator
un second outil libre utilisé ici pour du Lint (analyse
de code).Le résultat peut être simulé dans son navidateur web en utilisant les ressources locales de sa machine.
DigitalJS
a été conçu pour une utilisation dans le cadre
d’activités pédagogique. Bien qu’il ne soit pas parfait (et ne supporte
pas l’entièreté de SystemVerilog), il est suffisamment performant pour
être utilisé dans le contexte su cours. Il est ainsi possible de faire
des démonstrations et des travaux pratiques sans installer le moindre
outil sur sa machine.
La figure suivante montre un exemple de l’interface de l’outil:
Pour démarrer, il suffit:
Une fois lancé ou peut interagir avec le résultat en agissant sur les entrées du module. Pour les entrées binaires, l’outil affiche des boutons cliquables. Les sorties sont représentées par des leds rouges quand elles sont à 0 et vertes quand elles sont à 1.
Les entrées/sorties sur plus d’un bit sont représentées par leur valeur numérique (en hexadécimal par défaut, mais on peut changer la représentation).
Nous allons utiliser l’outil pour construire un additionneur de deux mots de 4 bits à propagation de retenue.
Le but ici est de:
Note pour plus tard Ceci est un exercice destiné à vous familiariser avec la syntaxe et les concepts du langage. Les opérateurs arithmétiques existent en SystemVerilog et au-delà de cet exercice, il sera préférable de décrire les additionneurs en utilisant l’opérateur prévu pour cela.
La structure de l’additionneur 1 bit:
Le code SystemVerilog associé:
module FA(
input logic a,
input logic b,
input logic ci,
output logic s,
output logic co );
assign s = a ^ b ^ ci;
assign co = a & b | a & ci | b & ci;
endmodule
Un additionneur 1 bit complet possède 3 entrées:
Il possède deux sorties la somme et une retenue.
Pour le décrire, nous devons déclarer un module (ici nommé
FA
) avec l’interface suivante:
a
, b
et
ci
(input carry),s
et co
(output carry).Nous vous rappelons que:
D’où le code SystemVerilog donné.
Travail à faire
Reprenez ce code dans DigitalJS
et
assurez-vous que le comportement obtenu est correct (par exemple et
reconstituant la table de vérité).
La structure de l’additionneur 4 bit:
La structure de l’additionneur 4 bit:
Le code SystemVerilog associé:
module ADDER4(
input logic [3:0] a,
input logic [3:0] b,
input logic ci,
output logic [3:0] s,
output logic co );
// réutiliser le module FA ?
endmodule
Comment réutiliser le module FA
déjà décrit.
Nous avons besoin ici d’un complément sur la syntaxe de SystemVerilog pour pouvoir décrire la structure d’un opérateur complexe en réutilisant des modules existants.
SystemVerilog permet de créer des instances d’un module existants.
Considérons l’exemple suivant avec un module X
:
module X (input logic a, output logic b);
//...
endmodule
Si nous voulons utiliser le module X
à l’intérieur d’un
autre module Y
, il faut l’instancier.
module Y(input logic i, output logic o);
// instance of module X
(.a(i), .b(o));
X X_0
endmodule
Dans cet exemple nous avons créé une instance X_0
du
module X
. Nous avons connecté:
a
de X_0
au signal i
de Y
,b
de X_0
au signal
o
de Y
.Ceci est appelé une description structurelle de
Y
.
Dans l’exemple précédent, les entrées/sorties du sous-module sont connectées directement aux entrées/sortie du module de niveau supérieur.
Si nécessaire, il est possible de déclarer des signaux internes au module de niveau supérieur.
Dans l’exemple suivant, le module Y
contient deux
instances du module X
(X_0
en
X_1
). Ces deux instances sont connectées en interne du
module Y
par le signal logic s
.
module Y(input logic i, output logic o);
logic s;
(.a(i), .b(s));
X X_0(.a(s), .b(o));
X X_1
endmodule
s
n’apparait pas dans l’interface de Y
, il
est interne.
La sortie b
de X_0
est connectée à
s
qui est ensuite connecté à l’entrée a
de
X_1
.
Les signaux internes peuvent aussi être utilisés comme sortie d’affectations continues.
Par exemple l’additionneur 1 bit aurait pu être décrit comme suit:
module FA2(
input logic a,
input logic b,
input logic ci,
output logic s,
output logic co );
logic hs, hc;
assign hs = a ^ b;
assign hc = a & b;
assign s = hs ^ ci;
assign co = hc | hs & ci;
endmodule
Ceci est utile pour structurer son code et le rendre plus lisible.
Avec ce complément de syntaxe, nous pouvons décrire la structure de l’additionneur 4 bits.
Le code SystemVerilog associé:
module ADDER4(
input logic [3:0] a,
input logic [3:0] b,
input logic ci,
output logic [3:0] s,
output logic co );
logic[4:0] c;
(.a(a[0]), .b(b[0]), .ci(c[0]), .s(s[0]), .co(c[1]));
FA FA_0(.a(a[1]), .b(b[1]), .ci(c[1]), .s(s[1]), .co(c[2]));
FA FA_1(.a(a[2]), .b(b[2]), .ci(c[2]), .s(s[2]), .co(c[3]));
FA FA_2(.a(a[3]), .b(b[3]), .ci(c[3]), .s(s[3]), .co(c[4]));
FA FA_3
assign c[0] = ci;
assign co = c[4];
endmodule
Notez aussi qu’il n’y a pas besoin d’importer de bibliothèque ou d’inclure de fichiers d’en-tête en SystemVerilog. Il suffit que l’outil voit les différents modules.
Pour DigitalJS, il suffit que les différents modules soient présents dans le même fichier ou dans des fichiers (onglets) différents.
Travail à faire Tester dans DigitalJS et vérifiez que le comportement est correct.
Complément: Comment décrire le comportement de l’additionneur (et non sa structure)
Habituellement, pour décrire un opérateur aussi commun qu’un aditionneur, on utilise les opérateurs arithmétiques de SystemVerilog pour décrire le comportement (plutôt que la structure). La décomposition en des opérateurs en portes élémentaire étant faite par l’outil informatique qui analyse le code (on parle d’outil de synthèse).
La seule difficulté ici étant de récupérer la retenue sortante. Pour cela, il suffit de faire l’addition sur un bit de plus en déclarant un signal interne, comme montré dans l’exemple suivant.
module ADDER4(
input logic [3:0] a,
input logic [3:0] b,
input logic ci,
output logic [3:0] s,
output logic co );
logic[4:0] s_i;
assign s_i = a + b + ci;
assign s = s_i[3:0];
assign co = s_i[4];
endmodule
En SystemVerilog il existe un opérateur de
concaténation qui permet de regrouper des éléments pour
construire des vecteurs/bus. Pour cela on utilise les accolades
{
et }
.
Par exemple:
logic a, b;
logic [1:0] c,d;
// {a,b} vecteur de 2 bits avec a en msb
assign d = {a,b};
// légal à droite et à gauche d'une affectation
assign {a,b} = c;
Ceci permet de simplifier l’écriture de l’additionneur en se passant du signal intermédiaire.
module ADDER4(
input logic [3:0] a,
input logic [3:0] b,
input logic ci,
output logic [3:0] s,
output logic co );
assign {co,si} = a + b + ci;
endmodule
SystemVerilog permet représenter de façon plus abstraite le comportement d’un bloc combinatoire.
Pour illustrer cela, nous allons prendre comme exemple le code décrivant un multiplexeur 2 vers 1.
module MUX21 ( input logic a, b,
input logic s,
output logic o );
assign o = s & a | ~s & b ;
endmodule
o = s \cdot a + \overline{s} \cdot b
Nous savons retranscrire, en SystemVerilog, l’équation logique d’un multiplexeur. Ceci permet de décrire le comportement de cette porte sans avoir à le décrire structurellement comme une combinaison de portes ET et OU.
Malgré cela, cette description reste assez bas niveau puisqu’on doit malgré tout connaitre l’équation booléenne.
module MUX21 ( input logic a, b,
input logic s,
output logic o );
assign o = s? a : b;
endmodule
o = \text{si}(s): a , \text{sinon}: b
En SystemVerilog, l’opérateur ternaire peut être utilisé pour exprimer ce comportement. En fonction de la valeur de s on obtient la valeur venant de a ou b.
Cette description du même multiplexeur est un peu plus abstraite, on exprime le comportement de la porte sans devoir expliciter son équation booléenne.
Attention ceci reste la description d’un composant électronique et non une instruction qui s’exécute. On compte sur les outils utilisés pour interpréter ce comportement et régénérer l’équation booléenne et la structure de portes logiques.
Cette écriture a l’avantage de permettre de décrire simplement un multiplexeur de bus. Nous n’avons plus besoin d’exprimer l’équation booléenne pour chaque bit!
Par exemple, pour multiplexer des bus de 4 bits, nous pouvons juste écrire:
module MUX21x4 ( input logic [3:0] a, b,
input logic s,
output logic [3:0] o );
assign o = s? a : b;
endmodule
Il serait intéressant de monter en abstraction et décrire ce comportement avec des constructions plus haut niveau, plus simple à lire, comprendre et faire évoluer.
Le comportement du multiplexeur pourrait être exprimé par une
structure explicite if...else
. Pour cela, SystemVerilog
ajoute la notion de processus que nous présentons dans la suite.
always_comb
module MUX21 ( input logic a, b,
input logic s,
output logic o );
always_comb
begin
if(s) o = a;
else o = b;
end
endmodule
o = \text{si}(s): a , \text{sinon}: b
Les processus permettent d’utiliser des constructions plus haut niveau pour décrire de la logique.
Ici, pour décrire la logique combinatoire, on voit apparaitre le bloc
précédé par le mot clé always_comb
. Ce bloc est délimité
par les mots clés begin
et end
(qui remplacent
les accolades {
et }
qu’on aurait en langage C
ou Java et qui sont utilisées en SystemVerilog pour la
concaténation)
Ce bloc est appelé processus.
Dans l’exemple précédent, ce processus décrit le comportement du même multiplexeur. Toujours à la charge de l’outil d’analyser ce comportement et de le remplacer par la structure de porte adéquate.
Note Comment l’interpréter et pourquoi
always_comb
?
C’est l’exécution de ce fragment de code qui décrit le comportement du bloc combinatoire. On doit garantir que le comportement décrit correctement de la logique combinatoire et que la valeur de la sortie est définie pour toutes les valeurs possibles des entrées à chaque fois que le processus est évalué.
Ce dernier point est important, et pour l’exemple du multiplexeur,
ceci veut dire que la valeur de la sortie doit être
définie pour les deux branches du if
.
case
et représentation des tables de véritéalways_comb
utilisation de
case
module MUX21 ( input logic a, b,
input logic s,
output logic o );
always_comb
begin
case(s)
0: o = a;
1: o = b;
endcase
end
endmodule
o = \text{si}(s): a , \text{sinon}: b
En plus de la structure if...else
, une construction
case
existe SystemVerilog. Elle permet d’énumérer les
différents cas.
Notez que la syntaxe est différente de celle du C et
qu’elle se termine par le mot clé endcase
.
Comme pour la structure if...else
la valeur de la sortie
doit être définie pour toutes les valeurs possibles des
entrées.
Avec cette nouvelle structure, nous pouvons décrire de façon compacte des comportements ou le nombre de cas est plus grand que deux.
Par exemple, voici la description d’un multiplexeur 4 \rightarrow 1 de bus de 4 bits:
module MUX41x4 ( input logic [3:0] a, b, c, d,
input logic [1:0] s,
output logic [3:0] o );
always_comb
begin
case(s)
0: o = a;
1: o = b;
2: o = c;
3: o = d;
endcase
end
endmodule
Comme l’entrée de sélection s est sur 2 bits, il y a 4 cas à décrire.
Attention le nombre de cas sera toujours une puissance de 2 (2^n ou n est la taille du signal testé). Si tous les cas ne sont pas utilisés, il faudra malgré tout donner une valeur à la sortie pour ces cas (sinon ce n’est pas de la logique combinatoire).
Pour cela, le langage prévoit un cas par défaut comme le montre l’exemple suivant:
module MUX31x4 ( input logic [3:0] a, b, c,
input logic [1:0] s,
output logic [3:0] o );
always_comb
begin
case(s)
0: o = a;
1: o = b;
default: o = c; // si s == 2 ou 3
endcase
end
endmodule
case
pour implémenter une table de véritéOn peut implémenter simplement une fonction si on connait sa table de vérité!
Exemple un décodeur 2 vers 4:
module DEC24 ( input logic [1:0] i,
output logic [3:0] o );
always_comb
case(i)
0: o = 4'b0001; // 1
1: o = 4'b0010; // 2
2: o = 4'b0100; // 4
3: o = 4'b1000; // 8
endcase
endmodule
En SystemVerilog, en plus de la base, on peut exprimer explicitement la taille (nombre de bit) sur lequel un nombre est représenté.
Un nombre se représente sous la forme N'Bxxxx
où
N
est la taille en bits et B
la base dans laquelle il est représenté (d
pour décimal,
b
pour binaire et h
pour hexadécimal).
Important
_
Exemples:
Nombre | Base | Taille | Valeur décimale | Binaire |
---|---|---|---|---|
42 |
10 | 32 bits | 42 | 00………101010 |
6'd42 |
10 | 6 bits | 42 | 101010 |
5'd42 |
10 | 5 bits | 10 | 01010 |
3'b101 |
2 | 3 bits | 5 | 101 |
8'b0101_0101 |
2 | 8 bits | 85 | 01010101 |
8'h55 |
8 | 8 bits | 85 | 01010101 |
9'h1_00 |
8 | 9 bits | 256 | 100000000 |
Décrire un décodeur combinatoire pour contrôler un afficheur 7 segments.
module display_ctrl(input logic [3:0] num,
input logic dot,
output logic [7:0] display7);
...endmodule
Nous désirons écrire le code SystemVerilog d’un décodeur combinatoire pour contrôler un afficheur 7 segments. Chaque segment est en réalité une LED, dont on contrôle la cathode.
Les 7 segments permettent d’afficher, en hexadécimal, toutes les valeurs d’un nombre entre 0 et 15. En plus, de ces 7 segments, notre afficheur contient un point que l’on peut contrôler de façon similaire.
Complétez, puis testez (en utilisant DigitalJS) le module suivant:
num
.case
.Notes les indices des bits de la sortie
display7
correspondent aux numéros sur la figure. Le point
correspond au bit de poids fort (numéro 7) de la même sortie. L’entrée
sur 4 bits num
correspond à la valeur à décoder, l’entrée
dot
à une commande séparée pour le point.
Magie de la simulation, DigitalJS vous affichera un afficheur, tant que la sortie s’appelle display7.
© Copyright 2022-2024, Télécom Paris. | |
Le contenu de cette page est mis à disposition selon les termes de la licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International. |