Menu

Verilog $display und $monitor: Aus einer Testbench drucken

Wie $display, $write und $monitor funktionieren - die Format-Spezifizierer, die du nutzen wirst, der Unterschied zwischen ihnen und wann welches das richtige Werkzeug ist.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Die C-printf-Familie von Verilog

Drei System-Tasks lassen dich auf die stdout des Simulators schreiben:

  • $display - einmal drucken, Newline anhängen.
  • $write - einmal drucken, ohne Newline.
  • $monitor - automatisch jedes Mal drucken, wenn sich ein beobachtetes Signal ändert.

Alle drei nehmen einen Format-String und eine Argumentliste, wie printf. Die Format-Spezifizierer sind ähnlich, aber Verilog-spezifisch.

$display: Der Default

$display ist das Arbeitspferd:

Du wirst etwa Folgendes sehen:

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

Anmerkungen:

  • %d polstert auf eine Default-Breite basierend auf der Operandengröße. Für ein 8-Bit-reg sind das 3 Zeichen (Platz für 255). Die führenden Leerzeichen können in tabellarischer Ausgabe hässlich aussehen - nutze %0d, um sie zu unterdrücken.
  • %h, %b, %o haben ähnliches Default-Padding. Der meiste Testbench-Code nutzt %0-Varianten, wenn Ausrichtung nicht hilfreich ist.
  • Der Newline am Ende ist automatisch. Setze kein \n ans Ende eines $display-Format-Strings - du bekommst eine Leerzeile.

Format-Spezifizierer

Was Verilog unterstützt:

SpezifiziererBedeutung
%bbinär
%ddezimal (signed, wenn Signal signed)
%h oder %xhex
%ooktal
%ceinzelnes ASCII-Zeichen (untere 8 Bit)
%sString
%tSimulationszeit
%mhierarchischer Name des aktuellen Scopes
%%ein literales %
%0Xkein führendes Padding, für %b, %d usw.

%b, %d, %h, %o sind die vier, die du in 95% der Fälle nutzt. %t ist das nächsthäufigste - immer, wenn du eine Log-Zeile mit Zeitstempel willst.

$write: Kein Newline

$write ist identisch zu $display, hängt aber keinen Newline an:

Ausgabe:

abc
done

Nützlich, um eine einzelne Zeile aus einem Schleifen-Body aufzubauen:

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

$monitor: Auto-Druck bei Änderung

$monitor registriert eine Beobachtungsliste. Der Simulator wertet neu aus und druckt, wenn sich ein im Format-String erwähntes Signal ändert:

Du wirst drei Zeilen sehen, eine pro Eingangsänderung. Kein manuelles $display nach jeder Stimulus-Änderung nötig - $monitor macht es.

Zwei Einschränkungen:

  • Nur ein $monitor kann aktiv sein. Ein weiterer Aufruf ersetzt die vorherige Beobachtungsliste. Nutze $monitoroff und $monitoron, um temporär zu unterdrücken und wieder zu aktivieren.
  • Änderungen im selben Zeitschritt fallen auf einen Druck zusammen. Ändern sich a und b beide zur Zeit 5, feuert der Monitor einmal mit beiden neuen Werten, nicht zweimal.

Wann was nehmen

  • $display: die meiste Testbench-Ausgabe. Explizit nach Stimulus, nach wichtigen Zustandsübergängen oder innerhalb eines abtastenden always @(posedge clk)-Blocks aufrufen.
  • $write: wenn du eine einzelne Zeile aus einer Schleife oder mehreren kleinen Stücken aufbauen willst.
  • $monitor: wenn du einen kleinen Satz Signale kontinuierlich verfolgen und nur Ausgabe sehen willst, wenn sie sich ändern. Nützlich fürs initiale Debuggen; in Regressions-Skripten schwieriger, weil die Ausgabe in der Gesamtanzahl Zeilen nicht deterministisch ist.

Für die meisten Workflows deckt $display alles ab. Greife zu $monitor nur, wenn kontinuierliche, änderungsgetriebene Ausgabe wirklich das ist, was du willst.

Mit Zeit arbeiten

$time liefert die aktuelle Simulationszeit als 64-Bit-Integer. Kombiniere mit %0t:

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

Die Ausgabe sieht aus wie at 25: signal flipped (die Einheit hängt von deinem timescale ab).

Brauchst du Sub-Tick-Präzision (selten), nimm stattdessen $realtime - es liefert einen real.

%t formatiert Zeit automatisch mit einer Default-Breite, die der Simulator wählt. %0t entfernt das Padding.

Abtasten an der Taktflanke

Ein sauberes Idiom zum Beobachten sequentieller Designs: ein separater always @(posedge clk)-Block, der einmal pro Zyklus druckt:

Dieses Sample-Muster garantiert eine Log-Zeile pro Takt - perfekt für Regressions-Tests, die per Pattern-Matching auf der Ausgabe arbeiten.

In Datei loggen

Öffne eine Datei mit $fopen, logge mit $fdisplay (das wie $display funktioniert, aber in einen File-Handle schreibt):

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

$fopen liefert einen 32-Bit-Handle; übergib ihn als erstes Argument an $fdisplay, $fwrite, $fstrobe usw. Die Funktionen sind ansonsten identisch zu ihren Konsolen-Druck-Geschwistern.

Wie es weitergeht

$display und seine Verwandten geben dir Text-Logs. Für visuelles Debugging - Signale als Spannungen über Zeit beobachten - willst du eine VCD-Waveform. Das nächste Doc, Dumpfile und VCD, behandelt $dumpfile und $dumpvars, die zwei Aufrufe, die deine Simulation in eine grafische Waveform verwandeln, durch die du scrollen kannst.

Häufig gestellte Fragen

Was ist der Unterschied zwischen $display und $monitor in Verilog?

$display druckt einmal, sofort, wenn es ausgeführt wird - wie printf in C. $monitor registriert eine Beobachtungsliste; wann immer sich ein Signal in dieser Liste ändert, wird die formatierte Nachricht automatisch ausgegeben. Es kann immer nur ein $monitor aktiv sein; ein erneuter Aufruf ersetzt die vorherige Liste.

Welche Format-Spezifizierer unterstützt Verilog $display?

Die gängigen: %b (binär), %d (dezimal), %h (hex), %o (oktal), %c (einzelnes Zeichen aus dem unteren Byte), %s (String), %t (Simulationszeit), %m (hierarchischer Instanzname). Nutze die %0d-Form, um Null-Padding zu unterdrücken - %d polstert auf eine Default-Breite, %0d produziert kein Padding.

Was ist $write in Verilog?

$write ist wie $display, fügt aber keinen Zeilenumbruch an. Nützlich, wenn du eine Ausgabezeile aus mehreren Aufrufen aufbaust. Ein abschließendes $display (ohne Argumente oder mit nachgestelltem Newline) beendet die Zeile.

Wie drucke ich die Simulationszeit in Verilog?

Mit $time (oder $realtime für Sub-Tick-Auflösung) und dem %t-Format-Spezifizierer: $display("at time %t: ...", $time);. Mit %0t unterdrückst du das Default-Padding. Für eine einfache Zahl von Zeiteinheiten funktioniert %0d mit $time ebenfalls: $display("t=%0d", $time);.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S