Ta strona używa ciasteczek (plików cookies), dzięki którym może działać lepiej. Dowiedz się więcejRozumiem i akceptuję

Kurs FPGA – #4 – pierwszy projekt, przykład VHDL

Programowanie 03.10.2017 Adam, Damian

Po instalacji sterowników i środowiska możemy wgrać pierwszy program. Pozwoli to na proste sprawdzenie, czy wszystko zostało odpowiednio zainstalowane i skonfigurowane.

W tej części kursu FPGA zajmiemy się wgraniem gotowego programu. Dzięki czemu przejdziemy szybko przez wszystkie etapy tworzenia projektu.


Nawigacja serii artykułów:
« poprzednia część

» Pokaż/ukryj cały spis treści «

Kup zestaw elementów i zacznij naukę w praktyce! Przejdź do strony dystrybutora »

Tworzenie pierwszego projektu w Xilinx ISE WebPack

Pora na wykonanie czegoś praktycznego. Od teraz zestaw Elbert v2 będzie już niezbędny. Na początek „Hello World” świata elektronicznego, czyli „mryganie” diodami świecącymi.

Krok 1. Na początek uruchamiamy „Xilinx ISE WebPack”:

Xilinx ISE Webpack – pierwsze uruchomienie.

Krok 2. W menu głównym wybieramy File → New Project. Otworzy się okno kreatora nowego projektu. W polu Top-level source type wskazujemy opcję HDL. Wybieramy nazwę i lokalizację dla nowego projektu i klikamy Next.

W polu z nazwą projektu należy unikać polskich znaków!

Xilinx ISE Webpack – tworzenie nowego projektu.

Krok 3. Pojawi się okno ustawień dla projektu. Jest ono bardzo ważne, tutaj trzeba określić typ układu FPGA. Kluczowe jest wypełnienie pól Family, Device, Speed.

Poprawne ustawienia widoczne są na poniższym zrzucie ekranu:

Xilinx ISE Webpack – tworzenie nowego projektu.

Następnie klikamy Next. W tym momencie wyświetlone zostanie podsumowanie projektu, które powinno wyglądać następująco:

Klikamy Finish.

Zestaw do kursu FPGA

Gwarancja pomocy na forum dla osób, które kupią poniższy zestaw!

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


Kup w Botlandzie »

Krok 4. Struktura projektu widoczna jest w lewym górnym rogu. Klikamy prawym przyciskiem myszy na symbolu układu xc3s50a-5tq144 i wybieramy New Source.

Tworzenie modułu VHDL w projekcie.

Krok 5. Pojawi się okno wyboru rodzaju pliku jaki ma być utworzony. Wybieramy VHDL Module. W polu File name można wpisać dowolną nazwę (bez polskich znaków). Należy się upewnić, że opcja Add to project jest zaznaczona. Następnie klikamy przycisk Next.

Tworzenie modułu VHDL w projekcie.

Krok 6. W tym momencie pojawi się kreator definiowania pinów dla naszej aplikacji (dokładniej mówiąc modułu VHDL). W tworzeniu tego pierwszego projektu nie będziemy go używać. W ten sposób nauczymy się lepiej składni VHDL.

Pozostawiamy wszystkie pola według ustawień domyślnych!

Tworzenie modułu VHDL w projekcie.

Krok 7. Teraz pokaże się podsumowanie kreatora naszego modułu. Przykładowa zawartość:

Klikamy Finish. Naszym oczom ukarze się nowy projekt:

Projekt z utworzonym modułem VHDL.

Krok 8. Domyślny kod programu powinien wyglądać następująco:

Wielkość tekstu w edytorze można zmieniać skrótem:
CTRL + kręcenie rolką myszki.

Omówienie wygenerowanego kodu

Poniżej znajduje się skrótowe omówienie kodu. Do dokładnych wyjaśnień przejdziemy w dalszych częściach kursu. Na ten moment głównym celem jest ogólne przedstawienie struktury programu i jego przetestowanie w praktyce!

W języku VHDL każda linia zaczynająca się od dwóch znaków minusa („–„) traktowana jest jako komentarz i nie jest uwzględniana w procesie syntezy.

W stworzonym pliku z kodem VHDL na samej górze (w komentarzach) ujęto informacje, które często pojawiają się w bardziej rozbudowanych programach. Znajdziemy tam datę utworzenia projektu oraz jego twórców.

W naszym przypadku (pierwszy, prosty program)
można się spokojnie pozbyć tych komentarzy.

Poniżej tego opisu znajdziemy kilka linii, które dołączają biblioteki. To dzięki nim możliwe jest użycie pewnych zdefiniowanych typów. Zagadnienie to będzie później dokładniej wyjaśnione.

W dalszej części tego przykładu będziemy używać operacji arytmetycznych, więc zgodnie z informacją w komentarzu musimy odkomentować linię use IEEE.NUMERIC_STD.ALL. Inne informacje z tej sekcji są zbędne więc możemy (ale nie musimy) okroić ją do poniższej wersji:

Poniżej umieszczona jest komenda entity (ang. jednostka). Entity określa pojedynczy układ. W tym miejscu trzeba się odwołać do koncepcji języka VHDL. Nasze entity opisuje parametry (wyprowadzenia) widziane z zewnątrz danego modułu VHDL. W jednym chipie FPGA może być skonfigurowane kilka zupełnie od siebie oddzielnych entity.

Pojęcia entity i moduł VHDL będą używane w tym kursie zamiennie!

W języku VHDL wejścia i wyjścia modułu VHDL opisuje się w bloku Port. Wewnątrz deklaruje się konkretne funkcje pinów. Drugim sformułowaniem, które określa moduł jest architecture. Określa ono wewnętrzne funkcjonowanie danego układu.

Poprzez analogię: Port to okładka, a architecture to wnętrze książki (entity).

W pierwszym projekcie wstawmy tam następujący fragment kodu (krótkie wyjaśnienia zawarte są w komentarzach):

W języku VHDL generalnie każdą „instrukcje” należy zakończyć średnikiem. Jednak częstym błędem jest dodawanie średnika za ostatnią deklaracją sygnału zewnętrznego w bloku „Port”.

Jak widać blok entity otrzymał nazwę zgodną z nazwą pliku (modułu) VHDL, który wcześniej utworzyliśmy w środowisku Xilinx ISE. W tym przypadku jest to mryganie_led. W bloku Port następuje deklaracja połączeń zewnętrznych (wyprowadzeń) dla naszego FPGA. Jak widać w naszej pierwszej aplikacji będzie to sygnał zegarowy (jako wejście) i sygnał o szerokości 8 bitów dla diod świecących (jako wyjście).

Nasza aplikacja ma powodować miganie LEDów. Zrealizujemy to w postaci układu liczącego – timera, który po określonej liczbie cykli sygnału zegarowego będzie powodował zmianę stanu diod. Sygnał zegarowy jest dostępny na płytce ElbertV2 – ma on częstotliwość 12 MHz. Podobnie sprawa ma się z diodami LED – są one dostępne na naszym zestawie ElbertV2.

Na tym etapie jedynie deklarujemy jakie sygnały będą wykorzystywane w aplikacji. Przypisaniem do konkretnych wyprowadzeń na płytce zajmiemy się później.

Każdy sygnał musi mieć zdefiniowany typ. W naszym przypadku jest to standard logic zapisywany jako STD_LOGIC. Typ STD_LOGIC jest ogólnym typem zalecanym standardowo przy pracy z układami PLD. W tym momencie nie będziemy się zbytnio rozwodzić nad szczegółami, aby móc szybko uruchomić pierwszy przykład.

Dobre praktyki języka VHDL

Język VHDL nie jest „case-sensitive” – nie odróżnia wielkich i małych liter. Mimo to zaleca się następujące konwencje:

  1. Słowa kluczowe VHDL zapisuje się małymi literami np.: „entity”.
  2. Własne definicje (np. stałych wartości) zapisuje się wielkimi literami.
  3. Nazwy i oznaczenia muszą zaczynać się od liter (potem mogą być znaki, cyfry i podkreślniki). Nie należy przekraczać długości 32 znaków dla nazw.
  4. Kompletne instrukcje VHDL są zakończone średnikiem. Do oddzielenia instrukcji służy przecinek lub dwukropek.

W dalszej części pliku VHDL Xylinx ISE umieścił automatycznie blok architecture o domyślnej nazwie Behavioral przypisanej do entity o nazwie mryganie_led.

Uwaga! Każdemu entity musi być przypisana minimum jedna architecture!

W tym miejscu wstawiamy poniższy kod (krótkie wyjaśnienia znajdują się w komentarzu):

Dokładniej mówiąc całą istotę działania danej architecture opisuje się pomiędzy begin, a end nazwa_architektury. W naszym bloku „architecture” przed „begin” zostały zawarte deklaracje sygnałów wewnętrznych.

Omówimy je teraz pobieżnie, szczegóły składni
będą wyjaśnione w dalszych częściach kursu.

Pierwsza linia, to deklaracja stałej – będzie to maksymalna wartość licznika, którego wyjście będzie zmieniało stan diod świecących.

Kolejną instrukcją jest deklaracja sygnału lokalnego o nazwie licznik. Zmienna ta jest typu bez znaku i ma długość 25 bitów.

Następnie deklarujemy sygnał mryganie, służy on jako bufor. Jego stan logiczny jest negowany przy każdym napełnieniu licznika. Musimy użyć bufora, gdyż wyjścia danego entity (w naszym przypadku chodzi o led), nie mogą być zmieniane w bloku process.

Kolejnym blokiem wewnątrz naszej architecture jest już to, co opisuje zachowanie naszej aplikacji i jest zawarte za instrukcją begin. Tutaj umieszczony został blok Process.

Warto zwrócić uwagę, że ten blok również
ma swoją oddzielną komendę begin!

W dalszej części kursu omówimy dokładniej jego składnię. Na ten warto jedynie wiedzieć, że odbywa się tutaj inkrementacja (zwiększanie wartości o jeden). Inkrementowany jest sygnał licznik do wartości zadeklarowanej jako licznik_limit.

Gdy licznik osiągnie maksymalną wartość następuje zmiana wartości sygnału mryganie na przeciwny. Umożliwia to włączanie i wyłączenie diod święcących.

Na końcu przekazujemy wartości sygnału lokalnego na piny zewnętrzne. Odbywa się to tutaj:

Powyższa instrukcja musi być zawarta przed
słowem kluczowym end danej architecture.

Na ten moment zawartość pliku (modułu) VHDL powinna wyglądać następująco:

Przygotowanie pliku konfiguracyjnego .ucf

Teraz zajmiemy się stworzeniem pliku .ucf (user constraint file). Jest on niezbędny, aby przypisać fizyczne sygnały (piny FPGA) nazwom stworzonym wewnątrz bloku Port naszego entity.

Uwaga! Operacje na pliku ucf są objęte pewnym ryzykiem. Błąd w przypisaniach odpowiednich pinów może prowadzić do uszkodzenia zestawu ElbertV2. Jeśli chcesz uniknąć takiej sytuacji możesz dodać gotowy plik elbertv2_mryganie.ucf.

Błąd w wypełnieniu pliku może prowadzić do zwarcia, np. gdy pin do którego na zestawie ElbertV2 podłączony jest przycisk przypiszemy jako wyjście diody. Nasz przycisk zwiera potencjał do masy, więc gdy będziemy w kodzie wymuszać na tym pinie jedynkę logiczną – nastąpi zwarcie i zniszczenie FPGA. Podobnych zagrożeń jest więcej – należy uważać!

Dodawanie gotowego pliku ucf

Krok 1. W tym celu klikamy prawym przyciskiem myszy na pliku VHDL w Xilinx ISE jak na poniższym zrzucie ekrany i wybieramy Add Source. W nowo otwartym oknie wybieramy plik elbertv2_mryganie.ucf (najlepiej zapisać go wcześniej w folderze z projektem). Klikamy OK.

kurs_fpga_3_35

dodawanie gotowego pliku ucf.

Krok 2. Jeśli wszystko poszło dobrze, to pojawi się poniższe okno:

dodawanie gotowego pliku .ucf 2.

Dodawanie gotowego pliku .ucf.

Należy się upewnić, że są zaznaczone opcje Implementation w kategorii Association oraz work w Library. Klikamy przycisk OK. To wystarczy by plik .ucf był dodany do projektu!

Samodzielne tworzenie pliku ucf

Tworzenie własnego pliku ucf, to nic strasznego – należy jednak zachować szczególną uwagę, bo prosty błąd (literówka) może doprowadzić do uszkodzenia zestawu.

Krok 1. Klikamy prawym przyciskiem myszy na nazwę modułu VHDL (zgodnie z poniższym zrzutem ekranu) i wybieramy opcję New source.

Tworzenie pliku ucf.

Krok 2. Jako rodzaj dodawanego modułu wybieramy Implementation Constraints File. Plik można nazwać dowolnie np. przypisanie_pinow. Upewniamy się koniecznie, że zaznaczone jest pole Add to project i klikamy Next.

Tworzenie własnego pliku ucf.

Krok 3. Program wyświetli podsumowanie ustawień, które może wyglądać następująco:

Jeśli wszystko się zgadza, to klikamy Finish i idziemy dalej.

Krok 4. Otwieramy plik ucf (jeśli nie otworzył się samoczynnie). Będzie on pusty. Wstawiamy do niego następujące komendy:

Za pomocą powyższych linii przypisujemy naszym sygnałom fizyczne piny układu. Informacje te są dość proste i najłatwiej zrozumieć ich „sens” analizując powyższy plik czytając wyjaśnienia:

  • NET oznacza połączenie,
  • LOC oznacza lokalizację, czyli numer pinu z dokumentacji układu.
  • IOSTANDARD oznacza standard napięciowy danego pinu (tutaj 3.3V),
  • PERIOD oznacza częstotliwość. Jej definiowanie ma sens oczywiście w przypadku sygnału zegarowego.
  • DRIVE oznacza wydajność prądową w mA dla danego pinu.
  • SLEW oznacza parametr, który można opisać jako zdolność do szybkiej zmiany stanu logicznego z 1 na 0 lub odwrotnie. W naszym przypadku szybkość nie stanowi priorytetu dlatego jest ustawiona na SLOW.

Wzrost szybkości jest okupiony większą emisją zakłóceń. Jest to złożone zjawisko. Więcej informacji na ten temat znaleźć można w Internecie pod hasłem Slew Rate.

Opis pinów można znaleźć na schemacie naszego zestawu uruchomieniowego. Na poniższych zrzutach ekranu widać wykorzystane przez nas piny:

Pin z sygnałem zegarowym.

Pin z sygnałem zegarowym.

Piny diod świecących.

Piny diod świecących.

Na koniec zapisujemy plik i przechodzimy do „wgrania programu”!

Wgrywanie konfiguracji na zestaw ElbertV2

Przed załadowaniem naszej aplikacji na płytkę ElvertV2 należy wykonać jeszcze jedną czynność. W panelu hierarchii projektu (po lewej stronie) na górze zaznaczamy plik VHDL. Poniżej wyświetli się lista możliwych procesów do wykonania na tym projekcie. Klikamy prawym przyciskiem myszy na Generate Programming File i wybieramy Process Properties.

Generowanie pliku bin.

W nowym oknie zaznaczamy pole Create Binary Configuration File, które znajdziemy na zakładce General Options. Następnie zatwierdzamy zmiany przyciskiem OK i idziemy dalej.

Zmiana ustawień projektu.

Teraz możemy już wygenerować plik zawierający konfigurację naszej aplikacji. Klikamy prawym przyciskiem myszy na Generate Programming File i wybieramy Run. Gdy wszystko pójdzie dobrze, to po pewnym czasie (do 5 minut) pojawią się zielone „ptaszki” przy polach:

  • Synthesize – XST,
  • Implement Design,
  • Generate Programming File.

Poprawne zakończenie procesu.

Do wgrania konfiguracji na FPGA potrzebny jest program ElbertV2Config do pobrania ze strony producenta lub bezpośrednio z tego miejsca. Pobranego pliku nie trzeba instalować, wystarczy go uruchomić. Program powinien wyglądać następująco:

Program służący do konfiguracji naszego FPGA na płytce ElbertV2

Program służący do konfiguracji FPGA na płytce ElbertV2.

W miejscu Select Port wybieramy numer portu COM odpowiadającego zestawowi ElbertV2. Numer ten można odczytać w Menadżer Urządzeń. Klikamy Open File i z katalogu naszego projektu wybieramy plik z rozszerzeniem bin (np. mryganie_led.bin). Klikamy przycisk Program.

Jeśli wszystko przebiegło poprawnie, to diody powinny migać!

Warto w tym miejscu wyjaśnić, na czym polega „wgrywanie konfiguracji”. W rzeczywistości podczas tego procesu programujemy pamięć FLASH znajdującą się na płytce ElbertV2.  Konfiguracja podczas podłączenia zasilania układu jest wgrywana do FPGA za każdym razem. 

W odróżnieniu od mikrokontrolerów układy FPGA przechowują konfiguracje do ustania zasilania. Nie ma tutaj procesu podawania rozkazu, jego dekodowania i wykonywania. Konfiguracja FPGA określa jego wewnętrzną strukturę „ad hoc” (łac. na dany moment).

Ćwiczenie 4.1

W ramach zadania domowego zajmij się przeanalizowaniem dzisiejszego, testowego programu. Spróbuj zmienić częstotliwość, z którą migają diody! W komentarzach czekamy na zdjęcia/filmy, dajcie znać, że wszystko działa poprawnie!

Podsumowanie

Od teraz każdy powinien już mieć skonfigurowane i gotowe do pracy środowisko. W kolejnych częściach kursu nie będziemy poświęcać już miejsca na opisywanie procesu tworzenia projektu i wgrywania konfiguracji do układu. W razie problemów zawsze będzie można wrócić do tego artykułu i podejrzeć jak było to robione za pierwszym razem. Warto również powoli oswajać się z pojęciami typu  enity, port oraz architecture.

W następnych artykułach zajmiemy się wykorzystaniem informacji z kursu techniki cyfrowej (zaczniemy od bramek logicznych). Stopniowo będziemy również poznawać poszczególne elementy składni języka VHDL.

» Pokaż/ukryj cały spis treści «

Kup zestaw elementów i zacznij naukę w praktyce! Przejdź do strony dystrybutora »

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

O autorze: 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.

Powiadomienia o nowych, darmowych artykułach!

Komentarze

codespy

13:43, 03.10.2017

#1

No nareszcie coś konkretnego. Pozdrawiam i czekam na kolejne odcinki.

Treker
Administrator

14:00, 03.10.2017

#2

codespy, działamy cały czas - kurs się rozkręca, zachęcam do eksperymentowania ;)

Mellon

7:44, 06.10.2017

#3

Wkradł się mały drobiazg - link do ElbertV2Config nie jest prawidłowy. Nasuwa mi się pytanie czy samo środowisko nie może zostać użyte do konfiguracji układu FPGA?

avr_pic

10:44, 07.10.2017

#4

Myślałem, że liczniki w układach FPGA będzie trzeba budować na pojedynczych bramkach :)

loocash

0:07, 08.10.2017

#5

Z niewielką modyfikacją dało radę odpalić to na maximatorze (akurat już to miałem zanim powstał kurs). Gif w załączniku.

Treker
Administrator

16:20, 14.10.2017

#6

Dziękuję za zwrócenie uwagi - link do konfiguratora jest już poprawiony :)

Zobacz powyższe komentarze na forum

FORBOT Damian Szymański © 2006 - 2017 Zakaz kopiowania treści oraz grafik bez zgody autora. vPRsLH.