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 \timescalealbo 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 #5to 5 nanosekund. Przy`timescale 1us / 1ns #5to 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.