Actions

Byggprocess ARM

From Chalmers Robotics

Denna sida går igenom hur det fungerar att bygga kod till ARM-processorer. Den går igenom saker såsom gcc, makefiler, kompileringsflaggor, länkning och annat under skalet på kompilatorn.

Sätta upp kompilator

Innan man kan börja bygga kod måste man ha en byggmiljö. I artikeln Förberedelse programmering STM32 gås det igenom hur man sätter upp en byggmiljö för ARM (framförallt skaffar sig kompilator).

Byggprocessen

Detta är en kort beskrivning av byggprocessen och hur man anropar GCC. Det är inte helt nödvändigt att känna till vad som händer i alla steg, men det kan vara bra att känna till dem ändå. För mer utförlig information, se GCCs sida samt en sammanställning av alla options man kan skicka med. Eftersom det som används för ARM är en specialvariant av GCC så finns det några extra flaggor och några kanske inte fungerar. För att den kod som du skriver i C (eller något annat språk) ska kunna köras av processorn måste den översättas (kompileras) till ett språk som processorn förstår (maskinkod). Det finns enorma mängder inställningar och kompilatorflaggor, och det gäller att veta vilka som ska användas. Kort sagt går kompilatorn igenom upp till sex steg. De första tre stegen (Assemblator, C-kompilator och Länkare) är egentligen hopbakat och använder samma kommando-anrop (arm-none-eabi-gcc med massa flaggor och grejer efteråt). De tre sista stegen är optional, men kan vara bra att köra.

Assemblator(Assembler)

Ifall du har assemblerkod i ditt projekt (vilket du har, även om du inte skrivit det själv). Startup-filen är ofta skriven i assembler. Assemblatorn behöver veta vilka include-filer (h-filer), d.v.s. vilka bibliotek du använder ("-I"-flaggan), ifall du har några preprocessor-kommandon ("-D"-flaggan) osv. Exempel på preprocessor-kommandon kan vara vilken hastighet din externa kristall har eller vilken processorserie du använder.

C-kompilator (C compiler)

This is where the magic happens. Här görs din C-kod om till maskinkod. Även C-kompilatorn behöver känna till vilka includes du har och tar även preprocessor-kommandon. Vidare är optimering viktigt för C-kompilatorn. GCC stödjer 5 optimeringsnivåer (O0-3 samt s), där -O0 är ingen optimering, -O1 är lite optimering, -O2 är mer och -O3 är mest. Vidare är -Os storleksoptimering. Dessutom finns det en hel hög med andra optimeringsflaggor. Exakt vad som skiljer de olika optimerings-nivåerna åt är svart magisk magi, men finns dokumenterat här. Generellt sett rekommenderas Os och O2. C-kompilatorn vill också veta vilken standard du kör på. Den vanligaste är C99. GCC jobbar på att börja stödja C11 fullt ut, men nästan all kod idag förutsätter C99. Denna flagga är -std=gnu99. Använd inte C90, för det är gammalt och böpigt.

Länkaren (Linker)

Här händer mer magi. Länkaren tar dina .o-filer (som kommer ut från Assemblatorn och C-kompilatorn), klistar ihop dom och lägger rätt kod på rätt ställe i minnet (t.ex. om någonting ska direkt till RAM, om någonting ska till extern FLASH osv) samt hur mycket som finns tillgängligt utifrån ett länkarscript. Länkarscriptet ska du inte skriva själv, för det är väldigt krångligt. Använd istället ett färdigt. Du lägger till länkarscriptet med "-T"-flaggan. Länkaren tar även hand om startfilen (den första kod som körs när processorn startar). Den filen behöver bara skickas med till kompilatorn. En flagga som är väldigt bra att använda är "Remove unused sections" ("-Xlinker --gc-sections"). Den tar bort all kod som inte används i ditt program, vilket gör att du alltid kan inkludera hela biblioteket utan att ditt program blir gigantiskt. Ut ur länkaren kommer en .elf-fil. Elf-filen är hela programmet och används t.ex. för debugging och vissa programmeringsmetoder. Den är också indata till de kommande kompilatorstegen.

