-
Notifications
You must be signed in to change notification settings - Fork 98
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
Tooling: Ideal developer experience #6
Comments
For starters, I'd like something like this to just work: // src/main.rs
#![no_std]
// Crate for the STM32F3DISCOVERY
#[macro_use]
extern crate f3;
#[start(f3)]
fn main() -> ! {
println!("Hello, world!");
loop {}
}
|
So, today (or actually by the time the next nightly is released), using the f3 crate this is as close as I can get to my ideal:
#![no_main]
#![no_std]
#[macro_use]
extern crate f3;
#[export_name = "main"]
pub fn main() -> ! {
iprintln!("Hello, world!");
loop {}
}
[target.thumbv7em-none-eabihf]
rustflags = [
"-C",
"link-arg=-Tstm32f3discovery.ld",
"-C",
"link-arg=-nostartfiles",
]
produce a binary that Just Works. Comparing it to my ideal:
It's not that bad except for the Also note that this example is relatively simple because I'm only targetting one device. But, ideally, whatever features we request in rustc/Cargo must be able to "scale" and support more complicated scenarios where one has to compile a program for a bunch of different devices that have different memory layouts (linker scripts) and must be compiled with different optimizations flags (-mcpu). |
Targeting different environments is extremely complicated, especially once it comes to making it scalable. In addition, as much as I'm sure we'd all like to provide a solution to everyone's problems, we can't foresee every possibility, so we should probably focus on something that is easy for beginners (sane defaults), but is easily expandable to work in a larger tool ecosystem. Platform IO has taken the approach of categorizing environments with three characteristics: platform, framework, board, with the goal of making it very nice to work within the PlatformIO world. Frameworks defines what the "core" standard library is, e.g. the functions provided by the Arduino environment. Libraries can then be written against the Arduino framework. This then allows any platform to implement the Arduino framework. Boards are constrained to a single platform, but usually support multiple frameworks. Bringing this over to the rust world, a platform could hold a lot of the same meaning. It could imply an entry point into the user application, doing whatever is necessary to call a clean, unmangled main entry. Going back to @japaric 's ideal, there are some potential areas worth investigating:
These three questions bring me to a more philosophical question: Where do you draw the line on what rust's responsibilities end? A batteries included approach is nice for beginners, but may end up being difficult for professional projects that need to integrate with other tools, whereas a unix "do one thing well" philosophy may be harder for beginners, but easier to include into bespoke development environments typical in the industry. I wrote this hoping to have a somewhat complete thought, but instead, I feel I only reached the tip of an iceberg of complications. Does anyone know any of the developers from PlatfromIO? Perhaps it's worth engaging them, even at this stage? |
I'm going to throw in one of my favorite quotes from Larry Wall: "Easy things should be easy, and hard things should be possible." The top priority should be making it as easy as possible for a moderately experienced Rust programmer to go from reading about embedded Rust programming online to having a working demo blinking a LED on an actual board. This includes every barrier that you can think of along the way, some of which may not have much to do with Rust, including
It's most important that this entire chain works really well for one or two boards first so that we can get as many Rust developers (and non-Rust embedded developers that are interested in learning Rust) on board and contributing both feedback and code. Command line tools and scripts are more appropriate at this early stage and can in many cases be integrated into IDEs, with Makefiles as a common denominator if needed. This is just as important for seasoned professionals that are evaluating Rust for serious projects. The quality of the out-of-box experience is a good proxy for the maturity of the ecosystem, plus professionals hate getting stuck even more than new hobbyists - this is work, after all, and they have enough experience to know that it's not simply their own lack of knowledge. Give a good developer a great experience, and he or she may be eager to do the work to port it to a different board. A frustrated developer is going to give up and look for something else. |
That's an interesting thought. Some crates are meant to be compiled for a particular target so it Right now you can omit the target in the command line if you specify it in the build.target field of We could move that build.target key to Cargo.toml itself and have Cargo use that value if the key This is certainly RFC material.
Cargo itself shouldn't deal with this. But users can create their custom Cargo subcommands: if e.g. If would be up to the community to come up with these tools to flash and debug Rust programs. I |
@jcsoo That sounds a lot like what I'm trying to do with my f3 crate and my Copper book. The f3 crate provides a high level, Arduino like API that people can use to play with the STM32F3DISCOVERY. Its main goal is to demonstrate that "you can totally hack embedded systems using Rust and it's actually easy". OTOH, the Copper book wants to be a reference that explains every detail/concept that you need to know to build a framework like the f3 crate from scratch. To address the particular points you mention:
The f3 can be used for this. It targets a single development board and if I add pictures (GIF) to it we could publitize it more.
The copper book has instructions for this. And about making it automatic, @brson has plans to add SDK feature to
The f3 has documentation for this and also more examples. But there's no automatic way to build the "blink" example.
The copper book has instructions about how to use OpenOCD+GDB to debug Rust programs that are running on a microcontroller. A related nicety is that the f3 has a
I'm here
I have experience doing embedded development with Eclipse and think that it wouldn't be too hard to integrate the existing tooling that the f3 crate uses (which is very similar to the tooling that you would for C development) but I don't know if I'll have the time this month to sit down and do it. |
Couldn't agree with that more. A great quote!
I'm trying to not think about details just yet, but rather raise the more general question about build systems and dependency trees. I'd say that if two crates require a different default target, they can't both be built. Maybe it's worth defining platform, framework and board so I can express myself better. Platform Framework Board So would this actually play out? I'm thinking there would be crates that provide frameworks for different platforms (targets), with most of them being board agnostic. There could then be creates for various boards that bring together a framework and a platform into something usable to start an application layer project. So if I wanted something Arduino like, there would be an arduino-stm32f4-discovery crate that would implement the "Arduino" framework (setup, loop functions). Most "application layer" creates that needed the Arduino API could depend on the Arduino platform, but that platform could be implemented by a number of different crates. I don't think the current dependency system has a good way to express this kind of dependency tree. I'm too much of a beginning to see how to do this, but it's almost like you would want an Arduino trait, a "Embedded Rust" trait, a "Free RTOS" trait, and then various crates could implement the traits needed. I just don't know how to express this using the crate system. Do people understand what I'm proposing? If so, what do you think? If not, what part of this doesn't make sense. I'd really recommend looking at platformio.org. I'm not in any way affiliated with them, but really like their way of making the developer experience super smooth. It really does go well, even when it require multiple compilers, multiple bootloading tools, all workingon many different host operating systems. |
Aha, I had stumbled across the Copper repository but hadn't actually looked at the book. It looks wonderful, and it looks like you have things very well covered. I'm going to have to pull out my STM32 board and try it out myself now!
This would be incredibly useful. Being able to install everything through a single rustup command immediately makes things simpler than almost all of the open source and commercial toolchains that I've seen. Combining that with a small set of cargo / xargo subcommands for flashing, gdb, and logging allows us to put a user friendly facade around the various utilities that are needed. Ideally we have simple subcommands that cover the basic tasks, along with concise, centralized help documentation. In addition to that we add the option to display the command lines that we are being submitted to the underlying tools such as OpenOCD or gdb as well as pointers to tool-specific documentation and a way to pass additional parameters. This makes it easy for expert users to see what we are doing and customize further as needed.
Probably the most important is Github itself. There's a lot of value to having people create Issues there - it gives them a chance to see if someone else has run into the same problem and forces them put a little bit of thought into describing the problem (often leading them to discover the solution on their own). Plus, solutions are there for people to see including links to bug fixes in case there are any that are required. It's also one of the more scalable tools we have since a lot of people can participate without having to be logged in to a particular system. I'm OK subscribing to a Github repo but I really can't keep IRC, Gitter + Slack windows open and still get work done. |
This is how I'd map the concepts you mention to Rust code:
// crate: arduino -- the Arduino "framework"
pub trait I2C {
fn write(&mut self, byte: u8);
fn read(&mut self) -> u8;
// ..
}
// crate adxl345 -- an accelerometer
#[cfg(feature = "arduino")]
extern crate arduino;
#[cfg(feature = "arduino")]
use arduino::I2C;
#[cfg(feature = "arduino")]
fn get_accel_x<I>(&mut I) -> f32 where I: I2C {
// ..
}
// (implementations for other frameworks go here)
// crate: arduino-stm32f3discovery -- Arduino framework for the STM32F3DISCOVERY
extern crate arduino;
use arduino::I2C;
// (re: static mut. this is just a example, we could expose this in different ways)
// in this case, the GPIO pins associated this I2C instance are
// hard-coded (not configurable by the user)
/// An instance of an I2C peripheral
pub static I2C1: I2C_Struct = (..);
pub struct I2C_Struct { .. }
impl I2C for I2C_Struct {
// ..
}
// The real entry point of all programs
#[no_mangle]
pub fn _start() {
extern {
// the user entry point
fn main() -> !;
}
zero_bss();
init_data();
I2C1::init();
// ...
main()
}
# Cargo.toml
[dependencies]
arduino-stm32f3discovery = "1.0.0"
adxl345 = { version = "1.0.0", optional = true }
[features]
# build the adxl345 library for the arduino framework
default = ["adxl345/arduino"] // src/main.rs
extern crate adxl345;
extern crate arduino_stm32f3discovery;
use arduino_stm32f3discovery::I2C1;
#[no_mangle]
fn main() -> ! {
let ax = adxl345::get_accel_x(&mut I2C1);
// ...
} Oh, and the platform is just a built-in target like |
You could even break the
// crate stm32
// There are only struct declarations in this crate
mod gpio {
// Registers common to all GPIO peripherals
pub struct Registers {
pub cr: RW<u32>,
pub odr: WO<u32>,
pub idr: RO<u32>,
// ..
}
}
// crate: stm32f3discovery
// GPIOA peripheral
pub fn gpioa() -> &'static gpio::Registers {
unsafe { &*(0xdeadbeef as *const _) }
}
// crate: arduino-stm32f3discovery
extern crate arduino;
extern crate stm32f3discovery;
use arduino::Serial;
pub static mut SerialPort: Serial_Struct = (..);
pub struct Serial_Struct { .. }
impl Serial for Serial_Struct {
// for example, use stm32f3discovery::gpioa and stm32f3discovery::uart1 here
} |
Something that @japaric mentioned that I would like to expand on is the potential power of the
|
This issue has not been updated for a number of years. I do think that we need a better story around developer experience and tooling, however I am not sure if this issue captures the current state of the art. I would propose that we close this issue. Marking this for a cleanup sweep. If we would like this to stay open, please provide an update to what this issue should be focused on. |
I am closing this issue, please feel free to open another issue if you would like this discussed further. |
Building programs, written in C or Rust, "manually" for microcontrollers is quite complicated as it
involves several device specific files (e.g. linker scripts) and configuration (e.g. compiler flags).
This issue is about what the ideal experience would look like for embedded Rust developers. I think
it's informative to study first what the experience of C/C++ developers looks like:
Embedded C/C++ developers usually (always?) use an IDE and a SDK to make the process
straightforward. Here's how the process looks like for them (disclaimer: this is my vague
recollection)
libraries (the soft float variant or the hard float variant?) to link to.
that runs before main.
inspection of memory, etc. the SDK also provides "registers maps" that describe the contents of
register in a human readable way: instead of hex-speak
0xdeadbeef
, you get "theCEN
bit ofthe
CR
register is set" but in a more graphical way.I think those are, at a "minimum", the conveniences that SDKs provide. SDKs may also include an
OS/RTOS but that sort of escapes the realm of tooling.
What are other niceties that C SDKs provide? And how do you picture the ideal Rust developer
experience? Finally, how could we materialize all those conveniences using Cargo? We probably want
to spawn more issues about this last question.
The text was updated successfully, but these errors were encountered: