.. _w04-clock:


==========================
Wykład 4: Sygnały zegarowe
==========================

Data: 24.10.2018

.. toctree::

.. contents::


Generatory sygnałów zegarowych
==============================

Istnieje wiele sposobów na wygenerowanie sygnału zegarowego.  Wspomnę tutaj
o trzech:

- oscylator pierścieniowy (ring oscillator)
- oscylator LC
- oscylator kwarcowy

Naprostszym typem oscylatora jest oscylator pierścieniowy -- jest to po prostu
nieparzysta liczba bramek logicznych NOT połączona w pierścień.  Jego
częstotliwość to 1 / (suma opóźnień bramek i połączeń między nimi).  Jest
to bardzo niedokładny oscylator (rzędu ±30%) i nie powinien być stosowany
w jakiejkolwiek sytuacji wymagającej konkretnej częstotliwości pracy,
ale bywa używany gdy potrzebny jest po prostu *jakiś* zegar.  Przykładem
oscylatora pierścieniowego jest wewnętrzny zegar konfiguracyjny układu
FPGA (jeśli został wybrany tryb konfiguracji w którym to FPGA generuje
sygnał zegarowy).

.. warning::

    Nie należy próbować tworzyć własnego oscylatora pierścieniowego używając
    programowalnej logiki -- nie mamy wystarczającej kontroli nad ułożeniem
    i połączeniem układu, by sensownie kontrolować częstotliwość.

Trochę bardziej skomplikowanym (bo wymagającym zewnętrznych komponentów)
generatorem jest generator LC, w ktĂłrym uĹźywamy rezonansu
układu złóżonego z kondensatora i cewki do wygenerowania zegara.
Częstotliwością takiego zegara jest ``1/(tau * sqrt(L*C))``.  Takim zegarem
jest na przykład ``MCLK`` na naszej płytce.  Wciąż, jest to zegar nieco
niedokładny (ma wahania rzędu ±1%) -- można to łatwo zobaczyć, próbując
użyć go do generowania sygnału VGA i podłączając monitor...

Do generowania sensownie dokładnych (±0.001%) zegarów w elektronice
używa się oscylatorów kwarcowych, które używają wibracji kryształu kwarcowego
do generowania sygnału zegarowego.  Dzięki elektronicznym układom przekształcającym
sygnały zegarowe (PLL), wystarcza jeden kryształ (zazwyczaj o częstotliwości rzędu
10MHz) do wygenerowania dowolnej liczby sygnałów zegarowych o dowolnych wartościach.


Dystrybucja i przełączanie sygnałów zegarowych
==============================================

W układach cyfrowych bardzo pożądane jest, by zbocza sygnału zegarowego
dochodziły jednocześnie do wszystkich przerzutników, którymi sterują.
Różnica w czasie przyjścia zbocza do różnych miejsc nazywa się clock skew
i powoduje szereg problemĂłw:

- jeśli suma clock skew i czasu hold jest większa niż czas propagacji
  między wyjściem jednego przerzutnika a wejściem drugiego,
  mamy naruszenie czasu hold
- clock skew efektywnie dodaje się do wielu czasów propagacji, zmniejszając
  maksymalną możliwą częstotliwość zegara

Aby zminimalizować clock skew, w układach FPGA istnieją specjalne sieci
dystrybucji sygnałów zegarowych, zaprojektowane tak, by długość ścieżki
od źródła sygnału do przerzutników była mniej-więcej stała.  W układach
Spartan 3E mamy 32 bufory globalne, będące źródłami takich sieci.
Narzędzia do syntezy same wywnioskują, które z naszych sygnałów powinny
używać buforów globalnych, ale jeśli chcemy, możemy poprosić o to jawnie::

    // clk_in jest źródłem sygnału zegarowego, clk_out używamy w przerzutnikach.
    BUFG moj_bufor_globalny(.I(clk_in), .O(clk_out));


Przełączanie sygnałów zegarowych
--------------------------------

Może się zdarzyć, że będziemy projektować układ, którego częstotliwość
powinna się zmieniać w czasie działania (tryb turbo, interfejsy sprzętowe
mające wolne/szybkie wersje, różne rozdzielczości VGA, itp).  Bufory globalne
pozwalają nam bezpiecznie przełączyć się między dwoma sygnałami zegarowymi
w trakcie pracy -- aby użyć tej funkcjonalności, należy użyć bloku ``BUFGMUX``::

    // S wybiera, który z sygnałów zegarowych będzie przesłany na wyjście.
    BUFGMUX moj_bufor_globalny(.I0(clk_in_a), .I1(clk_in_b), .S(ktory), .O(clk_out));

Wejście S powinno być zsynchronizowane do zegara ``clk_out``, żeby uniknąć problemów
z metastabilnością.  ``BUFGMUX`` zapewni bezpieczne przełączenie między zegarami --
po zmianie stanu ``S``, czeka aż stary zegar będzie w stanie niskim, po czym utrzymuje
stan niski na wyjściu aż do wystąpienia opadającego zbocza na nowym zegarze -- 
zapewnia to, że okres zegara wyjściowego jest co najmniej tak długi, jak krótszy
z okresów wejściowych.


Wyłączanie sygnałów zegarowych
------------------------------

Często przydaje się możliwość tymczasowego wyłączenia danego sygnału zegarowego
(ponieważ dany układ akurat powinien pracować tylko część czasu, bądź dla
zaoszczędzenia prądu).  Bufory globalne udostępniają również taką możliwość::

    // Będzie przekazywać na clk_out tylko te okresy clk_in, dla których enable == 1.
    // Pozostałe okresy będą miały stan niski przez cały czas trwania.
    BUFGCE moj_bufor_globalny(.I(clk_in), .O(clk_out), .CE(enable));

Jest to równoważne ``BUFGMUX``, w którym jeden z sygnałów wejściowych jest
zatrzymany i rĂłwny 0.


DLL -- niwelacja opóźnienia sygnałów zegarowych
===============================================

[XXX]


PLL -- zmiana częstotliwości sygnałów zegarowych
================================================

[XXX]


Komunikacja między domenami zegarowymi
======================================

Mając w układzie synchronicznym wiele domen zegarowych napotykamy na problem
przekazywania danych między nimi.  Dla pojedynczego sygnału (np. linii przerwania)
wystarczy synchronizator, ale przekazanie większej ilości danych wymaga bardziej
skomplikowanych układów.


Kod Graya
---------

Załóżmy, że chcemy przekazać między dwiema domenami jakąś liczbę, która może
zmienić się co najwyżej o 1 (w górę bądź w dół) w kolejnych cyklach zegara.
Przekazanie jej bezpośrednio przez tablicę synchronizatorów nie zadziała
-- przy zmianie, zmiany różnych bitów mogą dojśc w różnych cyklach do nowej
domeny zegarowej.  Istnieje jednak kodowanie liczb, które rozwiązuje ten
problem -- zapewnia, że każde kolejne liczby są kodowane do wektorów bitowych
różniących się w dokładnie jednej pozycji.  Jest to kod Graya.  Dla przykładu,
kod 4-bitowy:

- 0: 0000
- 1: 0001
- 2: 0011
- 3: 0010
- 4: 0110
- 5: 0111
- 6: 0101
- 7: 0100
- 8: 1100
- 9: 1101
- 10: 1111
- 11: 1110
- 12: 1010
- 13: 1011
- 14: 1001
- 15: 1000

Aby zakodować liczbę ``x`` do kodu graya, wystarczy policzyć ``x ^ (x >> 1)``.
Dekodowanie jest trochę bardziej skomplikowane, ale dośc efektywnie realizowalne
w sprzęcie.


FIFO
----

Do przekazania dużej ilości danych między domenami zegarowymi najczęściej używa
się kolejek FIFO zrealizowanych za pomocą bloków RAMu używanych jako buforów cyklicznych:

- jeden port działa tylko w trybie zapisu w domenie źródłowej
- drugi port działa tylko w trybie odczytu w domenie docelowej
- wskaźniki odczytu i zapisu są przekazywane między domenami zegarowymi w kodzie Graya


Metastabilność
==============

Niestety, w prawdziwym świecie nie istnieją układy w pełni cyfrowe -- każdy
układ jest tak naprawdę układem analogowym i można wprowadzić go w stany
pośrednie (gdzieś pomiędzy 0 i 1).  Zapisując taki stan (szczególnie stan
bliski 0.5) do zatrzasku lub przerzutnika, możemy go wprowadzić w stan
rĂłwnowagi niestabilnej -- dowolne zaburzenie spowoduje, Ĺźe spadnie w stan
0 lub stan 1, lecz może to zająć dużo więcej czasu niż normalny parametr
T_CKO.  Takie zjawisko nazywa się metastabilnością.

Metastabilność jest zjawiskiem niebezpiecznym, gdyż dany stan może być
różnie zinterpretowany przez podłączone wejścia różnych bramek logicznych,
powodując w naszym układzie synchronicznym przejścia niespójne zarówno
ze stanem logicznym 0 jak i stanem logicznym 1.  Byłoby bardzo niedobrze,
gdyby np. różne części składowe procesora nie zgadzały się na temat tego,
czy w danym cyklu procesor rozpoczyna obsługę przerwania.

Metastabilność może powstać na dwa sposoby:

1. Dajemy układowi synchronicznemu stan pośredni jako wejście (należy
   tego nie robić).
2. Nie przestrzegamy czasu setup bądź czasu hold.

Metastabilność jest zjawiskiem nieuniknionym, gdy dany układ synchroniczny
ma wejścia asynchroniczne (a praktycznie każdy ma) -- prędzej czy później,
wejście zmieni się odpowiednio blisko zbocza sygnału zegarowego, powodując
naruszenie czasu setup bądź hold.  Musimy więc sobie jakoś z tym poradzić.

Z drugiej strony, stany metastabilne trwają bardzo krótko -- praktycznie zawsze
rozwiązują się w ciągu jednego, ewentualnie dwóch cykli zegara.  Z tego powodu,
na sygnałach asynchronicznych używa się tzw. synchronizatorów -- łańcuchów
dwĂłch lub trzech przerzutnikĂłw::

    wire async_in;
    reg tmp1, tmp2;
    reg out;

    always @(posedge clk)
        tmp1 <= async_in;
        tmp2 <= tmp1;
        out <= tmp2;
    end

Jeśli przerzutnik ``tmp1 <= async_in`` złapie wejście w momencie zmiany stanu
i wejdzie w stan metastabilny, z bardzo dużym prawdopodobieństwem stan ten
rozpadnie się do stanu 0 bądź 1 zanim jego wyjście zostanie użyte przez
kolejny przerzutnik (ma na to cały cykl zegara).  Jeśli nawet to się nie
uda, na przerzutniku ``tmp2 <= tmp1`` mamy kolejną szansę.  Na wyjściu
``out`` z bardzo dużym prawdopodobieństwem dostajemy już czyste wyjście
bez metastabilności.

Zapobieganie metastabilności ma charakter probabilistyczny, a odporność układu
na metastabilność podaje się jako MTBF (mean time between failures) --
szacowany średni czas bezawaryjnej pracy.  Jeśli osiągniemy MTBF rzędu setek
tysięcy lat, możemy go uważać za odporny na metastabilność.  MTBF zależy od:

- Częstotliwości zegara.  Większa częstotliwość to:

  - więcej szans, że coś pójdzie nie tak
  - krĂłtszy okres czasu w synchronizatorze na rozpad stanu metastabilnego

- Technologii wykonania (nowsze układy FPGA mają dedykowane bloki
  synchronizacyjne z przerzutnikami wykonanymi w specjalnej technologii
  zoptymalizowanej na zapobieganie metastabilności)

- Liczby przerzutników w synchronizatorze (MTBF rośnie wykładniczo z liczbą
  przerzutników).  Dwa przerzutniki to absolutne minimum, dla bezpieczeństwa
  zaleca się trzy.

Synchronizatorów należy używać *zawsze* na asynchronicznych wejściach.
W przypadku wejść z układów synchronicznych pracujących z inną częstotliwością
zegara, należy zapewnić jeszcze jeden przerzutnik na początku, używający sygnału
zegarowego układu źródłowego -- zapobiega to wydostawaniu się stanów pośrednich
z logiki kombinacyjnej.  Jedyny przypadek, w którym wolno pominąć synchronizator
na wejściu to wejście z innego układu synchronicznego pracującego na ściśle
związanym sygnale zegarowym (np. naszym sygnale zegarowym podzielonym bądź
pomnożonym przez stałą przez układ generowania zegara) -- w tym wypadku
należy dobrze opisać wzajemną relację tych zegarów, by narzędzia analizy
czasowej mogły sprawdzić zachowanie czasów setup i hold.

Zjawisko metastabilności występuje zawsze, gdy dwie konfliktujące zmiany
stanów mogą się zdarzyć blisko siebie -- w szczególności może też dotyczyć
asynchronicznych sygnałów reset, bądź wejścia do zatrzasku (gdy zmienia się
jednocześnie ze zmianą 1 na 0 na wejściu E).