Files
atomthreads/ports/cortex-m/README.md
Tido Klaassen ca81d186a7 Added support for Cortex-M0
Added board nucleo-f072rb
2015-07-11 20:08:50 +02:00

5.2 KiB

ARM Cortex-M Port

Summary and Prerequisites

This port should run on any Cortex-M0/3/4/4F (M0+ not tested). It uses RedHat's newlib and [libopencm3] (https://github.com/libopencm3/libopencm3). You will also need an ARM compiler toolchain. If you want to flash or debug your target, openocd is also a must. For STM32 targets stlink might also be helpful.

On Debian systems, compiler, newlib and openocd can easily be installed by the package manager (untested, not going to set up a new system just to test this):

apt-get install gcc-arm-none-eabi binutils-arm-none-eabi
apt-get install libnewlib-arm-none-eabi libnewlib-dev
apt-get install openocd

Code Layout

The "classic" port components (code needed for task setup and context switching and the atomport{-private}.h headers) are residing in the top level port directory.

There are additional subdirectories:

  • boards contains subdirectories for specific hardware. Each board needs at least a Makefile, which defines certain variables describing the hardware used, as well as a list of extra object files needed. There will usually be at least a board_setup.c, which contains code to initialise the hardware properly (clocks, uart, systick timer)

  • common contains code needed by multiple boards, such as stub functions for newlib or routines shared by multiple boards using the same family of target MCUs.

  • linker contains linker script fragments for specific target MCUs. These just define the memory areas (RAM, Flash) of the chip and include libopencm3's main linker script for the appropriate chip family

  • libopencm3 unless you compile and link against an external installation of libopencm3, this will be a Git submodule containing, surprise!, libopencm3

  • build this is a temporary directory where all object files end up in and which will be removed by make clean

Build Preparation

Unless you decide to use an external installation of libopencm3, you will have to set up the libopencm3 sub-module:

git submodule add https://github.com/libopencm3/libopencm3.git
git submodule init
git submodule update

Optional: As of 2015-07-08 the libopencm3 API has not been declared stable. If future changes break the build, you can check out the older revision used while developing this port:

cd libopencm3
git checkout a4bb8f7e240c9f238384cf86d009002ba42a25ed

Building and Flashing

To build the test suite, run

make BOARD=*yourboard* all

where yourboard is the name of a directory in boards. If no BOARD is given, it defaults to nucleo-f103rb.

To build with an external libopencm3, run

make BOARD=*yourboard* OPENCM3_DIR=*/path/to/libopencm3* all

If your board Makefile also set up the necessary openocd variables, you can use it to flash the application image:

make BOARD=*yourboard* BINARY=*yourimage* flash

N.B.: with the ek-lm4f120xl board openocd will report an error after flashing. I think it is because it can not handle the changed core clock after the application starts, but I simply can't be bothered to further investigate this. The application gets flashed and you can ignore the error.

Adding New Boards

TODO

Port Internals

The Cortex-M port is different from the other architectures insofar as it makes use of two particular features. First, it uses separate stacks for thread and exception context. When the core enters exception mode, it first pushes xPSR, PC, LR, r0-r3 and r12 on the currently active stack (probably the thread stack in PSP) and then switches to the main stack stored in MSP. It also stores a special EXC_RETURN code in LR which, when loaded into the PC, will determine if on return program execution continues to use the MSP or switches over to the PSP and, on cores with an FPU, whether FPU registers need to be restored. The Cortex-M also implements a nested vectored interrupt controller (NVIC), which means that a running ISR may be preempted by an exception of higher priority.

The use of separate stacks for thread and exception context has the nice implication that you do not have to reserve space on every task's stack for possible use by ISRs. But it also means that it breaks atomthreads concept of simply swapping task stacks, regardless of if atomSched() was called from thread or interrupt context. We would have to implement different archContextSwitch() functions called from thread or exception context and also do messy stack manipulations depending on whether the task to be scheduled in was scheduled out in in the same context or not. Yuck! And don't get me started on nested exceptions calling atomIntExit()...

This is where the second feature comes handy, the PendSV mechanism. PendSV is an asynchronous exception with the lowest possible priority, which means that, when triggered, it will be called by the NVIC if there are no other exceptions pending or running. We use it by having archContextSwitch() set up a pointer to the TCB that should be scheduled in and then trigger the PendSv exception. As soon as program flow leaves the critical section or performs the outermost exception return, the pend_sv_handler() will be called and the thread context switch takes place.