Strona główna Artykuły ARM ARM toolchain - tutorial
ARM toolchain - tutorial
Ocena użytkowników: / 299
SłabyŚwietny 
Wpisany przez Freddie Chopin   
Niedziela, 17 Maj 2009 16:36
Spis treści
ARM toolchain - tutorial
ARM toolchain
Debugger
Edytor (IDE)
Eclipse + OpenOCD + GDB
Epilog
Wszystkie strony

Bez zbędnego lania wody przejdźmy do rzeczy... Do wygodnego tworzenia oprogramowania dla ARMów potrzeba nam:

  • edytora (IDE),
  • toolchaina (kompilator, assembler, linker, binutils, biblioteki, ...),
  • oprogramowania do debuggowania.

Edytor (IDE)

W roli edytora lub IDE dla ARMów spotykany jest zwykle jeden z dwóch programów - Programmers Notepad albo Eclipse. Poza tymi dwoma możliwe jest oczywiście zastosowanie wielu innych aplikacji, takich jak choćby CodeBlocks lub DevC++. Ponieważ moim zdaniem ogromne możliwości i duża popularność Eclipse rekompensują jego niewielkie wady (jest napisany w Javie, więc czasem potrafi się przyciąć), tutorial ten bazować będzie właśnie na Eclipse. Środowisko Eclipse umożliwia nam pisanie kodu, zarządzanie projektem, programowanie układu oraz jego debuggowanie - czego chcieć więcej? (;

ARM toolchain

Na chwilę obecna dostępnych jest wiele kompletnych i gotowych toolchainów dla ARMa - jedne są na wiele platform, inne tylko na jedną. Warto wymienić takie pakiety jak WinARM, GNUARM i Yagarto. Tutorial ten jednak oparty będzie jednak o inne rozwiązanie, a mianowicie o Sourcery G++ Lite pochodzące z CodeSourcery. Dlaczego? Przede wszystkim istotne jest to, że rozwój toolchaina firmy CodeSourcery jest sponsorowany przez ARM Ltd., dzięki czemu jest on zawsze najbardziej zgodny ze standardami ARM, a usprawnienia wprowadzane są najszybciej - dopiero po pewnym czasie trafiają one do oficjalnej dystrybucji GCC, a stamtąd dopiero do innych toolchainów. Poza tym warto nadmienić, że toolchain ten jest dostępny zarówno dla platformy Windows jak i Linux, obsługuje instrukcje typu ARM, Thumb i Thumb-2 oraz najnowsze architektury ARMów - takie jak choćby Cortex. Nowe wersje toolchaina od CodeSourcery wydawane są w półrocznych odstępach.

Oprogramowanie do debuggowania

Debugger GDB (GNU Debugger) potrzebuje tak zwanego "klienta GDB", który przyjmowałby i wykonywał jego rozkazy. W świecie ARMów rolę tą doskonale spełnia OpenOCD, które również jest aplikacją wieloplatformową. Obecnie OpenOCD obsługuje praktycznie wszystkie istniejące JTAGi do ARMa (m.in. klony Wigglera (LPT), układy oparte o FT2232 (USB), JLink, RLink i wiele innych) oraz większość istniejących rodzajów ARMów (w tym ARM7, ARM9 i Cortex-M3).

Instalacja krok po kroku...

Poniższy tutorial oparty jest o system Windows, niemniej jednak nie ma żadnego problemu, aby porady tu przedstawione zastosować dla systemu Linux (oczywiście nie dosłownie).


ARM toolchain

Pierwszym krokiem może być instalacja toolchaina, który można pobrać za darmo ze strony Codesourcery. Poszukiwać należy pakietu o nazwie Sourcery G++ Lite Edition for ARM w wersji EABI (tak zwany bare-metal).

W chwili pisania tego artykułu najbardziej aktualna wersja to Sourcery G++ Lite 2009q1-161 for ARM EABI, czyli wersja z pierwszego kwartału 2009 roku. Najnowszą wersję można znaleźć zawsze w taki sposób - Codesourcery > menu Products > Sourcery G++ > Editions > Lite > ARM > Download > wybrać wersję EABI
CodeSourcery downloadCodeSourcery Download

EABI to skrót od ARM Binary Interface - jest to standard który pozwala łączyć obiekty i biblioteki stworzone przez różne kompilatory

Proces instalacji nie kryje w sobie żadnych niespodzianek - pamiętać należy o tym, żeby zezwolić na zmodyfikowanie zmiennej PATH w systemie, co jest standardowo wybraną opcją.

Po zakończeniu instalacji warto ją przetestować. W tym celu należy otworzyć systemowy Wiersz polecenia

Wiersz polecenia (w angielskich wersjach systemu Windows - Command Prompt) można otworzyć na kilkanaście różnych sposobów. Dwa najbardziej oczywiste:
1. Start > Uruchom... (Run...) > cmd
2. Start > Wszystkie Programy (All Programs) > Akcesoria (Accessories) > Wiersz polecenia (Command Prompt)

W tym miejscu warto nadmienić po raz pierwszy, że GCC nie będzie działał poprawnie, jeśli podawane mu ścieżki i / lub nazwy plików / katalogów zawierać będą spacje

Aby przetestować, czy kompilator został poprawnie zainstalowany wystarczy wykonać polecenie arm-none-eabi-gcc --version w dowolnym folderze. Jeśli instalacja przebiegła pomyślnie, naszym oczom powinien ukazać się następujący komunikat (dokładna treść zależna oczywiście od zainstalowanej wersji)

C:\>arm-none-eabi-gcc --version
arm-none-eabi-gcc (Sourcery G++ Lite 2009q1-161) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Kolejnym testem może być kompilacja najprostszej możliwej aplikacji. W tym celu należy stworzyć przy użyciu dowolnego edytora tekstu gdzieś (pamiętając o spacjach w ścieżce dostępu!), na przykład w folderze test na dysku c:, plik main.c o zawartości

int main(void)
{
return 0;
}

Kompilacja pliku wykonywana jest ponownie z Wiersza polecenia przy użyciu komendy arm-none-eabi-gcc c:\test\main.c (ścieżkę oczywiście dopasować do swojej sytuacji). Kompilator podzieli się z nami oczywiście swoimi wątpliwościami dotyczącymi opcji kompilacji:

C:\>arm-none-eabi-gcc c:\test\main.c
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.3.3/../../../../arm-none-eabi/bin/ld.exe: warning: cannot find entry symbol _start; defaulting to 00008018

niemniej jednak po zakończeniu jego działania w folderze z którego został wywołany powinien znajdować się nowy plik o nazwie a.out i rozmiarze około 35kB. Sukces!


Debugger

Kolejnym kroczkiem, całkowicie niezależnym od poprzedniego, może być instalacja debuggera OpenOCD.

W chwili pisania tych słów OpenOCD dostępny jest w wersji 0.1.0 i jest to pierwszy oficjalny "release". Na stronie projektu OpenOCD (oraz w sekcji Download tej strony) dostępny jest instalator najnowszej wersji OpenOCD dla systemu Windows (nawiasem mówiąc stworzony przeze mnie [; ). Dodatkowo na stronie projektu można znaleźć kod źródłowy do samodzielnej kompilacji oraz skompilowaną paczkę dla Linuxa.

Proces instalacji OpenOCD jest w miarę oczywisty - sugeruję pozostawienie wszystkich opcji instalacji domyślnych - całość po zainstalowaniu zajmuje mniej niż 5MB.

Po zakończeniu instalacji warto sprawdzić jej poprawność - w Wierszu polecenia, w dowolnym folderze można wykonać komendę openocd --version, co powinno zaowocować następującym komunikatem (detale zależne oczywiście od wersji):

C:\>openocd --version
Open On-Chip Debugger 0.1.0 (2009-01-21-21:15) Release

BUGS? Read http://svn.berlios.de/svnroot/repos/openocd/trunk/BUGS

$URL: https:// Adres poczty elektronicznej jest chroniony przed robotami spamującymi. W przeglądarce musi być włączona obsługa JavaScript, żeby go zobaczyć. /svnroot/repos/openocd/tags/openocd-0.1.0/src
/openocd.c $

Jeśli OpenOCD działa samo w sobie, to można przetestować jak spisuje się w komunikacji z rzeczywistym układem za pośrednictwem interfejsu JTAG. Ponieważ kwestia konfiguracji OpenOCD wykracza poza tematykę tego artykułu (w przyszłości planowany osobny tekst na ten temat), skupię się na efekcie finalnym. Mój zestaw testowy to makieta z mikrokontrolerem LPC2103 (rdzeń ARM7TDMI-S) oraz JTAG-lock-pick. Aby połączyć się z układem w Wierszu polecenia wydać należy komendę openocd -f interface/jtagkey.cfg -f target/lpc2103.cfg (pliki konfiguracyjne mogą wymagać minimalnej modyfikacji - głównie w kwestiach szybkości rdzenia, która ogranicza też dozwoloną szybkość JTAGa). Efektem powinno być uruchomienie OpenOCD i następujące komunikaty:

C:\>openocd -f interface/jtagkey.cfg -f target/lpc2103.cfg
Open On-Chip Debugger 0.1.0 (2009-01-21-21:15) Release

BUGS? Read http://svn.berlios.de/svnroot/repos/openocd/trunk/BUGS

$URL: https:// Adres poczty elektronicznej jest chroniony przed robotami spamującymi. W przeglądarce musi być włączona obsługa JavaScript, żeby go zobaczyć. /svnroot/repos/openocd/tags/openocd-0.1.0/src
/openocd.c $
1000 kHz
Info : JTAG tap: lpc2103.cpu tap/device found: 0x4f1f0f0f (Manufacturer: 0x787,
Part: 0xf1f0, Version: 0x4)
Info : JTAG Tap/device matched
Warn : no telnet port specified, using default port 4444
Warn : no gdb port specified, using default port 3333
Warn : no tcl port specified, using default port 6666

Istotnym faktem jest to, że OpenOCD wciąż jest uruchomiony - okno Wiersza polecenia jest "zablokowane" dopóki oprogramowanie nie zakończy działania - albo w sposób naturalny (rozkaz zakończenia lub błąd uniemożliwiający dalszą pracę), albo w sposób wymuszony (kombinacja klawiszy Ctrl+C). OpenOCD wykorzystywany będzie głównie jako oprogramowanie wykonujące rozkazy GDB, nic nie stoi jednak na przeszkodzie, aby sterować nim za pomocą telnetu. Oby połączyć się z OpenOCD przez telnet konieczne jest otworzenie kolejnego Wiersza polecenia, w który wstukać należy polecenie telnet localhost 4444, co zaowocuje połączeniem z oprogramowaniem nasłuchującym na naszym komputerze na porcie 4444 - po poprawnym połączeniu tajemnicze oprogramowanie przedstawi się jako... Open On-Chip Debugger. Prostego testu można dokonać wydając poniższe komendy:

1. reset a następnie halt, co powinno zaowocować następującą odpowiedzią:

> reset
JTAG tap: lpc2103.cpu tap/device found: 0x4f1f0f0f (Manufacturer: 0x787,
Part: 0xf1f0, Version: 0x4)
JTAG Tap/device matched

invalid mode value encountered
cpsr contains invalid mode value - communication failure
> halt
target state: halted
target halted in ARM state due to debug-request, current mode: Abort
cpsr: 0x80000097 pc: 0x00000038

Oczywiście dla innego rdzenia lub innego układu odpowiedź będzie inna.

2. poll - komenda pozwalająca zbadać aktualny stan rdzenia, wartość rejestru statusowego (xPSR) oraz licznika programu (PC). Typowa odpowiedź na tą komendę (zakładając, że wcześniej rdzeń został zatrzymany) wyglądać może na przykład tak:

> poll
target state: halted
target halted in ARM state due to debug-request, current mode: Supervisor
cpsr: 0x80000093 pc: 0x00000038

3. reg - w tej formie komenda spowoduje wyświetlenie zawartości wszystkich rejestrów "ogólnych" rdzenia. Dla ARM7TDMI-S odpowiedź wygląda w sposób następujący:

> reg
(0) r0 (/32): 0x00000000 (dirty: 1, valid: 1)
(1) r1 (/32): 0x40001a18 (dirty: 1, valid: 1)
(2) r2 (/32): 0x40001a18 (dirty: 0, valid: 1)
(3) r3 (/32): 0x40001a14 (dirty: 0, valid: 1)
(4) r4 (/32): 0xffffffff (dirty: 0, valid: 1)
(5) r5 (/32): 0xffffffff (dirty: 0, valid: 1)
(6) r6 (/32): 0xffffffff (dirty: 0, valid: 1)
(7) r7 (/32): 0xffffffff (dirty: 0, valid: 1)
(8) r8 (/32): 0xffffffff (dirty: 0, valid: 1)
(9) r9 (/32): 0xffffffff (dirty: 0, valid: 1)
(10) r10 (/32): 0xffffffff (dirty: 0, valid: 1)
(11) r11 (/32): 0xffffffff (dirty: 0, valid: 1)
(12) r12 (/32): 0xffffffff (dirty: 0, valid: 1)
(13) r13_usr (/32): 0x40001af8 (dirty: 0, valid: 1)
(14) lr_usr (/32): 0xa07d0000 (dirty: 0, valid: 1)
(15) pc (/32): 0x400000d8 (dirty: 1, valid: 1)
(16) r8_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(17) r9_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(18) r10_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(19) r11_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(20) r12_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(21) r13_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(22) lr_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(23) r13_irq (/32): 0x00000000 (dirty: 0, valid: 0)
(24) lr_irq (/32): 0x00000000 (dirty: 0, valid: 0)
(25) r13_svc (/32): 0xffffffff (dirty: 0, valid: 0)
(26) lr_svc (/32): 0xffffffff (dirty: 0, valid: 0)
(27) r13_abt (/32): 0x00000000 (dirty: 0, valid: 0)
(28) lr_abt (/32): 0x00000000 (dirty: 0, valid: 0)
(29) r13_und (/32): 0x00000000 (dirty: 0, valid: 0)
(30) lr_und (/32): 0x00000000 (dirty: 0, valid: 0)
(31) cpsr (/32): 0x60000010 (dirty: 0, valid: 1)
(32) spsr_fiq (/32): 0x00000000 (dirty: 0, valid: 0)
(33) spsr_irq (/32): 0x00000000 (dirty: 0, valid: 0)
(34) spsr_svc (/32): 0x80000097 (dirty: 0, valid: 0)
(35) spsr_abt (/32): 0x00000000 (dirty: 0, valid: 0)
(36) spsr_und (/32): 0x00000000 (dirty: 0, valid: 0)
(37) debug_ctrl (/6): 0x05 (dirty: 0, valid: 1)
(38) debug_status (/5): 0x09 (dirty: 0, valid: 0)
(39) comms_ctrl (/32): 0x40000000 (dirty: 0, valid: 0)
(40) comms_data (/32): 0x00000000 (dirty: 0, valid: 0)
(41) watch 0 addr value (/32): 0x00000000 (dirty: 0, valid: 0)
(42) watch 0 addr mask (/32): 0xffffffff (dirty: 0, valid: 1)
(43) watch 0 data value (/32): 0xdeeedeee (dirty: 0, valid: 1)
(44) watch 0 data mask (/32): 0x00000000 (dirty: 0, valid: 1)
(45) watch 0 control value (/32): 0x00000100 (dirty: 0, valid: 1)
(46) watch 0 control mask (/32): 0x000000f7 (dirty: 0, valid: 1)
(47) watch 1 addr value (/32): 0x00000000 (dirty: 0, valid: 0)
(48) watch 1 addr mask (/32): 0x00000000 (dirty: 0, valid: 0)
(49) watch 1 data value (/32): 0x00000000 (dirty: 0, valid: 0)
(50) watch 1 data mask (/32): 0x00000000 (dirty: 0, valid: 0)
(51) watch 1 control value (/32): 0x00000000 (dirty: 0, valid: 0)
(52) watch 1 control mask (/32): 0x00000000 (dirty: 0, valid: 0)

4. step (kilkukrotnie), aby sprawdzić możliwość kontrolowania rdzenia. Próba użycia tej komendy wymaga oczywiście obecności jakiegoś programu w pamięci układu. Przy kolejnych wywołaniach tej komendy zauważyć można zmiany rejestru PC. Przykładowo może to wyglądać tak:

> step
target state: halted
target halted in ARM state due to single-step, current mode: User
cpsr: 0x60000010 pc: 0x400000dc

> step
target state: halted
target halted in ARM state due to single-step, current mode: User
cpsr: 0x60000010 pc: 0x400000e0

> step
target state: halted
target halted in ARM state due to single-step, current mode: User
cpsr: 0x60000010 pc: 0x400000e4

> step
target state: halted
target halted in ARM state due to single-step, current mode: User
cpsr: 0x60000010 pc: 0x400000e8

5. ... w manualu do OpenOCD można znaleźć dokładny opis wielu komend, którymi możemy kontrolować rdzeń lub odczytywać / zapisywać pamięć - celem tego artykułu nie jest jednak wymienienie ich wszystkich (; .


Edytor (IDE)

Finalny krok pozwoli sterować pracą wszystkich aplikacji za pomocą jednego programu. Eclipse umożliwia wykorzystanie plików makefile do kontrolowania procesu kompilacji. Możliwość obsługi dodatkowych - zewnętrznych - aplikacji umożliwi uruchamianie w tle OpenOCD, za pomocą którego można wgrywać do mikrokontrolera kod wynikowy. Możliwość debuggowania przy użyciu GDB ułatwi proces tworzenia oprogramowania i eliminacji błędów.

Przed rozpoczęciem używania Eclipse warto zainstalować najnowszą wersję Java Runtime Environment.

Najnowszą wersję Eclipse (w chwili pisania tego artykułu - 3.4.2 czyli Ganymede SR2) można znaleźć zawsze na stronie domowej projektu w sekcji Download. Do tworzenia oprogramowania na procesory ARM potrzebna będzie wersja Eclipse IDE for C/C++ Developers.
Eclipse download

Po ściągnięciu paczki przeznaczonej dla stosownego systemu operacyjnego wystarczy ją rozpakować do dowolnego folderu na dysku - Eclipse nie wymaga instalacji. Folderem tym może być C:\Program Files\Eclipse (; .

Po uruchomieniu Eclipse pojawi się pytanie o domyślną lokalizację projektów (Workspace) - wybierając ścieżkę należy zwrócić baczną uwagę, aby nie zawierała ona żadnych spacji, ponieważ to może przyczynić się do mylących błędów kompilacji, na pozór nie mających nic wspólnego z ścieżkami dostępu. Z tego względu proponowana domyślnie w systemie Windows ścieżka typu C:\Documents and Settings\[profil]\workspace nie jest dobrym rozwiązaniem. Dla celów przykładu niech będzie to c:\ARM . Po wybraniu ścieżki warto zaznaczyć opcję Use this as the default and do not ask again, dzięki czemu pytanie to nie będzie pojawiać się przy każdym uruchomieniu edytora. Jeśli w przyszłości zajdzie potrzeba zmiany domyślnego folderu, można tego dokonać za pomocą File > Switch Workspace > Other... .

Po pierwszym uruchomieniu Eclipse pojawia się okno powitalne, które pominąć można przy użyciu ikony skrajnie prawej ikony - Workbench. Warto w tym momencie przestawić od razu 2 rzeczy: odznaczyć opcję Build Automatically w menu Project, oraz odznaczyć opcję Start a build immediately w Project > Clean.

Ponieważ GCC dla ARMów nie jest w jakikolwiek sposób zintegrowane z Eclipse, przy okazji każdego nowego projektu konieczne jest ustawianie kilku opcji od nowa. Niestety nie wszystkie ustawienia przenoszą się przy okazji kopiowania projektu, więc bezpieczniej kliknąć parę razy przy okazji każdego nowego projektu.

Aby stworzyć nowy projekt, zgodnie z logiką należy wykorzystać opcję File > New > C Project. Po nazwaniu projektu - na przykład test - i wybraniu jego typu (Makefile project > Empty Project > -- Other Toolchain --) można od razu kliknąć na Finish. Opcje projektu można ustawiać w menu Project > Properties (albo po kliknięciu prawym klawiszem myszki na stworzony projekt w Project Explorer po lewej i wybraniu opcji Properties). Zestaw opcji które należy zmienić jest następujący:

  1. C/C++ Build - w zakładce Builder Settings należy odznaczyć opcję Use default build command i do nowo odblokowanego pola tekstowego w zamian make wpisać cs-make, gdyż tak właśnie zwie się make.exe pochodzący z pakietu CodeSourcery.
  2. C/C++ Build > Settings w zakładce Binary Parsers zaznaczyć należy GNU Elf Parser, a następnie - po kliknięciu tej opcji - w polach tekstowych poniżej należy podać ścieżkę do odpowiednich plików pochodzących z pakietu CodeSourcery - w przypadku standardowej instalacji pliki te znajdują się w folderze C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin\ ( C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-addr2line.exe , C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-c++filt.exe ).
Eclipse settings
Eclipse settings
Eclipse settings
Eclipse settings

Ponieważ każdy projekt zawiera zawsze pewne typowe i rzadko modyfikowane pliki (makefile, skrypt linkera, startup, ...), proces ich dołączania można przyspieszyć. Zasadniczo poza opcją klasyczną - punkt 1 - możliwe są dwa sposoby szybkiego dodawania plików do projektu. Tak więc dodawać pliki do projektu można następującymi sposobami:

  1. Klasycznie - tworzyć plik, a zawartość wpisać ręcznie lub skopiować z innego źródła. Do takowych działań wykorzystać można (standardowe) opcje File > New > ... lub za pomocą opcji New > ... dostępnej po kliknięciu prawym przyciskiem myszy w dowolny folder otwartego projektu.
  2. Masowo - wykorzystując mechanizm importowania plików w Eclipse - File > Import (lub prawym klawiszem w projekt i Import). Aby zaimportować pliki / foldery z dowolnego miejsca systemu plików wykorzystać należy opcję General > File System. Po wybraniu folderu z którego chcemy importować, możliwe jest wybranie plików i folderów jakie mają zostać zaimportowane oraz kilku dodatkowych opcji.
  3. Zewnętrznie - po modyfikacji zawartości folderu projektu i wybraniu opcji File > Refresh (lub prawym klawiszem w projekt i Refresh) wprowadzone zmiany zostaną wykryte i zapisane w projekcie.

Zakładając, że do projektu dodane są wszystkie potrzebne pliki, całość można skompilować za pomocą opcji Project > Build All lub w menu kontekstowym projektu Build Project lub - najwygodniej - skrótem klawiszowym Ctrl+B.

Warto pamiętać o tym, że kompilacja - jako proces zewnętrzny względem Eclipse - bazuje na zapisanych wersjach plików, a nie na tym co widoczne jest w edytorze. Eclipse przed kompilacją nie zapisuje i nie ostrzega przed zmodyfikowanymi plikami.

Przebieg procesu kompilacji można śledzić w zakładce Console, natomiast po zakończonym działaniu make'a w zakładce Problems można przejrzeć (ewentualne) wykryte błędy oraz ostrzeżenia.


Eclipse + OpenOCD + GDB

Do sprzęgnięcia wszystkich elementów w jedną całość brakuje jeszcze dwóch pluginów do Eclipse. Pluginy owe - jak i wszelkie uaktualnienia samego pakietu - można pobrać poprzez menu Help > Software updates.

Pierwsza wtyczka - Eclipse C/C++ GDB Hardware Debugging - należy do palety dodatków "firmowych", więc jedyne co należy zrobić, to w zakładce Available Software z dostępnego drzewa wybrać http://download.eclipse.org/tools/cdt/releases/ganymede > CDT Optional Features > Eclipse C/C++ GDB Hardware Debugging i kliknąć Install. Proces instalacji wtyczki może trochę potrwać - zależnie od aktualnego obciążenia serwerów i łącza.

Drugi z potrzebnych komponentów - Zylin Embedded CDT - wymaga kilku kliknięć więcej. Ponieważ wtyczka ta nie jest częścią standardowo dostępnej palety, należy dodać stronę macierzystą wtyczki do dostępnych źródeł aktualizacji. Aby tego dokonać, w zakładce Available Software należy wybrać opcję Add Site... i wprowadzić adres http://opensource.zylin.com/zylincdt . Po kliknięciu OK z drzewa będzie można wybrać http://opensource.zylin.com/zylincdt > Uncategorized > Zylin Embedded CDT. Po kliknięci w Install wtyczka zostania pobrana i zainstalowana.

W tym momencie warto dla pewności zresetować Eclipse, aby być pewnym, że zainstalowane właśnie wtyczki zostaną poprawnie uruchomione.

Do celów debuggowania aplikacji w Eclipse przewidziany jest inny układ okien. Predefiniowane układy okien w Eclipse zwane są Perspectives. Kod tworzony jest w C/C++ Perspective, natomiast debuggowanie możliwe jest w Debug Perspective. Otworzenie nowego układu możliwe jest przez menu Window > Open Perspective > Other... . Układy które już zostały otwarte można zmieniać w prawym górnym rogu okna Eclipse oraz w menu Window > Open Perspective.

Do Debug Perspective warto dodać zakładkę deassemblacji - menu Window > Show View > Disassembly. Zakładkę tą można umieścić według uznania w dowolnej z ramek Debug Perspective.

Pierwszym krokiem w sprzęgnięciu Eclipse ze sprzętem jest uruchomienie za jego pośrednictwem OpenOCD. Opcje umożliwiające uruchamianie dowolnych zewnętrznych aplikacji poprzez Eclipse znaleźć można w menu Run > External Tools. Ikonka tej grupy dostępna jest również standardowo na pasku narzędziowym. W celu uruchomienia OpenOCD wybieramy więc Run > External Tools > External Tools Configurations... i ikonką New launch configuration (po lewej na pasku narzędzi nad pustą obecnie listą) (lub opcja New z menu kontekstowego listy) tworzymy nowy element. W zakładce Main wprowadzić należy ścieżkę dostępu do OpenOCD - dla wersji 0.1.0 będzie to C:\Program Files\OpenOCD\0.1.0\bin\openocd.exe - oraz argumenty z którymi wywołany zostanie program, które zależne są od konfiguracji sprzętowej. W moim przypadku (JTAG-lock-pick + makieta z LPC2103) argumenty mają postać -f interface/jtagkey.cfg -f target/lpc2103.cfg . Wprowadzoną konfigurację warto nazwać w sposób jednoznaczny w polu Name na górze okna - niech będzie to na przykład "OpenOCD + jtagkey + lpc2103". Tak przygotowaną konfigurację można zatwierdzić przyciskiem Apply, a następnie uruchomić przyciskiem Run. Raz uruchomiona konfiguracja będzie później dostępna w sposób szybszy, a mianowicie bezpośrednio poprzez menu Run > External Tools (lub pod ikoną na pasku narzędzi).

Uruchomione OpenOCD pojawi się w zakładce Debug, a komunikaty OpenOCD powędrują do zakładki Console związanej z tym elementem.

Finalny krok na drodze do ujarzmienia debuggowania w Eclipse to wprowadzenie parametrów dla GDB. Opcje powiązane z debuggerem dostępne są w menu Run > Debug Configurations... . Dostęp do tej grupy opcji możliwy jest też poprzez przycisk Debug, który standardowo znajduje się na pasku narzędzi.

W oknie Debug Configurations wśród dostępnych na liście elementów powinna się znaleźć opcja GDB Hardware Debugging - po jej dwukrotnym kliknięciu stworzona zostanie nowa konfiguracja powiązana z tym typem debuggowania.

Konfiguracje powiązane są z projektami, więc pierwszą czynnością musi być przypisanie jednego z otwartych projektów do tej konfiguracji - można to zrobić bezpośrednio - wpisując nazwę projektu do pola Project - lub wybierając projekt z listy aktualnie otwartych przyciskiem Browse... . Po wybraniu projektu konieczne jest wybranie pliku wykonywalnego, który będzie debuggowany. Ponownie - względną ścieżkę do pliku można podać bezpośrednio w polu C/C++ Aplication, lub wybrać jeden z dostępnych dla danego projektu plików poprzez przycisk Search Project... .

W następnej zakładce - Debugger - znajdują się ustawienia samego GDB. W zakładce tej należy przede wszystkim wprowadzić ścieżkę do samego pliku wykonywalnego GDB - w przypadku standardowej instalacji pakietu CodeSourcery będzie to C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-gdb.exe . Drugim parametrem który należy zmienić w tej zakładce jest numer portu za pomocą którego GDB będzie komunikował się ze sprzętem - w przypadku OpenOCD standardowo jest to port o numerze 3333.

Ostatnią porcję zmian należy wprowadzić w zakładce Startup, która odpowiada za - zgodnie z nazwą zresztą - zachowanie GDB po podłączeniu do sprzętu. Pierwszą ramką która powinna budzić szczególne zainteresowanie jest ramka Initialization Commands. Opcje Reset and Delay oraz Halt najlepiej zostawić w spokoju - nie działają one prawidłowo z toolchainami dla ARMów. W dużym oknie tekstowym możemy wprowadzać różne komendy które zostaną wykonane po uruchomieniu GDB. Najciekawszą opcję jest możliwość wydawania komend bezpośrednio dla OpenOCD - uzyskać to można poprzez poprzedzenie komendy słówkiem monitor (lub w skrócie mon). Zestaw komend jaki należy wydać jest ściśle zależny od używanego układu docelowego oraz od efektu jaki chce się osiągnąć. Zasadniczo po uruchomieniu GDB resetuje się układ docelowy (a więc monitor reset) i zatrzymuje rdzeń (poprzez - na przykład - monitor halt lub monitor soft_reset_halt w przypadku np. LPC2000). Jeśli w kodzie zaszły jakieś zmiany konieczne jest załadowanie do pamięci układu nowego obrazu. Kusi aby użyć do tego celu opcji z ramki Load Image and Symbols, niemniej jednak i te opcje niezbyt dobrze współpracują z ARMami. Do załadowania obrazu można więc wykorzystać bezpośrednio komendę GDB - load - którą wpisać należy do pola w ramce Initialization Commands. Przedostatnia ramka - Runtime Options - pozwala na ustawienie "jednorazowego" breakpointa w dowolnym miejscu i odpalenie programu po tych wszystkich operacjach. Warto więc podejść do sprawy standardowo - zatrzymać program na początku funkcji main, gdyż zwykle nie ma potrzeby debuggowania assemblerowych startupów. W tym celu wystarczy zaznaczyć opcję Set breakpoint at i w pole tekstowe wpisać main oraz zaznaczyć opcję Resume.

W tym momencie warto może napisać parę słów na temat Initialization Commands. Ponieważ toolchain dla ARMów nie obsługuje opcji resetowania przez GDB, warto stworzyć sobie dwa "zestawy" konfiguracji debuggowania - jedna konfiguracja umożliwiać będzie załadowanie do procesora nowego obrazu programu, a druga wykorzystywać będzie obraz znajdujący się już w pamięci układu. Różnić będą się tylko obecnością komendy load.

Przykładowe komendy inicjalizujące dla LPC2103 i ładujące program do pamięci RAM:

monitor reset
monitor soft_reset_halt
monitor mww 0xE01FC040 0x0002
load

Zauważyć można dodatkową komendę mww 0xE01FC040 0x0002, która powoduje zapis (Memory Write Word) wartości 0x0002 pod adres 0xE01FC040, który - w ARM7 - odpowiada rejestrowi MEMMAP - wartość ta w tym rejestrze oznacza zmapowanie początku pamięci RAM w obszar wektorów przerwań, co odpowiada sytuacji gdy cały załadowany program znajduje się w pamięci RAM.

W tym momencie gotową konfigurację warto jakoś ładnie nazwać (na przykład LPC2103 + load) przy pomocy pola na górze okna i zatwierdzić całość przyciskiem Apply. Jeśli w tle wciąż uruchomione jest OpenOCD można od razu nacisnąć przycisk Debug i przejść do debuggowania programu! Po pierwszym uruchomieniu danej konfiguracji będzie ona dostępna w wygodniejszy sposób, a mianowicie bezpośrednio w menu Run > Debug History lub pod przyciskiem Debug na pasku narzędzi.

Jeśli wszystko się udało, po krótkiej chwili w zakładce Debug poniżej uruchomionego OpenOCD powinien pojawić się proces o ustalonej przez nas nazwie odpowiedzialny za GDB Hardware Debugging, a w nim zatrzymany pojedynczy wątek Thread [0]. Sukces!

Warto w tym momencie wspomnieć o kwestii konsol dla uruchomionych aplikacji. Wprawne oko podczas uruchamiania GDB zauważy kilkanaście linijek przewijających się przez zakładkę Console i znikających po pełnym uruchomieniu. Otóż każda z uruchomionych w tle aplikacji ma swój własny zestaw zakładek i danych, a więc i konsol. Jeśli w zakładce Debug klikniemy na proces OpenOCD (dokładniej - na element C:\Program Files\OpenOCD\0.1.0\bin\openocd.exe) to w zakładce Console pojawi się aktualna zawartość konsoli przyporządkowanej dla tego właśnie procesu. Konsola ta zaraz po odpaleniu GDB powinna zawierać mniej więcej taki tekst:

Open On-Chip Debugger 0.1.0 (2009-01-21-21:15) Release

BUGS? Read http://svn.berlios.de/svnroot/repos/openocd/trunk/BUGS

$URL: https:// Adres poczty elektronicznej jest chroniony przed robotami spamującymi. W przeglądarce musi być włączona obsługa JavaScript, żeby go zobaczyć. /svnroot/repos/openocd/tags/openocd-0.1.0/src
/openocd.c $
1000 kHz
Info : JTAG tap: lpc2103.cpu tap/device found: 0x4f1f0f0f (Manufacturer: 0x787,
Part: 0xf1f0, Version: 0x4)
Info : JTAG Tap/device matched
Warn : no telnet port specified, using default port 4444
Warn : no gdb port specified, using default port 3333
Warn : no tcl port specified, using default port 6666
Info : accepting 'gdb' connection from 0
Error: timed out while waiting for target halted
Warn : acknowledgment received, but no packet pending
Warn : target not halted
Info : JTAG tap: lpc2103.cpu tap/device found: 0x4f1f0f0f (Manufacturer: 0x787,
Part: 0xf1f0, Version: 0x4)
Info : JTAG Tap/device matched
requesting target halt and executing a soft reset
target state: halted
target halted in ARM state due to debug-request, current mode: Supervisor
cpsr: 0x000000d3 pc: 0x00000000
Warn : memory write caused data abort (address: 0xe01fc040, size: 0x4, count: 0x1)
Runtime error, file "command.c", line 456:

Natomiast jeśli w zakładce Debug wybrany zostanie element odpowiadający procesowi GDB (czyli C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-gdb.exe) naszym oczom ukaże się konsola samego GDB, której zawartość powinna wyglądać mniej więcej tak:

target remote localhost:3333
0x00000000 in ?? ()
monitor soft_reset_halt
load

Loading section .text, size 0x1adc lma 0x40000000
Start address 0x40000000, load size 6876
Transfer rate: 13 KB/sec, 6876 bytes/write.
tbreak main
Temporary breakpoint 1 at 0x400001a0: file src/main.c, line 258.
continue

Temporary breakpoint 1, main () at src/main.c:258
258 volatile unsigned int x,max=100000;

Warto zauważyć, że w zakładce Disassembly widać aktualną instrukcję C a pod nią odpowiadający jej zestaw instrukcji assemblerowych. W zakładce Variables zauważyć można wszystkie zmienne lokalne danej funkcji. Aby dodać zmienne globalne wystarczy kliknąć w obszar tej zakładki prawym przyciskiem myszy i skorzystać z opcji Add Global Variables... . W zakładce Registers po rozwinięciu elementu Main można natomiast podziwiać rejestry rdzenia.


Epilog

Na tym etapie temat niniejszego artykułu został wyczerpany - jeśli wszystkie kroki przebiegły poprawnie, możliwe jest szybkie tworzenie projektów w Eclipse, kompilowanie ich przy pomocy pakietu CodeSourcery i debuggowanie z pomocą OpenOCD - czyli w zasadzie wszystko co do szczęścia potrzebne. No... brakuje faktycznie jeszcze 6 numerów z kolejnego losowania Lotto (;

Artykuł ten na pewno nie jest idealny... O wszelkich zauważonych błędach, nieścisłościach, brakach, itp. napisz do mnie w komentarzu do artykułu albo poprzez formularz kontaktowy na stronie - chciałbym aby ten artykuł odpowiadał potrzebom typowego usera, który dopiero wchodzi w świat 32-bitowych rdzeni ARM, a taki cel może zostać osiągnięty jedynie przy współpracy z odbiorcami tego tekstu! Czekam więc na feedback (;

Zmieniony: Piątek, 12 Czerwiec 2009 11:31