rust-embedded / cortex-m

Low level access to Cortex-M processors
Apache License 2.0
812 stars 145 forks source link

Porcelain and Plumbing #409

Open thejpster opened 4 years ago

thejpster commented 4 years ago

See: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain

Perhaps cortex-m and cortex-m-rt could be split into two pieces in a different way. We would have one crate which is the plumbing for Cortex-M - it would handle the fundamentals of booting a Cortex-M core, interrupts, etc. Everyone, no matter what they were doing, would use this crate if they were on a Cortex-M. Then we have some porcelain, which is the nice user-facing bit. We might have several of those (for example, you could consider RTFM as some porcelain, or some RTOS). In a ideal world, the porcelain would fit with lots of different sorts of plumbing (risc-v-rt, msp430-rtc, etc), but sometimes a platform is just too weird for that to work.

We would perhaps need some traits (or some other API) to allow porcelain to access the plumbing, but it can be gritty and internal as users won't normally be exposed to it - they get exposed to the nice wipe-clean porcelain.

Example crates (some are fictional, for now)

Plumbing:

Porcelain

adamgreig commented 4 years ago

I like this idea but I worry it couldn't work quite the same way git does, because we're not only composing high-level operations made out of simple low-level constructs. Instead, our higher level (porcelain) crates generally provide additional functionality, but the low-level (plumbing) functionality is often still required as-is, directly by end applications. I guess basically "user-facing" is a hard line to draw when everything we're providing is a library; it's sort of all "user-facing".

For example, even using a frontend like RTFM which provides its own interrupt management, I still want to call cortex_m::sys_reset() from my own application, and I wouldn't expect RTFM to wrap all such methods (not least since they may vary per-platform). A very complete frontend that was essentially a full RTOS might totally wrap everything you could need, while a very simple frontend like the arduino-loop idea probably doesn't want to wrap much of anything.

Nevertheless I think splitting things up into more backend and more frontend crates could be useful, I'm just not sure exactly where to draw the line or how flexible to make it. Some things seem fairly easy -- like cortex_m_asm to provide access to the asm functions without bringing in all the peripheral stuff, or splitting the cortex_m PAC/HAL levels -- but I'd want to be happy we were getting something worth the complexity of making even more crates.

thejpster commented 4 years ago

Further, to avoid the kind of "oh no! maybe this isn't perfect?" kind of paralysis that can affect open-source projects with lots of members, here's a possible route to stabilisation of this idea that doesn't turn the world upside down and discourage adoption (I'm looking at you, KDE 4).

  1. Think about what people want from their porcelain.
  2. Think about that kind of plumbing best exposes the underlying features of our chosen architectures (MSP430, Cortex-M, Cortex-R and RISC-V at the moment, I think).
  3. Write a new porcelain crate for people who aren't using an RTOS or RTFM or anything like that. It looks nice on top, but it's ugly as hell underneath.
  4. Slowly iterate the back-end of the porcelain, moving stuff into cortex-m-rt.

This is broadly equivalent to a roadmap for fixing the peripheral side of Cortex-M:

  1. Think about the Cortex-M HAL.
  2. Think about the Cortex-M PAC.
  3. Write a Cortex-M HAL that's ugly inside but works and is nice to use.
  4. Stitch in a Cortex-M PAC underneath (and hopefully no-one using the HAL will notice as you do it)
  5. Deprecate/abandon/delete the cortex-m crate.

I'll also note some important prior art, because we aren't doing these things in a vacuum:

thejpster commented 4 years ago

want to call cortex_m::sys_reset() from my own application,

Right, but isn't that just rtfm::reset_cpu(), or freertos::mcu_reset() or somesuch?

adamgreig commented 4 years ago

want to call cortex_m::sys_reset() from my own application,

Right, but isn't that just rtfm::reset_cpu(), or freertos::mcu_reset() or somesuch?

It could be, but then does every frontend have to fully implement all its own methods for every backend function? Getting away from that sort of all-in-one thing is an advantage to having easy crates I think, unlike in C where the overhead of extra dependencies encourages monolithic RTOSs. There's no reason all the cortex-m specific functionality can't be exposed by a cortex-m crate that anyone can use, while RTFM can focus on just providing the interrupt-priority-based scheduling, and your HAL can focus on providing peripheral access, etc.

thejpster commented 4 years ago

I guess it depends what value you place on portability for an application. If you accept that an embedded application is targeted at and only runs on one particular model of one particular SoC, then sure, you can depend on some SoC specific porcelain. And actually, as I write that out, I'm starting to convince myself that that is generally true...

OK, so what does any embedded application do:

We should make it easy to do that, for a specific platform, but for any platform.