The Embedded Working Group Newsletter - 2


#1

This is the second bi-weekly newsletter of the Embedded WG where we highlight new progress, celebrate cool projects, thank the community, and advertise projects that need help!

If you want to mention something in the next newsletter, make sure to leave a comment on the issue.

Highlights

Embedded Projects

If you have an embedded project or blog post you would like to have featured in the Embedded WG Newsletter, make sure to mention it on the tracking issue for the next newsletter, we would love to show it off!

embedded-hal drivers

This is a list of recently released drivers that are part of the Weekly Driver Initiative. There are currently 5 Released Drivers, 14 WIP Drivers, and lots of TODOs!

embedded-hal Board/Chip Support Crates

Thanks

  • Thanks to all of the Rust Team and Working Group members who took time at the All Hands to tackle some important Embedded Issues
  • Thanks to Alex Chrichton who pushed a fix to a linker issue mentioned in our last newsletter

Help Wanted

  • Simon Sapin posted some working code for the DS3234 SPI RTC, and is looking for someone to turn it into a maintained crate!

If you have an embedded project that could use contributors or maintainers, leave a comment for the next newsletter!

Special Feature: The Embedded WG at the 2018 Rust All Hands

This week 15 or so Rust teams/working groups met for the Rust All Hands event in Berlin. Some members of the embedded WG were present and we had a chance to talk to the compiler, core and infra teams.

These are the highlights of our talks.

Embedded Rust on stable

We had previously identified 3 unstable features / issues that tie embedded development to the nightly channel in https://github.com/japaric/stable-embedded-rust. We talked to the other Rust teams about the possibility of addressing these issues in time for the 2018 edition release and the conclusion was that they thought that the timeline was possible. These are the 3 unstable features we are referring to:

Unstable Feature #1: xargo

We’ll ship a rust-std component (pre-compiled core+compiler-builtins) for the thumb* and msp430 targets. This removes the need for xargo so people will be able to do something like the following to cross compile to ARM Cortex-M:

rustup target add thumb7m-none-eabi
cargo build --target thumbv7m-none-eabi

Tracking issue: rust-lang/rust#49382.

Unstable Feature #2: compiler-builtins

extern crate compiler_builtins is unstable to directly use. The fix we have decided on is to inject that as part of the prelude you get from #![no_std].

So, today #![no_std] expands to something like this:

#![no_std]
extern crate core;

With our change the expansion will run like this:

#![no_std]
extern crate core;

// but this doesn't #![feature(compiler_builtins_lib)]
extern crate compiler_builtins;

In the future we might want to merge compiler-builtins into core but that requires more effort and can still be done if we do the #![no_std] prelude approach right now.

Tracking issue: rust-lang/rust#49380

Unstable Feature #3: panic_fmt

