Rust for embedded development: Where we are and what's missing


#21

(post split in two because discourse won’t let me cc more than 10 people)

@awygle

Off the top of my head I suggest two interfaces for many peripherals

I also think that we’ll probably need more than one set of traits to accommodate
the different async models that exist out there. Trying to fit all of them in
one set of traits will probably converge into a blocking API.

@jcsoo

Wow, that’s some really neat stuff you are working on. I’m looking forward to
their public releases.

My first project has to do with a command-line tool to improve the build /
load / run workflow when interacting with most of the broadly available
embedded debuggers and bootloaders such as ST-Link, JLink, Arduino and Teensy

If I may ask: Is that an OpenOCD re-implementation or something that builds on
top of it?

My other project is a bit broader and has a bit further to go, but has to do
with the tooling and work needed to build and maintain HALs (including
peripheral drivers) within and across device families, with the goal being a
hierarchy of crates comparable to existing vendor-provided SDKs.

Is that something along the lines of PlatformIO or something different?

@bestouff

It depends on what’s their architecture. If it’s something that LLVM supports
then it’s doable in principle. I recall reading somewhere that you needed a
custom gcc toolchain to build code for them though, so no idea if LLVM supports
them.

@alevy

Thanks for sharing!

Nothing technically (we’re using Rust!!)

Excellent :+1:

but key priorities for us are:

Noted.

Stable Rust support

lang_items is a tough one, but you should be able to drop asm! if you move your
syscalls into external assembly files though that adds a dependency on an
external assembler and reduces performance due to less inlining.


#22

Assume that I don’t know what I’m talking about, but does smoltcp start to scratch your itch for the IP stack?


#23

Indeed! Thanks for the pointer. I’ll take a deeper look once I’ve bootstrapped to that level but that does seem like what I’m looking for at first blush.


#24

The biggest obstacle so far is that everything seems to use futures through much higher-level interfaces and wrappers like tokio and such. I understand the mechanics of futures; I worked extensively with an excellent futures library for OCaml called Lwt, and am very fond of it. What I don’t have a grasp on is the specifcs on writing futures-aware Rust code. Also, based on this person’s experience I’m not even sure if it’s possible to write futures code with the ergonomics that I desire.

The async/await capable rustc branch (see above) seems extremely promising to me, easily the rustc feature I would like to see most. So yes.

We do not have a pressing need to do that and in fact, conditional on the state of async/await, we may even deprecate libfringe entirely in favor of those. It still has a niche even with async/await of course, but it’s much smaller–with async/await libfringe should virtually never be used with std because there is no legal way to make it play nicely with TLS that works on Windows, and being at the bottom of a no-std I/O ecosystem while also mandating MPU and separate page-sized-plus stacks for every task do not sound compatible to me, I do not expect community buy-in. I would certainly be skeptical myself.

Anyway, I believe the AArch64 port in the master branch should be trivially portable to ARM/Thumb and may do so as time permits.


#25

The async/await capable rustc branch (see above) seems extremely promising to me

Oh, wow. That looks really promising.

Anyway, I believe the AArch64 port in the master branch should be trivially portable to ARM/Thumb and may do so as time permits.

Thanks. That would be really appreciated.


#26

@japaric Espressif is doing an LLVM backend internally. No ETAs I can tell.

@jcsoo Have you seen https://github.com/m-labs/smoltcp?


#27

I have worked on Embedded Project for both fun and profit. In fact a recent embedded project made me decide to use Rust for a different project.

I have been following the progress of Embedded Rust for a while now and can offer an intermediate level user’s perspective.

  • In general tooling needs to get better. While I don’t consider installing another tool to be a big issue, cross compiling can still get tricky with various crates (I am looking at you OpenSSL). I don’t really want to go through that pain again.
  • I would like to see some higher level abstractions. A lot of the work I have seen is low level so fair (in general). I know that if I wanted too, I could get started with that, but I don’t really want to invest the time dealing with timers and registers.
  • As mentioned in this post, Traits and crates for various peripherals. The trait system works really well for this as PWM, ADC, DAC, UART, SPI, I2C, Parallel LCD, Interrupts and Timers all have the same format, but with different implementations for each processor. Some work actually defining how these peripherals should work in a common crate would go really far in making it easier for my to get involved.
  • Further to that comment, the mBed API is generally a lot nicer to use than the Arduino one. I particularly like the timer interrupt section that makes it really easy to create timer based programs.

Hopefully that is useful. Once the lower level stuff is sorted out, higher level APIs like Ethernet, WiFi, CoAP, HTTP and ZigBee can be implemented.


#28

@tl8roy

Thanks for the input!

In general tooling needs to get better.

Could you elaborate on this point? The comment about OpenSSL sounds like this is outside of the microcontroller space; what this problem with embedded Linux (RPi)? Have you seen the cross tool? It helps with cross compilation of std programs to several targets.


#29

OpenSSL was more of a comparable situation rather than a specific issue with embedded. It is now working, but was a frustrating and time consuming operation.

My understanding that to target AVR is that to need to also setup emscript and all that stuff. Certainly not as easy as just setting rustup and using cargo.

One of the reasons that Arduino is good, is that the IDE and programmer is built in. The IDE itself isn’t crash hot, but the system as a whole is good. If future Rust IDE support had such provisions as well, that would be icing on the cake.


#30

@tl8roy

My understanding that to target AVR is that to need to also setup emscript and all that stuff. Certainly not as easy as just setting rustup and using cargo.

The AVR backend hasn’t been enabled in upstream Rust so you have to compile a custom toolchain that has AVR support enabled to target AVR microcontrollers. So, yes certainly not easy for now. Once AVR support lands in tree you’ll be able to use the toolchain you installed via rustup.


#31

Right now this is building on top of OpenOCD, JLink’s command line tools, Bossa and Teensy Loader CLI, but in all of these cases I am considering a native Rust implementation to get additional functionality and reduce the number of dependencies; STLink, DAPLink / CMSIS-DAP, and JLink protocols are reasonably well documented at the USB level. This would be very useful for doing MCU probing, emulating command line arguments + return codes, debugging (setting data watches and breakpoints at the command line), and SWO / JLink RTT.

I’m also keeping an eye on https://github.com/mbedmicro/FlashAlgo.

I’m not interested in reimplementing all of OpenOCD, but there’s a useful subset of functionality that would be nice to have - especially if we could reduce the number of non-Rust dependencies needed to start embedded development. Your work with alternative linkers is the most important piece of that, though I’ve experimented with adding rustup-like functionality for managing the arm-gcc toolchain installation.

I’ve purposely avoided looking too closely at PlatformIO so that I’m not overly influenced by their overall design, but in the last couple of weeks I’ve relaxed that rule a bit and I can see that there are parallels. A lot of what PlatformIO is doing is implementing a C / C++ packaging system, which Rust doesn’t need since we have Cargo. They are also pulling in existing C / C++ frameworks such as MBed and Arduino which don’t currently have parallels in Rust.

There is also a library of board and MCU descriptions, but I don’t know the details of how they are implementing that yet. For Rust, this information should be encapsulated and published in crates where possible. The challenging part is making it so that board and MCU crates can share common implementations and drivers down to the peripheral level.

libopencm3 has done some of this, but in a fairly limited fashion using lots of C preprocessor functionality.


#32

No, I have not, and that looks very interesting. It doesn’t have IPV6 support yet, but I will have to try it out.


#33

Do not let the lack of IPv6 dissuade you as I personally consider IPv4 legacy that ought to be long dead. The only reason it’s in smoltcp is we had to support existing deployments and we had to do it quick. Otherwise smoltcp would have probably been IPv6-only.


#34

How do you find these calls?


#35

Espressif is doing an LLVM backend internally. No ETAs I can tell.

I suspect its more Igor’s personal project, but he is too busy: https://www.esp32.com/viewtopic.php?t=499#p8756


#36

Japaric: a great many thanks for almost single-handedly driving the embedded-Rust revolution :heart:
I am eyeing this with interest.

My background/input:
I’m working with the Particle Photon microcontroller and Adafruit Particle Neopixel kit to create on-stage light effects for a musician friend of mine.
I’ve already had “play with Japaric’s Rust-on-Particle quickstart” on my private to-do list for a while, and would be grateful for pointers on how to get the NeoPixel libs working/ported/wrapped/re-implemented-in-Rust (whichever is easiest).

