Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust support for RIOT #9799

Closed
11 tasks done
chrysn opened this issue Aug 20, 2018 · 21 comments · Fixed by #16274
Closed
11 tasks done

Rust support for RIOT #9799

chrysn opened this issue Aug 20, 2018 · 21 comments · Fixed by #16274
Assignees
Labels
Area: Rust Area: Rust wrapper Type: new feature The issue requests / The PR implemements a new feature for RIOT Type: tracking The issue tracks and organizes the sub-tasks of a larger effort

Comments

@chrysn
Copy link
Member

chrysn commented Aug 20, 2018

It would be nice to be able to write applications in Rust that would run on a RIOT board, especially when sharing libraries with non-embedded systems. Before I plunge head-first into a third attempt (after #5740 and #6162), I'd like to look at an outline of what needs to be done for this to be usable, and before that, to look at what being usable actually means in this case.

Usage scenarios I see are (to be ticked when there's actual need for them):

  • Having individual applications (eg. network services) written in Rust run on RIOT – this is what I'd like to do.
  • Write complete hardware drivers in Rust. API-call-wise, this is not so much different from applications, but might be in terms of the build system.
  • Use Rust libraries that provide C wrappers inside RIOT applications
  • Explore the viability of RIOT components as part of the Rust ecosystem. (eg. "Could GNRC and SAUL work for embedded-rust in general?")

Technically, the nontrivial tasks I see are:

  • Build system integration
  • Making RIOT API available in Rust as an embedded-hal
    • Finding adequate lifetimes (how long can I use a saul_reg?)
    • How to deal with API that's dependent on board.h or cpu_*.h? (Initial approach: #define UART_STDIO_DEV irrelevant and assert that it has no impact on the actual API functions)
    • How to deal with static inline functions? (This ties into the build system as AFAICT static is used to allow inlining down to board configuration like clock speed. Initial approach: Have the rump application export non-static versions of them.)
    • Finding a versioning scheme for the API that works both with Rust's semver versioning and RIOT (probably see [RFC] Versioning our user facing APIs #8561)
    • Making uart_stdio_write usable by Rust's write! (not actually tricky but done)

Given my use cases, my exploratory work is more focused on the API part; I'd like to have a "raw" (possibly bindgen generated) API that more or less just gets rid of the extern "C" parts, and Rust's famous safe zero-cost abstractions on top of that. Ideally, RIOT peripherals could thus implement the embedded-hal interfaces, making the OS agnostic drivers usable immediately.

As far as the build system is concerned, my use case comes with low expectations towards integration: what I currently do works reasonably well, which is making cargo output a static library for the application, and having a rump RIOT application that calls once into a rust_main(), which then sets up any threads etc. (Quite possibly, having main in Rust could work just as well). The only thing I needed in the build system was to set BASELIBS+=.../target/.../libapplication.a; from there, everything builds and flashes fine.

@kaspar030, @nmeum, what were your applications, and what are your expectations on build system integration? Given that embedded Rust has come some way in the meantime (eg. xargo is not needed any more), do you think one of the build system integration approaches should be resurfaced? How far did you get with making RIOT's APIs usable from Rust?

[Apologies for the many editing iterations; my new keyboard is more Ctrl-Return happy than my old one. This is the final version of the issue's initial comment, except for later additions to the checkboxes which are accompanied by new comments on the thread.]

@Citrullin
Copy link
Contributor

Citrullin commented Aug 21, 2018

The last time I looked into Rust they were still porting the stdlib to Rust. It wasn't possible to use the std on embedded devices. The issue is, that most of the Rust libraries are using std. So you have the option to rewrite the lib, so it doesn't use std anymore or to port the std parts you need. Besides that, there were a ton of other issues.

Found a good overview (January 2018) about the current situation: http://blog.japaric.io/embedded-rust-in-2018/

@chrysn
Copy link
Member Author

chrysn commented Aug 21, 2018 via email

@Citrullin
Copy link
Contributor

Citrullin commented Aug 21, 2018

@chrysn True. I just want to make the current situation clear. :)
I would be really happy to use RIOT with Rust :)
There are already some really interesting driver implementation & libraries: https://github.com/rust-embedded/awesome-embedded-rust

@nmeum
Copy link
Member

nmeum commented Aug 21, 2018

[…] @nmeum, what were your applications […]?

We were mostly interested in the safety features offered by Rust (e.g. memory safety) and thus were interested in using it in RIOT. However, due to the reasons outlined below, we ditched this after a while and experimented a bit with using Checked C in RIOT: https://github.com/beduino-project/riot-police

[…] and what are your expectations on build system integration?

The built system integration will likely be not as easy as a you might think at least not if you try to integrate Rust into the make based buildsystem as we did in #6162. Using xargo that might be easier. See also rust-lang/rust-roadmap-2017#12

[…] do you think one of the build system integration approaches should be resurfaced?

I think trying to integrate Rust into the make-based build system again is probably a bad idea. Due to the fact that the build system builds modules listed in USEMODULES in sequential order (which doesn't play well with rust if modules depend on each other since all dependencies need to be built first IIRC) we weren't, among other things, able to enable parallel builds.

How far did you get with making RIOT's APIs usable from Rust?

As we only wrote Rust FFI wrappers for RIOT modules written in C, we got quite far and even managed to built Rust applications on top of gnrc_udp with our Rust integration. https://github.com/beduino-project/RIOT contains are more up-to-date integration than the one in #6162.

However, upstream didn't really seem to like the fact that you have to maintain two separate code bases (Rust and C) then, so a better approach would probably be rewriting C code in Rust and providing a C interface (which also has some benefits from a security perspective).

@chrysn
Copy link
Member Author

chrysn commented Aug 24, 2018

@nmeum wrote:

Checked C

IMO any integration can only succeed if it does not require large changes to RIOT; there is, AFAICT, too large a user and developer base that are does not have the need to migrate away from anything to whom maintaining Checked C annotations in the code is much more of a hassle than it brings.

Same goes for maintaining separate code bases: if Rust (or any static checking support that exceeds sane coding practice like "label const what can be") needs additional API wrappers (which it will, otherwise applications need to have unsafe code all the time), those should be maintainable separately. RIOT users are of course welcome to contribute, but nobody can be stopped from adding to RIOT because they can't or don't want to maintain Rust wrappers.

For that reason, the approach I'm currently following on the integration side is to have a riot-sys crate that creates low-level unsafe Rust bindings to the C code (think extern "C" fn uart_stdio_read(buffer: *mut [u8], len: isize) -> isize), ideally automatically per-project. A set of safe-to-use wrappers can then be maintained as riot-thread, riot-shell, riot-gnrc etc.

at least not if you try to integrate Rust into the make based buildsystem

I was afraid so. At least for my applications, not having any formal integration is good enough -- there's a configured RIOT base system and a Rust application that's linked in, and they don't need to know much of each other. What did come to light trying to wrap GNRC is that it's paramount for the creation of struct wrappers to extract the build flags / headers from the RIOT build process, for any mismatch in ifdefs causes struct-ural differences (one language expects a field to be there and the other not).

Chances are (I'm just experimenting with this) that the minimal "interface" between the build systems would be that RIOT exports a riot-all.h file created using gcc -E (where riot-all.h includes all the system, device etc. headers, and the compiler expands all preprocessor statements, thus baking in the -D flags from the RIOT build system). Then cargo runs bindgen on that, thus creating a riot-sys crate. It builds a static library from that, which is then linked in by the RIOT build system in the last step.

What was your intention behind making the Rust application into individual RIOT modules? Did you expect multiple Rust code parts to be present at the same time?

even managed to built Rust applications on top of gnrc_udp

How did you manage the lack of lifetimes? For example, when wrapping SAUL, I'm getting no guarantees of how long the handle will be valid, as saul_reg_rm might be called at any time (maybe even in an interrupt), and any measurement on the device can be undefined behavior after that.

@chrysn
Copy link
Member Author

chrysn commented Aug 24, 2018

@Citrullin wrote:

some really interesting driver implementation & libraries [...] awesome-embedded-rust

Yes. One of the test cases I'm running is to wrap RIOT's i2c as an embedded-hal blocking I2C device and letting an established driver[1] work its magic on it.

[1]: https://github.com/klemens/si7021-rs -- not using embedded-hal yet, but I'm preparing a PR to that effect

@miri64 miri64 added the Type: new feature The issue requests / The PR implemements a new feature for RIOT label Sep 1, 2018
@chrysn
Copy link
Member Author

chrysn commented Nov 28, 2018

The WIP crates are now published as riot-wrappers and and riot-sys, along with small examples; they were initially presented during toda^Wyesterday's Rust meetup.

Compared to the earlier sketches, the issues of having varying type definitions have been overcome -- or rather, accepted: None of the crates involved builds without a RIOT_CFLAGS environment configured to RIOT's $(CFLAGS_WITH_MACROS) $(INCLUDES); those flags are needed anyway for bindgen to build the structs right (like whether -fshort-enums is present), and lifts the need to have a preprocessed header file.

This has also brought the build system integration to a point where I'd consider it usable; the cargo integration boilerplate is now down to the size of an example Makefile, as in this example. (It'd be a bit more if DEVHELP=0 were to trigger --release builds).

Please have a look at work so far and let me know if you have suggestions for its further development.

@miri64
Copy link
Member

miri64 commented Jan 30, 2019

I was recently made aware by some rust support implemented by a student of @kaibeckmann: maybe you can have a look there as well?

@chrysn
Copy link
Member Author

chrysn commented Jan 30, 2019 via email

@stale
Copy link

stale bot commented Aug 10, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Aug 10, 2019
@stale stale bot closed this as completed Sep 10, 2019
@sedddn
Copy link

sedddn commented Mar 26, 2020

I'm interested in contributing to RIOT, but am more a Rust dev than C (know both). Would devs be willing to reopen this issue if there is new interest in incorporating Rust into RIOT?

It was suggested to maybe write a PoC peripheral in Rust with glue code (w/ embedded_hal) to use in the rest of RIOT. Happy to start there. Thoughts?

@chrysn
Copy link
Member Author

chrysn commented Mar 26, 2020 via email

@sedddn
Copy link

sedddn commented Mar 26, 2020

Those do exist, as the riot-sys (equivalent to a PAC) crate, the
riot-wrappers (implementing ADC, Delay and other embedded-hal traits)
and the riot-examples at https://gitlab.com/etonomy/riot-examples/.

Wasn't aware of those, that's awesome!

I'm in the process of preparing examples that can be placed in the RIOT
examples folder, with one demoing an application written in Rust, and
one showing off a module.

Let me know if there are any review or contributions wanted, this sounds like exactly what I was looking for. Much appreciation.

@miri64
Copy link
Member

miri64 commented Mar 26, 2020

However, since this is your tracking issue @chrysn, I think this issue can stay open ;-).

@miri64 miri64 reopened this Mar 26, 2020
@stale stale bot removed the State: stale State: The issue / PR has no activity for >185 days label Mar 26, 2020
@miri64 miri64 added Area: Rust Area: Rust wrapper Type: tracking The issue tracks and organizes the sub-tasks of a larger effort labels Mar 26, 2020
@miri64
Copy link
Member

miri64 commented Mar 26, 2020

Type: tracking will keep the stale bot away ;-).

