Menu

$display i $monitor w Verilogu: wypisywanie z testbencha

Jak działają $display, $write i $monitor: specyfikatory formatu, których będziesz używać, czym się różnią i kiedy które z nich jest właściwym narzędziem.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Verilogowa rodzina printf z C

Trzy zadania systemowe pozwalają wypisywać na standardowe wyjście symulatora:

  • $display: wypisuje raz i dodaje znak nowej linii.
  • $write: wypisuje raz, bez znaku nowej linii.
  • $monitor: wypisuje automatycznie za każdym razem, gdy zmieni się obserwowany sygnał.

Wszystkie trzy przyjmują napis formatujący i listę argumentów, tak jak printf. Specyfikatory formatu są podobne, ale specyficzne dla Veriloga.

$display: wybór domyślny

$display to podstawowe narzędzie:

Zobaczysz mniej więcej coś takiego:

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

Uwagi:

  • %d dopełnia do domyślnej szerokości zależnej od rozmiaru argumentu. Dla 8-bitowego reg to 3 znaki (miejsce na 255). Spacje na początku mogą brzydko wyglądać w wyjściu tabelarycznym, więc użyj %0d, żeby je usunąć.
  • %h, %b, %o domyślnie też dopełniają. Większość kodu testbenchy używa wariantów z %0, gdy wyrównanie nie jest przydatne.
  • Znak nowej linii na końcu dodawany jest automatycznie. Nie wstawiaj \n na końcu napisu formatującego $display, bo dostaniesz pustą linię.

Specyfikatory formatu

Zestaw obsługiwany przez Veriloga:

SpecyfikatorZnaczenie
%bbinarnie
%ddziesiętnie (ze znakiem, jeśli sygnał jest ze znakiem)
%h lub %xszesnastkowo
%oósemkowo
%cjeden znak ASCII (młodsze 8 bitów)
%snapis
%tczas symulacji
%mhierarchiczna nazwa bieżącego zakresu
%%dosłowny znak %
%0Xbez dopełnienia na początku, dla każdego z %b, %d itd.

%b, %d, %h, %o to cztery specyfikatory, których będziesz używać w 95% przypadków. Następny pod względem popularności jest %t: zawsze wtedy, gdy chcesz linię logu ze znacznikiem czasu.

$write: bez nowej linii

$write działa identycznie jak $display, tylko nie dodaje znaku nowej linii:

Wyjście:

abc
done

Przydaje się do budowania jednej linii w ciele pętli:

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

$monitor: automatyczne wypisywanie przy zmianie

$monitor rejestruje listę obserwowanych sygnałów. Symulator ponownie oblicza i wypisuje komunikat, gdy zmieni się dowolny sygnał wymieniony w napisie formatującym:

Zobaczysz trzy linie, po jednej dla każdej zmiany wejść. Nie trzeba ręcznie wywoływać $display po każdej zmianie pobudzenia, bo $monitor robi to za ciebie.

Dwa ograniczenia:

  • Aktywny może być tylko jeden $monitor. Ponowne wywołanie zastępuje poprzednią listę. $monitoroff i $monitoron pozwalają tymczasowo go wyłączyć i włączyć z powrotem.
  • Zmiany w tym samym kroku czasowym łączą się w jedno wypisanie. Jeśli a i b zmienią się w chwili 5, monitor zadziała raz z obiema nowymi wartościami, a nie dwa razy.

Kiedy używać którego

  • $display: większość wyjścia testbencha. Wywołuj go jawnie po pobudzeniu, po ważnych przejściach stanów albo w próbkującym bloku always @(posedge clk).
  • $write: gdy chcesz zbudować jedną linię w pętli albo z kilku małych kawałków.
  • $monitor: gdy chcesz stale śledzić mały zestaw sygnałów i widzieć wyjście tylko przy ich zmianie. Przydatny przy wstępnym debugowaniu. Trudniej go używać w skryptach regresyjnych, bo łączna liczba linii wyjścia nie jest deterministyczna.

W większości zastosowań $display wystarcza do wszystkiego. Po $monitor sięgaj tylko wtedy, gdy naprawdę chcesz ciągłego wyjścia sterowanego zmianami.

Praca z czasem

$time zwraca bieżący czas symulacji jako 64-bitową liczbę całkowitą. Łącz go z %0t:

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

Wyjście wygląda jak at 25: signal flipped (jednostka zależy od timescale).

Jeśli potrzebujesz precyzji poniżej jednego tiku (rzadko), użyj zamiast tego $realtime, które zwraca real.

%t automatycznie formatuje czas z domyślną szerokością wybraną przez symulator. %0t usuwa dopełnienie.

Próbkowanie na zboczu zegara

Czysty idiom do obserwowania projektów sekwencyjnych: osobny blok always @(posedge clk), który wypisuje raz na cykl:

Ten wzorzec próbkowania gwarantuje jedną linię logu na takt zegara, co idealnie pasuje do testów regresyjnych dopasowujących wzorce w wyjściu.

Logowanie do pliku

Otwórz plik przez $fopen i loguj przez $fdisplay (który działa jak $display, ale zapisuje do uchwytu pliku):

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

$fopen zwraca 32-bitowy uchwyt. Przekaż go jako pierwszy argument do $fdisplay, $fwrite, $fstrobe i innych. Poza tym te funkcje działają identycznie jak ich odpowiedniki wypisujące na konsolę.

Co dalej

$display i spółka dają logi tekstowe. Do debugowania wizualnego, czyli obserwowania sygnałów jako napięć w czasie, potrzebujesz przebiegu VCD. Następny artykuł, Dumpfile i VCD, omawia $dumpfile i $dumpvars, dwa wywołania, które zamieniają symulację w graficzny przebieg, po którym możesz się przewijać.

Najczęściej zadawane pytania

Jaka jest różnica między $display a $monitor w Verilogu?

$display wypisuje raz, od razu w chwili wykonania, jak printf w C. $monitor rejestruje listę obserwowanych sygnałów: gdy zmieni się którykolwiek z nich, sformatowany komunikat jest wypisywany automatycznie. Aktywny może być tylko jeden $monitor naraz, a ponowne wywołanie zastępuje poprzednią listę.

Jakie specyfikatory formatu obsługuje $display w Verilogu?

Najczęstsze to: %b (binarnie), %d (dziesiętnie), %h (szesnastkowo), %o (ósemkowo), %c (jeden znak z młodszego bajtu), %s (napis), %t (czas symulacji), %m (hierarchiczna nazwa instancji). Formy %0d używaj, żeby pozbyć się dopełnienia: %d dopełnia do domyślnej szerokości, a %0d nie dodaje żadnego dopełnienia.

Czym jest $write w Verilogu?

$write działa jak $display, ale nie dodaje znaku nowej linii. Przydaje się, gdy chcesz zbudować jedną linię wyjścia z kilku wywołań. Linię kończy $display (bez argumentów albo z końcowym znakiem nowej linii).

Jak wypisać czas symulacji w Verilogu?

Użyj $time (albo $realtime, gdy potrzebujesz rozdzielczości poniżej jednego tiku) ze specyfikatorem %t: $display("at time %t: ...", $time);. %0t wyłącza domyślne dopełnienie. Dla zwykłej liczby jednostek czasu działa też %0d z $time: $display("t=%0d", $time);.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