avr-rust / ruduino

Reusable components for the Arduino Uno.
Apache License 2.0
695 stars 49 forks source link

Rust on an arduino Leonardo #8

Closed Kethku closed 6 years ago

Kethku commented 6 years ago

I recently got a shiny new keyboard from https://shop.keyboard.io/ :)

I am truly loving the hardware, but given that all of the configuration is done in arduino c it makes me a bit sad. I have been spending some time looking into rust and enjoying the zero cost abstractions and the convenient type systems. This all got me thinking that I would really love to be able to program for my keyboard in rust. The existing firmware for it is highly plugin based which would be a great fit for rusts crate system and is currently pretty annoying to get working in arduino c/c++.

All of this lead me to this repo and organization. I am pretty new to arduino/avr programming however so I have a couple of questions.

First: How likely is using something like avr-rust on an arduino leonardo to be successful? Will I need to be extra careful with progmem when compared to a c version? Does rust have a large standard library which will take up too much space or is it relatively bare bones? My guess is that this is entirely up to me as I will have to recompile core anyways, but I'm not sure.

Second: What is the status of rust on avr. I saw a couple of threads in the main repo indicating that rust was updated to the correct version of LLVM, but I also saw that there are some outstanding bugs with LLVM which makes things difficult including i128 types not working. Are these insurmountable at the moment or is there a happy path for ignoring them?

Third: I saw a separate issue and pull request related to adding support for different boards, I think, this would be necessary for building firmware for my keyboard as it is based on the leonardo instead of the uno. Is this still something for which progress is being made or am I left in the dark?

I'm not sure if this is the correct place to ask these questions. Sorry if not. Clearly I am not super experienced with microcontroller programming, so any assistance in this project would be greatly appreciated!

Restioson commented 6 years ago

Avr-rust is very usable, but far from batteries-included (you must compile it yourself). It looks like the Arduino Leonardo uses the ATmega32u4 as its main chip. Personally I haven't used that chip with avr-rust, but I think it probably should be supported (@dylanmckay can you confirm?).

In response to code size, I've found that (at least for blink) it is smaller in avr-rust than in c. The bugs you should be able to avoid in most places, but that being said there are places where it does trip you up (at least from personal experiences). Unfortunately, these can be linker errors, so you don't get the comfort of nice rust errors. The PR is adding support for autogenerated boards (from XML descriptor files). I'm not sure whether this is relevant to the ATmega32u4.

wez commented 6 years ago

I can confirm that atmega32u4 is a working target (I am also a keyboard firmware guy! you may want to check out: https://github.com/wez/flutterby-rs) but when I last tried to use this in earnest back in november I hit some code gen problems relating to progmem that were complete showstoppers for my project. Those may have been resolved by now, but I haven't been tracking too closely.

dylanmckay commented 6 years ago

I've used the Arduino Leonardo with the LLVM backend successfully in the past.

One issue you will have is that the avr-rust/ruduino crate only supports ATmega328p, although that'll be fixed in #4 . This means that you will need to do the lower level IO manually, like the standard AVR C way.

It will be possible to use something like avrd with the Leonardo. This crate defines all of the MCU-specific constants you will need to do IO.

Kethku commented 6 years ago

Very cool all. Thank you so much for the pointers. I have a couple of follow up questions. I imagine that linking to existing arduino libraries from rust is somehow impossible? I know that it is very possible to link to c and c++ libraries on normal platforms, but given that it hasn't been done on avr processors from what I have seen I'm guessing that something blocks this from happening?

I'm also curious if you can go the other way. Assuming above is not an option, is it possible to have a c program call into a rust binary on avr platforms? I'm looking for any way to gradually get started with rust code on a keyboard instead of having to rewrite everything from scratch which would be a big task given how much arduino code depends on arduino specific libraries. Again, sorry for the newby questions. I'm not super familiar with low level programming like this :)

dylanmckay commented 6 years ago

I imagine that linking to existing arduino libraries from rust is somehow impossible? I know that it is very possible to link to c and c++ libraries on normal platforms, but given that it hasn't been done on avr processors from what I have seen I'm guessing that something blocks this from happening?

Actually, this is possible. The LLVM AVR backend matches the same calling convention as AVR-GCC.

This means you can use avr-gcc in build scripts to generate object/library files, and link them normally using extern.

Take a look at the avr-libc bindings for an example. You can see the API documentation here.

I'm also curious if you can go the other way. Assuming above is not an option, is it possible to have a c program call into a rust binary on avr platforms?

Yes, this is also possible. Declare the Rust functions as #[no_mangle] pub extern fn and it should be good to go.

Again, sorry for the newby questions. I'm not super familiar with low level programming like this :)

No worries at all, feel free to pop into the Gitter room if you have any more questions!

Kethku commented 6 years ago

Very cool. Will do. Thanks for the help.

shepmaster commented 6 years ago

The LLVM AVR backend matches the same calling convention as AVR-GCC.

As a pedantic note, this is only true when you use the "C" calling convention:

extern "C" fn foo() {}

extern "C" {
    fn bar() {}
}

Rust's calling convention is not guaranteed and is very unlikely to match any existing calling convention.