Code SystemVerilog : | Sélectionner tout |
1 2 | a <= b; b <= a; |
Code C : | Sélectionner tout |
temp = a; a = b; b = temp;
Il y a de l'idée, mais ce n'est pas encore cela. Car en HDL (Hardware Description Language), il n'y a pas de « variables » comme dans les langages de programmation, mais il y a une notion qui semble s'en rapprocher, celle des registres.
Voyons une démonstration avec deux registres 3 bits synchrones (out1 et out2) dont on peut charger le contenu en mettant le signal de contrôle enable à l'état haut, et que l'on peut permuter en mettant le signal de contrôle swap à l'état haut :
Code SystemVerilog : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | module essai ( input logic clk, enable, swap, input logic[2:0] in1, in2, output logic[2:0] out1, out2 ); always_ff @(posedge clk) begin // sensibilité au front montant de l'horloge if (enable) begin // si chargement des registres out1 <= in1; out2 <= in2; end else if (swap) begin // permutation des sorties out2 <= out1; out1 <= out2; end end endmodule |
La simulation fonctionnelle ci-dessous montre un exemple avec le chargement des registres out1 et out2 avec les valeurs 3 et 5 présentées en entrée, sur le premier front montant de l'horloge clk repéré en rouge. Ces valeurs sont maintenues en sortie après ce front, comme mémorisées. On voit ensuite l'effet de la permutation des valeurs de sortie au 2è front montant repéré en rouge :
L'analyse RTL donne le schéma suivant, où j'ai mis en surbrillance le flot des transferts lorsque le signal swap est activé (swap=1, enable=0) :
swap=1, enable=0
On voit en rouge et en bleu que la sortie de chaque registre (3 bits) est dirigée vers l'entrée de l'autre registre, ce qui permet la permutation attendue des signaux en sortie.
Une fois le transfert effectué au front d'horloge, on peut voir ci-dessous le flot formant une boucle où la sortie de chaque registre est redirigée vers son entrée pour maintenir son état :
swap=0, enable=0
Alors comment bien interpréter cette écriture quelque peu déroutante ?
Code SystemVerilog : | Sélectionner tout |
1 2 | out2 <= out1; out1 <= out2; |
La sémantique de l'affectation dans les blocs always_ff
L'opérateur <= est donc bien un opérateur d'« affectation ».
- Le signal du bus en sortie du registre évoqué à droite du signe <= porte l'état du registre à un instant présent. Et cet état est transmis à l'entrée du registre évoqué à gauche du signe <=, et sera pris en compte au front d'horloge suivant. Une ligne comme out2 <= out1; est donc une opération de transfert entre registres.
- Il n'y a pas d'ordre d'exécution des affectations dans un bloc always_ff. Toutes les instructions d'affectation sont concurrentes et exécutées sur le même front d'horloge.
On peut aussi bien écrire :
Code : Sélectionner tout 1
2out2 <= out1; out1 <= out2;
Code : Sélectionner tout 1
2out1 <= out2; out2 <= out1;
Ce dernier point souligné en gras est fondamental.
Mais alors... Que fait cet autre code ci-dessous ?
Code SystemVerilog : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | module quefaitcetruc ( input logic clk, in_serial, output logic[3:0] Q ); always_ff @(posedge clk) begin // sensibilité au front montant de l'horloge Q[3] <= in_serial; Q[2] <= Q[3]; Q[1] <= Q[2]; Q[0] <= Q[1]; end endmodule |
Réponse :