Décrire la logique combinatoire
Utiliser assign et always @* pour produire une sortie qui dépend uniquement des entrées actuelles.
Ce qu'est un circuit combinatoire
Un circuit combinatoire ne garde pas d'état. Pour une combinaison d'entrées donnée, ses sorties ont toujours la même valeur, après le délai de propagation physique.
Un multiplexeur, un décodeur et une addition sont des exemples classiques. En Verilog, on peut les décrire avec une affectation continue ou un bloc procédural combinatoire.
L'affectation continue assign
assign convient bien à une expression courte. La cible est un réseau, généralement déclaré wire.
module alarm_logic (
input wire i_smoke,
input wire i_overtemp,
input wire i_service_mode,
output wire o_alarm
);
assign o_alarm = (i_smoke | i_overtemp) & ~i_service_mode;
endmoduleL'expression est active en permanence. Quand une entrée change, la simulation recalcule o_alarm.
Plusieurs affectations continues décrivent plusieurs chemins logiques en parallèle :
module compare_byte (
input wire [7:0] i_a,
input wire [7:0] i_b,
output wire o_equal,
output wire o_a_greater
);
assign o_equal = (i_a == i_b);
assign o_a_greater = (i_a > i_b);
endmoduleLe bloc always @*
Une décision avec plusieurs branches devient souvent plus lisible dans un bloc always.
module command_decode (
input wire [1:0] i_command,
output reg [3:0] o_enable
);
always @* begin
o_enable = 4'b0000;
if (i_command == 2'b00)
o_enable = 4'b0001;
else if (i_command == 2'b01)
o_enable = 4'b0010;
else if (i_command == 2'b10)
o_enable = 4'b0100;
else
o_enable = 4'b1000;
end
endmoduleLa cible o_enable est un reg parce qu'elle est affectée dans un bloc procédural. Le bloc reste pourtant combinatoire : il ne contient ni horloge ni mémorisation.
@* demande au simulateur de rendre le bloc sensible aux signaux lus dans son contenu. Cette notation Verilog-2001 évite d'oublier manuellement une entrée dans une liste telle que @(i_command).
Utiliser les affectations bloquantes
Dans un bloc combinatoire, les affectations bloquantes = sont le choix habituel. La valeur est mise à jour avant l'instruction suivante du même passage dans le bloc.
module add_or_sub (
input wire [7:0] i_a,
input wire [7:0] i_b,
input wire i_subtract,
output reg [8:0] o_result
);
reg [8:0] v_a_ext;
reg [8:0] v_b_ext;
always @* begin
v_a_ext = {1'b0, i_a};
v_b_ext = {1'b0, i_b};
if (i_subtract)
o_result = v_a_ext - v_b_ext;
else
o_result =
Les variables intermédiaires sont calculées avant o_result. Avec des affectations non bloquantes ici, la simulation pourrait réutiliser leurs anciennes valeurs et ne plus représenter le circuit attendu.
Affecter chaque sortie dans tous les cas
Pour rester combinatoire, chaque sortie et chaque variable locale doit recevoir une valeur sur tous les chemins possibles.
Une méthode simple consiste à attribuer une valeur par défaut au début du bloc, puis à la remplacer dans certaines branches :
always @* begin
o_grant = 1'b0;
if (i_request & i_ready)
o_grant = 1'b1;
endSans la première affectation, o_grant devrait conserver sa valeur lorsque la condition est fausse. Le synthétiseur aurait alors besoin d'un latch.
assign ou always ?
Les deux styles peuvent produire le même matériel.
- Utiliser
assignpour une équation directe ou un choix très court. - Utiliser
always @*pour plusieurs sorties, des valeurs par défaut ou des branches imbriquées. - Ne pas piloter la même cible avec les deux styles.
- Garder
=dans les blocs combinatoires et<=dans les blocs séquentiels.
À retenir
- La logique combinatoire dépend uniquement des entrées actuelles.
assignpilote unwire.- Une sortie affectée dans
always @*se déclareregen Verilog. @*construit la liste de sensibilité à partir des signaux lus.- Toutes les sorties doivent recevoir une valeur sur tous les chemins.
📝 Tester mes connaissances - Quiz du chapitre