Kurs FPGA – #5 – bramki logiczne, multiplekser

Kurs FPGA – #5 – bramki logiczne, multiplekser

Pora na samodzielne tworzenie programów. Tym razem zajmiemy się bramkami logicznymi, które omówiliśmy w kursie techniki cyfrowej

Dodatkowo poznamy w praktyce multipleksery. Są to zupełnie nowe elementy, których jeszcze nie mieliśmy okazji testować w innych kursach.

Bramki logiczne w VHDL

Język VHDL jest przeznaczony do tworzenia struktur układów cyfrowych. Zaczniemy więc od najprostszych "cegiełek" z których można zbudować działające urządzenie - bramek logicznych.

Na początku tworzymy nowy projekt (analogicznie do opisu z poprzedniego artykułu). Nie będziemy już ponownie opisywać całego procesu, aby nie rozciągać artykułu. W nowym projekcie tworzymy plik typu VHDL Module i przechodzimy do praktyki!

Bramka AND na FPGA

Bramka AND jest iloczynem logicznym. Stan logiczny wysoki wszystkich wejść daje sygnał wyjściowy wysoki. W każdym innym przypadku na wyjściu pojawi się stan logiczny niski (0).

Przykład tabeli prawdy dla bramki AND z dwoma wejściami widoczny jest poniżej. Szczegóły na temat samych funktorów logicznych (bramek) znaleźć można w kursie techniki cyfrowej oraz na naszych elektronicznych ściągach.

Stwórzmy teraz własną bramkę AND na FPGA. Wejściami będą dwa przyciski oznaczone na naszym zestawie (SW1 oraz SW2). Wyjściem będzie jedna z diod świecących. Cały układ będzie więc symbolicznie wyglądał następująco:

FPGA w roli bramki AND.

FPGA w roli bramki AND.

Przykładowy program realizujący to zadanie widoczny jest poniżej. Zacznijmy od jego omówienia:

Najpierw dodajemy biblioteki: library IEEE umożliwia nam skorzystanie ze standardowych konstrukcji VHDL takich jak entity/architecture itd. Linijka use IEEE.STD_LOGIC_1164.ALL; umożliwi nam skorzystanie z typów std_logic oraz std_logic_vector (więcej na temat tych typów w dalszej części artykułu).

Aby zaimplementować bramkę logiczną AND w VHDL struktura bloku entity powinna wyglądać następująco (dokładnie blok ten omówimy przy następnym projekcie). Mówiąc w skrócie w tym miejscu określamy wejścia i wyjścia budowanego układu:

W bloku architecture między słowami kluczowymi begin/end opisujemy działanie układu. W tym wypadku interesuje nas, aby na LED(0) ustawić wynik operacji Switch(0) and Switch(1).

Aby przetestować bramkę należy utworzyć plik .ucf. Plik ten opisuje połączenia naszych nazw z bloku entity do odpowiednich pinów FPGA. Podobnie jak w poprzedniej części kursu są dwie drogi. Pierwsza, to użycie gotowego pliku - elbertv2_bramki.ucf (poprzez Add source...).

Drugi sposób, to samodzielne, ręczne utworzenie ucf'a. Wtedy tworzymy plik typu Implementation Constraints File. W jego wnętrzu odpowiednio opisujemy połączenia:

W przypadku przycisków należy dodać komendę "| PULLUP". Wynika to z faktu, że na naszej płytce ElbertV2 przyciski (Switche) są połączone z masą (GND) bez dodatkowych rezystorów podciągających. Wycinek z schematu używanego zestawu:

kurs_fpga_4_10

Fragment schematu używanego zestawu.

Poniżej widać, że przyciski, których używamy podłączone są do pinów 80 i 79:

kurs_fpga_4_11

Fragment schematu używanego zestawu.

kurs_fpga_4_12

Fragment schematu używanego zestawu.

Gdy wszystko jest gotowe można przeprowadzić syntezę kodu w Xilinx ISE i sprawdzić układ w praktyce! Należy pamiętać, aby we właściwościach opcji Generate Programming File zaznaczyć Create Binary Configuration File. Proces generowanie i wgrywania plików na płytkę został dokładnie opisany w poprzedniej części kursu.

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 »

Program wgrany i... jakie spostrzeżenia? Pamiętaj, że przyciski z których korzystamy jako wejście to SW1 i SW2. Układ nie działa tak, jak się tego spodziewaliśmy!

Błędne działania bramki AND - animacja.

Błędne działania bramki AND - animacja.

Układ działa jak zanegowana bramka OR (czyli NOR)! Przyjmując, że wciśnięty przycisk dawałby jedynkę logiczną to działanie układu odpowiada właśnie bramce NOR. My jednak chcieliśmy zaimplementować bramkę AND, skąd więc taki wynik? Okazuje się, że wszystko jest w porządku, jedynie nasze przyciski działają według logiki odwrotnej.

Aby doświadczyć poprawnego działania bramki AND zgodnie z intuicyjnym sposobem testowania układu (wciśnięcie przycisku = logiczne 1), należy w bloku Architecture zanegować wejścia:

Od teraz układ będzie działał zgodnie z naszą intuicją - wciśnięcie przycisku będzie oznaczało stan wysoki (logiczne 1). Po ponownym przeprowadzeniu procesu syntezy i wgraniu konfiguracji wszystko powinno działać zgodnie z przyjętymi założeniami.

Poprawne działanie bramki AND w praktyce.

Poprawne działanie bramki AND w praktyce.

Ostateczny, poprawny kod widoczny jest poniżej:

Inne bramki logiczne 

W poniższej tabeli znajduje się wykaz wszystkich operatorów logicznych w języku VHDL. Warto we własnym zakresie przećwiczyć tworzenie innych bramek w praktyce! Jest to podstawa języka VHDL, którą warto opanować do sprawnego korzystania z FPGA.

Przykładowo, aby sprawdzić działanie bramki OR na FPGA w bloku Architecture wpisujemy:

Cały kod w VHDL powinien wyglądać wtedy następująco:

Poprawne działanie bramki OR:

Bramka OR w praktyce.

Bramka OR w praktyce.

Czym jest multiplekser?

Multiplekser jest popularnym układem w technice cyfrowej. Jego działanie polega na tym, że z liczby N sygnałów wejściowych na wyjściu przekazywany jest stan logiczny tylko jednego z nich. O tym które z wejść będzie widziane na wyjściu decydują dodatkowe wejścia adresowe.

Mówiąc prościej: mamy kilka numerowanych wejść i jedno wyjście. Dodatkowo występują linie adresowe, na których ustawiamy numer wejścia, który ma być przekazany na wyjście. Adresacja dokonywana jest za pomocą binarnego systemu liczbowego. Przykład na adresie 2 bitowym:

  • 00 w systemie binarny, oznaczać będzie wejście nr 0,
  • 01 w systemie binarny, oznaczać będzie wejście nr 1,
  • 10 w systemie binarny, oznaczać będzie wejście nr 2,
  • 11 w systemie binarny, oznaczać będzie wejście nr 3.

Działanie tego układu w praktyce widoczne jest na poniższej animacji:

Wybór wejścia w multiplekserze - animacja.

Wybór wejścia w multiplekserze - animacja.

Multipleksery wykorzystuje się w systemach, gdzie jest zapotrzebowanie na zaoszczędzenie liczby połączeń, którymi przesyłane są sygnały. Maksymalna liczba wejść danych multipleksera adresowanych poprzez linie adresowe wyraża się wzorem N=2^A, gdzie: A - liczba wejść adresowych, N - liczba wejść danych.

Implementacja multipleksera w VHDL

Pora uruchomić taki układ na FPGA. Załóżmy, że wejściami naszego multipleksera będą cztery przyciski SW1, SW2, SW3, SW4. Za pomocą przełączników suwakowych DIP-switch będziemy ustawiać adres wejścia, które ma być przekazane na wyjście, czyli diodę świecącą.

Opis przycisków używanych do testu multipleksera.

Opis przycisków używanych do testu multipleksera.

Program realizujący to zadanie widoczny jest poniżej:

Zajmijmy się teraz analizą powyższego kodu. Zacznijmy od samej góry:

Wektory w VHDL

W bloku entity deklarujemy pewne typy sygnałów. Są to magistrale (nazywane również "bus"), a w języku VHDL określa się je mianem "vector" (wektor). Sygnał tego typu jest połączeniem określonej liczby bitów. Prawidłowa deklaracja wygląda jak na poniższym schemacie:

Nasz przykład z powyższego programu jest następujący:

Wektor ten nosi nazwę Switch i jest kierunku wejściowego (jest traktowany jako wejście dla naszego FPGA), ma długość 4 bitów. Jego najstarszy bit (patrząc od lewej strony) ma numer 3.

Język VHDL daje możliwość odwrotnego numerowania bitów w wektorze. W takim przypadku najstarsze bity posiadają najmniejsze numery. By to osiągnąć należy w nawiasie zamiast downto wpisać to. Bardzo rzadko korzysta się z tego rodzaju deklaracji i nie warto robić sobie takich nawyków. Może to ograniczyć czytelność kodu i tym samym utrudnić analizę błędów.

Sygnały lokalne

Na początku bloku "architecture" umieszczamy deklarację sygnałów lokalnych.

Sygnały te nie są wyprowadzane na zewnątrz chipu FPGA. Są one używane jedynie jako zmienne pomocnicze. Sygnały lokalne deklaruje się miedzy architecture i begin. Przykładem sygnału lokalnego jest "licznik" wykorzystany w poprzedniej części kursu. Sygnał ten służył wtedy do zapamiętania stanu licznika.

Sygnały lokalne deklaruje się podobnie jak sygnały zewnętrzne z bloku Port w danym entity, jednak z następującymi różnicami:

  • deklaracje należy poprzedzić dyrektywą signal,
  • po dwukropku nie określa się kierunku.

Konstrukcja with-select

Za słowem begin znajduje się blok konstrukcji with-select. Umożliwia ona wybór przypisania sygnału wyjściowego (bądź sygnału lokalnego) do jednej z możliwości wyróżnionych komendą when. Wybór jest uzależniony od wartości sygnału przełączającego umieszczonego między with, a select w pierwszej linijce.

W naszym przykładzie działanie tego mechanizmu wygląda następująco:

  • gdy stan logiczny sygnału "szyna_buforowa_adresow" będzie "00" to na wyjściu naszego multipleksera będzie wystawiony sygnał z wejścia "szyna_buforowa_wejsc(0)".

Generalnie nie ma potrzeby podawania wszystkich kombinacji możliwych dla przełącznika w instrukcjach when, wartości pozostałe wystarczy opisać komendą others jak w ostatniej linijce.

Przypisanie wektorów wejściowych do sygnałów lokalnych

W ostatnim bloku przypisujemy sygnały (wektory) wejściowe do sygnałów lokalnych. Operacja ta jest połączona z równoczesną negacją wszystkich bitów danego wektora. Ma to na celu przywrócenie intuicyjności działania  multipleksera, tak aby po wciśnięciu przyciski były rozumiane przez "tabelę prawdy" w konstrukcji "with-select" jako logiczne 1.

Na koniec jest jeszcze jedna rzecz wymagająca pewnego wyjaśnienia. W naszym przykładzie konstrukcja with-select oraz przypisania do wektorów buforujących wykonywane są równocześnie. Nie ma tu żadnego znaczenia kolejność tych bloków kodu między sobą. Nie ma też żadnego opóźnienia między przypisaniem do sygnałów buforujących, a ich użyciem w konstrukcji with-select.

Tworzenie pliku ucf dla multipleksera

Ostatnim krokiem jest przygotowanie pliku ucf. Tworzymy więc plik typu "Implementation Constraints File" i uzupełniamy go poniższą zawartością:

Działanie multipleksera widoczne jest w praktyce na poniższej animacji. Gdy na przełącznikach przesuwanych ustawiony jest adres 00, to na diodzie reprezentowany jest sygnał z wejścia 0. Inaczej mówiąc wciśnięcie tego przycisku spowoduje włączenie diody (pozostałe przyciski nie powodują żadnych zmian). Po wybraniu adresu 01 do diody będzie przepływał sygnał od wejścia numer 1 itd.

Działanie układu z multiplekserem w praktyce - animacja.

Działanie układu z multiplekserem w praktyce - animacja.

Podsumowanie

Po tych kilki przykładach wiele powinno się rozjaśnić. Warto poświęcić teraz trochę czasu na eksperymentowanie z innymi bramkami logicznymi. Oczywiście nie trzeba do każdej tworzyć osobnego programu - można spokojnie testować po kilka bramek jednocześnie (w końcu mamy 6 przycisków i 8 diod)! Dobrym treningiem będzie również na pewno rozbudowanie multipleksera, aby obsługiwał więcej wejść.

W następnym odcinku zajmiemy się kolejnym, nowym elementem - rejestrem przesuwnym!

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.

bramki, fpga, kurs, kursFPGA, multiplekser

Trwa ładowanie komentarzy...