Swift for bare-metal/RTOS based microcontroller

(Andy Lau) #1

Hi folks!

This is my first thread and sorry it seems a little long. I made a title for each part, you can pick what you are interested in : )

There are few discussion about using Swift on microcontrollers. Maybe most of you guys here are pure software engineers. I’m an electronic engineer, I’m eager to use such a modern language in my work(I mean bare-metal/RTOS based microcontroller, nothing to do with Raspberry Pi, that's a totally different thing). Just image, you can truly leave the Apple system and using Swift language for IOT hardware, like arduino, mbed or MicroPython do, but in a more modern language. That's what I plan to do.

In the past half year, I was trying really hard to make this dream come true, below is all the related information.

Hardware and traditional toolchain:

To achieve my purpose, I chose the ARM Cortex-M7 chip for my experiment. The performance of the chip is excellent. The LLVM backend has good support for it. And I can add extra ROM and SDRAM to my hardware if
Swift consumes too much sources. Once the experiment succeed, I can manage to produce a bunch of them in a short time. I use Arm Gcc toolchina https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads for their headers, libc, libm, libgcc, libstdc++ etc. It matchs the ARM chip very well.

Modifying to the compiler(Swift 4.2 right now):

Thanks to the talented structure of LLVM. It's not hard to add my own BareMetal toolchina triple(thumbv7em--none-eabi for now) to the Swift Compiler. I need to thanks @Brian_Gesiak here. His blog modocache.io gave me a deep insight to the swift compiler! I also add "-function-sections" and "-fdata-sections" to the compiler which is very important to reduce the final size of a static linked object file.

Bare-metal or RTOS:

Refer to this project: https://github.com/spevans/swift-project1 (Thanks to @spevans). It is a truly bare-metal swift program on the x86 machine. It’s really an awesome project. Simon Evans implemented all the lower stuff he need to run his swift program(a swift kernel) in bare-metal. Most of the lower stuff are related to memory management which the runtime relied on. In my practice, I want to do this easier. I choose a RTOS called Zephyr to be my lower kernel, so I don’t need to care about memory management. In addition, this Zephyr is POSIX compatible, so it's easier to port thread related stuff later.

Cross compiling the stdlib:

After I add my own triple to the compiler, it's very smooth to compile the whole stdlib.

Cross compiling the Runtime:

The clang already support triple thumbv7em--none-eabi, so it's not hard to compile most of the runtime files(Just did some miner change for the incompatible places). At first, I thought all the *.mm files in the runtime and stubs directory are Objective-C related. Since I won't use OC at all, I excluded them when I compiling. But then I found even with my pure easy swift program, it might emit calls to the functions in those .mm file. I don't know why. So here comes the question:

1. When compiling the Runtime and stubs files, can I exclude all the *.mm file in the runtime and stubs directory if I only use pure swift on my hardware? If so, how can I prohibit the call to those function in the *.mm files?

The biggest problem now, libicu, UNICODE stuff:

When compiling the stubs/UnicodeNormalization.cpp, I found it is heavily depend on the libicu, which is another huge project http://site.icu-project.org/download. After some research, seems no one tried to cross compile the libicu to a bare-metal microcontroller, it is heavily relied on system call itself. At first, I thought this might affect the swift String type a lot, if it's impossible to cross compile the libicu. I may just have to implement my own C/C++ like String type. But I was wrong, all the swift stdlib are heavily relied on libicu. It's so frustrating. Many basic type such as Dictionary would emit runtime call to the libicu : (

I don’t know shall I implement my whole microstdlib?(like @carlos4242 is doing) First, that's a huge of work. Second, I want my porting remain the same as the official Swift. Here comes my another question.

2. Do you guys have any good idea on how can I handle the libicu stuff?

Any related information would be helpful. Thanks very much!

(Lars Sonchocky-Helldorf) #2

Hi @madandylau, have you seen these posts on the topic:


(Andy Lau) #3

Hi @IOOI, thanks for your kind reply!

I’ve noticed those topics before. @JetForMe seemed very interested in this topic also. I’ll say hi here : )

What they were discussing is how to reduce the features of Swift language so it can fit the microcontroller. What I am trying to do is keeping the language features as much as possible. So I cannot avoid the swift runtime(written in C++) and stdlib(written in swift). These two parts constitute libswiftCore.

The background of how a swift application runs
@spevans gave an explanation here https://github.com/spevans/swift-project1/blob/master/doc/kstdlib.md about his swift kernel on a bare-metal x86 machine. Normally, the libswiftCore need support from libc++, libc, libicu, and some OS functions. He implemented most things in his tiny klibc library. It’s just like a tiny OS + libc + libc++. And he stubbed out many features he doesn't need, such as libicu support and thread support.

What I’m trying to do is similar. But I don’t need to implement everything myself. There are already good choices. The Arm GCC toolchain has provided libc and libstdc++. The Zephyr RTOS has provided memory management which is needed by swift runtime.


Why not Raspberry Pi
Maybe you will ask why I don’t just use Raspberry Pi, they already support Swift. I think any electronic engineer could tell you the answer. It’s too complicated on both hardware and the Linux OS. Too much things are out of control to make a stable electronic product. Especially for the Linux OS, in contrast to the RTOS, you can even implement you own in less than 1000 line of codes. Even I choose the Zephyr RTOS as my kernel, it’s not particularly more complicated than bare-metal.

The difficulties I've met
Since I had already added the triple thumbv7em—none-eabi to my swift compiler. It’s no problem to emit object file which can run on the cortex-m7 chip directly. The RTOS just provide very fundamental functions to the upper layer. What I’m struggling with is the different library dependency and compatibility problem. It's really a headache. If I want to support most of the function in stdlib, I think I need some way to provide the libicu functions. That's how most of stdlib types depend on(String type for example).

If someone here can give me some hint, I'll appreciate that a lot. Thanks you guys!