There’s an accepted RFC (#2070) for a stable mechanism to select the behavior of panic! in no_std context, and there’s a know issue where the arguments of panic_fmt are kept in the binary even when they are unused by the panic_fmt implementation (cf. rust-lang/embedded-wg#41).

The main concern here was whether we’ll be able to fix the binary size problem with the accepted design or if we’ll need some new design. The compiler team thinks that this can be fixed with the existing design using MIR only rlibs but it’s unlikely this will get fixed in time for the edition release. nagisa will likely propose an alternate solution that involves having Cargo select the panic provider crate.

Non critical unstable features

There are some other unstable features that don’t prevent you from doing embedded development, however they come up often when doing no-std development. We had a chat with people on the compiler team about them.

Unstable Feature #4: const fn

This feature has been proposed for stabilization (cf. rust-lang/rust#24111).

Unstable Feature #5: asm!

Background: Some assembly operations can be implemented as external assembly files that are then called into using FFI; other ops though do need to be inlined into the function from which they are called to prevent losing semantics. Using external assembly file can be done on the stable channel. The second type of operation requires the unstable asm! macro.

The compiler team is not 100% sure on whether they want to stabilize inline assembly for the edition release. The embedded WG has proposed an alternative proposal: expose some assembly operations that need to be inlined as “Rust intrinsics” – in a similar fashion to how SIMD is being implemented; these intrinsics would be in the core::asm::$ARCH module and they could either be implemented by lowering to a LLVM intrinsics or using inline assembly. For example:

pub mod asm {
    pub mod arm {
        #[inline(always)]
        pub fn cpsid() {
            unsafe {
                asm!("cpsid i" ::: "memory" : "volatile");
            }
        }
    }
}

These would be stable APIs with an unstable implementation. If LLVM assembler syntax the implementations of these functions would have to be updated.

The embedded WG will submit an RFC proposing the asm module and that will include a list of assembly operations that (a) are common and (b) need to be inlined for different architectures.

Unstable Feature #6: #[used]

This experimental feature has been in the compiler for a while and it’s required in some scenarios when using LTO to prevent the compiler some function / static that needs to be in the final binary.

We’ll try to get it stabilized by the edition release but it’s not a high priority feature.

Stability of the Embedded Targets

We don’t only want to make embedded Rust possible on stable; we also want to make sure the embedded targets don’t regress. So we are going to add tests to rust-lang/rust CI to make sure regression block PRs from landing.

That effectively will make some of the embedded targets into the tier 1 platform. The core team is fine with adding the thumb* targets (ARM Cortex-M) to tier 1. Less maintained, still in development and not fully mature targets like AVR, MSP430 and RISCV will become tier 2 – they’ll be tested but won’t block PRs and rust-std binaries will be produced but it’s not guaranteed there will be binaries available for all nightly / beta / stable releases.

We’ll open an issue to discuss with the infra team the exact tests we want to add and track progress on that, but have already told them about the kind of tests we want to add and they thought those kind of tests are possible to implement. The kind of tests we discussed were:

  • It compiles and links
  • Runs the cross compiled binary in QEMU doesn’t crash and exits with exit code 0.
  • Tracking the binary size of a program compiled with -Os / -Oz and notify someone or fail the build if the size changes by +/- 5-10% or something.

LLVM backends that are not yet in rustc

There are two embedded LLVM backends that have not yet been enabled in rustc for different reasons: AVR and RISCV.

In the case of AVR the main reason is that some LLVM codegen bugs prevent you from building core for AVR. These bugs are related to 128-bit integers and formatting floats. The libs team discussed this some time ago and they decided they are fine with landing arch specific #[cfg] attributes to remove 128-bit integers and other things like float APIs.

In the case of RISCV the LLVM backend is currently under active development and our current version of LLVM doesn’t fully support RISCV. We would have to backport several patches to make RISCV work on our LLVM version. The core team feels OK with backporting those patches as long as they have already landed in upstream LLVM, and are not still under review.

Tooling

Getting tooling for e.g. binary inspection (e.g. objdump) can be hard on some platforms (e.g. Windows) specially for architecture which currently are not too widely used (e.g. RISCV). We can improve the situation here by shipping llvm tools with the Rust toolchain – with one set of those tools you can inspect all the architectures that Rust supports. These are the thoughts of the core / infra team regarding this:

  • We put these tools in the sysroot and have them always shipped with the Rust toolchain so no rustup component add required – this is already the case with lld. It might not be possible to provide these tools on all platforms but it’s very likely they’ll be available on tier 1 platforms.
  • We do not add these tools to the user $PATH. Instead some Cargo subcommand will be created by the embedded WG (e.g. cargo objdump) and that subcommand will look for llvm-objdump in the sysroot and do the right thing.
  • We make no guarantees about the semantics and interface of these tools being preserved over time (e.g. CLI changes, user facing output format changes, etc.).

The Embedded Rust Book

We decided on an initial audience for the embedded book; we will be targeting people that know some embedded stuff and some Rust. The main reason for this is that if someone knows one and not the other then they can go and read existing Rust documentation or the Discovery book and then read the embedded book. For more details check rust-lang-nursery/embedded-wg#56.


#2

Great news! Thanks for all the hard work. :smiley:

I noticed it was mentioned at the all-hands that there was some discussion of potentially using Generators for cooperative scheduling/async-style syntax on embedded devices. Did anything concrete come from that? Ideally I’d love to see the syntax not require any heap allocations but it may not be practical.


#3

This sounds amazing :smiley:

Writing a stable no_std rust application with custom allocator, custom panic and no dependencies to external code could than be possible in the near future? That would be so awesome!!


#4

Awesome report @japaric. So many good things happening. Keep it coming.


#5

I think @rnestler had some ideas on how embedded could work with async code. I’ll ping him.


#6

I agree that it’s a good idea to make Rust better for embedded/small systems. Such systems with limited resources must often be quite reliable, otherwise when they don’t work they often cause troubles to people and make our society and some infrastructures worse.

Such systems often require some bit twiddling (binary arithmetic code), often deal with values coming/going into sensors and actuators that have precise specs. The code should model those specs succinctly, reliably, and the code should be clear. Often the range of values is limited (having hundreds or thousands of value is often more common than working with whole u64 ranges. Sometimes you have have to handle even less different values, and a subgroup of them could have semantic meaning while values in a range could just encode a number), memory is short or should be used with parsimony, stack-allocation of small amounts of data is common and it should be handy, widely supported, and as reliable as possible. Sometimes it’s even useful to prove the correctness of some critical routines. Sometimes data is tainted and the compiler should help handle such attributes mechanically to avoid programmer mistakes, and sometimes you need to clear memory reliably (the compiler should not remove some memory zeroing if they contain sensitive data). Often you want to index arrays and slices with indexes that have less than 32/64 bits, yet you want to avoid “as” casts as much as possible because they are bug-prone. So I think it’s a good idea to take a bit wider vision of this field and the features it could use. The C language isn’t the only language to take a look at. This post is not meant to cover everything that could be useful, but it’s a start.

So let’s expand some of those points:

Bit twiddling is common in embedded code and low-level code, Rust type system should repel the C tradition of weak typing and help the programmer with some strong typing. This is an example, “Bit Level Types for High Level Reasoning”, by Ranjit Jhala:

Often the range of values is limited. Rust type system should offer Ada-style ranged values, this is Ada code:

type Serial_Number is range 0 .. 999_999 with Size => 24;

(In that Ada code “Size” is the optional number of bits used to represent it). This should not go in a crate, ranged values is a basic feature that all Rust programmers should be encouraged to use. Rust type system should catch mistakes and help the programmer fix the code. And the compiler should discard as many as possible range tests at compile-time. And the compiler should handle programs that use a large number of ranges efficiently. You can use sub-ranges also to introduce safer casts.

Subranges of tag-only enums is equally important. Sometimes you want to give a name to only a subgroup of values and handle the others like numbers. This view of enums from the Ada language is going to be very useful for Rust code too (and it’s far from the C way of thinking about enums).

Variable-length arrays, stack-allocation of bounded sets and associative arrays should be encouraged, and their syntax improved. Currently the handling of arrays has too many limitations. Conversions between slices and fixed size arrays is lacking and should be improved a lot. The type system should track the length of as many slices as possible to make the conversions safer, simpler and more efficient.

Handling of arrays with indexes different from usize should be introduced, to remove as many unreliable “as” casts a possible. If your code handles several arrays/slices/vectors at the same time you also probably want to specify distinct strong types for their indexes (as in Ada language).


#7

This was mentioned in the embedded-wg repo but I think it is worth mentioning this here since the proposed solution is not a 100% solution to the problem. Calling a sequence of std::asm::{...} intrinsics is not necessarily equivalent to a single asm!("...", ...) statement that writes the assembly for all intrinsics at once. So while that solution might be a temporary solution for some assembly instructions in some platforms, it is neither a solution for all assembly instructions (unless we add all assembly instructions from all ISAs to std::asm) and definitely not a solution for permutations of assembly instructions (unless we start adding permutations of assembly instructions to std::asm).


#8

@leonardo There were a couple of pre-rfcs recently about bit twiddling:


#9

Those could be useful, but they are just ways to read/write parts data seen as bit vectors. The type system extension I’ve referred to is to help the programmer reason and catch higher level mistakes in bit-twiddling code. Is such kind of Rust code common enough to justify that type system complexity increase?


#10

That’s a great question that I’ve been thinking about too. Personally, my leaning is that while the use case is important enough to have std library support, it is perhaps not common enough to change the type system itself…