Menu

Verilog $display et $monitor : afficher depuis un testbench

Comment $display, $write et $monitor fonctionnent - les format specifiers que tu utiliseras, la différence entre eux, et quand chacun est le bon outil.

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

La famille printf de C en Verilog

Trois tâches système te laissent afficher sur la stdout du simulateur :

  • $display - affiche une fois, ajoute un saut de ligne.
  • $write - affiche une fois, pas de saut de ligne.
  • $monitor - affiche automatiquement chaque fois qu'un signal surveillé change.

Les trois prennent une chaîne de format et une liste d'arguments, comme printf. Les format specifiers sont similaires mais spécifiques à Verilog.

$display : le défaut

$display est le pilier :

Tu verras quelque chose comme :

Hello, world.
byte_val  = ab
byte_val  = 10101011
byte_val  = 171
byte_val  = 171 (no padding)
multi: nibble=1010 count=42

Notes :

  • %d padde à une largeur par défaut basée sur la taille de l'opérande. Pour un reg 8 bits, c'est 3 caractères (place pour 255). Les espaces de tête peuvent paraître moches dans une sortie tabulaire - utilise %0d pour les supprimer.
  • %h, %b, %o ont un padding similaire par défaut. La plupart du code de testbench utilise les variantes %0 quand l'alignement n'est pas utile.
  • Le saut de ligne à la fin est automatique. Ne mets pas \n à la fin d'une chaîne de format $display - tu obtiendrais une ligne vide.

Format specifiers

L'ensemble que Verilog supporte :

SpecifierSignification
%bbinaire
%ddécimal (signé si le signal est signé)
%h ou %xhex
%ooctal
%cun seul caractère ASCII (8 bits bas)
%sstring
%ttemps de simulation
%mnom hiérarchique de la portée courante
%%un % littéral
%0Xpas de padding de tête, pour n'importe lequel de %b, %d, etc.

%b, %d, %h, %o sont les quatre que tu utiliseras 95% du temps. %t est le suivant le plus courant - chaque fois que tu veux une ligne de log horodatée.

$write : pas de saut de ligne

$write est identique à $display sauf qu'il n'ajoute pas de saut de ligne :

Sortie :

abc
done

Utile pour construire une seule ligne à partir d'un corps de boucle :

$write("[");
for (integer i = 0; i < 8; i = i + 1) $write("%h ", arr[i]);
$display("]");

$monitor : affichage auto sur changement

$monitor enregistre une liste de surveillance. Le simulateur ré-évalue et affiche chaque fois qu'un signal mentionné dans la chaîne de format change :

Tu verras trois lignes, une pour chaque changement dans les entrées. Pas besoin d'appeler manuellement $display après chaque changement de stimulus - $monitor le fait.

Deux limitations :

  • Un seul $monitor peut être actif. L'appeler à nouveau remplace la liste de surveillance précédente. Utilise $monitoroff et $monitoron pour supprimer temporairement et réactiver.
  • Les changements dans le même pas de temps se collapsent en un seul affichage. Si a et b changent tous deux au temps 5, le monitor tire une fois avec les deux nouvelles valeurs, pas deux fois.

Quand utiliser chacun

  • $display : la plupart des sorties de testbench. Appelle-le explicitement après le stimulus, après les transitions d'état importantes, ou à l'intérieur d'un bloc d'échantillonnage always @(posedge clk).
  • $write : quand tu veux construire une seule ligne à partir d'une boucle ou de plusieurs petits morceaux.
  • $monitor : quand tu veux suivre un petit ensemble de signaux en continu et ne voir la sortie que quand ils changent. Utile pour le debug initial ; plus dur à utiliser dans les scripts de régression parce que la sortie n'est pas déterministe en termes de nombre total de lignes.

Pour la plupart des workflows, $display couvre tout. Tends la main vers $monitor seulement quand une sortie continue pilotée par les changements est vraiment ce que tu veux.

Travailler avec le temps

$time retourne le temps de simulation courant comme un entier 64 bits. Associe-le avec %0t :

$display("at %0t: signal flipped", $time);

La sortie ressemble à at 25: signal flipped (l'unité dépend de ton timescale).

Si tu as besoin d'une précision sub-tick (rare), utilise $realtime à la place - il retourne un real.

%t formate automatiquement le temps avec une largeur par défaut que le simulateur choisit. %0t retire le padding.

Échantillonnage sur front d'horloge

Un idiome propre pour monitorer les designs séquentiels : un bloc always @(posedge clk) séparé qui affiche une fois par cycle :

Ce motif d'échantillonnage garantit une ligne de log par horloge - parfait pour les tests de régression qui font du pattern matching sur la sortie.

Logger dans un fichier

Ouvre un fichier avec $fopen, log avec $fdisplay (qui marche comme $display mais écrit dans un handle de fichier) :

integer fd;
initial begin
    fd = $fopen("results.txt", "w");
    $fdisplay(fd, "test=%s status=%s", test_name, status);
    $fclose(fd);
end

$fopen retourne un handle 32 bits ; passe-le en premier argument à $fdisplay, $fwrite, $fstrobe, etc. Les fonctions sont sinon identiques à leurs sœurs qui affichent sur la console.

La suite

$display et ses amis te donnent des logs textuels. Pour le debug visuel - voir les signaux comme des tensions au cours du temps - tu veux un waveform VCD. Le prochain document, Dumpfile et VCD, couvre $dumpfile et $dumpvars, les deux appels qui transforment ta simulation en un waveform graphique que tu peux parcourir.

Questions fréquentes

Quelle est la différence entre $display et $monitor en Verilog ?

$display affiche une fois, immédiatement, quand il est exécuté - comme printf en C. $monitor enregistre une liste de surveillance ; chaque fois qu'un signal de la liste change, le message formaté est affiché automatiquement. Un seul $monitor peut être actif à la fois ; l'appeler à nouveau remplace la liste de surveillance précédente.

Quels format specifiers Verilog $display supporte-t-il ?

Les courants : %b (binaire), %d (décimal), %h (hex), %o (octal), %c (un seul caractère depuis l'octet bas), %s (string), %t (temps de simulation), %m (nom hiérarchique de l'instance). Utilise la forme %0d pour supprimer le padding zéro - %d padde à une largeur par défaut, %0d ne produit aucun padding.

Qu'est-ce que $write en Verilog ?

$write est comme $display mais n'ajoute pas de saut de ligne. Utile quand tu veux construire une ligne de sortie à partir de plusieurs appels. Le $display de fin de ligne (sans arguments ou avec un saut de ligne final) termine la ligne.

Comment afficher le temps de simulation en Verilog ?

Utilise $time (ou $realtime pour une résolution sub-tick) avec le format specifier %t : $display("at time %t: ...", $time);. Utilise %0t pour supprimer le padding par défaut. Pour un compte entier simple d'unités de temps, %0d avec $time marche aussi : $display("t=%0d", $time);.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER