Menu

Timescale et délais en Verilog : contrôler le temps de simulation

Comment la directive timescale fixe l'unité de #delay`, les règles pour combiner différentes unités entre fichiers, et comment les délais interagissent avec la logique cadencée.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

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 #1 veut dire. 1ns dit qu'un tick est une nanoseconde.
  • Précision (second nombre) : à quel point la simulation suit finement le temps dans cette unité. 1ps dit 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ériodePériodeFréquence
#12 ns500 MHz
#2.55 ns200 MHz
#510 ns100 MHz
#1020 ns50 MHz
#2550 ns20 MHz
#50100 ns10 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 :

  • $time retourne un entier 64 bits en unités de l'unité du timescale courant.
  • $realtime retourne un real en 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 `timescale en haut de chaque fichier. 1ns / 1ps est le défaut sûr.
  • Utilise #delay seulement 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.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER