Co oznacza "czas" w Verilogu
Verilog nie ma wbudowanego pojęcia sekund. Symulator przesuwa "jednostki czasu", czyli dowolne całkowite tyknięcia, a twój kod używa #N, żeby odczekać N z nich. To dyrektywa `timescale przekłada te tyknięcia na rzeczywisty czas.
`timescale 1ns / 1ps
module test;
initial begin
$display("t = %0t", $time); // 0
#5;
$display("t = %0t", $time); // 5 ns
#1.5;
$display("t = %0t", $time); // 6500 ps (or 6.5 ns)
$finish;
end
endmodule
Dwa parametry:
- Jednostka (pierwsza liczba): co oznacza
#1.1nsmówi, że jedno tyknięcie to jedna nanosekunda. - Precyzja (druga liczba): jak dokładnie symulacja śledzi czas w obrębie tej jednostki.
1psmówi, że ułamkowe opóźnienia są zaokrąglane do pikosekund.
Precyzja nie może być grubsza niż jednostka (1ps / 1ns jest niedozwolone). Typowe wybory:
1ns / 1ps: standard de facto. Wszystko w nanosekundach, precyzja poniżej nanosekundy dla dowolnych opóźnień bramek.1ps / 1ps: gdy modelujesz ekstremalnie szybkie układy albo gdy każde opóźnienie jest krótsze niż nanosekunda.1us / 1ns: dla wolnych symulacji w skali systemów wbudowanych (czasy bajtów UART, wolne protokoły).
Gdzie to umieścić
`timescale to dyrektywa kompilatora, a nie konstrukcja na poziomie modułu. Umieszcza się ją na samym początku pliku, przed jakimkolwiek module:
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
Jej zasięg to "od tego miejsca w kolejności kompilacji aż do następnego `timescale albo końca kompilacji". Ma to subtelną konsekwencję: plik bez jawnego `timescale dziedziczy to, co zadeklarował wcześniej skompilowany plik, a to zależy od kolejności, w jakiej kompilator czyta pliki. Unikaj niespodzianek: umieść `timescale na początku każdego pliku.
Edytor w przeglądarce w tej dokumentacji ustawia za ciebie domyślny timescale (zwykle 1ns / 1ps), dlatego przykłady z wcześniejszych stron działały bez jego deklarowania. W prawdziwym projekcie warto pisać go jawnie.
#delay w praktyce
Po `timescale 1ns / 1ps:
#5 // wait 5 ns
#100 // wait 100 ns
#1.5 // wait 1.5 ns (precision allows it)
#0.001 // wait 1 ps (just barely above precision)
#0 // zero-delay; useful for ordering events at the same time
Wewnątrz bloku initial lub always #N wstrzymuje przebieg proceduralny na N jednostek czasu. Symulator wstrzymuje ten blok (inne współbieżne bloki działają dalej) i wznawia go po upływie opóźnienia.
Opóźnienie możesz postawić przed przypisaniem:
#10 a = 1; // wait 10 ns, then assign
data <= #2 new_value; // schedule the non-blocking assignment 2 ns from now
Pierwsza forma to podstawa testbenchy. Druga (opóźnione przypisanie nieblokujące) służy w symulacji na poziomie bramek do modelowania opóźnienia propagacji.
Generowanie zegara
Klasyczny wzorzec:
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
Przy timescale 1ns / 1ps #5 to 5 ns. Zegar przełącza się co 5 ns, co daje okres 10 ns, czyli zegar 100 MHz. Żeby zmienić częstotliwość, zmień opóźnienie:
| Pół okresu | Okres | Częstotliwość |
|---|---|---|
#1 | 2 ns | 500 MHz |
#2.5 | 5 ns | 200 MHz |
#5 | 10 ns | 100 MHz |
#10 | 20 ns | 50 MHz |
#25 | 50 ns | 20 MHz |
#50 | 100 ns | 10 MHz |
Jeśli chcesz ułamkowego półokresu (#2.5), precyzja musi go obsługiwać. Precyzja 1ps radzi sobie ze wszystkim aż do 0,001 ns, więc każda rozsądna częstotliwość jest w porządku.
Mieszanie timescale w wielu plikach
Prawdziwy projekt składa się z wielu plików, a każdy może deklarować inny timescale. Symulator interpretuje opóźnienia w danym pliku według jego własnego timescale. Jeśli module_a.v ma `timescale 1ns / 1ps i używa #5, to oznacza 5 ns. Jeśli module_b.v ma `timescale 1us / 1ns i używa #5, to oznacza 5 us.
Zwykle tego nie widać, bo symulator i tak prezentuje jedną globalną oś czasu, ale oznacza to, że to samo #N w dwóch plikach może znaczyć coś zupełnie innego. Rozwiązanie: wybierz jeden timescale (standardem branżowym jest 1ns / 1ps) i umieść go na początku każdego pliku. Nie mieszaj.
Opóźnienia nie są syntezowalne
Kluczowa sprawa: #delay istnieje tylko na potrzeby symulacji. Narzędzie do syntezy czyta:
// In synthesizable RTL - WRONG
always @(posedge clk) begin
out <= #2 in;
end
…i albo ignoruje #2 (większość narzędzi), albo odrzuca tę konstrukcję (surowsze lintery). Timing prawdziwego sprzętu wyznaczają zegar i opóźnienie propagacji bramek, a obu tych rzeczy nie widać w kodzie źródłowym.
Zasada: używaj # tylko w testbenchach. Syntezowalny RTL nie zawiera opóźnień #. Jeśli chcesz opóźnienia w syntezowalnym kodzie, tak naprawdę potrzebujesz licznika, który odlicza w dół w takt zegara. Tak "czeka" prawdziwy sprzęt.
$time a $realtime
Dwa sposoby odczytania bieżącego czasu symulacji:
$timezwraca 64-bitową liczbę całkowitą wyrażoną w jednostce bieżącego timescale.$realtimezwraca wartośćrealwyrażoną w jednostce bieżącego timescale, ale z pełną precyzją.
Do logowania w testbenchu $time prawie zawsze wystarcza. Po $realtime sięgaj tylko wtedy, gdy w wydrukach potrzebujesz precyzji poniżej jednego tyknięcia.
Praktyczne wskazówki
- Zawsze deklaruj
`timescalena początku każdego pliku.1ns / 1psto bezpieczna wartość domyślna. - Używaj
#delaytylko w testbenchach. Traktuj jego brak w syntezowalnym kodzie jak twardą regułę. - Dopasuj okresy zegara do docelowej częstotliwości. Jeśli symulujesz projekt 50 MHz, użyj okresu zegara 20 ns, bo niedopasowane okresy mogą maskować błędy zależne od timingu.
- Do pobudzeń liczonych w cyklach używaj
@(posedge clk)zamiast#. To rozwiązanie jest odporne na zmiany okresu zegara.
Co dalej
To już wszystkie strony tych samouczków Verilog. Od podstaw języka (wire a reg, moduły, operatory), przez bloki proceduralne i sterowanie przepływem, po projektowanie synchroniczne i maszyny stanów, aż do narzędzi testbenchowych, które dowodzą, że wszystko działa. Czas coś zbudować: edytor online obok tej dokumentacji to ten sam symulator, którego używaliśmy cały czas, gotowy na każdy moduł, który naszkicujesz.
Najczęściej zadawane pytania
Co oznacza `timescale 1ns / 1ps w Verilogu?
Mówi symulatorowi: 'jedna jednostka czasu w tym pliku to jedna nanosekunda, a czas jest śledzony z dokładnością do pikosekundy.' Po tej dyrektywie #5 czeka 5 ns, #1.5 czeka 1,5 ns (w zaokrągleniu do pikosekund), a $time jest podawany w nanosekundach. Pierwsza liczba to jednostka, druga to precyzja.
Czy każdy plik Verilog potrzebuje `timescale?
Dobra praktyka mówi: tak. Zasięg dyrektywy kończy się na następnym `timescale albo na końcu kompilacji, więc pliki bez niej dziedziczą to, co zadeklarował wcześniej skompilowany plik. Przez to timing staje się niedeterministyczny między kolejnymi buildami. Umieść `timescale 1ns / 1ps na początku każdego pliku źródłowego (to najpopularniejsza konwencja), a nic cię nie zaskoczy.
Co oznacza #5 w Verilogu?
#5 przesuwa czas symulacji o 5 jednostek czasu. Jednostkę określa aktywna dyrektywa `timescale. Przy `timescale 1ns / 1ps #5 to 5 nanosekund. Przy `timescale 1us / 1ns #5 to 5 mikrosekund. Liczba może być ułamkowa: #1.5 działa, jeśli precyzja jest drobniejsza niż jednostka.
Czy #delay jest syntezowalne w Verilogu?
Nie. #delay wpływa tylko na symulację, a narzędzie do syntezy je ignoruje albo odrzuca. Timing prawdziwego sprzętu wynika z sygnału zegara i opóźnienia propagacji bramek, a nie z instrukcji #. Używaj # swobodnie w testbenchach, ale nigdy nie pisz go w syntezowalnym RTL.