L’affectation d’un signal de type wire
s’effectue au moyen de l’instruction
assign
:
assign nom de signal = expression;
Si le signal est de type vecteur, il est possible d’affecter un bit ou un groupe de bits dans un intervalle d’indices :
assign nom de signal[indice] = expression;
assign nom de signal[indice:indice] = expression;
Si l’expression retourne un vecteur, il est possible de le décomposer en plusieurs signaux de destination :
assign {nom de signal, nom de signal, ...} = expression;
Exemple :
wire a;
wire [2:0] b;
wire [3:0] c;
assign {a, b, c} = 8'b10110101
// a vaut 1'b1
// b vaut 3'b011
// c vaut 4'b0101
En VHDL, l’affectation conditionnelle
(when...else
) est une instruction à part entière.
En Verilog, on obtient le même effet au moyen de l’opérateur conditionnel
?:
comme ceci :
assign nom de signal = condition ? expression si vrai : expression si faux;
À quelques exceptions près, Verilog fournit les mêmes opérateurs que le langage C :
Syntaxe | Opération |
---|---|
a + b |
a plus b |
a - b |
a moins b |
a * b |
a multiplié par b |
a / b |
a divisé par b |
a % b |
a modulo b |
a ** b |
a à la puissance b |
a < b |
a strictement inférieur à b |
a <= b |
a inférieur ou égal à b |
a > b |
a strictement supérieur à b |
a >= b |
a supérieur ou égal à b |
a == b |
a égal à b |
a != b |
a différent de b |
!a |
a non nul |
a && b |
a non nul et b non nul |
a || b |
a non nul ou b non nul |
~a |
non logique bit à bit sur a |
a & b |
et logique bit à bit entre a et b |
a | b |
ou logique bit à bit entre a et b |
& a |
et logique entre tous les bits de a |
| a |
ou logique entre tous les bits de a |
a << b |
a décalé de b bits vers la gauche |
a <<< b |
a décalé de b bits vers la gauche (préserve le signe) |
a >> b |
a décalé de b bits vers la droite |
a >>> b |
a décalé de b bits vers la droite (préserve le signe) |
a ? b : c |
si a est non nul, alors b , sinon c |
Dans le cas des opérateurs logiques, si deux opérandes n’ont pas la même taille, le plus petit est complété avec des 0 même s’il est signé.
Les opérateurs de comparaison retournent le bit x
si l’un des opérandes contient
des x
ou des z
.
Il existe également les opérateurs ===
et !==
qui incluent les x
et z
dans la comparaison.
On écrit entre accolades, séparées par des virgules, les expressions dont on souhaite concaténer les résultats :
{expression, expression, ...}
À l’intérieur des accolades, il est possible de répéter une même expression plusieurs fois en utilisant la syntaxe suivante :
nombre de répétitions{expression}
Exemple :
wire a;
wire [2:0] b;
wire [3:0] c;
wire [7:0] d;
wire [7:0] e;
assign a = 1'b1;
assign b = 3'b011;
assign c = 4'b0101;
assign d = {a, b, c}; // d vaut 8'b10110101
assign e = {2{0}, 2{b}}; // e vaut 8'b00011011
L’instruction always
est similaire à l’instruction process
du VHDL.
Le plus souvent, on utilisera la syntaxe suivante :
always @ liste de sensibilité
instruction séquentielle ou bloc
Comme en VHDL, la liste de sensibilité indique les noms des signaux qui, lorsqu’ils changent de valeur, déclenchent l’exécution des instructions du processus. Lorsqu’un processus décrit un circuit combinatoire, elle peut prendre l’une des formes suivantes :
always @ nom de signal
instruction séquentielle ou bloc
always @ (nom de signal, nom de signal, ...)
instruction séquentielle ou bloc
always @ *
instruction séquentielle ou bloc
La troisième variante ci-dessus est disponible à partir de Verilog 2001,
elle signifie que la liste de sensibilité doit être construite automatiquement
à partir du corps du processus.
Le même mécanisme existe en VHDL 2008 avec le mot-clé all
.
Le corps du processus peut être constitué d’une seule instruction, ou d’un bloc
d’instructions entre les mots-clés begin
et end
:
begin
instruction
...
end
Les instructions utilisables dans un processus sont :
On distingue deux variantes de l’affectation :
<=
.=
.L’affectation bloquante se comporte comme l’affectation d’une variable
en VHDL :
elle prend effet immédiatement, ce qui permet de réutiliser le
résultat d’un calcul dans les instructions suivantes.
Si nous devions écrire le module Comparator
avec un processus, on
pourrait écrire :
always @(a_i, b_i) begin
lt_o = a_i < b_i;
gt_o = a_i > b_i;
// lt_o et gt_o sont à jour au moment d'exécuter cette instruction :
eq_o = ~(lt_o | gt_o);
end
L’affectation non bloquante prend effet à la fin du processus
(en VHDL, on dit qu’elle prend effet avec un retard fictif ).
Pour obtenir le même comportement que dans l’exemple ci-dessus,
il faudrait inclure lt_o
et gt_o
dans la liste de sensibilité
pour forcer un recalcul de eq_o
à chaque fois qu’ils sont mis à jour :
always @(a_i, b_i, lt_o, gt_o) begin
lt_o <= a_i < b_i;
gt_o <= a_i > b_i;
// Cette instruction utilise les anciennes valeurs de lt_o et gt_o :
eq_o <= ~(lt_o | gt_o);
end
Les signaux affectés dans un processus doivent être déclarés avec le mot-clé reg
ou integer
.
Dans le cas d’un port de sortie, vous pouvez utiliser ensemble les
deux mots-clés output
et reg
:
output reg y_o
if
L’instruction if
a une syntaxe similaire au langage C :
if (condition)
instruction séquentielle ou bloc
else
instruction séquentielle ou bloc
La condition est considérée comme vraie si elle retourne une valeur
différente de 0, x
ou z
.
Un bloc d’instructions est délimité par les mots-clés begin
et end
.
La clause else
est obligatoire si un processus décrit un circuit
combinatoire.
case
Cette instruction ressemble à l’instruction switch
du langage C,
et à l’instruction case
du VHDL.
case (expression)
valeur: instruction séquentielle ou bloc
valeur, valeur, ...: instruction séquentielle ou bloc
default: instruction séquentielle ou bloc
endcase
Il existe deux variantes casez
et casex
:
casez
traite les bits z
comme des valeurs indifférentes.casex
traite les bits z
et x
comme des valeurs indifférentes.Cela permet de simplifier l’écriture de certaines fonctions logiques,
comme dans l’exemple ci-dessous, avec l’instruction case
:
always @(cmd, a, b)
case (cmd)
3'b000, 3'b001: y = a + b;
3'b010: y = a - b;
3'b011: y = b - a;
3'b100, 3'b110: y = a & b;
3'b101, 3'b111: y = a | b;
endcase
Avec l’instruction casez
:
always @(cmd, a, b)
casez (cmd)
3'b00z: y = a + b;
3'b010: y = a - b;
3'b011: y = b - a;
3'b1z0: y = a & b;
3'b1z1: y = a | b;
endcase
Lorsqu’elles sont utilisées comme valeurs indifférentes, les valeurs x
et z
peuvent s’écrire sous la forme d’un point d’interrogation :
always @(cmd, a, b)
casez (cmd)
3'b00?: y = a + b;
3'b010: y = a - b;
3'b011: y = b - a;
3'b1?0: y = a & b;
3'b1?1: y = a | b;
endcase
for
Sa syntaxe est similaire à la boucle for
du langage C :
for (initialisation; condition; opération)
instruction séquentielle ou bloc
L’exemple ci-dessous utilise une boucle for
pour compter le nombre de bits
à 1 dans un vecteur représentant l’état de seize interrupteurs :
module NumberOfSwitchesUp (
input [15:0] switches_i,
output reg [4:0] count_o
);
always @ switches_i begin
integer n;
count_o = 0;
for (n = 0; n < 16; n = n + 1)
if (switches_i[n])
count_o = count_o + 1;
end
endmodule
Nous avons déclaré une variable locale n
de type integer
comme compteur
de boucle.
Grâce aux affectations bloquantes, les affectations count_o = count_o + 1
auront l’effet souhaité.
Il n’est pas nécessaire de déclarer d’autre variable.