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 `timescale ou à 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 `timescale active. Avec `timescale 1ns / 1ps, #5 fait 5 nanosecondes. Avec `timescale 1us / 1ns, #5 fait 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.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

Commencer