Ditt fullständiga kommando kommer att vara en LÅNG harrang av text, men du kommer aldrig att se hela kommandot på en gång (såvida du inte vill själv) oavsett om du skriver egen makefil eller använder Eclipse. Exempel på de kompilatorflaggor som bör skickas med är:

arm-none-eabi-gcc -std=gnu99 -g -O2 -Wall -Tstm32_flash.ld -mlittle-endian -mthumb -mthumb-interwork -nostartfiles -mcpu=cortex-m3 \
-I<path_to_includes> \
-DHSE_VALUE=8000000UL \
-T<path_to_linkerscript> \

Flash image (objcopy)

I nästa steg kompileras elf-filen till en fil som man ofta använder för att ladda in i processorn. Det finns ett par olika filformat, men gemensamt för dem är att det är närmare den data som ska hamna i microcontrollers minne i slutändan. Exempel på några vanliga format är .bin (binary) och .hex (intel hex). Kommandon som kan köras är: <source lang="bash"> arm-none-eabi-objcopy -O ihex proj_name.elf #trollar ut en .hex-fil arm-none-eabi-objcopy -O bin proj_name.elf #trollar ut en .bin-fil </source>

Listfil (objdump)

Skapar en list-fil som innehåller t.ex. opkoder tillsammans med assemblerkoden och vart dina funktioner börjar osv. Den kan vara bra att ha vid avancerad felsökning och om man vill vara säker på att processorn gör precis som du tror att den ska göra. Det kan också vara lärorikt att se vad som genereras av C-kod. Kommandot är: <source lang="bash"> arm-none-eabi-objdump -h -S proj_name.elf #trollar ut en listfil med assemblerkod och funktionssymboler </source>

Programstorlek (size)

Skriver ut den kompilerade storleken av ditt program, samt hur mycket som ligger i vilket minne. Väldigt värdefullt att ha, särskilt om man har en liten processor, eller ifall man vill spara på RAM. Kommandot och resultatet är: <source lang="bash"> arm-none-eabi-size --format=berkeley IntroARM.elf

  text	   data	    bss	    dec	    hex	filename
  9440	     20	    336	   9796	   2644	IntroARM.elf

Finished building: IntroARM.siz </source> Text är det som går in i ditt flash, data och bss är det som hamnar i RAM och dec är det totala. Alla storlekar är i byte.

Makefil

För att kunna kompilera kod behöver man en makefil för ditt projekt. En makefil är som en att-göra-lista för kompilatorn, som säger t.ex. vilka filer som ingår i projektet, hur kompilatorn ska optimera, vilka filer som ska genereras osv. Makefilen skriver du antingen själv eller så låter du en IDE (t.ex. Eclipse) ta hand om det. Det råder en del delade meningar om vilken metod som är bäst, men här är några för- och nackdelar med båda metoderna:

  • Skriva själv
    • + Ditt projekt blir mer portabelt (du är t.ex. inte beroende av en IDE och det är enklare för andra att kompilera din kod).
    • + DU kan använda vilken texteditor eller IDE som helst (nästan), och behöver inte förlita dig på en viss IDE.
    • + Du får mer kontroll över byggprocessen och kan göra mer avancerade saker
    • - Det är kan vara krångligare än att låta en IDE ta hand om det och du får inte lika bra stöd från IDEn.
    • - Det kan vara en stor tröskel att komma över att börja köra makefiler
    • - Du måste ändra mer manuellt när du gör ett nytt projekt
    • - Du måste komma ihåg att lägga till nya filer i din makefil
  • Låta en IDE generera makefil (i detta exempel är det Eclipse som gäller)
    • + Det är enklare att komma igång med
    • + Du kan tweaka ditt projekt och din byggprocess med ett grafiskt gränssnitt
    • + Din IDE kan ge dig mycket bättre stöd om den får generera makefilen själv
    • - Du är mer beroende av en viss IDE
    • - Även om det mesta går att göra via Eclipses grafiska gränssnitt kan en del avancerade saker vara svåra att göra (som hade varit enklare med en egenskriven makefil).

Värt att nämna är att du kan använda Eclipse (eller annan IDE) även om du skriver din egen makefil. Summan av kardemumman är att automatgenerad makefile är mer automatisk, men tillåter för mindre insyn och friheter. Däremot kan det vara väldigt bra och lärorikt att kunna en del om byggprocessen.

Skriva manuellt

Om du ska skriva en egen makefil så är det bra att ha en färdig File:Makefile.txt att utgå från. Det du kommer behöva ändra på är primärt:

  • Vilka c-filer ingår i ditt projekt?
    Alla c-filer som ingår i ditt projekt behöver finnas med i SRCS-variabeln. Varje gång du skapar en ny fil till ditt projekt behöver du lägga till det här.
  • Projektets namn
    I PROJ_NAME lägger du vad ditt projekt ska heta (t.ex. IntroARM eller Pelles_Robot). Alla filer som genereras kommer få detta namn (t.ex. IntroARM.elf, IntroARM.hex osv)
  • Vart dina kompilatorer finns
    Detta lägger du i BINPATH. Typiskt finns dina kompilatorer i "<DITT_ANVÄNDARNAM>/sat/bin", men kan även finnas på andra ställen, såsom i /usr/bin eller /opt/bin (den sistnämnda primärt om du kör OSX)
  • Vilka flaggor som ska med
    De flaggor som finns i exempel-makefilen är ganska bra. De förklaras närmare i avsnitten Kompilatorflaggor. Det flaggor du primärt behöver bry dig om är:
    • -DSTM32F10X_LD_VL - Ifall du använder STs bibliotek ställer detta in vilken processor-familj du använder.
    • -DHSE_VALUE=8000000UL - Specar vilken frekvens (i Hz) din externa kristall är (ifall du har någon). Också specifikt för STs bibliotek. Är default på 8MHz. Se till att ha med UL på slutet för att explicit göra den till unsigned long.
    • -mcpu=cortex-m3 - Ställer in vilken processor-kärna du har. Kommer troligen vara antingen cortex-m3 eller cortex-m4.
    • -I - ställer in alla vägar till de h-filer du använder, såsom de h-filer som du har skrivit, h-filerna till alla bibliotek osv.
    • -L - ställer in vägar till externa bibliotek du använder. TODO: här får någon annan förklara hur det funkar.

När du har ändrat på makefilen och anpassat den till ditt system är det bara att se till att ha kod på rätt ställen och köra make. Ut ska det då komma ett antal filer, bl.a. en elf-fil, en hex- och/eller en bin-fil. Dessutom kommer det att stå storleken i terminalen.

Göra ett makefil-projekt Eclipse

TODO: detta får någon som faktiskt använder denna metod skriva.

IDE (Eclipse med ARM-plugin)

Om man inte vill skriva egna makefiler och krångla med det kan man låta eclipse skapa makefilerna åt dig. Denna metod består av två huvudsteg: Ställa in alla bygginställningar och länka in biblioteken. Här antas det att du använder dig av STs bibliotek, men det är fortfarande applicerbart för andra bibliotek. Du behöver ARM-pluginet som du hämtar genom att gå till Help-Install New Software, klicka på Add och skriva in följande länk: http://sourceforge.net/projects/gnuarmeclipse/files/Eclipse/updates/ som location. Detta låter dig installera ARM-pluginet. När du har det kan du skapa ett nytt C-projekt (File->New->C Project) och välja ARM Cross Target Application och där välja summon-toolchainen. Hitta på ett bra namn och tryck finish. Markera projektet och gå in i properties (alt+enter). Här är det ett antal saker som behöver ställas in:

  • PATH-variabeln
    Först måste du ställa in din PATH-variabel. Detta gör du under C/C++ build->Environment. Klicka på Select... (till höger) och hitta variabeln som heter PATH. Markera den och glöm inte att markera Add to all configurations. När du lagt till den markerar du den och klickar på Edit... Lägg till vägen dit där dina kompilatorer finns, typiskt <YOUR_USERNAME>/sat/bin, men kan också vara i /opt/bin (för er som kör OSX och har gjort fel och använder Macports). Du använder : som avskiljare.
  • C/C++ build finns settings. Här ställer du in alla flaggor, länkarscript och liknande. Följande behöver din attention:
    • Target Processor - Här ställer du in vilken kärna du använder, typiskt cortex-m3 eller cortex-m4. Här ställer du även in hur du ska använda flyttal. Ifall du använder en processor med FPU (typiskt STM32F4xx) så vill du ha hardfloat och ställer in "FP instructions" för Float ABI och "FPv4 SP D16" för FPU Type. Använd också både Thumb och Thumb Interwork.
    • Debugging - Här kan du ställa in om du ska debugga. Detta gås inte igenom här.
    • Additional tools - Se till att alla options (Flash image, Extended listing och Size) är valda.
    • Assembler - Se till att det står arm-none-eabi-gcc.
      • Preprocessor - Tryck på det lilla gröna pluset och ange HSE_VALUE=8000000UL ifall din externa kristall har frekvens 8MHz. För andra frekvenser anger du såklart den korrekt som HSE_VALUE. Markera också att preprocessorn ska användas ifall den inte är markerad.
      • Warnings - Här ställer du in alla varningar. Tips är att köra bara Wall.
    • C Compiler - Återigen, se till att det står arm-none-eabi-gcc som Command
      • Preprocessor - Ange ytterligare två symboler: USE_STDPERIPH_DRIVER samt STM32F10X_LD_VL eller vilken processorserie du nu har. En mer utförlig lista finns under | Kompilatorflaggor.
      • Optimization - Här ställer du in vilken optimering du ska använda. Tips är att använda Os. Ifall du ska använda FPU måste du köra O2 eller O3. Vidare kan du även se till att Function Sections och Data Sections är ikryssade (det gör ditt program lite mindre).
      • Warnings - Återigen är Wall vettigt.
      • Miscellaneous - Ställ in så att du använder C99. 'OBS, viktigt'. Använd C99 med GNU extensions. Ingen annan option ska vara vald.
    • Linker - kontrollera återigen att Command är arm-none-eabi-gcc (sista gången!).
      • General - Kryssa i "Do not use standard start files" och ange ett länkarscript i textrutan (eller köra browse). Länkarscripten ligger undangömda i STs bibliotek, så därför kommer det finnas några här senare. TODO: ladda upp länkarscript. Välj det länkarscript som passar för din processors Flash- och RAM-storlek. Se till att Remove Unused Sections är ikryssad. Detta gör att du kan inkludera och kompilera alla bibliotek varje gång, men att de kastas bort ifall de inte används.
    • Flash Image - se till att det står arm-none-eabi-objcopy i command.
      • Output - Välj det output som passar, vilket beror på vilken metod du använder för att ladda över ditt program. Typiskt är detta ihex eller binary.
    • Listing - Se till att det står arm-none-eabi-objdump
      • General - -h och -S är bra options. Du kan såklart ställa in andra om du behöver det. Information finns här
    • Size - Se till att det står arm-none-eabi-size
      • General - Välj vilket utskriftsformat du vill ha. Berkeley är standard, så använd det.

Nu ska vi länka in alla filer som vi vill ha. Först ska vi länka in alla c-filer vi vill ha (t.ex. STs bibliotek). Detta gör man under C/C++ General->Paths and Symbols. Välj Source Locations och klicka på Link Folder. Klicka i Link to folder in file system och leta upp och lägg till de filer du vill ha. Typiskt vill du ha src-mappen för STs bibliotek (eller något annat bibliotek) samt CMSIS (C-stöd för ARM-kärnan). Under Includes ska du lägga till alla h-filer som du ska använda. Detta inkluderar h-filerna till biblioteken, samt lite arm-none-eabi-bibliotek under sat/arm-none-eabi/include samt sys-include.

Flyttalsprocessor

I en del processor (de med cortex-m4-kärna) finns det en flyttalsprocessor som är väldigt snabb på att beräkna flyttal. Exempel är STM32F4xx som sitter på STM32F4Discovery. För att göra detta krävs det vissa specialinställningar för kompilatorflaggorna. man kan inte köra Os, då funkar den dåligt.

Kompilatorflaggor

Till ARM har GCC en del speciella kompilatorflaggor