I’m getting the impression that the NeoPixels are singlehandedly driving quite a few people into the microcontroller space, because they want fancy kitchen lighting, art-projects, or whatever…
I can attest that making colourful lights is quite the motivator :wink: so having “Rust can drive NeoPixels” as a marketing-:ballot_box_with_check: would be pretty cool in my opinion.


#37

Hello

Thanks for your great work and congratulations.

Anyway, to somewhat answer the questions, the company I work in builds routers (as well as other things) and seeing how friendly Rust is compared with C, we decided to put Rust into production for one of the programs in there. It is questionable if what we build is still „embedded“ ‒ the things don’t have screen and run ARMv7 or PowerPC processors (depending on the model), but we have things like 2GB of RAM. Therefore, some of the embedded challenges don’t apply here (we have real linux kernel and mostly normal userspace, we can afford to allocate memory dynamically), while others do (we need to cross-compile).

Currently, there’s a proof of concept that Rust will work on the devices (it is possible to build binaries that actually run), but there’s still some clean up work and need to integrate into the whole build chain.

Anyway, to list the challenges:

  • As there are not compatible binary releases, building the std library took some experimenting. The Xargo tool is great and helps a lot (basically does all the work), but figuring how to use it wasn’t exactly easy ‒ there isn’t much documentation on how to configure which compiler to use, and so on.
  • Documentation on how to define own target triple is also a bit sparse.
  • The biggest current issue we face is that musl implies static linking. But we don’t have static musl available, we use dynamic linking. And trying to „cheat“ and claim it is glibc and liking against musl at the end didn’t work (there are some cfg attributes in the code to cope with the differences). I’ve glimpsed some later activity around this problem, but haven’t looked into it lately more, so this might have disappeared already.
  • For larger use (not just one of the programs there, but a lot of the codebase) we would need smaller binaries and hopefully dynamic linking to the standard library. Not to be able to switch it independently, but to ensure it is present only once on the device. While 256MB of flash memory is a lot in the router world, it’s not infinite and having several-MB binaries can be problematic if we want to use Rust for every second program in there.

#38

@SimonSapin

I just call arm-none-eabi-objdump -Cd on the binary and grep for function calls that have the name “panic” in them. Figuring out where they came from can be tricky due to inlining; sometimes sprinkling #[inline(never)] on functions can help to investigate.

I had a trick to ensure at link time that a program is free from calls to rust_begin_unwind but I think it doesn’t work anymore.

@juleskers

I got WS2812 LEDs working with Rust before. Making a blog post about that is in my TODO list. I haven’t checked the neopixel library but my approach uses PWM and DMA transfer to achieve low CPU usage.

@vorner

Thanks!

In general, I’m aware of the problems that the “router” space has, but solving their most pressing problems require sustantial work, e.g. a proper dynamic linking story for Rust, which is out of scope for my contract. However, I can work on smaller items like documentation.

As there are not compatible binary releases

Which targets are you using? I thought we had ARM and PowerPC relatively well covered. Are you using uclibc?

there isn’t much documentation on how to configure which compiler to use

Hmm, are you using a custom compiler?

Documentation on how to define own target triple is also a bit sparse.

Indeed. The best documentation is the compiler source code at the moment.

The biggest current issue we face is that musl implies static linking.

This is being worked on.

we would need smaller binaries and hopefully dynamic linking to the standard library.

You can get some dynamic linking using -C prefer-dynamic (i.e. cargo rustc -- -C prefer-dynamic) but this is not fine grained I believe; it will try to use dynamic linking for everything rather than for just std. Also, that produces dylibs which contain metadata which you probably don’t need for release. cdylibs make more sense for deploys as they don’t contain metadata but I haven’t played with them myself.


#39

Hi. I’ve done some embedded development in the past on Rabbit and PIC microcontrollers. Given my experience with their C compilers, I’m very, very excited about the potential for Rust in this space, and especially for pure Rust. In my opinion, one of the things that would help most right now is simply having a central place on the rust-lang.org website to find the basics for getting started, etc. Right now that information is scattered in blog posts, posts on /r/rust, and other places, and I actually learned a fair number of new things just from reading the “Current Status” part of your post. So just having something like that on a “Rust for Embedded Software” section of the rust-lang.org website (and keeping it reasonably up-to-date) would be really useful.

Thanks for working on this!


#40

In my case, it’s an ARMv4 (with no operating system).