[Firmware dev] GPS fietscomputer: CMake build systeem

Door Anteros op maandag 20 juli 2015 16:59 - Reacties (7)
Categorie: Technisch, Views: 2.519

Een goed onderhoudbaar softwareproject begint bij een begrijpbare opzet van de projectstructuur en het build systeem. Er zijn diverse manieren om dit te bewerkstelligen en ik heb de volgende structuur gekozen:

http://static.tweakers.net/ext/f/LZbHAmixhNBYJOtn8K03YFBX/full.png

De root van het project bestaat uit diverse subdirectory’s:
  • buildtools : Hierin staat de cross-compiler voor het compileren en linken van de sources
    • Voor zowel Linux als Windows is een aparte cross-compiler gebruikt en afhankelijk van de host wordt de relevante cross-compiler aangeroepen
  • firmware : Hierin staan alle sub-systemen en sub-componenten, inclusief de twee executables (boot loader en de applicatie)
    • Sub-systemen behoren tot het project en zijn in principe niet 1:1 herbruikbaar
    • Sub-systemen bestaan uit een inc en src directory. De inc directory bevat alle openbare header-files die de API-functies beschrijven. De src directory bevat een of meerdere source-files en lokale header-files.
    • Sub-componenten behoren ook tot het project maar zijn wel 1:1 herbruik- en configureerbaar. De cfg directory in de ./firmware/ directory bevat daarom een subdirectory voor elk sub-component wat een zogenaamde ‘config’ header-file bevat – bv een vcp_cfg.h file -

      http://static.tweakers.net/ext/f/T4JoJLqL9I1XxNEwm1klc7nB/full.png
    • Sub-componenten bestaan verder ook uit een inc en een src directory.
    • Hieronder de huidige directory-structuur van mijn project en zoals te zien is, zijn cmdLine, configman, eventlog, stm32f2cube, ubicom en vcp herbruikbare componenten.

      http://static.tweakers.net/ext/f/QQ3Im8Jbq2kpNg5ON1w1AYak/full.png
  • build : Deze subdirectory wordt gebruikt voor het bouwen en linken van de executable(s)
  • refInfo : Hierin staat allerlei documentatie, voorbeeldcode, etc. wat gebruikt kan worden voor naslag tijdens de ontwikkeling

    http://static.tweakers.net/ext/f/5HDmiXxAg98MblkX5j9HVjjw/full.png
  • .git : De standaard GIT subdirectory
Voor het build systeem maak ik gebruik van Makefiles in combinatie met CMake. CMake parsed speciale CmakeLists.txt bestanden en genereert daaruit de Makefiles. Deze Makefiles kunnen vervolgens afgetrapt worden voor het daadwerkelijk bouwen van de executable(s).

Omdat er relatief weinig informatie beschikbaar is voor het gebruik van CMake in combinatie met een cross-compiler, wil ik graag het een en ander samenvatten om hiermee hopelijk wat mensen op weg te helpen met CMake.

CMake in embedded development
CMake werkt met het concept van executables en libraries. Een executable is een applicatie en in mijn geval zijn dat de boot loader (boot) en de hoofdapplicatie (applic). Alle sub-systemen en sub-componenten in mijn project zijn gedefinieerd als statische libraries. Ze worden dus gelinkt als .a bestanden om later weer gelinkt te worden met de executable(s).

Mijn CMake-configuratiebestanden staan als volgt in mijn project – overigens hebben nog niet alle libraries CMakeLists.txt bestanden - :

http://static.tweakers.net/ext/f/1opnA59zjKXGa0gB2g05w3pk/full.png

Omdat CMake er normaliter vanuit gaat dat de software gecompileerd gaat worden voor de local-host, moet je CMake nu vertellen dat je wilt compileren voor een embedded target. Dit kan middels een zogenaamd toolchain-bestand.

En toolchain CMake bestand wordt aangemaakt en daarin staat vermeldt welke compiler en linker CMake moet gebruiken. Dit bestand wordt geplaatst in de root van het CMake project (./firmware/ in mijn geval).

http://static.tweakers.net/ext/f/evUG6fbw2kJdYwqTvyt9wlT5/full.png

De inhoud van mijn toolchain file arm.toolchain.cmake is als volgt:

http://static.tweakers.net/ext/f/hvm3cEaeOSdjj6RTEup379n3/full.png

Korte uitleg:
  • ${PROJECT_SOURCE_DIR} : Is het absolute pad waar, in dit geval, arm.toolchain.cmake staat.
  • CMAKE_FORCE_C_COMPILER : Vertelt CMake dat CMake geen compiler check moet uitvoeren en dat de aangegeven compiler gebruikt moet worden.
Nu CMake weet welke compiler te gebruiken, is het ook nodig om CMake te vertellen wat de compiler- en linkeropties zijn. Die opties zijn nodig om de compiler o.a. te vertellen wat het optimalisatieniveau is, welke warnings en errors deze moet genereren, enz. De linker moet o.a. weten welke linker-file gebruikt moet worden om de objecten en libraries te linken, of de linker standaard libraries zoals libc mee moet nemen, enz.

Deze compiler- en linkeropties worden gezet in de root CMakeLists.txt en in een executable - ‘boot’ of ‘applic’ in mijn geval - CMakeLists.txt.

De inhoud van de root CmakeLists.txt is in mijn geval als volgt:

http://static.tweakers.net/ext/f/pdnjPwDL3SpAzZS7jzRiu3qD/full.png

Korte uitleg:
  • set(LIBS …) : Dit zijn alle project libraries en executables die CMake moet gebruiken
  • add_definitions(…) : Hiermee kan je definities toevoegen die je vervolgens kan gebruiken in de code – #ifdef <MIJN_DEFINITIE> … #endif
  • set(C_MAKE_C_FLAGS …) : Dit zijn de compiler opties die CMake meegeeft aan de compiler
  • include_directories(…) : Hiermee vertel je CMake waar deze de verschillende header-files kan vinden
  • add_subdirectory(…) : Hiermee vertel je CMake dat een library of executable toegevoegd moet worden aan het project. CMake gaat dan de CMakeLists.txt-file in die subdirectory parsen en uitvoeren

Zoals hierboven beschreven staat, worden de linkeropties gedefinieerd in een executable. In mijn boot loader geval staat deze CMakeListst.txt dus in de ./firmware/boot/ directory en ziet er als volgt uit:

http://static.tweakers.net/ext/f/cuoYe5eARDVOMqXlwS0zYU0f/full.png

Korte uitleg:
  • set(FILE_MEMORY_LAYOUT …) : Deze CMake variabele bevat een absoluut pad naar de boot loader linker-file
  • set(CMAKE_EXE_LINKER_FLAGS …) : De linker opties die CMake meegeeft wanneer de linker aangeroepen wordt
  • add_custom_command(…) : Hiermee vertel je CMake dat dit commando uitgevoegd moet worden als de bestanden vectors.c en/of vectors.h benodigd zijn. Deze bestanden worden dynamisch door AWK gegeneerd om de Interrupt Vector Tabel te definiŽren
  • set(LIBDEPENDS …) : Hierin staan libraries die benodigd zijn om de boot loader executable te kunnen linken
Laatste stap
Nu alles klaar is kan CMake afgetrapt worden om de Makefiles te genereren. Dit wordt gedaan in de ./build/ directory met het volgende commando:

http://static.tweakers.net/ext/f/FgK2ghRUoScHgrOdSlxsiyjt/full.png

De output ziet er als volgt uit:

http://static.tweakers.net/ext/f/1Q87U2hF5R6tFeyUNcylMg8o/full.png

Nu kunnen we het project bouwen met make:

http://static.tweakers.net/ext/f/kOGUNPh6iNzeqOlnB5onL6zW/full.png

Als alles goed gaat staat er nu een .elf executable in ./build/boot/. Met het commando readelf <executable> -h kan je wat informatie opvragen:

http://static.tweakers.net/ext/f/3RTCU3wT3S9e3yWwe3XHt9k5/full.png

En de output:

http://static.tweakers.net/ext/f/4zuMlW2EFGrvIP2lbRVPCxHz/full.png

Slotwoord
CMake maakt het (voor mij) makkelijker om een build systeem op te zetten wat leesbaar en goed onderhoudbaar is. Niet alleen dat, het is nu een fluitje van een cent om een nieuwe library toe te voegen of speciale varianten van de software te bouwen. Deze en nog enkele andere voordelen maken het voor mij in ieder geval een perfecte keus!

Ik hoop dat het met deze blog-post nu wat duidelijker is hoe je CMake kan gebruiken in embedded softwareprojecten. Wellicht een overweging waard voor jouw (embedded) softwareproject?

Referenties

Volgende: [Elek. GPS fietscomputer] Linker, HAL en huidige boot loader status 07-'15 [Elek. GPS fietscomputer] Linker, HAL en huidige boot loader status

Reacties


Door Tweakers user EvertH, maandag 20 juli 2015 17:32

Bijzonder interessante blogs, keep it up :)
Stel dat ik wat zou willen prutsen met zo'n platform, kun je me dan iets aanraden (qua tutorials/literatuur)? Gewoon een Cortex-M3-bordje van het internet plukken?

Door Tweakers user unglaublich, maandag 20 juli 2015 18:31

EvertH schreef op maandag 20 juli 2015 @ 17:32:
Bijzonder interessante blogs, keep it up :)
Stel dat ik wat zou willen prutsen met zo'n platform, kun je me dan iets aanraden (qua tutorials/literatuur)? Gewoon een Cortex-M3-bordje van het internet plukken?
Dit is echt een top project: https://developer.mbed.org/platforms/. Serieuze hardware development bordjes met een online IDE en firmware die het compileren en programmeren juist weer erg makkelijk maakt. Staat ook vol met voorbeelden.

[Reactie gewijzigd op maandag 20 juli 2015 18:32]


Door Tweakers user Blokker_1999, maandag 20 juli 2015 19:12

Het is op deze moment dat ik wenste wat meer van de C talen af te kennen. Verder dan .Net en een proof of concept in C++ bij elkaar hacken ben ik nooit geraakt.

Door Tweakers user Jogai, dinsdag 21 juli 2015 08:17

Blokker_1999 schreef op maandag 20 juli 2015 @ 19:12:
Het is op deze moment dat ik wenste wat meer van de C talen af te kennen. Verder dan .Net en een proof of concept in C++ bij elkaar hacken ben ik nooit geraakt.
Je kan de .net mf route gaan: "The overhead of the base platform is about 200K of Flash and 30K of RAM" Al zal dat niet zo low-level gaan als Anteros ons nu laat zien.

Waarom wordt .txt en .md door elkaar gebruikt? Ik zou 1 van de 2 kiezen, maar niet mixen.

Door Tweakers user Sissors, dinsdag 21 juli 2015 08:46

Dat is voor een MCU wel een enorme overhead. Voor een low-end telefoon hardware is dat waarschijnlijk prima, maar een gemiddelde MCU zou ik niet zoveel overhead op kwijt willen zijn (het past uberhaupt niet op de gemiddelde MCU).

Ik denk dat daarvoor mbed inderdaad een prima startpunt is. Die is in principe ook C++ (Ook al kan je als je echt wil er ook gewoon in C op developpen).

Overigens misschien omdat ik de route assembly -> C -> C++ heb genomen heb ik nooit het probleem gezien van andere, maar ik lees regelmatig dat mensen bang zijn over het memory management zelf moeten doen in C/C++ als ze eraan beginnen vanuit een 'hogere' taal. Memory management is heel simpel: Gebruik geen* malloc en new. Probleem opgelost :).

* Uiteraard is het soms heel nuttig om wel te gebruiken, maar dat moet de uitzondering zijn.

Door Tweakers user Anteros, dinsdag 21 juli 2015 09:06

@Jogai: .txt en .cmake zijn de default extensies voor CMake. CMakeLists.txt is de standaard file-name voor CMake scripts en daar wil ik dus niet van afwijken. Voor wat betreft .md: Je bedoelt zeker de README.md? Die wordt standaard aangemaakt in Bitbucket.org (Online GIT repositories) en krijgt dan de .md extensie. Ik zal die tzt veranderen in .txt (heeft gen prio :) )

@Sissors: mbed ziet er interessant uit en ik zal het in de gaten houden. Vooral de aankomende versie 3 lijkt een stuk volwassener te worden. Voor nu zie ik er voor mijn project geen duidelijke voordelen in, maar wie weet kan dat nog ooit veranderen.

'Malloc'/'new'/'delete' kunnen inderdaad voor veel ellende zorgen, vooral over tijd. Wat je ook wel ziet is dat 'malloc'/'new'/'delete' alleen gebruikt worden tijdens de initialisatie van de software. Na deze initialisatie/opstart-fase 'mag' je ze dan niet meer gebruiken. Het dynamisch geheugengebruik blijft dan binnen de perken waardoor de kans op memory-leaks verkleind wordt.

Door Tweakers user Sissors, dinsdag 21 juli 2015 18:46

Ik denk ook niet dat mbed direct voor jou heel interessant is. Al kan het denk ik ook ervaren embedded programmeurs tijd kan besparen als het niet een heel kritisch ontwerp (qua snelheid/opslag). Maar primair zou ik zeggen dat het voor de minder ervaren/onervaren een prima punt is om te beginnen. Je kan snel zaken maken, maar je kan ook probleemloos bij kritische componenten wel direct de registers aanspreken om het efficienter te doen.

Daarnaast heb je gratis de volledige Keil compiler, die volgens mij toch een stukje beter is dan een GCC bijvoorbeeld.

Reageren is niet meer mogelijk