Menu

$display e $monitor in Verilog: stampare da un testbench

Come funzionano $display, $write e $monitor: gli specificatori di formato che userai, le differenze tra loro e quando ciascuno è lo strumento giusto.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

La famiglia printf di Verilog

Tre system task ti permettono di stampare sullo stdout del simulatore:

  • $display: stampa una volta e aggiunge un a capo.
  • $write: stampa una volta, senza a capo.
  • $monitor: stampa automaticamente ogni volta che cambia un segnale osservato.

Tutti e tre accettano una stringa di formato e un elenco di argomenti, proprio come printf. Gli specificatori di formato sono simili, ma specifici di Verilog.

$display: la scelta predefinita

$display è il cavallo da tiro:

Vedrai qualcosa del genere:

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

Note:

  • %d riempie fino a una larghezza predefinita che dipende dalla dimensione dell'operando. Per un reg a 8 bit sono 3 caratteri (lo spazio per 255). Gli spazi iniziali possono risultare brutti in un output tabellare: usa %0d per eliminarli.
  • Anche %h, %b, %o hanno un riempimento simile per default. La maggior parte del codice dei testbench usa le varianti %0 quando l'allineamento non serve.
  • L'a capo finale è automatico. Non mettere \n alla fine della stringa di formato di un $display: otterresti una riga vuota.

Specificatori di formato

Quelli supportati da Verilog:

SpecificatoreSignificato
%bbinario
%ddecimale (con segno se il segnale è con segno)
%h o %xesadecimale
%oottale
%csingolo carattere ASCII (8 bit bassi)
%sstringa
%ttempo di simulazione
%mnome gerarchico dello scope corrente
%%un % letterale
%0Xnessun riempimento iniziale, per %b, %d e gli altri

%b, %d, %h, %o sono i quattro che userai il 95% delle volte. Subito dopo viene %t, ogni volta che vuoi una riga di log con il timestamp.

$write: senza a capo

$write è identico a $display, ma non aggiunge un a capo:

Output:

abc
done

È utile per costruire una sola riga dal corpo di un ciclo:

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

$monitor: stampa automatica a ogni cambiamento

$monitor registra una lista di osservazione. Il simulatore rivaluta e stampa ogni volta che cambia uno qualsiasi dei segnali citati nella stringa di formato:

Vedrai tre righe, una per ogni cambiamento degli ingressi. Non serve chiamare a mano $display dopo ogni variazione dello stimolo: ci pensa $monitor.

Due limitazioni:

  • Può essere attivo un solo $monitor. Richiamarlo sostituisce la lista di osservazione precedente. Usa $monitoroff e $monitoron per sospenderlo temporaneamente e riattivarlo.
  • I cambiamenti nello stesso passo temporale producono una sola stampa. Se a e b cambiano entrambi al tempo 5, il monitor scatta una volta con entrambi i nuovi valori, non due.

Quando usare ciascuno

  • $display: la maggior parte dell'output dei testbench. Chiamalo esplicitamente dopo uno stimolo, dopo transizioni di stato importanti o dentro un blocco di campionamento always @(posedge clk).
  • $write: quando vuoi costruire una sola riga da un ciclo o da diversi piccoli pezzi.
  • $monitor: quando vuoi seguire di continuo un piccolo insieme di segnali e vedere output solo quando cambiano. È utile nel primo debug; è più scomodo negli script di regressione, perché l'output non è deterministico nel numero totale di righe.

Nella maggior parte dei casi $display basta per tutto. Passa a $monitor solo quando ti serve davvero un output continuo guidato dai cambiamenti.

Lavorare con il tempo

$time restituisce il tempo di simulazione corrente come intero a 64 bit. Abbinalo a %0t:

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

L'output è del tipo at 25: signal flipped (l'unità dipende dal tuo timescale).

Se ti serve una precisione inferiore al tick (raro), usa invece $realtime, che restituisce un real.

%t formatta automaticamente il tempo con una larghezza predefinita scelta dal simulatore. %0t elimina il riempimento.

Campionare sul fronte di clock

Un idioma pulito per osservare progetti sequenziali: un blocco always @(posedge clk) separato che stampa una volta per ciclo:

Questo pattern di campionamento garantisce una riga di log per clock: perfetto per i test di regressione che confrontano l'output con dei pattern.

Scrivere il log su file

Apri un file con $fopen e scrivi con $fdisplay (che funziona come $display ma scrive su un handle di file):

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

$fopen restituisce un handle a 32 bit; passalo come primo argomento a $fdisplay, $fwrite, $fstrobe e così via. Per il resto queste funzioni sono identiche alle loro sorelle che stampano sulla console.

Cosa viene dopo

$display e compagni ti danno log testuali. Per il debug visivo, cioè guardare i segnali come tensioni nel tempo, ti serve una forma d'onda VCD. La prossima pagina, Dumpfile and VCD, tratta $dumpfile e $dumpvars, le due chiamate che trasformano la simulazione in una forma d'onda grafica da scorrere.

Domande frequenti

Qual è la differenza tra $display e $monitor in Verilog?

$display stampa una sola volta, subito, quando viene eseguito, come printf in C. $monitor registra una lista di osservazione: ogni volta che cambia uno dei segnali della lista, il messaggio formattato viene stampato automaticamente. Può essere attivo un solo $monitor alla volta; richiamarlo sostituisce la lista precedente.

Quali specificatori di formato supporta $display in Verilog?

I più comuni: %b (binario), %d (decimale), %h (esadecimale), %o (ottale), %c (un carattere preso dal byte basso), %s (stringa), %t (tempo di simulazione), %m (nome gerarchico dell'istanza). Usa la forma %0d per eliminare il riempimento iniziale: %d riempie fino a una larghezza predefinita, %0d non aggiunge riempimento.

Cos'è $write in Verilog?

$write è come $display ma non aggiunge un a capo. È utile quando vuoi costruire una riga di output con più chiamate. Il $display finale (senza argomenti o con un a capo in coda) chiude la riga.

Come si stampa il tempo di simulazione in Verilog?

Usa $time (o $realtime per una risoluzione inferiore al tick) con lo specificatore di formato %t: $display("at time %t: ...", $time);. Usa %0t per eliminare il riempimento predefinito. Per un semplice conteggio intero delle unità di tempo funziona anche %0d con $time: $display("t=%0d", $time);.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA