Hacker News new | past | comments | ask | show | jobs | submit login
Running Julia bare-metal on an Arduino (seelengrab.github.io)
195 points by cbkeller 10 months ago | hide | past | favorite | 26 comments

This is really impressive, and a great write up. I’ve been following the work on static compiling Julia to x86 libraries from the GPUCompiler.jl folks but I didn’t expect to see Julia working on an Arduino any time soon. With some kind of basic GC support (even if just using a bump allocator) it seems like a good fraction of the language could actually be available. Most tight loops hopefully don’t allocate so it would mostly just be necessary for creating initial arrays and mutable structs.

Haha, thank you!

Yeah, this was mostly an experiment to see what kinds of problems I run into - I didn't expect this to actually work out! It's still very finicky, relying on LLVM to remove dead runtime calls and so on, but non-allocating stuff with immutables should work, though I haven't actually tested it.

This is very promising for embedded control applications. Bootstrapping this with a simple allocator and even something as basic as FreeRTOS [1] combined with CBinding.jl [2] for portability would be game-changing for many applications, and directly compete with e.g. MathWorks' Arduino Simulink target [3]. ESP8266/32 next, once LLVM support [4] lands?

[1]: https://microcontrollerslab.com/use-freertos-arduino/

[2]: https://github.com/analytech-solutions/CBinding.jl

[3]: https://www.mathworks.com/help/supportpkg/arduino/run-on-tar...

[4]: https://github.com/espressif/llvm-project

I didn't know about FreeRTOS, interesting!

I don't think CBinding.jl is an option, since the whole point of the arduino is that the julia runtime can't run there (and doesn't fit in the first place - there's not enough RAM and swapping to the SD seems bad). Linking with C created .o files should work though?

I don't own any ESP32, but if that happens, yes I'd very much like to try!

Would you accept an ESP32 for your edification? No strings attached. Email me

I think CBinding.jl could be helpful if one wanted to write a target-specific shim library in C and link against it. I would expect that some of the glue is going to be easier to write in C than in Julia, and you can play some tricks with macros to create bare modules [1] within some kind of `@arduino_setup`/`@arduino_loop`-style blocks.

Edit: By the way, those target-specific shims might already exist as part of the Arduino IDE. PlatformIO [2] is also another possible collider of these types of platform-specific glues.

[1]: https://docs.julialang.org/en/v1/manual/modules/#Default-top...

[2]: https://docs.platformio.org/en/latest/boards/index.html

That'd be awesome, sure! I'll shoot you an email.

PlatformIO is nice, yes - I've already been suggested linking/using that, but I haven't looked into it yet. I also haven't worked with that before, so it'd be completely new territory for me.

Hey! Would you be interested in collaborating on this? I have a wide selection of hardware I'd be happy to contribute as well. You can reach me at my username at gmail dot com.

Took a while, but email is sent - thanks again, and I hope shipping will work itself out!

ESP32 should be quite capable, it is much better than many MS-DOS PCs.

I use Julia as my main programming language and I learned so much from reading this. I never knew Julia had Base.llvmcall, good to know!

Just goes to show how important it can be to read code in completely different fields than the one you're working in. Some problems are common for others so they've already been solved. Knowing that a solution exists is half the battle.

Ah yeah, `llvmcall` is also part of the secret to the performance of LoopVectorization.jl / VectorizationBase.jl AFAIU.

Conveniently, it's also pretty easy to check how any LLVM IR you've added manually with `llvmcall` is fitting in with the Julia-generated IR around it using `@code_llvm`.

LLVM is one of those truly awesome projects. LLVM gets a new backend and then for the most part many of the languages are able to generate code for it.

Want to see Rust on that new m68k backend or 6502 backend mostly out of morbid curiosity.

While great, it is following the path others have trailed before.


One example among many others.

Off topic but related: I played around with getting Nim running on an Arduino.

1. I found a critical bug almost immediately, but Andreas fixed it within 24 hours, and was super helpful.

2. Once the issue was resolved, it was surprisingly straight forward to get it running. I started by wrapping the arduino libs, and quickly shifted to wrapping the AVR libraries directly.

One take away was: Nim (and Julia) are likely really nice languages to run in these sort of low-level environments.

Another take away: there's an opportunity to develop a low-level C-based library to better support efforts like this. Both AVR and Arduino libraries weren't designed to be wrapped in something like Nim. And the Arduino codebase could use a healthy refactoring.

Nim also has some pretty good ESP32 bindings: https://github.com/elcritch/nesper

> And the Arduino codebase could use a healthy refactoring.

So could Nim's stdlib and compiler backend, but the leaders have no intention of doing that, and never have, which is why there is a hard fork.

Lies, Nim 2 is in development.

Cool! How big were the emitted nim binaries?

Not to bad! I don't have a detailed analysis, but they were small. Maybe a few extra K over straight C code.

The default arduino toolchain, which is a g++ wrapper, supports C++, even though the whole system only has only 1 kilobyte of RAM.

The C++ 'new' operator does seem to be supported, so I assume it has some kind of heap allocator - although I imagine that when you've only got 1 kilobyte to play with it gets quickly eaten by the allocators datastructures and C++ vtable pointers, not to mention the impact of fragmentation if you allocate any objects more than a few tens of bytes.

With placement new you don't need an heap, and using C++ doesn't require to use classes, there is still so much improvements over bare bones C.

In any case, when all we have is 1 KB, the real answer is Assembly.

I'm not as familiar with how C++ does allocations compared to julia, but I'd imagine the limitations would be similar - if `new` can allocate memory on the stack, I can't imagine why it wouldn't work out of the box.

C++ really isn't my forte though.

With placement new you can allocate wherever you want, as the memory region is one of the parameters.

You can also overload it, so that it can be given as implicit parameter.

Modern C++ compilers are also able to do escape analysis and remove new altogether,


If you try AVR instead of X64, the optimization will be missing, which is only a side effect of the backend not having as much attention in what it looks into.

If you comment out the delete[] line, it returns the same output. Does that mean leaking memory is undefined behaviour?

Leaking memory is always undefined behaviour regardless of the language when talking about manual memory management.

What guarantees can you assure about the state of the application?

In any case, in this specific example for x64 it doesn't matter, because new gets optimized away.

However, if that optimization isn't done, e.g. AVR backend, then nasal daemons will be summoned.

It's kind of a silly exercise anyway nowadays when, for example, a Raspberry Pi Pico is $4 and has 256K RAM. And there many other nice 32 bit boards.

Applications are open for YC Summer 2023

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact