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:
%ddopełnia do domyślnej szerokości zależnej od rozmiaru argumentu. Dla 8-bitowegoregto 3 znaki (miejsce na255). Spacje na początku mogą brzydko wyglądać w wyjściu tabelarycznym, więc użyj%0d, żeby je usunąć.%h,%b,%odomyś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
\nna końcu napisu formatującego$display, bo dostaniesz pustą linię.
Specyfikatory formatu
Zestaw obsługiwany przez Veriloga:
| Specyfikator | Znaczenie |
|---|---|
%b | binarnie |
%d | dziesiętnie (ze znakiem, jeśli sygnał jest ze znakiem) |
%h lub %x | szesnastkowo |
%o | ósemkowo |
%c | jeden znak ASCII (młodsze 8 bitów) |
%s | napis |
%t | czas symulacji |
%m | hierarchiczna nazwa bieżącego zakresu |
%% | dosłowny znak % |
%0X | bez 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ę.$monitoroffi$monitoronpozwalają tymczasowo go wyłączyć i włączyć z powrotem. - Zmiany w tym samym kroku czasowym łączą się w jedno wypisanie. Jeśli
aibzmienią 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 blokualways @(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);.