This is something I've talked about with Giovanni almost one year ago, and finally me and TexZK put together something working (pretty stable now).
I'll try to summarize the idea and the implementation in this post.
Basically, we needed some sort of bootloader, but with a different approach: instead of a small, non-ChibiOS, firmware which writes the whole application (ChibiOS + user code), we want a "static", complete, OS flashed in memory while users can dynamically load applications (aka ChibiOS threads) at runtime.
In this way:
- the bootloader can rely on ChibiOS low level drivers for the communication peripheral used to upload the application
- the flash memory used by the bootloader and by ChibiOS is not "wasted", as the application calls ChibiOS functions from there
- the OS could be "ROMmed", to have a static, safe, OS which could maybe enable code certifications etc etc
Basically, I've explored three ways to accomplish this:
- "partial linking": a complete OS is flashed, while user applications are linked (on the PC) to the corresponding ch.elf file (using gcc --just-symbols option). The application code also needs to be relocated at linking time
- Position Independent Code: as with "partial linking", the application is linked to the ELF file corresponding to the already OS flashed into the microcontroller, but being PIC it does not need to be relocated. On ARM, if I've understood correctly, we need to compile everything with -mlong-calls as function addresses are relative by default (this introduces a little overhead).
- Runtime linking: this is the approach followed by Contiki, where linking is actually done on the microcontroller. The static OS then needs to hold the symbols table, which can be pretty huge (in my tests, I had more than 8kb of symbols table, with something like 400 entries). Moreover, standard ELF format has a lot of additional informations, os Contiki uses a "compact ELF" (CELF) format, which is not standard.
We excluded the Runtime relocation approach as the symbol table can grow too much and we want rely on standard ELF tools.
The PIC option, besides from the overhead of direct calls, would enable simple loading of user code, which is simply linked to ch.elf without any relocation needed. But there is the Global Offset Table (GOT), which is needed for everything does not go on the stack, that i really did not understood how works and I think it needs support by the OS.
We finally decided to go with the "partial linking" method, which is simpler and does not have any overhead.
The process is like this:
- the user compiles and flashes ChibiOS, with the app loader, on the microcontroller and keeps the corresponding ELF file
- additional applications are compiled as usual on the PC, but not linked for now
- a simple python script (using ELFtools, so I guess the same can be done with standard elf commands and some Makefile magic) extracts the size of .text, .data and .bss sections and asks the microcontroller where these section will be located on the flash/RAM (.text goest o flash, starting from the end of the static OS, while memory needed by .data and .bss is allocated on the heap)
- the application is then linked with --just-symbols=ch.elf, so the linker extracts symbols from the OS ELF file, and with --section-start to relocate everything to the correct addresses
- the corresponding HEX is flashed into the microcontroller
- finally, .data and .bss sections are initialized and the application is started (we extract the address of the "entry point" thread function and create the thread)
A part of the flash memory is reserved to hold the information about flashed applications (application name, sections size and offset and application entry point), so application can be started also after a reset.
The loader uses a BaseChannel to communicate, so it should be simple to support different communication peripherals (we currently use UART).
The only modification we applied to ChibiOS is to add Trim and Grow functions to memcore: the app loader reserves memory to the applications starting from RAM end, so we "trim" the *endmem pointer in memcore to avoid future allocations to overlap application memory.
The current implementation is on github: https://github.com/tinito/Bootloader
We are using the flash helper functions by mabl, which are for the STM32F1, so only that family is supported up to now (the F4 has a complitely different memory organization). To abstract it, we need to write a complete Flash memory driver, which is currently only planned.
To test it:
- patch ChibiOS with the provided memcore.c/.h files
- compile and flash ChibiOS as usual, enabling all the features which could be needed by the applications
- compile the example applications (in the apps/ folder) running "make"
- load them running "make load" (you may need to specify the correct serial port in the load.py script)
Everything looks pretty stable now, and any comment is appriciated. We also would like to test the PIC approach, but I'm not sure about what we need to add to ChibiOS to support the GIT.. any idea?
Dynamic application (aka threads) loading
- Giovanni
- Site Admin
- Posts: 14893
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 997 times
Re: Dynamic application (aka threads) loading
Great, so you went ahead with this idea, I was considering this "resident OS" idea only for ChibiOS 3.0.
I am impressed with all the analysis on the organization and tools you did.
Giovanni
I am impressed with all the analysis on the organization and tools you did.
Giovanni
Re: Dynamic application (aka threads) loading
I assume that I barely know nothing of this stuff compared to you two guys. However, it looks very well done. I like to see the effort you put into this
When I get some spare time, I might try it out.
This was always an interesting topic for myself as well since I always wanted to create desktop-like applications with ChibiOS/GFX.
~ Tectu
This was always an interesting topic for myself as well since I always wanted to create desktop-like applications with ChibiOS/GFX.
~ Tectu