Menu

Verilog $display e $monitor: Imprimindo de um testbench

Como $display, $write e $monitor funcionam - os format specifiers que você vai usar, a diferença entre eles e quando cada um é a ferramenta certa.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

A família printf do C em Verilog

Três system tasks te deixam imprimir para o stdout do simulador:

  • $display - imprime uma vez, anexa nova linha.
  • $write - imprime uma vez, sem nova linha.
  • $monitor - imprime automaticamente toda vez que um sinal monitorado muda.

Todos os três pegam uma string de formato e uma lista de argumentos, igualzinho ao printf. Os format specifiers são similares mas específicos do Verilog.

$display: O padrão

$display é o burro de carga:

Você verá algo como:

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

Notas:

  • %d enche até uma largura padrão baseada no tamanho do operando. Para um reg de 8 bits, isso são 3 caracteres (espaço para 255). Os espaços à esquerda podem ficar feios em saída tabular - use %0d para suprimi-los.
  • %h, %b, %o têm padding similar por padrão. A maioria do código de testbench usa variantes %0 quando o alinhamento não é útil.
  • A nova linha no final é automática. Não coloque \n no fim de uma string de formato $display - você terá uma linha em branco.

Format Specifiers

O conjunto que o Verilog suporta:

SpecifierSignificado
%bbinário
%ddecimal (signed se sinal é signed)
%h ou %xhex
%ooctal
%ccaractere ASCII único (8 bits baixos)
%sstring
%ttempo de simulação
%mnome hierárquico do escopo atual
%%um % literal
%0Xsem padding à esquerda, para qualquer de %b, %d, etc.

%b, %d, %h, %o são os quatro que você vai usar 95% do tempo. %t é o próximo mais comum - sempre que você quer uma linha de log com timestamp.

$write: Sem nova linha

$write é idêntico ao $display exceto que não anexa uma nova linha:

Saída:

abc
done

Útil para construir uma única linha a partir do corpo de um loop:

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

$monitor: Auto-imprimir em mudança

$monitor registra uma watch list. O simulador reavalia e imprime sempre que algum sinal mencionado na string de formato muda:

Você verá três linhas, uma para cada mudança nas entradas. Sem necessidade de chamar $display manualmente depois de cada mudança de estímulo - $monitor faz isso.

Duas limitações:

  • Só um $monitor pode estar ativo. Chamá-lo de novo substitui a watch list anterior. Use $monitoroff e $monitoron para temporariamente suprimir e re-habilitar.
  • Mudanças dentro do mesmo time step colapsam em uma impressão. Se a e b ambos mudam no tempo 5, o monitor dispara uma vez com ambos os novos valores, não duas.

Quando usar cada um

  • $display: a maior parte da saída de testbench. Chame explicitamente depois de estímulo, depois de transições de estado importantes ou dentro de um bloco always @(posedge clk) de amostragem.
  • $write: quando você quer construir uma única linha a partir de um loop ou vários pedaços pequenos.
  • $monitor: quando você quer rastrear um pequeno conjunto de sinais continuamente e só ver saída quando mudam. Útil para debug inicial; mais difícil de usar em scripts de regressão porque a saída não é determinística em termos de contagem total de linhas.

Para a maioria dos workflows, $display cobre tudo. Recorra a $monitor só quando saída contínua dirigida por mudança é genuinamente o que você quer.

Trabalhando com tempo

$time retorna o tempo atual de simulação como um inteiro de 64 bits. Pareie com %0t:

$display("em %0t: sinal virou", $time);

A saída fica como em 25: sinal virou (a unidade depende do seu timescale).

Se você precisa de precisão sub-tick (raro), use $realtime no lugar - retorna um real.

%t automaticamente formata tempo com uma largura padrão que o simulador escolhe. %0t tira o padding.

Amostrando na borda de clock

Um idioma limpo para monitorar designs sequenciais: um bloco always @(posedge clk) separado que imprime uma vez por ciclo:

Esse padrão de amostragem garante uma linha de log por clock - perfeito para testes de regressão que fazem pattern-match na saída.

Logging para um arquivo

Abra um arquivo com $fopen, log com $fdisplay (que funciona como $display mas escreve em um handle de arquivo):

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

$fopen retorna um handle de 32 bits; passe-o como primeiro argumento para $fdisplay, $fwrite, $fstrobe e por aí vai. As funções são além disso idênticas a seus irmãos que imprimem no console.

O que vem a seguir

$display e amigos te dão logs de texto. Para debug visual - vendo sinais como tensões ao longo do tempo - você quer uma forma de onda VCD. O próximo doc, Dumpfile and VCD, cobre $dumpfile e $dumpvars, as duas chamadas que transformam sua simulação em uma forma de onda gráfica pela qual você pode rolar.

Perguntas frequentes

Qual a diferença entre $display e $monitor em Verilog?

$display imprime uma vez, imediatamente, quando é executado - como printf em C. $monitor registra uma watch list; sempre que qualquer sinal na lista muda, a mensagem formatada é impressa automaticamente. Só um $monitor pode estar ativo por vez; chamá-lo de novo substitui a watch list anterior.

Quais format specifiers o $display do Verilog suporta?

Os comuns: %b (binário), %d (decimal), %h (hex), %o (octal), %c (caractere único do byte baixo), %s (string), %t (tempo de simulação), %m (nome de instância hierárquico). Use a forma %0d para descartar zero-padding à esquerda - %d enche até uma largura padrão, %0d não enche.

O que é $write em Verilog?

$write é como $display mas não anexa uma nova linha. Útil quando você quer construir uma linha de saída a partir de múltiplas chamadas. O $display de fim-de-linha (sem argumentos ou com uma nova linha à direita) termina a linha.

Como imprimo o tempo de simulação em Verilog?

Use $time (ou $realtime para resolução sub-tick) com o format specifier %t: $display("em tempo %t: ...", $time);. Use %0t para suprimir o padding padrão. Para uma contagem inteira simples de unidades de tempo, %0d com $time também funciona: $display("t=%0d", $time);.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR