KursyPoradnikiInspirujące DIYForum

Kurs FPGA – #6 – rejestry przesuwne w VHDL

Kurs FPGA – #6 – rejestry przesuwne w VHDL

Bramki logiczne i multipleksery, które poznaliśmy w poprzednim artykule to niezbędne podstawy. Pora poznać kolejny, nowy element układanki, którym są rejestry przesuwne.

Po krótkim wstępie teoretycznym przejdziemy do implementacji jednej z wersji rejestru w VHDL.

Czym jest rejestr przesuwny?

Tym razem pora na rejestry przesuwne (ang. shift register), które najczęściej wykorzystywane są do konwersji sygnałów z szeregowych na równoległe i odwrotnie. Przykładowo przy komunikacji z wykorzystaniem UART niezbędna jest zamiana informacji wystawionych na szynę danych, wprowadzenie ich do rejestru przesuwnego, a następnie wysyłanie kolejnych bitów z wyjścia rejestru zgodnie z sygnałem zegara taktującego. 

Przykład działania rejestru przesuwnego, w którym z każdym taktem zegara przesuwamy jego zawartość, czyli "00000101" o jeden w lewo, widoczny jest poniżej:

Animacja - przesuwanie

Animacja - przesuwanie "101" w lewo.

Wraz z każdym taktem zegara zawartość rejestru przesuwana jest o jeden bit w lewo, a na zwolnionej pozycji pojawia się logiczne zero. Gdy dojdziemy do końca dane znajdują się już "poza rejestrem", więc cały rejestr wypełniony jest zerami. 

Odmiana rejestru

Na potrzeby kursy (dla ciekawszych programów) zajmiemy się zmodyfikowanym "rejestrem", w którym dane przesunięte poza rejestr pojawiają się na jego początku. Przykładowo przesunięcie "101" będzie wyglądało następująco:

Animacja - przesuwanie

Animacja - przesuwanie "101" w lewo, wersja z powrotem danych do rejestru.

Kierunek pracy rejestru przesuwnego

Dane w rejestrze mogą być przesuwane w lewo lub w prawo. Przykładowo dla rejestru, w którym wpisano na początku "00000001" przesunięcie w lewo wygląda następująco:

Animacja - przesuwanie

Animacja - przesuwanie "1" w lewo, wersja z powrotem danych do rejestru.

Natomiast przesuwanie w prawo wyglądałoby tak:

Animacja - przesuwanie

Animacja - przesuwanie "1" w prawo, wersja z powrotem danych do rejestru.

W lepszym zrozumieniu tego zagadnienia pomocne będzie przejście do praktyki! Spróbujmy stworzyć rejestr, który będzie działał tak jak powyższa animacja (jedynka będzie reprezentowana przez świecącą diodę).

Gotowe zestawy do kursów Forbota

 Komplet elementów  Gwarancja pomocy  Wysyłka w 24h

Zestaw uruchomieniowy Elbert v2 - Spartan 3A z wszystkimi niezbędnymi peryferiami do wykonania ćwiczeń z kursu FPGA!

Zamów w Botland.com.pl »

Rejestr przesuwny w VHDL

Tak jak w poprzednich ćwiczeniach zacznijmy od gotowego programu, który następnie dokładnie omówimy. Poniżej widoczna jest kompletna implementacja rejestru przesuwnego w VHDL:

Pora omówić poszczególne fragmenty programu. Zacznijmy od samej góry:

Blok entity - sygnały wejściowe i wyjściowe

W bloku entity deklarujemy, że będziemy korzystać z jednego sygnału wejściowego (sygnału zegarowego) oraz 8 sygnałów wyjściowych (diod świecących):

Blok architecture - sygnały lokalne

Na początku bloku architecture deklarujemy sygnały lokalne. W tym wypadku będzie to nasz rejestr przesuwny, licznik oraz limit dla licznika.

Sygnał lokalny rej_przesuwny to wektor od długości 8 bitów. Długość ta została dobrana w taki sposób, aby możliwe było wykorzystanie wszystkich diod świecących z zestawu Elbert.

Binarnie ma ona wartość 00000001, czyli dziesiętnie "jeden". W systemie szesnastkowym (Hex) zapisywana jako 01. W języku VHDL, aby daną wartość podać w systemie binarnym należy poprzedzić ją znakiem b lub B (VHDL nie zwraca uwagi na wielkość liter). Gdy wartość podawana jest w systemie szesnastkowym to poprzedzamy ją x lub X.

Kolejne deklaracje sygnałów lokalnych są takie same jak w jednym z poprzednich ćwiczeń:

  • deklaracja stałej o nazwie LICZNIK_LIMIT, która ma wartość 12 mln (zestaw wyposażony jest w sygnał zegarowy o częstotliwości około 12 MHz),
  • kolejny sygnał to wektor licznik o długości 25 bitów. Jego długość wynika z możliwości przechowywania wartości o odpowiedniej wielkości (do około 33 mln dziesiętnie).

W dalszej części kodu zaczyna się opis funkcjonalności aplikacji:

W powyższym wycinku kodu mamy to co jest we wnętrzu bloku architecture za słowem kluczowym begin, czyli jest to blok process. Blok ten jest o tyle charakterystyczny, że instrukcje w nim zawarte wykonywane są w sposób sekwencyjny, jak np. w języku programowania C.

Proces w języku VHDL deklaruje się słowem kluczowym process. Poprzedzać je może nazwa procesu (nie jest obowiązkowa, lecz dla zwiększenia czytelności kodu warto ją stosować), w naszym wypadku jest to zliczanie_i_przesuwanie. Za dyrektywą proces wewnątrz nawiasu umieszcza się nazwy sygnałów, których zmiana stanu wywołuje rozpoczęcie procesu. W naszym przypadku jest to sygnał zegarowy. Dalej umieszczona jest komenda begin oznaczająca początek bloku kodu z instrukcjami z wnętrza bloku process.

Wewnątrz procesu zliczanie_i_przesuwanie znajdują się dwie instrukcje warunkowe. Po słowie if umieszcza się warunek. W naszym przypadku jest to wystąpienie zbocza narastającego sygnału zegarowego, po warunku umieszcza się dyrektywę then. Działanie instrukcji warunkowej jest następujące: gdy warunek jest spełniony, to wykonaj instrukcje znajdujące się po słowie then.

Nasz proces posiada dwie instrukcje warunkowe - jedna jest zagnieżdżona w drugiej. Pierwszy, nadrzędny warunek odpowiada za detekcję zbocza narastającego sygnału zegarowego. Drugi odpowiada za przeprowadzanie zerowania sygnału licznik i instrukcje przesuwania (dla sytuacji gdy będzie spełniony warunek, tzn. sygnał licznik osiągnie wartość LICZNIK_LIMIT).

Wśród instrukcji widać jeden, nowy zapis:

Powyższa instrukcja jest równoważna przypisaniu wszystkim bitom danej magistrali wartości po prawej stronie znaku '=>' (w naszym przypadku chodzi o zerowanie). Jest to po prostu wygodniejszy zapis od:

Poniższa instrukcja realizuje rdzeń funkcjonalności naszego rejestru przesuwnego:

Następuje skopiowanie najmłodszego bitu rej_przesuwny(0) na najstarszą pozycje. Tajemniczy operator & oznacza przeprowadzanie połączenia (konkatenacji), czyli "sklejanie bitów". Często tego pojęcia używa się do operacji na łańcuchach znakowych (stringach) w innych językach programowania. Nasza instrukcja powoduje przestawienie wartości z najmłodszego bitu na najstarszą pozycje.

Animacja - konkatenacja w praktyce.

Animacja - konkatenacja w praktyce.

W sytuacji, gdy drugi warunek nie będzie spełniony, to zostanie wykonana instrukcja inkrementacji wewnątrz instrukcji else. Blok ten służy do określenia, które instrukcje mają zostać wykonane w sytuacji, gdy warunek nie jest spełniony. Blok else jest domyślnie interpretowany jako należący do najgłębiej zagnieżdżonego warunku, który nie posiada jeszcze swojego else.

Po warunku i odpowiadającym im bloku else należy umieścić komendę end if. Dodajemy ją dla każdego poziomu zagnieżdżenia osobno. Jak mamy dwa poziomy zagnieżdżenia, jak w naszym przypadku, to umieszczamy dwa razy end if.

Na końcu bloku architecture jest zapisana instrukcja przypisania rejestru do diod świecących. To właśnie tutaj jest przeprowadzany proces przypisania kolejno wszystkich bitów magistrali (wektora) rej_przesuwny do linijki LED. Oczywiście bity są przypisywane odpowiednio do swoich pozycji, tj. bit rej_przesuwny(0) będzie przypisany do led(0) i tak dalej.

W nowo utworzonym projekcie wklejamy powyższy kod. Wynikiem jego działania będzie kolejne świecenie każdej z diod (od D1 do D8). Diody będą reprezentowały 8 bitowy rejestr, w którym będziemy przesuwać w prawo logiczną jedynkę, która zostanie na początku do niego wpisana.

Działanie układu w praktyce -

Działanie układu w praktyce - "1" przesuwane w prawo.

Oczywiście konieczny będzie również nowy plik UCF. Można pobrać gotowy plik lub utworzyć własny z poniższą zawartością:

Jeśli zmienimy w programie wartość początkową rejestru z:

Na:

To na płytce zacznie nam wędrować 5, czyli binarnie "101":

Działanie układu w praktyce -

Działanie układu w praktyce - "101" przesuwane w prawo.

Inspiracje na zadania dodatkowe dla chętnych

Zainteresowani samodzielną pracą mogą pokusić się o przebudowanie programu. Warto zacząć od czegoś łatwego czyli zmiany częstotliwości migania diod. Kolejny krok, to odwrócenie kierunku pracy rejestru (niech przesuwa dane w lewo). 

Warto również spróbować stworzyć pierwotną wersję opisywanego rejestru, czyli taką, w której dane wychodzą "za rejestr" i nie wracają  na jego początek:

Działanie układu w praktyce -

Działanie układu w praktyce - "101" przesuwane w prawo (bez powrotu danych).

Programowaniem vs. tworzeniem aplikacji w VHDL

W tym momencie warto jeszcze raz podkreślić różnice między programowaniem klasycznym, a VHDLem. Komputer/mikrokontroler wykonuje operacje według ściśle określonego przepisu. Przepis ten jest nazywany programem komputerowym. Procesory znane z komputerów PC, czy mikrokontrolery wykonują wszystkie operacje według rozkazów zapisanych w pamięci.

Z układami programowalnymi jest inaczej. Mamy tutaj potężne narzędzie jakim jest możliwość wydzielenia bloków, które umożliwią wykonywanie określonych funkcjonalności niezależnie od siebie. Podobną filozofie można dostrzec w układach peryferyjnych mikroprocesorów.

Dwie kluczowe różnice między programowaniem mikrokontrolerów, a tworzeniem kodu dla PLD:

  • Możliwość tworzenia instrukcji współbieżnych (ang. concurrent statements). Pewne konstrukcje językowe w VHDL (np. while-select) mogą się wykonywać równocześnie i niezależnie od siebie.
  • Proces tłumaczenia kodu z postaci tworzonej przez człowieka do formy czytelnej dla układu w przypadku języka VHDL jest określany jako "Synteza". W klasycznych językach programowania nazywane jest to kompilacją. W przypadku Syntezy nie powstaje lista rozkazów, które układ miałby wykonać. Generowana jest konfiguracja wewnętrznych bloków logicznych.

Podsumowanie

Podstawy VHDL za nami. Bramki, multipleksery oraz różne warianty rejestrów przesuwnych, to bardzo dobry trening w nauce FPGA. Warto wykonać jak najwięcej takich prostych ćwiczeń, aby opanować w praktyce pracę z VHDL. Właśnie podczas takich zadań najłatwiej oswoić się z architekturami, blokami, procesami oraz innymi elementami specyficznymi dla tego języka.

W kolejnych odcinkach zajmiemy się tworzeniem bardziej rozbudowanych programów. Przed nami między innymi automaty stanów skończonych oraz robienie "porządków" w projektach.

Nawigacja kursu

Autor kursu: Adam Bemski
Redakcja: Damian Szymański
Testy, ilustracje: Piotr Adamczyk

O autorze: Adam Bemski

Adam Bemski
Autorem kursu jest Adam Bemski, specjalista od systemow wbudowanych. Pracuje w obszarze automatycznego testowania urządzeń z funkcjonalnością IoT. Adam dodatkowo prowadzi zajęcia z techniki mikroprocesorowej na wyższej uczelni DHBW Stuttgart. Więcej szczegółów o Adamie na blogu adambemski.com.

fpga, kurs, kursFPGA, rejestr, vhdl

Trwa ładowanie komentarzy...