Recently, on the Gitter Ada Programming Language lobby, there have been discussions on cross-compiling for Cortex M micro-controllers, specifically the BBC micro:bit, using the Raspberry Pi.
TL;DR: it's not going to be easy, unfortunately. Maybe later (or earlier?) Raspbian releases will help.
The Pi uses an ARM processor, and its native compiler is configured for ARM (its target is arm-linux-gnueabihf
- the hf
means ‘hard floating-point support’). The GCC-8 compiler is capable of generating code for many ARM CPUs: gcc -mcpu=x
generates
arm2 arm250 arm3 arm6 arm60 arm600 arm610 arm620 arm7 arm7d arm7di arm70 arm700 arm700i arm710 arm720 arm710c arm7100 arm7500 arm7500fe arm7m arm7dm arm7dmi arm8 arm810 strongarm strongarm110 strongarm1100 strongarm1110 fa526 fa626 arm7tdmi arm7tdmi-s arm710t arm720t arm740t arm9 arm9tdmi arm920 arm920t arm922t arm940t ep9312 arm10tdmi arm1020t arm9e arm946e-s arm966e-s arm968e-s arm10e arm1020e arm1022e xscale iwmmxt iwmmxt2 fa606te fa626te fmp626 fa726te arm926ej-s arm1026ej-s arm1136j-s arm1136jf-s arm1176jz-s arm1176jzf-s mpcorenovfp mpcore arm1156t2-s arm1156t2f-s cortex-m1 cortex-m0 cortex-m0plus cortex-m1.small-multiply cortex-m0.small-multiply cortex-m0plus.small-multiply generic-armv7-a cortex-a5 cortex-a7 cortex-a8 cortex-a9 cortex-a12 cortex-a15 cortex-a17 cortex-r4 cortex-r4f cortex-r5 cortex-r7 cortex-r8 cortex-m7 cortex-m4 cortex-m3 marvell-pj4 cortex-a15.cortex-a7 cortex-a17.cortex-a7 cortex-a32 cortex-a35 cortex-a53 cortex-a57 cortex-a72 cortex-a73 exynos-m1 xgene1 cortex-a57.cortex-a53 cortex-a72.cortex-a53 cortex-a73.cortex-a35 cortex-a73.cortex-a53 cortex-a55 cortex-a75 cortex-a75.cortex-a55 cortex-m23 cortex-m33 cortex-r52
which certainly includes the cortex-m0
in the micro:bit (version 1; cortex-m4
in version 2 boards).
As an introduction to this, consider Inspirel’s Ada and SPARK on ARM Cortex-M on-line tutorial, the executable commands in which are written to be run on a Pi (for example, in AdaCore and FSF compilers, the ARM cross-compiler is called arm-eabi-gcc
, whereas on the Pi it’s just gcc
).
The sources, slightly adapted from Chapter 3, are (spec):
package Program is procedure Run with Export, Convention => C, External_Name => "run"; end Program;
{body):
package body Program is procedure Run is begin loop null; end loop; end Run; end Program;
(loader script):
OUTPUT_FORMAT("elf32-littlearm") OUTPUT_ARCH(arm) MEMORY { flash (RX) : ORIGIN = 0x00000000, LENGTH = 256K sram (RWX) : ORIGIN = 0x20000000, LENGTH = 16K } SECTIONS { .vectors : { LONG(_estack) LONG(run + 1) FILL(0) } > flash .text : { . = ALIGN(512); *(.text) } > flash PROVIDE(_estack = ORIGIN(sram) + LENGTH(sram)); }
which built as
$ gcc -c -mcpu=cortex-m0 -mthumb -mfloat-abi=soft program.adb $ nm program.o U __aeabi_unwind_cpp_pr0 00000000 D program_E 00000000 T run $ ld -T flash.ld -o program.elf program.o ld: program.o:(.ARM.exidx+0x0): undefined reference to `__aeabi_unwind_cpp_pr0'
Hmm.
After considerable research, I find that I can get the same result on the Mac with GCC 10, but only by specifying -funwind-tables
.
You’d think that I could suppress this using -fno-unwind-tables
, and get back to the Inspirel results, but it turns out that on this Raspbian (6.3 desktop, GCC 8.3 compiler) gnat1
(the actual compiler) inserts the unwind tables regardless of the setting of the switch! cc1
, the C compiler, behaves as you would expect.
As a more challenging project, I tried working with Cortex GNAT RTS. To use this, you also need FreeRTOS, at version 10.0.1 (that will unpack to FreeRTOS-Kernel-10.0.1
; if you do that in your home directory, you’ll either have to change the directory name to FreeRTOSv10.0.1
, set the environment variable FREERTOS_RELEASE
to FreeRTOS-Kernel-10.0.1
, or edit cortex-gnat-rts/FreeRTOS.gpr
to suit).
Starting in the cortex-gnat-rts/microbit
directory, to build the RTS, make
fails with
$ make gprbuild -p -P build_runtime.gpr gprconfig: can't find a toolchain for the following configuration: gprconfig: language 'c', target 'arm-eabi', default runtime gprconfig: can't find a toolchain for the following configuration: gprconfig: language 'ada', target 'arm-eabi', runtime '/home/pi/cortex-gnat-rts/microbit' Ambiguous variable substitution, need to specify the language (in TARGET) Invalid setup of the gprconfig knowledge base GNAT-TEMP-000001.TMP:1:01: "project" expected gprbuild: processing of configuration project "/tmp/GNAT-TEMP-000001.TMP" failed make: *** [Makefile:22: all] Error 4
The problem here is that build-runtime.gpr
specifies
for Target use "arm-eabi";
which gprbuild
(actually, gprconfig
) uses to decide which compiler to use; it does this (see $prefix/share/gprconfig/compilers.xml
) by looking for *-gnatls
or just gnatls
, and then in that executable’s tree for Ada RTS files under lib/gcc/$TARGET/$gcc_version
.
On the Pi (with GNAT installed), there is indeed a suitable compiler, with target arm-linux-gnueabihf
.
You might think, why would I specify that target when I’m compiling for a bare board with a cortex-m0
processor? The answer is, that left to its own devices, the compiler generates code for the Pi’s own CPU, but that doesn’t prevent it generating code for other CPUs on request.
We could say gprbuild -P build_runtime.gpr --target=arm-linux-gnueabihf
, which picks up the actual target CPU switches from this in runtime.xml
:
package Compiler is Common_Required_Switches := ("-mlittle-endian", "-msoft-float", "-mcpu=cortex-m0", "-mthumb");
though an alternative is to define TARGET
on the make
command line:
$ make TARGET=arm-linux-gnueabihf gprbuild -p -P build_runtime.gpr Compile [C] port.c In file included from /usr/include/features.h:448, from /usr/include/arm-linux-gnueabihf/bits/libc-header-start.h:33, from /usr/include/stdint.h:26, from /usr/lib/gcc/arm-linux-gnueabihf/8/include/stdint.h:9, from /home/pi/FreeRTOSv10.0.1/FreeRTOS/Source/include/FreeRTOS.h:49, from /home/pi/FreeRTOSv10.0.1/FreeRTOS/Source/portable/GCC/ARM_CM0/port.c:33: /usr/include/arm-linux-gnueabihf/gnu/stubs.h:7:11: fatal error: gnu/stubs-soft.h: No such file or directory # include <gnu/stubs-soft.h> ^~~~~~~~~~~~~~~~~~ compilation terminated. gprbuild: *** compilation phase failed
Oh dear.
The Pi CPU has hard floating-point support, so the system includes have no need to supply the soft support.
Worse, these are the includes for the Pi! We need bare-metal includes, as you would get with Newlib.
Anyway, to cut a long story short(er), I tried the same with Cortex GNAT RTS’s STM34 runtime, and found that, although an executable was generated, it didn’t run, because it included Pi-related code.
No comments:
Post a Comment