Ce que « temps » veut dire en Verilog
Verilog n'a pas de notion intégrée de secondes. Le simulateur avance des « unités de temps » - des ticks entiers arbitraires - et ton code utilise #N pour en attendre N. La directive `timescale est ce qui mappe ces ticks au temps réel.
`timescale 1ns / 1ps
module test;
initial begin
$display("t = %0t", $time); // 0
#5;
$display("t = %0t", $time); // 5 ns
#1.5;
$display("t = %0t", $time); // 6500 ps (ou 6.5 ns)
$finish;
end
endmodule
Deux paramètres :
- Unité (premier nombre) : ce que
#1veut dire.1nsdit qu'un tick est une nanoseconde. - Précision (second nombre) : à quel point la simulation suit finement le temps dans cette unité.
1psdit que les délais fractionnaires sont arrondis à la picoseconde.
La précision ne peut pas être plus grossière que l'unité (1ps / 1ns est illégal). Choix courants :
1ns / 1ps- le standard de facto. Tout en nanosecondes, précision sub-nanoseconde pour tout délai de porte.1ps / 1ps- pour modéliser des circuits extrêmement rapides ou quand chaque délai est sub-nanoseconde.1us / 1ns- pour les simulations lentes à l'échelle embarquée (temps d'octets UART, protocoles lents).
Où la mettre
`timescale est une directive du compilateur, pas une construction au niveau module. Elle va tout en haut d'un fichier, avant tout module :
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
Sa portée est « depuis ce point dans l'ordre de compilation, jusqu'au prochain `timescale ou la fin de la compilation ». Ça a une implication subtile : un fichier sans `timescale explicite hérite de ce que le précédent fichier compilé a déclaré, ce qui dépend de l'ordre dans lequel le compilateur lit les fichiers. Évite la surprise : mets `timescale en haut de chaque fichier.
L'éditeur du navigateur sur ces docs fixe un timescale par défaut pour toi (typiquement 1ns / 1ps), c'est pourquoi les exemples des docs précédents marchaient sans en déclarer un. Dans un vrai projet tu serais explicite.
#delay en pratique
Après `timescale 1ns / 1ps :
#5 // attend 5 ns
#100 // attend 100 ns
#1.5 // attend 1.5 ns (la précision le permet)
#0.001 // attend 1 ps (juste au-dessus de la précision)
#0 // délai zéro ; utile pour ordonner des événements au même temps
À l'intérieur d'un bloc initial ou always, #N bloque le flux procédural pendant N unités de temps. Le simulateur pause ce bloc (les autres blocs concurrents continuent) et reprend après le délai.
Tu peux préfixer une assignation avec un délai :
#10 a = 1; // attends 10 ns, puis assigne
data <= #2 new_value; // planifie l'assignation non-blocking 2 ns à partir de maintenant
La première forme est le pilier des testbenches. La seconde (non-blocking retardée) est utilisée dans la simulation au niveau portes pour modéliser le délai de propagation.
Générer une horloge
Le motif classique :
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
Avec un timescale 1ns / 1ps, #5 fait 5 ns. L'horloge bascule toutes les 5 ns, donnant une période de 10 ns - une horloge à 100 MHz. Pour changer la fréquence, change le délai :
| Demi-période | Période | Fréquence |
|---|---|---|
#1 | 2 ns | 500 MHz |
#2.5 | 5 ns | 200 MHz |
#5 | 10 ns | 100 MHz |
#10 | 20 ns | 50 MHz |
#25 | 50 ns | 20 MHz |
#50 | 100 ns | 10 MHz |
Si tu veux une demi-période fractionnaire (#2.5), ta précision doit la supporter - la précision 1ps gère n'importe quoi jusqu'à 0.001 ns, donc toute fréquence raisonnable est OK.
Mélanger les timescales entre fichiers
Un vrai design a beaucoup de fichiers, et ils peuvent déclarer des timescales différents. Le simulateur utilise le timescale propre à chaque fichier pour interpréter les délais dans ce fichier. Si module_a.v dit `timescale 1ns / 1ps et utilise #5, c'est 5 ns. Si module_b.v dit `timescale 1us / 1ns et utilise #5, c'est 5 us.
C'est largement invisible parce que le simulateur présente un axe de temps global quoi qu'il en soit - mais ça veut dire que le même #N dans deux fichiers peut vouloir dire des choses très différentes. La correction : choisis un timescale (le standard industriel est 1ns / 1ps) et mets-le en haut de chaque fichier. Ne mélange pas.
Les délais ne sont pas synthétisables
Point crucial : #delay n'existe que pour la simulation. L'outil de synthèse lit :
// Dans le RTL synthétisable - FAUX
always @(posedge clk) begin
out <= #2 in;
end
…et soit ignore le #2 (la plupart des outils) soit rejette la construction (les linters plus stricts). Le timing du vrai matériel est déterminé par l'horloge et par le délai de propagation des portes - les deux invisibles au code source.
La règle : utilise # seulement dans les testbenches. Le RTL synthétisable n'a pas de délais #. Si tu te retrouves à vouloir un délai dans du code synthétisable, ce que tu veux vraiment, c'est un compteur qui décompte sur l'horloge - c'est comme ça que le vrai matériel « attend ».
$time vs $realtime
Deux façons de lire le temps de simulation courant :
$timeretourne un entier 64 bits en unités de l'unité du timescale courant.$realtimeretourne unrealen unités de l'unité du timescale courant, mais avec pleine précision.
Pour le logging de testbench, $time suffit presque toujours. Tends la main vers $realtime seulement quand tu as besoin d'une précision sub-tick dans les instructions d'affichage.
Conseils pratiques
- Déclare toujours
`timescaleen haut de chaque fichier.1ns / 1psest le défaut sûr. - Utilise
#delayseulement dans les testbenches. Traite son absence dans le code synthétisable comme une règle statique. - Fais correspondre les périodes d'horloge à ta fréquence cible. Si tu simules un design à 50 MHz, utilise une période d'horloge de 20 ns - des périodes mal accordées peuvent masquer des bugs sensibles au timing.
- Pour le stimulus compté en cycles, utilise
@(posedge clk)plutôt que#. C'est robuste face aux changements de période d'horloge.
La suite
Tu as maintenant vu chaque document de ces tutoriels Verilog. Depuis les fondamentaux du langage (wire vs reg, modules, opérateurs), à travers les blocs procéduraux et le control-flow, dans le design synchrone et les machines à états, et enfin l'outillage de testbench qui prouve que tout marche. Il est temps de construire quelque chose - le playground à côté de cette doc est le même simulateur qu'on utilise depuis le début, prêt pour n'importe quel module que tu esquisses.
Questions fréquentes
Que veut dire `timescale 1ns / 1ps en Verilog ?
Ça dit au simulateur : « une unité de temps dans ce fichier est une nanoseconde, et les temps sont suivis avec une précision en picosecondes ». Après cette directive, #5 attend 5 ns, #1.5 attend 1.5 ns (arrondi à la picoseconde), et $time est reporté en nanosecondes. Le premier nombre est l'unité ; le second est la précision.
Faut-il `timescale dans chaque fichier Verilog ?
Bonne pratique : oui. La portée de la directive se termine au prochain \timescaleou à la fin de la compilation, donc les fichiers sans en héritent ce que le précédent fichier compilé a déclaré. Ça rend le timing non déterministe entre les builds. Mets`timescale 1ns / 1ps` en haut de chaque fichier source - c'est la convention la plus courante - et tu n'auras jamais de surprise.
Que veut dire #5 en Verilog ?
#5 avance le temps de simulation de 5 unités de temps. L'unité vient de la directive \timescaleactive. Avec`timescale 1ns / 1ps, #5fait 5 nanosecondes. Avec`timescale 1us / 1ns, #5fait 5 microsecondes. Le nombre peut être fractionnaire -#1.5` marche si ta précision est plus fine que l'unité.
#delay est-il synthétisable en Verilog ?
Non. #delay n'affecte que la simulation - l'outil de synthèse l'ignore ou le rejette. Le timing du vrai matériel vient du signal d'horloge et du délai de propagation des portes, pas des instructions #. Utilise # librement dans les testbenches ; ne l'écris jamais dans du RTL synthétisable.