@kaspar030
Copy link
Contributor

I'm in the process of preparing examples that can be placed in the RIOT examples folder, with one demoing an application written in Rust, and one showing off a module.

Loking forward. I just started seriously getting into Rust, I can rewiew it.

@keestux
Copy link
Contributor

keestux commented Jan 15, 2021

@chrysn building the saul_blink example from riot-examples succeeds for BOARD=stm32l476g-disco. But it fails for others. For example BOARD=sodaq-sara-sff, gives

  cargo:rerun-if-changed=riot-bindgen.h

  --- stderr
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:13:9: warning: 'RIOT_APPLICATION' macro redefined [-Wmacro-redefined]
  note: previous definition is here
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:14:9: warning: 'BOARD_SODAQ_SARA_SFF' macro redefined [-Wmacro-redefined]
  note: previous definition is here
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:16:9: warning: 'CPU_SAMD21' macro redefined [-Wmacro-redefined]
  note: previous definition is here
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:18:9: warning: 'MCU_SAMD21' macro redefined [-Wmacro-redefined]
  note: previous definition is here
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:20:9: warning: 'RIOT_VERSION' macro redefined [-Wmacro-redefined]
  note: previous definition is here
  riot-bindgen.h:17:9: warning: 'UINT16_MAX' macro redefined [-Wmacro-redefined]
  /usr/lib/llvm-10/lib/clang/10.0.0/include/stdint.h:601:9: note: previous definition is here
  riot-bindgen.h:18:9: warning: 'UINT32_MAX' macro redefined [-Wmacro-redefined]
  /usr/lib/llvm-10/lib/clang/10.0.0/include/stdint.h:557:10: note: previous definition is here
  /home/kees/src/RIOT/cpu/cortexm_common/include/cpu.h:33:10: fatal error: 'stdio.h' file not found
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:13:9: warning: 'RIOT_APPLICATION' macro redefined [-Wmacro-redefined], err: false
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:14:9: warning: 'BOARD_SODAQ_SARA_SFF' macro redefined [-Wmacro-redefined], err: false
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:16:9: warning: 'CPU_SAMD21' macro redefined [-Wmacro-redefined], err: false
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:18:9: warning: 'MCU_SAMD21' macro redefined [-Wmacro-redefined], err: false
  /home/kees/src/riot-examples/saul_blink/bin/sodaq-sara-sff/riotbuild/riotbuild.h:20:9: warning: 'RIOT_VERSION' macro redefined [-Wmacro-redefined], err: false
  riot-bindgen.h:17:9: warning: 'UINT16_MAX' macro redefined [-Wmacro-redefined], err: false
  riot-bindgen.h:18:9: warning: 'UINT32_MAX' macro redefined [-Wmacro-redefined], err: false
  /home/kees/src/RIOT/cpu/cortexm_common/include/cpu.h:33:10: fatal error: 'stdio.h' file not found, err: true
  thread 'main' panicked at 'Unable to generate bindings: ()', /home/kees/.cargo/registry/src/github.com-1ecc6299db9ec823/riot-sys-0.3.2/build.rs:60:10

@chrysn
Copy link
Member Author

chrysn commented Apr 2, 2021

I s'ppose there's an update due here:

  • There is now Add some Rust building infrastructure and example #16274 that claims to solve this.
  • The trouble with static inlines is largely resolved by using C2Rust. That comes with its own cost (mainly that there needs to be manual casting back and forth between the C2Rust and bindgen generated types, and that the (to C) transparent change of making something static inline can thus break things on the Rust side because a new cat is needed), but it's all still better than manual transpilation.
  • The riot-wrappers grew -- they still only cover spotlights, but there are now more of them (for example, sockets are now exposed in terms of embedded-nal, a network abstraction layer; the microbit LED matrix display is exposed as an embedded-graphics paint surface)
  • Building modules or packages rather than application in RIOT became much more feasible now that there is XFA -- that spares us the hassle of generating C headers that'd then be fed back to RIOT. An example I'm working on is exposing WS281x pixels through SAUL. I've had it running with a mock of auto-initialization. Following the recent move from linking .a files to linking .o files makes this very smooth, actually.

@MrKevinWeiss MrKevinWeiss added this to the Release 2021.07 milestone Jun 22, 2021
@MrKevinWeiss MrKevinWeiss removed this from the Release 2021.07 milestone Jul 15, 2021
@miri64
Copy link
Member

