Skip to content

Porcelain and Plumbing #409

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

Open
thejpster opened this issue Mar 3, 2020 · 5 comments
Open

Porcelain and Plumbing #409

thejpster opened this issue Mar 3, 2020 · 5 comments

Comments

@thejpster
Copy link
Contributor

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:

  • cortex-m-plumbing
  • risc-v-plumbing
  • msp430-plumbing
  • nrf9160-pac

Porcelain

  • arduino-loop (some new crate I just made up - imagine it does a basic setup()/loop() thing like Arduino)
  • freertos-rs (although being a C based RTOS it will have its own internal mechanism for handling types of CPU, imagine this is a pure-Rust equivalent to FreeRTOS)
  • rtfm
  • nrf52-hal
@adamgreig
Copy link
Member

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
Copy link
Contributor Author

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:

  • FreeRTOS works on a bunch of CPU architectures, and handles interrupts, mutual-exclusion, critical sections, etc.
  • TockOS I think supports several architectures too?
  • Arm's CMSIS will have some good ideas on how to abstract a Cortex-M, although obviously in a vendor specific way.
  • Linux is another example of abstracting CPU features across a variety of architectures.

@thejpster
Copy link
Contributor Author

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
Copy link
Member

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
Copy link
Contributor Author

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:

  • Provide a reset vector for the CPU
  • Provide other Interrupt vectors, either statically, or dynamically at run-time (with a vector table in RAM, or through a fixed redirection function)
  • Initialise the stack pointer, init .data, and zero .bss
  • Configure the peripherals - clocks, UART, SPI, timers, etc
  • Run the rest of the program meme
    • Rattle between a bunch of threads, sleeping when there's nothing to do or
    • Rattle around an event loop, sleeping when there's nothing to do

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

@adamgreig adamgreig transferred this issue from rust-embedded/cortex-m-rt Jan 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants