Za nami kilka ćwiczeń praktycznych, wiemy już, że FPGA działa poprawnie i możemy tworzyć na jego podstawie przeróżne układy cyfrowe.
Podczas analizy bardziej złożonych projektów przydaje się możliwość symulowania tworzonego układu. Dzięki wykorzystaniu nowej funkcji ISE, otrzymamy wykresy (przebiegi czasowe), które pozwolą ocenić czy układ działa poprawnie.
Na tym etapie nauki VHDLa korzystanie z symulacji nie jest konieczne. Warto jednak wykonać ćwiczenia z tej części kursu FPGA, aby poznać możliwości naszego środowiska.
Symulacja działania układu FPGA z ISim
Symulacja "to potężne narzędzie", które pozwala na tworzenie jeszcze lepszych układów. Jest to ważny etap podczas przygotowywania większych projektów. Symulacja pozwala odpowiedzieć na pytanie "Czy układ działa poprawnie?" przed wgraniem konfiguracji do rzeczywistego układu.
Może to uchronić nas przed błędami, a nawet przed uszkodzeniem sprzętu!
Zacznijmy od programu, na którym będziemy trenować. Najlepiej wrócić do prostego przykładu z poprzednich lekcji. Poniżej widoczny jest kod VHDL bramki AND z piątej części kursu FPGA.
-- dodajemy biblioteki
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- tu okreslamy jakie wejscia i wyjscia bedzie mial nasz uklad cyfrowy
entity podstawowe_bramki is
Port( Switch : in STD_LOGIC_VECTOR(1 downto 0); -- dwa przyciski jako wejscia bramki logicznej
LED : out STD_LOGIC_VECTOR(0 downto 0)); -- dioda led jako wyjscie naszej bramki logicznej
-- zwroc UWAGE! na dodatkowy nawias zamykajacy blok Port
-- przed srednikiem!
end podstawowe_bramki;
-- slowo "Behavioral" odnosi sie do stopnia abstrakcji opisu naszej architektury
-- nie musisz sie nim na tym etapie zbytnio przejmowac
-- zagadnienie to obejmuje bardziej zaawansowane elementy tworzenia aplikacji w VHDL
architecture Behavioral of podstawowe_bramki is
begin
-- opis dzialania naszej aplikacji zawiera sie w ponizszej instrukcji;
LED(0) <= (not Switch(0)) and (not Switch(1));
end Behavioral;
Oraz zawartość niezbędnego pliku UCF:
NET "LED[0]" LOC = P46 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "Switch[0]" LOC = P80 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "Switch[1]" LOC = P79 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
Warto wgrać program dla testu i sprawdzić, czy wszystko działa poprawnie!
Poprawne działanie bramki AND w praktyce.
Normalnie kolejność powinna być odwrotna (najpierw symulacja). Podczas nauki korzystania z symulatora warto jednak unikać dodatkowych problemów stąd wcześniej upewniliśmy się jak układ działa w praktyce.
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!
Masz już zestaw? Zarejestruj go wykorzystując dołączony do niego kod. Szczegóły »
Przygotowanie do symulacji
Wiemy, że program działa, przejdźmy więc do symulacji. Na czym ma ona polegać? Chcemy, aby program przetestował działanie całego układu. W przypadku bramki AND najlepiej będzie jeśli sprawdzimy jak zachowuje się "wyjście układu" w zależności o wszystkich możliwych stanów na wejściach (00, 01, 10, 11).
Inaczej mówiąc chcemy przetestować wszystkie warianty występujące w tabeli prawdy dla AND:
Krok 1. W projekcie dotyczącym bramek logicznych klikamy prawym przyciskiem myszy (PPM) w drzewie projektu na pliku/module VHDL i wybieramy New Source:
Dodajemy nowy plik...
Krok 2. Z listy typów plików wybieramy VHDL Test Bench. Jako nazwę podajemy "test_bramek". Warto się upewnić, że opcja Add to project jest zaznaczona. Na koniec klikamy Next.
Tworzenie pliku TestBench.
Krok 3. Pojawi się pole wyboru modułu VHDL, dla którego ma być stworzony testbench. Mamy do wyboru jeden moduł więc nie musimy się nad tym więcej zastanawiać. Klikamy Next.
Testbench to, w tym wypadku, nazwa pliku zawierającego opis symulacji!
Krok 4. Pojawi się okno podsumowania, w którym klikamy tylko Finish.
Ostatni etap przed wprowadzeniem zmian.
Krok 5. Teraz przełączamy widok środowiska na Simulation. Odpowiednie pole do zaznaczenia znajduje się po lewej stronie ISE (nad drzewkiem projektu).
Do wyboru jest tam Implementation oraz Simulation:
Zmiana widoku na "Simulation".
Po przełączeniu widzimy nowy widok hierarchii projektu. Plik test_bramek to opis symulacji. Po rozwinięciu widoku pojawi się plik uut - podstawowe_bramki. Jest to moduł VHDL z naszym kodem, który będzie poddany symulacji.
Warto zwrócić uwagę, że hierarchia jest zorganizowana w taki sposób, że plik z symulacją jest położony "nad" testowanym modułem VHDL!
Rozwinięty widok symulacji.
Wróćmy do pliku z opisem symulacji, czyli test_bramek. Poniżej widoczny jest wygenerowany przez ISE kod. Usunięte zostały jedynie zbędne fragmenty, jak np. długie wstępne komentarze:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY test_bench IS
END test_bench;
ARCHITECTURE behavior OF test_bench IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT podstawowe_bramki
PORT(
Switch : IN std_logic_vector(1 downto 0);
LED : OUT std_logic_vector(0 downto 0)
);
END COMPONENT;
--Inputs
signal Switch : std_logic_vector(1 downto 0) := (others => '0');
--Outputs
signal LED : std_logic_vector(0 downto 0);
-- No clocks detected in port list. Replace <clock> below with
-- appropriate port name
constant period : time := 10 ns;
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: podstawowe_bramki PORT MAP (
Switch => Switch,
LED => LED
);
-- Stimulus process
stim_proc: process
begin
-- insert stimulus here
end process;
END;
Na pierwszy rzut oka kod symulacji jest podobny do kodu aplikacji w VHDL, są jednak pewne różnice. Kod rozpoczyna się standardowo od dołączenia bibliotek:
biblioteki wykorzystywane w symulacji
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
Następnie umieszczony jest pusty blok entity.
blok entity w pliku symulacji
ENTITY test_bench IS
END test_bench;
Niżej zaczyna się blok architecture. Na początku następuje zdefiniowanie komponentów. Tutaj są wszystkie sygnały, które w module VHDL były deklarowane wewnątrz "Port" w sekcji "entity". Blok ten został wypełniony automatycznie:
blok component w pliku symulacji
COMPONENT podstawowe_bramki
PORT(
Switch : IN std_logic_vector(1 downto 0);
LED : OUT std_logic_vector(0 downto 0)
);
END COMPONENT;
Niżej znajduje się deklaracja sygnałów lokalnych pliku z symulacją:
deklaracja sygnałów lokalnych w pliku symulacji
--Inputs
signal Switch : std_logic_vector(1 downto 0) := (others => '0');
--Outputs
signal LED : std_logic_vector(0 downto 0);
W pliku symulacji istnieje również możliwość zadeklarowania stałych, które będą używane w opisie procesu symulacji:
deklaracja stałych w pliku symulacji
constant period : time := 10 ns;
Tworzenie instancji (podczepienie sygnałów testujących do modułu VHDL):
podczepienie sygnałów testujących do modułu VHDL w pliku symulacji
-- Instantiate the Unit Under Test (UUT)
uut: podstawowe_bramki PORT MAP (
Switch => Switch,
LED => LED
);
Niżej znajduje się najciekawsza część, czyli miejsce na kod naszej symulacji:
-- Stimulus process
stim_proc: process
begin
-- insert stimulus here
end process;
W przypadku naszej bramki (zgodnie z wcześniejszym opisem) chcemy zasymulować działanie układu we wszystkich możliwych konfiguracjach wejść. Gotowy kod będzie więc wyglądał tak:
kod symulacji dla bramek logicznych
-- Stimulus process
stim_proc: process
begin
-- insert stimulus here
wait for 50 ns;
Switch(0) <= '0';
Switch(1) <= '0';
wait for period;
Switch(0) <= '1';
Switch(1) <= '0';
wait for period;
Switch(0) <= '0';
Switch(1) <= '1';
wait for period;
Switch(0) <= '1';
Switch(1) <= '1';
wait;
end process;
Mamy tutaj blok "process", który poznaliśmy przy tworzeniu rejestru przesuwnego. To co jest nowe, to instrukcje zawarte wewnątrz tego bloku. Znajdują się tam komendy wait for, a zaraz po nich wpisany jest okres oczekiwania. Jako okres należy podać stałą zadeklarowaną wcześniej (jak to zrobiliśmy ze zmienną period) lub podać liczbę wraz z odpowiednim przyrostkiem:
Między liczbą, a przyrostkiem musi znaleźć się spacja!
Powyższy program pozwoli na sprawdzenie działania układu dla kolejnych kombinacji wejść AND. Zaczynając od 00, przez 10, 01, do 11. Po każdej zmianie wejść będziemy czekać przez czas ustalony w stałej period.
Cały program testujący dostępny jest poniżej:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY test_bench IS
END test_bench;
ARCHITECTURE behavior OF test_bench IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT podstawowe_bramki
PORT(
Switch : IN std_logic_vector(1 downto 0);
LED : OUT std_logic_vector(0 downto 0)
);
END COMPONENT;
--Inputs
signal Switch : std_logic_vector(1 downto 0) := (others => '0');
--Outputs
signal LED : std_logic_vector(0 downto 0);
-- No clocks detected in port list. Replace <clock> below with
-- appropriate port name
constant period : time := 10 ns;
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: podstawowe_bramki PORT MAP (
Switch => Switch,
LED => LED
);
-- Stimulus process
stim_proc: process
begin
-- insert stimulus here
wait for 50 ns;
Switch(0) <= '0';
Switch(1) <= '0';
wait for period;
Switch(0) <= '1';
Switch(1) <= '0';
wait for period;
Switch(0) <= '0';
Switch(1) <= '1';
wait for period;
Switch(0) <= '1';
Switch(1) <= '1';
wait;
end process;
END;
Uruchomienie procesu symulacji
Krok 1. Tym razem skorzystamy z symulatora ISim, więc należy się upewnić, że jest on wybrany w ustawieniach projektu. Klikamy PPM w hierarchii projektu na b_bramki_logiczne i wybieramy opcję "Design Properties". Następnie w pozycji Simulator wybieramy wyżej wspomniany ISim:
Wybór odpowiedniego symulatora.
Po zatwierdzeniu wyboru symulatora upewniamy się, że plik z symulacją jest zapisany. Następnie zaznaczamy go w hierarchii projektu. W poniższym menu klikamy PPM na Behavioral Check Syntax i dalej na Run.
Bardzo ważne, aby w hierarchii projektu zaznaczony był plik z symulacją!
Przeprowadzanie poprawności symulacji.
Gdy sprawdzanie symulacji zakończy się poprawnie pokaże się zielona ikona, tak jak miało to miejsce w procesie syntezy pliku VHDL.
Krok 2. Przed uruchomieniem symulacji należy zdefiniować jak długa ma trwać symulacja. W tym celu klikamy PPM na Simulate Behavioural Model i wybieramy Process Properties.
Ustawienie czasu trwania symulacji.
W polu Simulation Run Time określamy czas trwania symulacji. W przypadku testowania bramki AND wystarczy 100 ns.
Ustawienie czasu trwania symulacji cd.
Krok 3. Teraz możemy uruchomić test! Zaznaczamy plik z symulacją w hierarchii projektu. Dalej klikamy PPM na Simulate Behavioural Model i wybieramy Run.
Krok 4. Po chwili powinno się pojawić nowe okno* programu-symulatora ISim. Dla łatwiejszej analizy przebiegów warto kliknąć wtedy ikonkę Zoom to Full View.
*Jeśli program się nie uruchomił, to wystarczy usunąć poniższy plik i zrestartować środowisko.
Podczas pierwszego startu symulatora może również pojawić się problem z antywirusem. Avast bierze ten plik np. w "kwarantannę" i otwiera go w sposób bezpieczny. Po chwili pokazuje się komunikat, że plik nie jest groźny. Należy wtedy kliknąć opcję Kontynuuj wykonywanie:
Komunikat z programu antywirusowego.
Jeśli po chwili nie pojawi się program symulatora lub będzie się ciągle ładował, to wystarczy zrobić restart środowiska. Od tej pory wszystko powinno działać poprawnie, bez konieczności usuwania plików itd.
Krok 5. Analiza wyników. Gdy mamy włączone okno symulatora, to warto "poklikać" i sprawdzić jakie funkcje ułatwiające analizowanie wyników tam wbudowano. Szczególnie ważne jest dobre ustawienie przybliżenia (zoom).
Wynik naszej symulacji widoczny jest na poniższym zrzucie ekranu:
Otrzymana symulacja.
Na powyższym zrzucie ekranu widać trzy wykresy, które reprezentują:
stan wejścia nr 1,
stan wejścia nr 2,
stan wyjścia układu.
Teraz można sprawdzić czy otrzymane przebiegi zgadzają się z przewidywanym działaniem układu. Jak widać, jeśli na wejściu mamy 00, to na wyjściu jest 1. Przy innych kombinacjach na wyjściu mamy 0. Nasz program został wcześniej zmodyfikowany w taki sposób, aby działał poprawnie przy "odwrotnej logice" przycisków. Stąd odwrotny wynik naszej symulacji. Należy pamiętać o tej zamianie podczas analizy wyników lub zanegować przyciski podczas symulacji!
Wynik symulacji przy zanegowanych przyciskach w testbenchu wygląda następująco:
Wynik symulacji przy zanegowanych wejściach układu.
Reasumując, cały proces tworzenia symulacji przebiega następująco:
Najpierw deklarujemy wejścia i wyjścia dla obiektu, który ma być testowany (wewnątrz bloku "Component"). Są takie same, jak w bloku "port" w "entity" testowanego modułu.
Następnie deklarujemy sygnały analogiczne do tych z bloku "Component", jednak tutaj są one sygnałami lokalnymi dla "architecture" danego testu.
Za "BEGIN" następuje przypisanie/tworzenie instancji sygnałów lokalnych danego testu z tym co ma być testowane wewnątrz bloku "component".
Otrzymane wyniki mówią o reakcji wyjść - tutaj już sami musimy ocenić, czy układ działa zgodnie z oczekiwaniami.
Podsumowanie
Oczywiście w tym przypadku zdecydowanie łatwiej było sprawdzić działanie programu w praktyce (wgrywając konfigurację do FPGA). Przy bardziej rozbudowanych projektach warto jednak mieć na uwadze możliwość przeprowadzenia szczegółowej symulacji. Podejrzenie tego, co dzieje się "wewnątrz naszego FPGA" okazuje się bardzo pomocne np. podczas wyszukiwania błędów.
Autor kursu: Adam Bemski Redakcja: Damian Szymański
Ilustracje, testy: 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.
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY na bazie Arduino i Raspberry Pi.
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY z Arduino i RPi.
Trwa ładowanie komentarzy...