Objective-C on Embedded MCU

In these days, I spend time pretty much for Objective-C; not for iOS nor OSX, but embedded.

Oracle is pushing hard their Java to embedded these days (This is not the first time though.), Arduino; which uses “Java-like” language; keeps going well, and another platform called mbed is introducing C++ based high-layer framework. JavaScript board is actually exist (Tessel) as well!

…Why not Objective-C?

I guess people believe that: ObjC is Apple proprietary language and can’t compile with any other than Apple’s devices which is not correct.
Objective-C actually consists of “Pure C + ObjC runtime”, and there is several runtimes are exist.
“libobjc” is one of the famous implementation included in gcc, “GNUStep” is another popular runtime, and Apple’s one.
These 3 are long-time de-facto standard for ObjC, but I want to introduce another newer implmentation: ObjFW

ObjFW is developed by Jonathan Schleifer, it is small, modern ObjC, and notably portable. ObjFW is not only a runtime, but also a library as well. That means, if you write some code top on the ObjFW library, you could have it runnable both ObjFW runtime and Apple’s runtime. So it pretty much “portable”. Another good thing is that is lightweight. He has done running his runtime on small gaming devices includes Wii, PSP, and NintendoDS.

Anyway, I like ObjFW, and I decided to use ObjFW for embedded device. Using ObjC in embedded device has many advantages.

  1. You can use “Pure C” anytime anywhere.
  2. It also means that you can use assembly anytime.
  3. Powerful object-oriented language, but still faster than Java.

The only disadvantage that I think is large memory footprint. Even if use ObjFW it would consume 600KB+ at least. That means you cannot run without external RAM. (Embedded Java is better in that issue.)
Because of that, you gonna need some bootloader for program/execute code with external RAM. So that would be another disadvantage in contrast to C/C++ execution from flash. (And most of the MCU is pretty much optimized performance for flash execution. RAM execution is usually slower than flash execution actually.)

A week ago, ObjFW was not supported (not tested) on Embedded MCU. I tried to compile it for ARM Cortex-M4F and it was success. (I did some minor hacks to work, and this is my forked repository: https://github.com/FantomJAC/objfw/tree/cortex-m4f)
I’ve contacted Jonathan about that I’ve done build for Cortex-M4, and he put that in his blog! Thanks!
Then we discussed a lot and finally ObjFW supports Cortex-M4 officially.
Now everyone can try ObjFW on your Cortex-M4 board using his master branch!

Due to lack of documents about how to build & execute, I put some tutorial here.

First, you need Clang/LLVM 3.5. Actually, 3.5 is still in development but you can build from trunk.
Then you need GCC ARM Embedded. This is an prebuilt gcc package maintained by ARM.

Next, you need to do some workaround with gcc. Reason here is that Clang is still immature with cross-compilation unfortunately.

1. Make a symbolic link for clang

cd /path_to_toolchain
ln -s arm-none-eabi usr

2. Put a bash script called arm-none-eabi-clang in /path_to_toolchain/bin
This script adds ARM cross-compile options to clang forcibly. Do not put FPU options if you are using Cortex-M4 w/o FPU.

/path_to_llvm/bin/clang --target=arm-none-eabi \
-march=armv7e-m -mthumb -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \
-isysroot /path_to_toolchain $@

3. Rename arm-none-eabi-gcc to arm-none-eabi-gcc.real

cd /path_to_toolchain/bin
mv arm-none-eabi-gcc arm-none-eabi-gcc.real

4. Put a bash script called arm-none-eabi-gcc in /path_to_toolchain/bin
Ahh this is so hackish :( Clang passes gcc all options from command line. So gcc gets angry for those ObjC options which is not supported.

#!/bin/bash
for ARG in "$@"
do
if [[ $ARG != -fobjc* ]] && \
[[ $ARG != -fconstant-string-class* ]] && \
[[ $ARG != -fblocks ]] && \
[[ $ARG != -fno-constant-cfstrings* ]]
then
NEW_ARGS="$NEW_ARGS $ARG"
fi
done
arm-none-eabi-gcc --specs=rdimon.specs $NEW_ARGS

5. configure & make
The link option here is to satisfy autoconf’s linkage checks.

./autogen.sh
export PATH=$PATH:/path_to_toolchain/bin
export LDFLAGS=--specs=rdimon.specs
./configure --host=arm-none-eabi --disable-shared --disable-threads --disable-sockets --disable-files
make

OK, got errors? Yes, the final task should fail because there is no way to link ObjFW’s tests application.
To run the tests, you need to create a static library that contains whole tests code objects.

6. Linkage
So the link process is pretty much different between those IDE/SDKs. If you are using GCC ARM Embedded as well, you might add some linker script & init codes to ObjFW autoconf script. Or if you are an user of IAR, MDK…well it should be done in each individual IDE. The static library “libobjfw.a” is portable anyway.
I use GNU ARM Eclipse Plug-in with GCC ARM Embedded. Here is the tips for that.

  • Remove unused sections is better be unchecked
  • In Library->Libraries, put objfw, m.
  • User “ObjC” library should be added in Miscellaneous->Other linker flags as -Wl,–whole-archive -lmyapp -Wl,–no-whole-archive
  • Make sure the linker script for external RAM

My debugging hardware environment is Silabs EFM32WG-DK3850. It has 4MB PSRAM and 16MB Flash, so enough specs for ObjC development. I wrote PSRAM bootloader from scratch. There is no example how to boot from PSRAM, because of that, maybe I should open-source that later.