Le bloc de construction : un flip-flop D
Tout en design synchrone revient à un minuscule morceau de matériel : le flip-flop D. Il a une entrée d'horloge, une entrée de données et une sortie de données. À chaque front montant d'horloge, il capture la valeur de D et la maintient jusqu'au front suivant. C'est tout.
En Verilog :
always @(posedge clk) begin
q <= d;
end
Trois lignes, un flip-flop. Le <= est non-blocking, ce qui est exactement le comportement des vrais flip-flops (couvert dans Blocking vs Non-blocking). La sensibilité posedge clk est ce qui le rend séquentiel.
Empile beaucoup de ceux-ci avec de la logique combinatoire entre eux et tu as n'importe quel circuit synchrone que tu voudrais construire.
Un registre avec reset
Les vrais designs ont toujours besoin d'un reset - un moyen de mettre le système dans un état connu au power-on ou à la demande :
C'est un reset synchrone - la condition de reset est évaluée sur un front d'horloge comme n'importe quelle autre entrée. Le synthétiseur produit un flip-flop avec un mux 2 vers 1 sur son entrée de données : quand reset est haut, le mux alimente zéro ; sinon il alimente d.
Pour la plupart des designs, le reset synchrone est le bon choix. C'est un timing plus simple, ça joue bien avec les FPGA, et le signal de reset peut venir de n'importe où - il n'a pas besoin de sa propre source alignée sur l'horloge.
Un registre avec enable
Souvent tu veux un registre qui ne se met à jour que quand quelque chose le dit. Utilise un if à l'intérieur du bloc cadencé, et compte sur le comportement implicite « garde la valeur précédente » du else manquant :
C'est le « registre à load enable » canonique. Il apparaît partout - registres de configuration, étages de pipeline qui n'avancent que quand l'aval est prêt, compteurs qui pausent et reprennent. L'omission du dernier else est délibérée et sûre à l'intérieur d'un bloc cadencé : un flip-flop se souvient déjà de sa valeur précédente, donc « ne rien faire » veut dire « maintenir ».
Dans un bloc combinatoire le même code inférerait un latch. Règles différentes, même syntaxe.
Un compteur
Un compteur est un registre dont la prochaine valeur est sa valeur courante plus un :
Le compteur s'incrémente à chaque cycle d'horloge quand enable est haut. Après 16 cycles il revient à 0 (parce qu'on a déclaré 4 bits et 15 + 1 déborde à 0). Ce wrapping est intrinsèque à l'arithmétique N bits et c'est exactement comme se comporte un vrai compteur matériel.
Un registre à décalage
Empiler des flip-flops te donne un registre à décalage. L'astuce est de faire tous les décalages dans une seule instruction non-blocking :
Le corps est out <= {out[WIDTH-2:0], in} - concatène les bits bas du out courant avec le nouveau in, et assigne le tout. Comme c'est non-blocking, le RHS lit l'ancien out avant que le LHS ne se mette à jour. L'effet est un décalage N bits propre en un seul cycle d'horloge.
C'est le motif registre à décalage dans sa forme la plus petite. Il se généralise aux LFSR, émetteurs série, désérialiseurs - tout design où les données se déplacent à travers une chaîne de flip-flops à chaque horloge.
Pipelines
Un pipeline est une chaîne de registres séparés par de la logique combinatoire. Chaque étage traite les données de l'étage précédent et alimente le suivant :
Trois étages, trois cycles de latence, mais un nouveau résultat chaque cycle une fois le pipeline plein. La latence est de 3 horloges parce que les données passent à travers trois flip-flops ; le débit est de 1 opération par horloge parce que les trois étages travaillent simultanément sur des entrées différentes.
C'est comme ça que les designs haute performance atteignent leurs objectifs de débit : garde les étages courts, pipeline plus profondément, et laisse le parallélisme faire le travail.
Reset asynchrone (quand tu en as besoin)
Parfois tu ne peux pas attendre un front d'horloge pour asserter le reset - la puce est en cours d'arrêt, l'horloge est gatée, un watchdog externe tire la ligne. Dans ces cas :
always @(posedge clk or negedge reset_n) begin
if (~reset_n) q <= 0;
else q <= d;
end
La liste de sensibilité a maintenant à la fois un front d'horloge et un front de reset. Le flip-flop répond immédiatement à l'un comme à l'autre. reset_n est par convention actif-bas (asserté quand bas), c'est pour ça que le test est ~reset_n.
Le reset asynchrone a des compromis : plus dur à analyser en timing, peut causer de la métastabilité s'il n'est pas géré soigneusement à la désassertion, pas portable sur toutes les architectures FPGA. Utilise le reset synchrone par défaut, l'async seulement quand le design l'exige.
La suite
Tu peux maintenant construire n'importe quel datapath synchrone. Le prochain document - Machines à états finis - met ensemble un registre cadencé avec une instruction case pour produire l'idiome FSM standard : le pilier de chaque contrôleur, moteur de protocole et bloc de décision en design numérique.
Questions fréquentes
Qu'est-ce que la logique cadencée en Verilog ?
De la logique dont les mises à jour sont contrôlées par un signal d'horloge. L'idiome standard est always @(posedge clk) target <= next_value; - à chaque front montant de clk, target capture next_value. Cette seule ligne décrit un flip-flop en matériel. En empiler beaucoup avec de la logique combinatoire entre eux construit compteurs, registres à décalage, pipelines - tout ce qui est synchrone.
Quelle est la différence entre reset synchrone et asynchrone en Verilog ?
Le reset synchrone utilise always @(posedge clk) if (reset) ... - le reset est échantillonné sur un front d'horloge comme n'importe quelle autre entrée. Le reset asynchrone utilise always @(posedge clk or negedge reset_n) if (~reset_n) ... - le bloc se déclenche soit sur un front d'horloge soit sur une assertion de reset, donc le reset prend effet immédiatement. Synchrone est le choix par défaut ; asynchrone sert quand le reset doit être garanti de prendre effet même si l'horloge est morte.
Comment construire un pipeline en Verilog ?
Empile plusieurs étages de always @(posedge clk) stageN_reg <= stageN_combinational; - la logique combinatoire de chaque étage alimente le registre de l'étage suivant, et chaque registre capture sur le même front d'horloge. Le résultat est un pipeline où de nouvelles données entrent chaque cycle et sortent N cycles plus tard, avec un débit d'un résultat par horloge.
Qu'est-ce qu'un registre à décalage en Verilog ?
Une chaîne de flip-flops où la sortie de chacun alimente l'entrée du suivant. Chaque front d'horloge décale chaque bit d'une position. La version Verilog canonique utilise des assignations non-blocking : out <= {out[N-2:0], in}; - cette seule ligne crée un registre à décalage N bits qui prend un bit par cycle depuis in.