Conditions, case et latchs involontaires
Écrire des choix complets, rendre les priorités visibles et éviter les mémoires créées par oubli.
Ce que décrit un if
Dans un bloc combinatoire, une chaîne if puis else if décrit une priorité. La première condition vraie choisit le résultat.
module request_priority (
input wire [2:0] i_request,
output reg [2:0] o_grant,
output reg o_valid
);
always @* begin
o_grant = 3'b000;
o_valid = 1'b1;
if (i_request[2])
o_grant = 3'b100;
else if (i_request[1])
o_grant = 3'b010;
else if (i_request[0])
o_grant = 3'b001;
else
o_valid = 1'b0;
end
endmoduleLa requête 2 est prioritaire sur les deux autres. Les valeurs par défaut couvrent aussi le cas sans requête.
Utiliser case pour un choix par valeur
case convient lorsque plusieurs valeurs d'une même expression choisissent un résultat.
module mux4_byte (
input wire [1:0] i_select,
input wire [7:0] i_a,
input wire [7:0] i_b,
input wire [7:0] i_c,
input wire [7:0] i_d,
output reg [7:0] o_data
);
always @* begin
o_data = 8'h00;
case (i_select)
2'b00: o_data = i_a;
2'b01: o_data = i_b;
2'b10: o_data
Les quatre valeurs binaires sont écrites et une branche default traite les valeurs inconnues vues en simulation. La valeur initiale de o_data rend aussi l'intention combinatoire immédiate.
Comment apparaît un latch
Ce bloc est incomplet :
module incomplete_gate (
input wire i_enable,
input wire [7:0] i_data,
output reg [7:0] o_data
);
always @* begin
if (i_enable)
o_data = i_data;
end
endmoduleLorsque i_enable vaut 0, aucune nouvelle valeur n'est donnée à o_data. Pour respecter cette description, le circuit doit conserver l'ancienne valeur. Le synthétiseur infère donc un latch.
Si l'objectif était de sortir zéro lorsque le bloc est désactivé, il faut l'écrire :
always @* begin
o_data = 8'h00;
if (i_enable)
o_data = i_data;
endLe latch n'est pas une erreur de langage. Il devient un problème lorsqu'il apparaît par oubli dans une logique que l'on pensait combinatoire.
Valeurs par défaut
Pour un bloc combinatoire de plusieurs lignes :
- attribuer une valeur par défaut à chaque sortie ;
- remplacer ces valeurs dans les branches utiles ;
- vérifier les chemins
if,elseetcase; - regarder les avertissements de latch après synthèse ou lint.
Cette méthode est souvent plus lisible que répéter chaque sortie dans toutes les branches.
case, casez et casex
case compare les bits tels qu'ils sont, y compris x et z en simulation. casez permet d'ignorer certains bits marqués comme haute impédance ou indifférents.
casex traite aussi les x comme indifférents. Cela peut cacher une entrée inconnue et donner une simulation trop optimiste. Pour les décodeurs RTL débutants, rester sur case avec une branche default est plus sûr.
Conditions dans un bloc cadencé
Dans un bloc always @(posedge i_clk), une branche manquante n'infère pas un latch. Elle demande simplement au registre de conserver sa valeur.
always @(posedge i_clk) begin
if (i_load)
o_data <= i_data;
endIl faut donc lire le contexte avant de conclure :
- branche manquante dans
always @*: mémoire combinatoire, souvent un latch involontaire ; - branche manquante dans un bloc cadencé : conservation normale du registre.
À retenir
- Une chaîne
if/else ifcrée une priorité visible. caseest pratique pour choisir selon la valeur d'un même signal.- Un bloc combinatoire doit affecter toutes ses sorties sur tous les chemins.
- Une sortie oubliée peut créer un latch.
casexpeut masquer des valeurs inconnues ; utilisercasepar défaut.
📝 Tester mes connaissances - Quiz du chapitre