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:
%driempie fino a una larghezza predefinita che dipende dalla dimensione dell'operando. Per unrega 8 bit sono 3 caratteri (lo spazio per255). Gli spazi iniziali possono risultare brutti in un output tabellare: usa%0dper eliminarli.- Anche
%h,%b,%ohanno un riempimento simile per default. La maggior parte del codice dei testbench usa le varianti%0quando l'allineamento non serve. - L'a capo finale è automatico. Non mettere
\nalla fine della stringa di formato di un$display: otterresti una riga vuota.
Specificatori di formato
Quelli supportati da Verilog:
| Specificatore | Significato |
|---|---|
%b | binario |
%d | decimale (con segno se il segnale è con segno) |
%h o %x | esadecimale |
%o | ottale |
%c | singolo carattere ASCII (8 bit bassi) |
%s | stringa |
%t | tempo di simulazione |
%m | nome gerarchico dello scope corrente |
%% | un % letterale |
%0X | nessun 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$monitoroffe$monitoronper sospenderlo temporaneamente e riattivarlo. - I cambiamenti nello stesso passo temporale producono una sola stampa. Se
aebcambiano 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 campionamentoalways @(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);.