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 :
%dpadde à une largeur par défaut basée sur la taille de l'opérande. Pour unreg8 bits, c'est 3 caractères (place pour255). Les espaces de tête peuvent paraître moches dans une sortie tabulaire - utilise%0dpour les supprimer.%h,%b,%oont un padding similaire par défaut. La plupart du code de testbench utilise les variantes%0quand 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 :
| Specifier | Signification |
|---|---|
%b | binaire |
%d | décimal (signé si le signal est signé) |
%h ou %x | hex |
%o | octal |
%c | un seul caractère ASCII (8 bits bas) |
%s | string |
%t | temps de simulation |
%m | nom hiérarchique de la portée courante |
%% | un % littéral |
%0X | pas 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
$monitorpeut être actif. L'appeler à nouveau remplace la liste de surveillance précédente. Utilise$monitoroffet$monitoronpour supprimer temporairement et réactiver. - Les changements dans le même pas de temps se collapsent en un seul affichage. Si
aetbchangent 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'échantillonnagealways @(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);.