Jednorazowy blok proceduralny
initial begin ... end to rodzeństwo always. Różnica: initial wykonuje się dokładnie raz, zaczynając w chwili 0, a potem się kończy. Nie ma listy czułości ani zapętlenia.
Istnieje z jednego powodu: przygotowania testbencha. Ustawianie wartości początkowych, uruchamianie pobudzenia, otwieranie plików logów, wywołanie $finish po ustalonym czasie symulacji, czyli wszystko, co ma się wydarzyć raz na początku przebiegu w znanej kolejności.
Prześledź, co robi symulator:
- Czas przechodzi do 0.
- Blok
initialstartuje. Pierwszy$displaywypisuje tekst. dataprzyjmuje wartość8'hA5.#10przesuwa czas symulacji o 10 jednostek.- Drugi
$displaywypisuje tekst. $finishkończy symulację.
Blok wykonał się raz. Nie ma drugiego przebiegu.
Kanoniczny szkielet testbencha
Prawie każdy testbench, jaki napiszesz, używa takiego bloku initial:
Przyjrzyj się kształtowi, bo to szablon testbencha, po który będziesz sięgać za każdym razem:
- Otwórz plik zapisu VCD (
$dumpfile,$dumpvars), omówiony w artykule Dumpfile i VCD. - Ustaw wartości początkowe i trzymaj reset przez kilka cykli.
- Podawaj pobudzenie jako serię zmian przeplatanych opóźnieniami
#. $finish, żeby czysto zakończyć symulację.
To wszystko. Każdy test w Verilogu, jaki zobaczysz, łącznie z przykładami od producentów, stosuje ten wzorzec.
Kilka bloków initial działa równolegle
Kilka bloków initial w tym samym module startuje w chwili 0 jednocześnie. Każdy wykonuje własne instrukcje we własnym przedziale czasu:
Symulator uruchamia oba bloki w chwili 0. Pierwszy $display bloku B wykonuje się od razu. Blok A czeka 5 jednostek przed wypisaniem. Blok B wypisuje ponownie w chwili 2. Drugie wypisanie bloku A następuje w chwili 15. $finish bloku B kończy symulację w chwili 22.
Rozdzielanie przygotowań, generowania zegara i pobudzenia na osobne bloki initial to częsty styl: każdy blok jest krótki i robi jedną rzecz.
Inicjalizacja w deklaracji
Popularna zwarta forma: inicjalizacja reg od razu w deklaracji. Większość symulatorów traktuje to tak samo jak initial:
reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;
To odpowiednik:
reg clk;
reg reset;
reg [7:0] count;
initial begin
clk = 0;
reset = 1;
count = 0;
end
Zwartą formę zobaczysz w testbenchach: umieszcza wartość początkową tuż obok deklaracji, gdzie łatwo ją wyłapać wzrokiem.
initial działa tylko w symulacji
Narzędzia do syntezy ignorują bloki initial. Prawdziwy sprzęt nie ma "chwili zero", w której wykonuje się kod startowy: ma włączenie zasilania, sygnały resetu i konfigurację wynikającą z tych zdarzeń. Jeśli rejestr ma zaczynać w znanym stanie w prawdziwym sprzęcie, steruj nim sygnałem resetu wewnątrz always @(posedge clk):
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
Gałąź if (reset) to syntezowalny odpowiednik initial state = IDLE. Reset to odpowiedź na pytanie "jak zainicjalizować rejestr w prawdziwym sprzęcie?".
Niektóre przepływy FPGA akceptują ograniczone initial do ustawiania wartości początkowych rejestrów (na przykład narzędzia Xilinx), ale to rozszerzenie konkretnego producenta. Nie polegaj na nim w przenośnym kodzie.
Co zobaczysz wewnątrz initial
Kilka typowych wzorców poza standardowym szkieletem testbencha:
Zakończenie po czasie
initial begin
#1000 $finish; // safety net: kill the sim after 1000 time units
end
Osobny initial, którego jedynym zadaniem jest narzucenie twardego limitu czasu symulacji. Nawet jeśli główny blok pobudzenia zablokuje się, czekając na sygnał, który nigdy nie nadejdzie, ten blok zadziała i zakończy przebieg.
Konfiguracja przebiegów
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test); // dump everything under the `test` scope
end
Te dwie linie każą symulatorowi zapisać plik VCD z każdym sygnałem w zakresie testu. Bez nich nie dostaniesz przebiegów.
Początkowa zawartość pamięci
reg [7:0] memory [0:255];
initial begin
$readmemh("image.hex", memory);
end
$readmemh wczytuje plik w formacie szesnastkowym do tablicy. Używa się go w testbenchach procesorów do wstępnego załadowania pamięci instrukcji. Tylko w symulacji.
Typowe błędy
Używanie initial w logice syntezowalnej. To się nie zsyntezuje. Zamiast tego używaj sygnałów resetu.
Brak $finish. Bez niego symulator działa, dopóki coś innego go nie zatrzyma (domyślny limit czasu, ręczne przerwanie itp.). W szybkim teście to nie problem, ale w skrypcie regresyjnym zawsze używaj $finish.
Brak #delay między przypisaniami pobudzenia. Jeśli napiszesz a = 0; b = 1; bez opóźnienia, oba przypisania zajdą w tym samym czasie symulacji i testowany układ może zobaczyć je jednocześnie, a nie jako osobne zdarzenia. Między odrębnymi zdarzeniami pobudzenia wstawiaj #1 albo dłuższe opóźnienie.
Próba sterowania wire z initial. Ta sama reguła co dla always: jedynym dozwolonym celem jest reg.
Co dalej
Znasz już oba rodzaje bloków proceduralnych. Następny artykuł omawia temat, który początkującym w Verilogu myli się najczęściej: Przypisania blokujące i nieblokujące. Wiedza o tym, kiedy użyć =, a kiedy <=, to różnica między działającym przerzutnikiem a bałaganem pełnym wyścigów.
Najczęściej zadawane pytania
Czym jest blok initial w Verilogu?
initial begin ... end to blok proceduralny, który wykonuje się dokładnie raz na początku symulacji, w chwili 0. To standardowe miejsce na przygotowanie stanu testbencha: inicjalizację sygnałów, otwarcie plików logów, wywołanie $dumpfile/$dumpvars, podanie pobudzenia i zakończenie symulacji przez $finish. W jednym module może istnieć kilka bloków initial. Wszystkie startują równolegle w chwili 0.
Jaka jest różnica między initial a always w Verilogu?
initial wykonuje się raz w chwili 0, a potem się kończy. always wykonuje się w nieskończoność: ma listę czułości i budzi się, gdy zmienią się wymienione sygnały. initial używa się prawie wyłącznie w testbenchach. always to podstawowe narzędzie zarówno testbenchy, jak i syntezowalnego RTL.
Czy blok initial jest syntezowalny?
W zwykłym Verilogu nie. Narzędzia do syntezy ignorują bloki initial, bo prawdziwy sprzęt nie ma 'chwili zero', w której wykonuje się kod startowy. Niektóre narzędzia FPGA akceptują ograniczoną formę do ustawiania wartości początkowych rejestrów, ale w ogólnym przypadku to konstrukcja tylko do symulacji. Trzymaj bloki initial w testbenchach, a logikę syntezowalną inicjalizuj sygnałami resetu.
Czy moduł w Verilogu może mieć kilka bloków initial?
Tak. Każdy blok initial startuje w chwili 0 i wykonuje się do końca niezależnie od pozostałych. Podział przygotowań na kilka bloków to częsty wzorzec w testbenchach: jeden blok generuje zegar, drugi podaje pobudzenie, trzeci zapisuje przebiegi. Działają współbieżnie od chwili 0, a symulator przeplata ich instrukcje w miarę upływu czasu.