Embedded Development Kit - Procesor PowerPC® w układzie FPGA

Zadaniem jest implementacja systemu wbudowanego wykorzystującego elementy składowe układu Virtex2Pro: procesor PowerPC oraz logikę programowalną.

Na początku należy uruchomić system projektowy EDK (Embedded Development Kit - ikona na pulpicie). Po uruchomieniu wyświetli się następujące okno:

 

 

Należy wybrać opcję domyślną: Base System Builder wizard

Następnie wyświetli się okienko:

 

 

Należy wcisnąć przycisk Browse ...

 

 

oraz dodać nowy katalog projektowy za pomocą klawisza:

W przykładzie nazwa katalogu projektowego to edk.

Po dodaniu nowego katalogu należy do niego wejść klikając w jego nazwę:

 

 

A następnie zaakceptować domyślną nazwę pliku projektu system.xmp naciskając przycisk Save:

 

 

Teraz można ostatecznie zaakceptować wybór naciskając przycisk OK:

 

 

Następnie wyświetli się okienko:

 

 

Należy zaakceptować klikając przycisk Next.

Wyświetli się kolejne okienko:

 

 

W polu Board vendor wybieramy Xilinx, a w polu Board name wybieramy nazwę płytki laboratoryjnej: XUP Virtex-II Pro Development System.
Pozostałe pola pozostawiamy bez zmian i wciskamy Next. Wyświetli się kolejne okienko:

 

 

Wciskamy Next niczego nie zmieniając (wybieramy procesor PowerPC), wyświetli się kolejne okienko:

 

 

W polu Processor clock frequency wybieramy 200.00 MHz,

w polu Cache setup zaznaczamy Enable, naciskamy Next.

Wyświetli się kolejne okienko:

 

 

W polu Baudrate (bits per second): wybieramy 115200, we wszystkich polach włączamy przerwania zaznaczając: Use interrupt. Następnie naciskamy Next.

Wyświetli się kolejne okienko:

 

 

W tym okienku nic nie zmieniamy i naciskamy Next.

Wyświetli się kolejne okienko:

 

 

Jest to dodatkowa pamięć BRAM (Block RAM) – wbudowana w FPGA, pozwalająca na diagnostykę pamięci głównej DDR SDRAM oraz na „bootloop” czyli pętlę zatrzymującą procesor po resecie (pamięć ta może być ładowana do układu FPGA razem ze sprzętem w pliku *.bit). Za pomocą przycisku Add Peripheral można dodać do projektu dodatkowe urządzenia takie jak timer, watchdog czy kolejna pamięć BRAM. W tym przypadku jednak nic nie dodajemy.
W polu Memory size wybieramy 64 KB. Następnie naciskamy Next.

Wyświetli się kolejne okienko:

 

 

W okienku tym aktywujemy pamięć cache dla wybranych rodzajów pamięci kodu i danych:
Zaznaczamy pola ICache oraz Dcache dla pamięci DDR_SDRAM.

Następnie naciskamy Next.

Wyświetli się kolejne okienko:

 

 

Okienko to pozwala na ustawienie urządzenia wejścia/wyjścia używanego do obsługi konsoli (xil_printf itp.) oraz urządzenia pamięci mapowanego w obszar startowy procesora.

W tym okienku nic nie zmieniamy i naciskamy przycisk Next.

Wyświetli się kolejne okienko:

 

 

Okienko to pozwala na określenie rodzajów pamięci używanych do obsługi określonych sekcji programu testowego MemoryTest. Program testowy mieści się w całości w pamięci BRAM więc łatwo można przetestować całą pamięć DDR SDRAM. Pamięć BRAM może być inicjalizowana razem ze sprzętem w pliku *.bit.

W tym okienku także nic nie zmieniamy i naciskamy Next.

Wyświetli się kolejne okienko:

 

 

Okienko to pozwala na określenie rodzajów pamięci używanych do obsługi określonych sekcji pamięci programu testowego PeripheralTest. Wybór DDR SDRAM oznacza konieczność ładowania programu przez debugger.

W polu Interrupt Vec wybieramy DDR_SDRAM, a następnie naciskamy Next.

Wyświetli się okienko podsumowania:

 

 

Naciskamy przycisk Generate w celu wygenerowania projektu.

Wyświetli się okienko zakończenia:

 

 

Naciskamy przycisk Finish.

 

Po chwili wyświetli się główne okno projektu:

 

 

Konieczne jest jeszcze wykonanie niewielkiej modyfikacji modułu obsługującego warstwę MAC sieci Ethernet. W tym celu klikamy podwójnie na Ethernet_MAC w okienku Bus Interfaces (na liście urządzeń). Wyświetli się następujące okienko konfiguracyjne:

 

 

W okienku tym zaznaczamy Include Second Receiver Buffer oraz Include Second Transmitter Buffer. Pozwoli to na zwiększenie wydajności obsługi warstwy MAC dzięki podwójnemu buforowaniu. Następnie naciskamy OK.

 

Po powrocie do głównego okna programu można uruchomić syntezę:

Wybieramy w głównym menu: Hardware, a następnie: Generate Bitstream. Można także nacisnąć przycisk:

 

Powinien rozpocząć się proces syntezy i implementacji projektu który trwa około 20 minut. Syntezy i implementacji nie trzeba będzie już powtarzać ponieważ w sprzętowej części ćwiczenia nic nie będzie już zmieniane.

Następne ćwiczenie będzie także wykorzystywało rezultaty syntezy i implementacji otrzymane w tym ćwiczeniu.

 

Po zakończeniu syntezy i implementacji należy przygotować projekt do komunikacji z płytką Virtex2Pro (także inne czynności jak kopiowanie drzewa urządzeń potrzebne do następnego ćwiczenia będą w tym momencie wykonane). Należy w tym celu kliknąć na pulpicie w ikonę USB config in EDK. Skrypt ten wyszuka wszystkie projekty EDK przeznaczone na płytkę V2Pro w katalogu domowym i zmodyfikuje odpowiednio parametry komunikacji z płytką (włączy USB).

 

Następnie można przystąpić do wykonania zadań:

 

 

Zadanie 1:

 

Uruchomienie programu TestApp_Memory w pamięci BRAM (wbudowanej w układ Virtex2Pro).

 

Klikamy w zakładkę Applications, klikamy prawym przyciskiem myszy na nazwę projektu TestApp_Memory i zaznaczamy pole Mark to Initialize BRAMs. Dzięki temu wybrany program będzie załadowany do pamięci BRAM i wczytany do układu V2Pro razem ze sprzętem (w pliku *.bit). Ponieważ pamięć BRAM jest ograniczona nie każdy projekt może być do niej załadowany w całości. Zazwyczaj jest ona używana do przechowywania małego programu bootloadera. Kiedy bootloader nie jest używany pamięć BRAM zazwyczaj zawiera mały „bootloop” – pętlę pozwalającą odzyskać kontrolę debuggera nad procesorem po resecie, następnie debugger może załadować testowany program do dowolnego obszaru pamięci.

Program TestApp_Memory jest przeznaczony do testowania pamięci DDR SDRAM i w związku z tym mieści się w pamięci BRAM.

 

 

Można także obejrzeć skrypt linkera w którym znajdziemy definicje obszarów pamięci używanych przez poszczególne sekcje naszego programu. Plik ten jest tworzony automatycznie i w tym przypadku nie ma potrzeby jego modyfikacji.

 


Można także przejrzeć pliki źródłowe projektu *.c oraz *.h.

Specjalny plik xparameters.h zawiera ważne definicje związane z konfiguracją sprzętu i nie powinien być zmieniany ręcznie.

 

Następnie należy skompilować projekt – klikamy prawym przyciskiem myszy na nazwę projektu i wybieramy Build Project (można ewentualnie przedtem wybrać Clean Project). W sytuacji kiedy projekt nie był skompilowany, a zostanie naciśnięty klawisz ładowania projektu do płytki:  kompilacja nastąpi automatycznie, następnie program zostanie dołączony do pliku *.bit i załadowany do płytki.

 

Przed załadowaniem projektu do płytki należy uruchomić program MINICOM (ikona na pulpicie) i ustawić prędkość 115200 bps  (CTRL-A Z P I <Enter>).

 

Sprawdzamy czy przewód RS232 jest podłączony do płytki V2Pro (ewentualnie przełączamy go z płytki Spartan-3 do V2Pro).

 

Następnie klikamy na klawisz   i obserwujemy czy program się uruchomił (komunikaty na konsoli MINICOM).

 

Proszę pokazać rezultaty prowadzącemu laboratorium.

 

 

Zadanie 2:

 

Uruchomienie programu TestApp_Peripheral w pamięci DDR SDRAM i zademonstrowanie działania debuggera.

 

W tym celu należy zaznaczyć w projekcie domyślnym pętli bootloop pole Mark to Initialize BRAMs (klikamy prawym przyciskiem myszy na nazwę projektu domyślnego: Default: ppc405_0_bootloop).

Jak możemy stwierdzić zaglądając do skryptu linkera program uruchamiany jest w pamięci DDR SDRAM, zaś w BRAMie znajduje się jedynie początek kodu pozwalający na skok do głównego programu w pamięci DDR SDRAM. Dlatego konieczne jest początkowe umieszczenie w pamięci BRAM bootloopa który zostanie zastąpiony skokiem do początku kodu programu dopiero po załadowaniu pamięci DDR SDRAM przez debugger.

 

Projekt należy skompilować analogicznie jak w poprzednim zadaniu.

 

Następnie należy wysłać projekt sprzętowy (z bootloopem) do płytki V2Pro (klawisz:). Projekt uruchomi się, procesor będzie czekał w pętli na przerwanie z debuggera.

 

Potem uruchamiamy debugger XMD naciskając klawisz .

Na początku system zażąda ustawienia opcji debuggera:

 



Należy potwierdzić naciskając klawisz OK. Następnie wyświetlone zostanie okienko konfiguracji debuggera XMD:

 



W polu JTAG Cable należy ustawić:

Type: USB

Frequency: 6000000

 

Następnie należy nacisnąć klawisz OK. Spowoduje to uruchomienie się XMD. Należy pozostawić ten program uruchomiony.

 

Kolejną czynnością będzie uruchomienie debuggera aplikacji. Należy kliknąć w ikonę  co spowoduje wyświetlenie menu wyboru aplikacji:

 

 

Należy wybrać aplikację: TestApp_Peripheral  i nacisnąć OK.

 

Następnie wyświetli się okienko debuggera aplikacji:

 

 

Klikając na ikonę   załadujemy do pamięci i uruchomimy nasz program.

Program zatrzyma się na widocznej powyżej w postaci czerwonego kwadratu pułapce (domyślny breakpoint na początku programu). Aby kontynuować wykonywanie programu naciskamy:   (continue)  spowoduje to wykonanie wszystkich testów układów peryferyjnych (należy zaobserwować rezultaty na konsoli MINICOM).

Po wykonaniu testów program ponownie zatrzyma się na domyślnej pułapce (breakpoint - exit).

 

Proszę pokazać rezultaty prowadzącemu laboratorium.

 

Można poeksperymentować z ustawianiem innych pułapek (klikając w małe minusy po lewej stronie linii kodu), a także z innymi funkcjami debuggera aplikacji.

 

Ponowne uruchomienie aplikacji (włącznie z ponownym załadowaniem) możemy uzyskać za pomocą klawisza:

 

Innym sposobem szybkiego uruchomienia programu w pamięci DDR SDRAM bez uruchamiania debuggera aplikacji jest korzystanie z linii komend debuggera XMD:

 

XMD% cd TestApp_Peripheral

XMD% dow executable.elf

..............................

..............................

..............................

XMD% run

 

 

 

Zadanie 3:

 

Uruchomienie własnego programu.

 

Zadanie polega na utworzeniu aplikacji serwera telnet sterującego za pomocą komend tekstowych diodami LED oraz odczytującego stan przełączników DIP. Do realizacji projektu wykorzystamy bibliotekę uIP (stos TCP/IP dla mikrokontrolerów: http://en.wikipedia.org/wiki/UIP_(micro_IP)).

 

Przed stworzeniem nowego projektu zakładamy dla niego katalog w którym będziemy przechowywać pliki źródłowe. W naszym przykładzie katalog ten to:

~/edk/uip:

 

mkdir ~/edk/uip

 

Do tego katalogu kopiujemy pliki (proszę wykonać następujące komendy):

 

cp /opt/uip-1.0/uip/* ~/edk/uip

cp /opt/uip-1.0/apps/telnetd/* ~/edk/uip

cp /opt/uip-1.0/unix/clock-arch.* ~/edk/uip

cp /opt/uip-1.0/unix/uip-conf.h ~/edk/uip

cp /opt/uip-1.0/lib/* ~/edk/uip

rm ~/edk/uip/uip-split.*

 

Teraz można stworzyć nowy projekt - klikamy w Add Software Application Project.

 

 

Otwiera się okienko:

 

 

Nadajemy nazwę projektowi wpisując ją w polu Project Name (w naszym przykładzie jest to uip).  Naciskamy przycisk OK.

 

Następnie w powstałej zakładce nowego projektu klikamy prawym przyciskiem myszy na pole Sources i wybieramy opcję Add Existing Files.

 

 

Następnie dopisujemy do projektu wszystkie pliki *.c z katalogu projektu ~/edk/uip.

Analogicznie postępujemy w polu Headers z plikami nagłówkowymi (*.h).

 

Modyfikujemy plik uip_conf.h zawierający konfigurację stosu TCP/IP:

Zmieniamy w linii 107 parametr UIP_CONF_BUFFER_SIZE na 2000 (nie ma potrzeby oszczędzania pamięci tak jak w implementacjach na mikrokontrolerach).

Zmieniamy w linii 114 parametr UIP_CONF_BYTE_ORDER na UIP_BIG_ENDIAN (mamy do czynienia z takim właśnie procesorem).

Zmieniamy w linii 121 parametr UIP_CONF_LOGGING na 0.

Dodatkowo w liniach 148/149 należy zamiast  webserver.h   włączyć  telnetd.h.

 

Teraz pozostaje napisać plik główny programu: main.c integrujący bibliotekę uIP z bibliotekami Xilinxa służącymi do obsługi GPIO i Ethernet-MAC.

 

Szczegółowe działanie programu powinno być następujące:

Adres MAC urządzenia – 00:01:02:03:04:05

Adres IP urządzenia – 192.168.1.2  (adres portu eth1 komputera laboratoryjnego jest następujący: 192.168.1.1, netmaska: 255.255.255.0).

Zamiast nieaktywnych pozycji menu serwera telnet:

  stats   - show network statistics

  conn   - show TCP connections

należy wstawić pozycje:

  leds    - set LEDs

  dip      - show DIP switch setting

 

Komenda leds powinna wymagać 4-znakowego parametru złożonego z zer i jedynek. Zero oznacza diodę zgaszoną, jedynka zapaloną. Pozycja pierwsza z lewej steruje diodą pierwszą z lewej, itd. Po uruchomieniu aplikacji wszystkie diody powinny być zgaszone. Proszę pamiętać o aktualizacji helpa.

Przetestować aplikację (ma działać ping i telnet).

 

Proszę pokazać rezultaty prowadzącemu laboratorium.

 

Rady i zalecenia ułatwiające wykonanie zadania:

  1. Dokumentacja uIP znajduje się na dysku lokalnym:
    /opt/uip-1.0/doc/html/index.html
  2. Na dysku lokalnym znajduje się także przykładowa główna pętla programu (można ją wykorzystać we własnym module main.c):
     /opt/uip-1.0/doc/example-mainloop-with-arp.c
    Jest ona dostosowana do obsługi httpd – więc trzeba zamienić odwołania do httpd na odwołania do telnetd. Główna pętla programu nie wykorzystuje przerwań.
  3. Pliki nagłówkowe Xilinxa które będą potrzebne:
    #include "xparameters.h"
    #include "xemaclite.h"
    #include "xenv.h"
    #include "xgpio.h"
    #include "xtime_l.h"
    plik xparameters.h zawiera parametry komponentów sprzętowych, m.in. adresy,
    plik xemaclite.h zawiera funkcje i struktury związane z obsługą sterownika sieci Fast Ethernet,
    plik xenv.h zawiera definicje niektórych przydatnych funkcji, takich jak memcpy czy memset,
    plik xgpio.h zawiera funkcje i struktury związane z obsługą modułów GPIO Xilinxa,
    plik xtime_l.h zawiera definicje funkcji obsługujących wbudowany w procesor PowerPC licznik czasu.
  4. Proszę pamiętać o włączeniu pamięci cache:
    XCache_EnableICache(0xc0000000);
    XCache_EnableDCache(0xc0000000);
  5. Dokumentacja modułów sprzętowych Xilinxa jest dostępna w pliku lokalnym:
    /opt/Xilinx/10.1/EDK/doc/usenglish/proc_ip_ref_guide.htm
  6. Dokumentacja driverów do modułów sprzętowych Xilinxa jest dostępna w pliku lokalnym:
    /opt/Xilinx/10.1/EDK/doc/usenglish/xilinx_drivers_api_toc.htm
    Informacje o wersjach driverów można uzyskać naciskając ikonę:
      i wybierając pole Drivers.
  7. Dokumentacja bibliotek systemowych Xilinxa jest dostępna w pliku lokalnym:
    /opt/Xilinx/10.1/EDK/doc/usenglish/oslib_rm.pdf
    Znajduje się tam między innymi opis biblioteki zegara xtime_l.h
  8. Dobrym źródłem przykładów są także aplikacje testujące: TestApp_Memory i TestApp_Peripheral.
  9. Drukowanie na konsoli jest realizowane przez funkcję xil_printf() będącą zubożoną wersją standardowej funkcji printf().
  10. Należy opracować odpowiednie funkcje-wrappery realizujące podstawowe operacje na urządzeniu sieciowym: network_device_send, network_device_read i network_device_init.
  11. Należy pamiętać o ustawieniu adresu MAC także w bibliotece uIP (taki sam jak dla drivera MAC) za pomocą funkcji uip_setethaddr(...)
  12. W dokumentacji uip jest informacja że wysyłany pakiet należy zmontować z dwóch lokalizacji (uip_buf – nagłówki Ethernet i TCP/IP oraz uip_appdate – dane aplikacji) – w naszym przykładzie drugi wskaźnik pokazuje na koniec pierwszego – więc można obsłużyć nadawanie ramki z jednym razem (cały pakiet o długości uip_len z bufora uip_buf).
  13.  Obsługę diod LED i odczytu DIP-switcha można zrealizować odpowiednio modyfikując shell.c
  14.  GPIO channel zarówno przy wyjściu jak i przy wejściu ustawić na 1.
  15.  W pliku clock_arch.c należy zmodyfikować sposób pomiaru czasu na taki, który jest dostępny w naszym środowisku (np. można skorzystać z licznika wbudowanego w procesor PowerPC – biblioteka xtime_l.h).