[HN Gopher] Asynchronous Rust on Cortex-M Microcontrollers
___________________________________________________________________
Asynchronous Rust on Cortex-M Microcontrollers
Author : picture
Score : 115 points
Date : 2023-07-19 17:39 UTC (5 hours ago)
(HTM) web link (interrupt.memfault.com)
(TXT) w3m dump (interrupt.memfault.com)
| chaxor wrote:
| I believe that Tock (tockos.org) and Theseus
| (https://github.com/theseus-os/Theseus) are in this area a bit as
| well, just from an actual OS perspective.
|
| I don't know much about this area, but it would be wonderful if
| these could work with the Libre compute boards, like the AM Logic
| S905X (Lepotato) or the Rock chip, since they're so much cheaper
| than a Pi. Would be great to get a dedicated simple system up to
| toy with Rust OS for only 30$.
| jvanderbot wrote:
| I could be wrong, but isn't every multi-task / RTOS essentially
| supporting this? Wake up, check flags, do work if required.
|
| Of course if you're bare-metal programming, it might be nice to
| have a lightweight mechanism for this.
| Conscat wrote:
| If you're asking why do coroutines exist, it's because context
| switching in kernel space and synchronization have very high
| overhead by comparison. You use those features when you want
| parallelism, not concurrency.
|
| For parallelism, you also usually want a userspace task
| scheduler because simply spawning threads in kernelspace is
| much slower. Cilk and Go provide this built into their language
| runtime.
| kevin_thibedeau wrote:
| Context switches on Cortex-M are relatively cheap with
| optimizations to limit the amount of data that has to be
| stacked.
| lmm wrote:
| Relative to what? They might be cheaper than some
| architectures, but they're still a hell of a lot more
| expensive than not doing them.
| tadfisher wrote:
| There's also the simplicity of bare embedded in Rust, because
| you can mostly[1] use Cargo, depend on the right embedded_hal
| crates for your target, and be up and running with a hardware-
| backed async runtime and everything. Zephyr, Tock, Mynewt, etc.
| all have bespoke build/deployment systems and driver
| ecosystems. That said, RTOS makes actually deploying working
| products much more realizable, so pick your poison.
|
| 1: I think you still need xargo/cargo-xbuild for no-std
| embedded sysroots, but there may have been some upstream
| development on that front since I last played in this sandbox.
| the__alchemist wrote:
| Embedded-HAL is interesting to me - in principle it's a great
| concept; writing hardware-agnostic, portable code.
|
| I don't use it in my firmwares because: - All
| of the embedded-HAL libs I've found (eg drivers) have had
| notable limitations or ergonomics problems. - It
| appears unsuitable for use with DMA, which I use for all
| runtime IO - The APIs tend to be a mess due to heavy
| use of typestates
| junon wrote:
| Building core/std/whatever using unstable flags and --target
| works for me these days. I can do everything with cargo and
| make it even more ergonomic with a toolchain file.
|
| Now if Cargo had a way of producing multiple variants of the
| same project (e.g. different --targets with pre-set features)
| just with `cargo build` then things would be a lot better.
|
| As of now, a Makefile is still needed to bring it all
| together.
| kibwen wrote:
| We were using cargo-make for something like this, though
| eventually we switched to using Cargo's experimental
| support for artifact dependencies: https://doc.rust-
| lang.org/nightly/cargo/reference/unstable.h...
| monocasa wrote:
| > I think you still need xargo/cargo-xbuild for no-std
| embedded sysroots, but there may have been some upstream
| development on that front since I last played in this
| sandbox.
|
| Cargo's build-std flag is the bespoke way now. Unstable, but
| working it's way to stable. AFAIK cargo/build aren't getting
| any more development in lieu of build-std.
| vvanders wrote:
| That's good to hear, I've used that in the past to drive
| down codesize even on non embedded targets by compiling for
| size and rebuilding the stdlib.
| Archit3ch wrote:
| > you can mostly[1] use Cargo, depend on the right
| embedded_hal crates for your target, and be up and running
| with a hardware-backed async runtime and everything
|
| Can one use Rust's embedded_hal from a different programming
| language?
| sophacles wrote:
| IIUC you can make some api in rust that uses the HAL, which
| ties your code into board specific stuff.
|
| That api you write can be exposed via C abi so you can link
| C or languages that have an FFI to C to the rust code.
| https://docs.rust-embedded.org/book/interoperability/rust-
| wi...
| azubinski wrote:
| It's different and very old story.
|
| Protothreads are classic now:
| https://dunkels.com/adam/pt/index.html
|
| Contiki OS is also a piece of embedded development classic:
| https://en.wikipedia.org/wiki/Contiki
|
| In general, it's more about syntax than semantics. Semantics is
| generally difficult and rarely changes.
| dmitrygr wrote:
| So, cooperative multitasking with no control of actual
| scheduling? Cute, but a real RTOS is the solution to most any "i
| need to do more than one thing" in the embedded world.
| amluto wrote:
| spawner.spawn(blinky(led)).unwrap();
|
| Has anyone seriously tried structured concurrency for single-
| threaded async Rust? The pattern where main effectively leaks a
| task seems kind of gross to me, and it's exactly what structured
| concurrency tries to solve.
|
| (I realize that Rust's async is inherently semi-structured.)
| swsieber wrote:
| IIRC been some proposals floating around, but one major block
| is asncy drop. This is the article I'm thinking of
| specifically: https://blog.yoshuawuyts.com/tree-structured-
| concurrency/
| AceJohnny2 wrote:
| Edit: commented on wrong post, meant for
| https://news.ycombinator.com/item?id=36791506
|
| Offtopic:
|
| The cursive italics are apparently a feature of the Victor Mono
| [1] font used for the full page. While it'd be amusing in Tumblr
| context (where cursive is used for hyperbolic emphasis), I can't
| fathom why one would consider it in a code context.
|
| You can change it (at least on Safari) by going into developer
| tools, clicking any node, and removing "Victor Mono" from --font-
| family
|
| [1] https://rubjo.github.io/victor-mono/
| kibwen wrote:
| That may be specific to your machine, I don't see any italics
| on mine, and the CSS just looks like `font-family: 'Source Code
| Pro', monospace;`.
| [deleted]
| strangattractor wrote:
| I new all that time I spent programming the Mac back in 1987
| would come in handy. Asnyc programming - cooperative multitasking
| never go out of style.
| 0xfedbee wrote:
| Cooperative multitasking in embedded has to be the worst idea
| ever. Anyone who has done any serious work in embedded systems
| will tell you how bad is it when a faulty task blocks everything.
| Please stop advertising async in embedded.
| fleventynine wrote:
| It really comes down to memory requirements. If you can afford
| to give every task it's own stack and dispatch it directly from
| prioritized interrupts, that's great. Even better if you can
| use memory protection hardware to isolate the tasks from each
| other.
|
| But if you have many long running tasks that only need to keep
| a handful of bytes of state when they're waiting, a mechanism
| like rust async can allow for huge memory savings while mostly
| retaining the same code style as stackful tasks.
|
| And you can even use both approaches in the same program, with
| isolated stacks for realtime critical tasks and cooperative
| state machines for everything else.
| junon wrote:
| ... anything faulty in an embedded world can block the chip.
|
| This is why watchdog timers exist. Async does not shut off
| watchdog. Not sure what your point is.
| kevin_thibedeau wrote:
| Watchdogs shouldn't be triggered in the normal course of
| events. They are a last line of defense for exceptional
| circumstances that will render a device inoperative, not a
| cure-all for poor system design.
| rcxdude wrote:
| Sure, but a system where a task has ceased to execute is in
| pretty much all circumstances a system where the watchdog
| (or some other assert) should trigger. (the whole toyota
| case involved them getting raked over the coals because
| their system did not in fact detect a failed task and do
| this).
| jacquesm wrote:
| Think of a watchdog timer in the same way you think about
| that big red handle on a train, 'emergency use only, abuse
| will be punished'.
| RealityVoid wrote:
| You have a task. You do an infinite loop in the task. The
| task is now blocking lower prio tasks in the RTOS. If the
| task is broken, it's broken.
| blackbeans wrote:
| Actually I never found cooperative multitasking a real issue.
| You don't want faulty tasks in your embedded application
| anyway. With cooperative multitasking it at least is easy to
| spot which task is the culprit.
|
| If I understand the article correctly, it seems that in this
| case the multitasking also isn't controlled by calling yield in
| custom user code, but rather always from the implementation
| that makes async/await work.
| jacquesm wrote:
| > You don't want faulty tasks in your embedded application
| anyway.
|
| Faults are a way of life in software, they are unavoidable
| because each and every piece of software rests on a bunch of
| assumptions and if any one of those or a combination of them
| do not hold then you have a fault. Failure to envision that
| fault and a way to deal with it in embedded systems can cause
| damage to property, injury and loss of life.
|
| None of this is simple, not in theory and definitely not in
| practice.
| rcxdude wrote:
| Sure, but an RTOS doesn't help you much with safety
| critical guarantees. When you're worrying about that you're
| also worrying about redundancy of the hardware your
| software is running on, for example. The only thing that an
| RTOS is really helpful for is making it a bit easier to
| argue that some realtime guarantees can be met even if
| other code running on the device may have worst-case
| runtimes longer than your deadline (this doesn't really
| become a mitigation for a truly defective task, though,
| since at that point all bets are off unless you have some
| good task isolation). But this is not all embedded systems,
| not even all safety-critical ones, and there are other
| solutions available if you are not using an RTOS which can
| give you the same or better options in that case(like
| placing the critical code in a high-priority interrupt
| handler, for example).
| the__alchemist wrote:
| Most of my programming these days is on Cortex-M, using Rust. The
| article is another prior in my suspicion that Async embedded Rust
| is designed with toy use cases in mind (blinky); I don't know how
| it would work in practical firemware because I haven't seen
| examples.
|
| Another possibility is that my brain and learning patterns don't
| work well with Async. I have a hard time understanding it, and it
| feels like a layer of obfuscation or misdirection. It's an
| abstraction that may be more suitable for other learning styles,
| or different program structures or requirements than the ones
| I've worked on.
|
| I'd love to see an RTOS for Rust, but am worried it will be
| Async.
|
| (I am referring to the Async/Await pattern; not asynchronous
| programming or concurrency)
| steveklabnik wrote:
| At work, we have a non-async RTOS
| https://hubris.oxide.computer/ though it is a bit harder to use
| than "add a dependency in Cargo.toml" like most projects.
|
| The original designer of Hubris, Cliff, has his own async RTOS
| that he uses for personal projects. He recently has been
| writing some blog posts on this that may be of interest to you:
|
| * http://cliffle.com/blog/async-inversion/
|
| * http://cliffle.com/blog/composed-concurrency-in-drivers/
|
| The former is a general introduction to async, and the second
| is about applying it to write an I2C driver.
|
| There already are some general RTOSes for Rust, and they do
| tend to use async:
|
| * https://rtic.rs/2/book/en/
|
| * https://github.com/embassy-rs/embassy (this one is used in
| the article)
|
| * https://tockos.org/
| the__alchemist wrote:
| Cliff's articles are outstanding!
|
| Re Rtic: I'm using the V1 of it, which is non-Async; it seems
| to offer smarter locking than using critical sections +
| Mutex, since it's capable of interrupting a lower-priority
| task with a higher one, depending on which resources are
| locked.
| KingLancelot wrote:
| [dead]
| sebastiandb wrote:
| This article about async in Python helped me understand it
| pretty well, since it explains them in terms of coroutines,
| which are very intuitive for me: https://mleue.com/posts/yield-
| to-async-await/
|
| Another thing that helps me get it is comparing it to the
| continuation passing style, where you never return from a
| function, you just take an argument that's basically a function
| pointer bound to an environment, and at the end of the
| function, instead of returning, you call your input function,
| giving it another function and environment as input, repeating
| the cycle. It's very similar to the transformation of callbacks
| within callbacks within callbacks pattern in JavaScript to the
| async/await pattern.
| jayd16 wrote:
| Once you realize async/await is just sugar over the familiar
| callback hell, a lot of the mystery fades away and it's
| easier to groc.
___________________________________________________________________
(page generated 2023-07-19 23:00 UTC)