miri64 commented Dec 16, 2021

Aren't there still some tasks here left to do?

@chrysn
Copy link
Member Author

chrysn commented Dec 16, 2021

You're right, the "closes:" commit resolved most but not all of this.

What's open -- apart from increasing the oxidlzed API surface, but that's incremental and out of scope for here -- is support for libraries or other system components. There's a follow-up in #16833. What's in there lets Rust modules put code into XFA, and given how we often extern void foo_init(void);, that should do -- we might consider going evem further (defining user API in Rust with externs and cbindgen'ing headers), but that's probably more for 3rd party libs that do that anyway.

The rest is gradual improvements.

Reopening until non-app modules work (spoiler: works already in #16833, just needs dedusting & rebase), but apps are doable now already.

@chrysn chrysn reopened this Dec 16, 2021
@chrysn
Copy link
Member Author

chrysn commented Sep 22, 2022

Support for RIOT modules has been included already in 2022.07.

I think we can close this now -- going through the last open items:

  • We already have a hardware drive
  • Rust libraries can just offer C functions as extern void foo(void); (all current examples go through the OS, but nothing is keeping them)
  • Feedback from RIOT is going into embedded-nal-async
  • Lifetime questions are largely answered -- cases of "hey how long does this live" generally still come up, but (eg. in the case of mount points) we've found different solutions depending on the precise case.
  • Static inline functions are now all transpiled in C2Rust. There are still some friction points, but they don't hurt badly any more.
  • Versioning can be independent from RIOT's; in various cases we've seen that API changes in RIOT don't affect riot-wrappers uses. Generally, particular versions of riot-wrappers will require particular versions of RIOT, and usually there can be some overlap. (riot-sys migrates similarly, but stands no chance of actual stability -- it's versioned on a best-effort base, and has semver guarantees only when the underlying RIOT doesn't change).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Rust Area: Rust wrapper Type: new feature The issue requests / The PR implemements a new feature for RIOT Type: tracking The issue tracks and organizes the sub-tasks of a larger effort
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants