ChanServ changed the topic of #rust-embedded to: Welcome to the Rust Embedded IRC channel! Bridged to #rust-embedded:matrix.org and logged at https://libera.irclog.whitequark.org/rust-embedded, code of conduct at https://www.rust-lang.org/conduct.html
_whitelogger has joined #rust-embedded
stephe has joined #rust-embedded
cr1901 has joined #rust-embedded
mrkajetanp has joined #rust-embedded
<cr1901> adamgreig: I do have some thoughts about this, but not sure I can word them well into a comment on the issue. Hopefully I can words better tomorrow
crabbedhaloablut has quit [Ping timeout: 268 seconds]
crabbedhaloablut has joined #rust-embedded
<re_irc> <Lachlan> Anyone know the alignment of f64 on arm?
<re_irc> <Lachlan> It seems to be either 4 or 8, depending on arch
<re_irc> <James Munns> Easy to test with "Layout::new::<f64>().align()"
<re_irc> <James Munns> oh I gues there's "core::mem::align_of::<f64>()" too
<re_irc> <James Munns> should be the same thing
<re_irc> <Lachlan> Oh lmao
<re_irc> <Lachlan> It's 8
<re_irc> <Lachlan> it seems
fabic has joined #rust-embedded
starblue has quit [Ping timeout: 255 seconds]
starblue has joined #rust-embedded
GenTooMan has quit [Ping timeout: 268 seconds]
GenTooMan has joined #rust-embedded
Amadiro_ has joined #rust-embedded
Amadiro__ has quit [Ping timeout: 240 seconds]
causal has quit [Quit: WeeChat 3.5]
starblue has quit [Ping timeout: 246 seconds]
Socke has quit [Ping timeout: 246 seconds]
genpaku has quit [Ping timeout: 246 seconds]
starblue has joined #rust-embedded
genpaku has joined #rust-embedded
Socke has joined #rust-embedded
funsafe has joined #rust-embedded
crabbedhaloablut has quit [Quit: No Ping reply in 180 seconds.]
crabbedhaloablut has joined #rust-embedded
emerent has quit [Ping timeout: 272 seconds]
emerent has joined #rust-embedded
<re_irc> <henrik_alser> Anyone working on support for the Nuvoton M480 series?
fabic has quit [Ping timeout: 255 seconds]
fabic has joined #rust-embedded
fabic has quit [Remote host closed the connection]
fabic has joined #rust-embedded
fabic has quit [Ping timeout: 240 seconds]
mrkajetanp has quit [Ping timeout: 276 seconds]
mrkajetanp has joined #rust-embedded
explore has joined #rust-embedded
starblue has quit [Ping timeout: 255 seconds]
starblue has joined #rust-embedded
<re_irc> <gauteh> I published my defmt-serial crate, which tries to make it easier to send defmt-messages over serial. Coupled with socat and defmt-print it works pretty decently: https://twitter.com/gauteh/status/1541723433276416002 / https://github.com/gauteh/defmt-serial - very experimental at the moment.
<re_irc> <James Munns> Hey gauteh, it looks like it blocks while the serial port is sending, yeah?
<re_irc> <gauteh> Yes
<re_irc> <gauteh> I think defmt-rtt can also block.. ?
<re_irc> <gauteh> but only if buffer is full?
<re_irc> <James Munns> Gotcha! I think I showed you defmt-bbq (https://github.com/jamesmunns/defmt-bbq/), that will give you a FIFO that you can push into, and drain from the other side via serial
<re_irc> <gauteh> oh-- nice, yeah, I was looking at defmt-bbq and defmt-rtt while writing this
<re_irc> <James Munns> defmt-rtt also has it's own ringbuffer, and yeah, it only blocks if the buffer is full
<re_irc> <gauteh> that would make it even better
<re_irc> <James Munns> though defmt-bbq will _drop_ messages instead of blocking if the buffer is full
<re_irc> <gauteh> trade-offs.. trade-offs.. I don't know what a user would expect
<re_irc> <James Munns> Anyway, congrats on the release!
<re_irc> <James Munns> I built defmt-bbq explicitly to be built on top of, e.g. for serial or ethernet logging
<re_irc> <gauteh> Thanks; a small project, but could be useful for people getting started that don't have a hw debugger
<re_irc> <gauteh> or for debugging equipment in the field
<re_irc> <James Munns> if you'd like to use it as a base, it should definitely be wrappable, but like you said, always tradeoffs :)
<re_irc> <gauteh> I think it would be better. If I have time I'll make it configurable with a feature-flag
<re_irc> <James Munns> The biggest downside is that the user needs to "poll" the consumer side, or give it over to something like a UART interrupt to constantly drain
<re_irc> <James Munns> whereas your current impl "directly pushes" into the serial port
<re_irc> <James Munns> (which means less API surface, but does mean blocking to send, though on a fast serial port, that shouldn't be TOO long honestly)
<re_irc> <gauteh> ๐Ÿ‘๏ธ
<re_irc> <gauteh> If someone was using UART to log messages already, and just want to replace it with defmt, the behaviour would match
<re_irc> <gauteh> so I don't think it will be too surprising for the user
<re_irc> <James Munns> yeah, that makes a ton of sense!
<re_irc> <adamgreig> Lachlan: Where's it 4?
fabic has joined #rust-embedded
<re_irc> <James Munns> Sharing this here, in hopes it inspires someone to go make this happen :D
<re_irc> <James Munns> TL;DR: Miri, the "rust interpreter" that is used for evaluating things like constants, as well as strict dynamic testing (e.g. "miri test") has the ability to totally emulate any target that Rust supports (since it's interpreting, not compiling).
<re_irc> You can even interpret tests _for another platform_, e.g. interpret windows tests on linux, or interpret big-endian targets on a little-endian system.
<re_irc> It can also interpret bare metal targets, like cortex-m, but those targets don't have the stdlib, which means they don't have the built-in test harness. Applications like "defmt-test" bring their own no-std version.
<re_irc> It would be AWESOME to also be able to use miri as an alternative to testing in QEMU for unit tests that don't need hardware interactions, but are sensitive to alignment or size differences from your standard 64-bit desktop.
<re_irc> <Robert Jรถrdens> Testing/emulating/simulating embedded is often limited not so much by the availability of the architecture core part but by the lack/maintenance/completeness of the peripherals, interrupt controller, memory controller, memory model, instruction scheduling, cache, bus implementation etc. For many cases some of those would still be required. But as you say, for things that focus on core things like alignment/size...
<re_irc> ... issues, miri testing would be great!
<re_irc> <James Munns> Yeah! For example writing an allocator, or some kind of math library or something (though - not sure if things like ASM/intrinsics work in miri), would be perfect (even miri testing on my desktop has helped find a lot of potential bugs).
<re_irc> If nothing else, installing miri is WAY easier than setting up QEMU IMO :D
<re_irc> <James Munns> Plus you get all of the "sanitizer" features miri already provides (for better or worse - speed wise) without any additional configuration
bjc has joined #rust-embedded
dc740 has quit [Remote host closed the connection]
<re_irc> <omar_u8> When creating functions in embedded Rust, what is the best approach to pass around peripherals? Lets say for example that I want a function that can control any gpio pin or even pwm channel, is it best to keep moving around the device peripheral handle from function to function? This is especially if for scaling purposes I decide at a later time to expand the functionality of that function.
<re_irc> <James Munns> So this isn't _exactly_ answering your question, but passing around the peripheral (usually as a reference or a mutable reference) is pretty common.
<re_irc> <James Munns> That being said, often you aren't so much passing around the item itself, but usually giving the peripheral to some struct or task or other component that is responsible for that peripheral
<re_irc> <James Munns> Rather than making a function that controls any pin or pwm channel, make a struct (or trait) that contains a pin, that just does pin-type things, then if you have three different "components" that hold a pin, they are each just different structs that hold a pin.
<re_irc> <omar_u8> Ah, I see. I think I've seen something like that in one of the Knurlings exercises.
<re_irc> <James Munns> Yeah, like for:
<re_irc> I'd say put all the "pin control" or "pwm control" behaviors in a "Pin" or "Pwm" struct, then you have:
<re_irc> struct Wiggler { pin: Pin }
<re_irc> > I want a function that can control any gpio pin or even pwm channel
<re_irc> <James Munns> they all call "self.pin.whatever()", but the "any pin" part is a functionality of the "Pin" struct
<re_irc> <James Munns> This is also typically how things like the "embedded-hal" traits work
<re_irc> <James Munns> like if those are all output pins, you can now make your structs generic over any type that implements the OutputPin trait
<re_irc> <James Munns> e.g.
<re_irc> struct Wiggler<T: OutputPin> {
<re_irc> }
<re_irc> pin: T
<re_irc> <James Munns> (and so on)
<re_irc> <omar_u8> Makes a lot of sense. I'm getting there, slowly but surely ๐Ÿ˜€ Thanks a bunch James!
<re_irc> <James Munns> The general name for this is "composition".
<re_irc> Instead of making types X, Y, and Z all do something with a T, have the T do a thing, and make X, Y, and Z all hold the T
<re_irc> <James Munns> (this is usually contrasted with object oriented programming or inheritance. Instead of "FancyPin IS a Pin", it's "FancyPin HOLDS a Pin")
<re_irc> <omar_u8> Awesome, got it.
<re_irc> <James Munns> Also, just for completion, traits are sort of sideways associated with this, if you have PinSimple and PinFancy, you don't say they both ARE pins (e.g. inherit from the Pin class), you say they implement the Pin interface (e.g. trait Pin). This is most commonly used to complement composition, when you say "I don't care _what kind_ of thing I am holding, as long as it can fulfill the Pin interface". So it's still...
<re_irc> ... composition because you are building on top of the functionality of something, just in this case it's an abstract interface, rather than a concrete type.
<re_irc> <James Munns> I find that when you start thinking about "who needs to hold what data and structs, and how do they communicate/interact with each other", designing things in rust starts getting a lot easier, conceptually
<re_irc> <James Munns> because the borrow checker more or less forces you into thinking about those questions
<re_irc> <James Munns> I know this is way more theory/lore than you were asking for, but I've taught a bunch of rust, and I find a lot of questions like yours are helped by looking at the "big picture" sometimes :)
<re_irc> <omar_u8> Absolutely, I really appreciate the elaborate answer. I think I get the gist of it and it makes a ton of sense. Though as expected, I'm going to need more practice and a lot of mistakes to adjust my mindset.
<re_irc> <James Munns> Definitely! And whenever something is _hard to do_, stepping back and asking _why it is hard_ is an important step!
<re_irc> <James Munns> Sometimes it's just not being familiar with some Rust concept that would help in this case
<re_irc> <James Munns> but sometimes it's because you're trying to do something that is difficult to explain to the compiler/borrow checker
<re_irc> <James Munns> Sometimes trying to do things "like python" or "like C" or "like C++" in Rust is like pulling teeth!
<re_irc> <James Munns> And there might just be a different approach/pattern/way of approaching things that are "easier to explain to the compiler"
<re_irc> <James Munns> (which is what I'm getting at here)
<re_irc> <omar_u8> James Munns: Exactly my issue these days ๐Ÿ˜…
<re_irc> <James Munns> No worries! It's a strategy that helps a lot when learning new languages (mostly!)
<re_irc> <James Munns> Just Rust has some very different/stricter requirements
<re_irc> <James Munns> and sometimes it's easier to "unlearn" old habits than it is to "port" them to rust.
<re_irc> <James Munns> (funnily enough, I found people with LESS programming experience, or experience in WAY different areas than C/C++ often learned Rust faster, because they had less to "unlearn")
<re_irc> <omar_u8> Yeah, I guess theres that thing about old habits dying hard :)
<re_irc> <James Munns> Yeah! But the nice thing is that "designing with ownership/the borrow checker in mind" is actually a super portable concept
<re_irc> <James Munns> It's a lot easier to "write Rust" in C, than it is to "write C" in Rust :D
<re_irc> <James Munns> (with lots of caveats, of course)
dc740 has joined #rust-embedded
<re_irc> <omar_u8> James Munns: Very true, taking proper practices to a more flexible language so to speak, rather than importing bad practices to a stricter language.
<re_irc> <James Munns> or rather, "architectures that work in Rust are likely to work in C/C++", but "architectures that work in C/C++ are not guaranteed to be possible in Rust"
<re_irc> <James Munns> squares and rectangles, and all that.
<re_irc> <frankcryptic> "I'll help anyone interested on how to earn $25000 in 3days to 1week from the crypto market. But you will have to pay me my commission! when you receive your profit! if interested send me a direct message via WhatsApp by asking me HOW for more details on how to get started +1 (570) 801-0862
<re_irc> <James Munns> (promoted my new account to admin status, CC adamgreig for visibility, if you'd like me to nerf my old account, I can do that. I just had to go load my other account before I could mod that last spam message)
<re_irc> <adamgreig> Nw
causal has joined #rust-embedded
fabic has quit [Ping timeout: 246 seconds]
<re_irc> <adamgreig> room meeting time again! agenda is https://hackmd.io/jA_Fz-GSQE2kTIKJ4TbPFQ, please add anything you'd like to discuss or announce and we'll start in a few mins
<re_irc> <adamgreig> ooh, rust 1.62 is out on thursday, with "cargo add" built in
<re_irc> <adamgreig> ok, let's start! couple of announcements from me
<re_irc> <adamgreig> you may remember "cross", which was a working group project for a time, and after a ton of work from some new maintainers they've released a very long awaited 0.2.2, with 0.3 around the corner
<re_irc> <adamgreig> there's been some recent discussion about what APIs might be useful for strict provenance with pointers created from fixed integers, like for MMIO mapped peripherals in embedded, here: https://github.com/rust-lang/rust/issues/98593
<re_irc> <adamgreig> and from lulf we have a bunch of fun-sounding new embedded crates:
<re_irc> * 'reqwless' (https://crates.io/crates/reqwless) - an async HTTP client using `embedded-io` traits.
<re_irc> * `ector` (https://crates.io/crates/ector) - no-alloc actor framework for embedded devices based on embassy.
<re_irc> * `embedded-update` (https://crates.io/crates/embedded-update) - a pluggable firmware update protocol for embedded devices.
<re_irc> * blogpost on firmware updates, part 2 https://blog.drogue.io/firmware-updates-part-2/
<re_irc> <adamgreig> and from lulf we have a bunch of fun-sounding new embedded crates:
<re_irc> - "ector" (https://crates.io/crates/ector) - no-alloc actor framework for embedded devices based on embassy.
<re_irc> - 'reqwless' (https://crates.io/crates/reqwless) - an async HTTP client using "embedded-io" traits.
<re_irc> - "embedded-update" (https://crates.io/crates/embedded-update) - a pluggable firmware update protocol for embedded devices.
<re_irc> - blogpost on firmware updates, part 2 https://blog.drogue.io/firmware-updates-part-2/
<re_irc> <adamgreig> * "reqwless"
<re_irc> <lulf> been working on pulling out some of the stuff we've been adding to drogue-device over the past year++ :) hopefully useful for others as well
<re_irc> <adamgreig> looks good! I love the names :D
<re_irc> <jamwaffles> Is this the right meeting/place/time to talk about getting another "stm32" release?
<re_irc> <adamgreig> strictly speaking no as stm32-rs is a separate project, outside the embedded wg
<re_irc> <adamgreig> I am the right person to pester about it though ๐Ÿ˜…
<re_irc> <therealprof> Too much pressure. ๐Ÿค“
<re_irc> <jamwaffles> Noted, I'll dip out of the meeting then. Sorry for disturbing!
<re_irc> <adamgreig> you can bug me afterwards though :P
<re_irc> <jamwaffles> yessir. Here or in #stm32-rs:matrix.org (https://matrix.to/#/#stm32-rs:matrix.org) ?
<re_irc> <adamgreig> don't mind, probably #stm32-rs:matrix.org (https://matrix.to/#/#stm32-rs:matrix.org) makes more sense
<re_irc> <jamwaffles> Sweet, thanks!
<re_irc> <adamgreig> last week we discussed someone's proposal to add a new feature to svd2rust to emit features per "groupName" attribute in SVD peripherals, which is now a PR: https://github.com/rust-embedded/svd2rust/pull/617
<re_irc> <richarddodd> It's great to see some more high-level stuff, and especially blog posts!
<re_irc> <adamgreig> I don't know if anyone's had a chance to look over that yet? I only had a quick glance at the weekend
<re_irc> <therealprof> Nope, sorry. Hadn't noticed it.
<re_irc> <therealprof> richarddodd: Speaking of which: lulf please make sure to add to the newsletter if not done already
<re_irc> <adamgreig> ok, and the other bits from last week were the discussion about dropping "nb" from embedded-hal entirely for 1.0, and then potentially keeping e-h-a a separate crate forever instead of planning to merge it back in later, to simplify the current module structure of like "embedded_hal::spi::blocking::x" vs "::asynch::x" etc
<re_irc> <adamgreig> dunno if anyone had any further thoughts on those issues? removing "nb" seems generally popular on https://github.com/rust-embedded/embedded-hal/issues/177#issuecomment-1162212193
<re_irc> <adamgreig> dunno if thejpster is around?
<cr1901> I still don't think the binary choice for writing using the embedded HAL should be "async or blocking". But I'm for removing them from the crate
<cr1901> I like the async versions being in a different crate
<re_irc> <adamgreig> if they're removed from the crate, they're effectively gone - I guess you could make a new embedded-hal-nb set of traits, but I imagine a lot of HALs would just not implement them
<cr1901> Well, the HAL abstraction as-is is too coarse a granularity for writing apps with "main thread plus interrupt". The nb API comes closest to what I want
<cr1901> Tho I think I could emulate what I want w/ the async versions by calling poll directly
<re_irc> <adamgreig> yea, that's fair, I guess that's sort of what thejpster was getting at with https://github.com/rust-embedded/embedded-hal/issues/177#issuecomment-1165698989 too
<re_irc> <adamgreig> but yes, I guess the question is whether the async versions can basically provide you with everything you need anyway, in which case perhaps there's no need for nb
<re_irc> <adamgreig> but maybe in practice that's too unergonomic and so it still makes sense to use nb?
<re_irc> <adamgreig> it would be good to hear from people actually using the nb traits, as I think most people in the conversation so far just use blocking or async and so have no issue deleting the nb ones
<re_irc> <therealprof> cr1901: How so? interrupt + blocking API works great
<re_irc> <thejpster> Yo
<re_irc> <dirbaio> futures are essentially nb with wakers, you can still poll futures manually
<re_irc> <dirbaio> you don't _have_ to use async/await syntax
<re_irc> <dirbaio> you don't _have_ to use a multi-task executor
<re_irc> <dirbaio> you don't _have_ to use wakers
<re_irc> <dirbaio> if you don't use all these, you essentially get the same as "nb" apis
<re_irc> <thalesfragoso> Plus Pin
<cr1901> therealprof: Honestly, this might not be a concern in practice, b/c I've not yet had this problem, but 1/2
<re_irc> <thalesfragoso> Which is a bit of a pain sometimes
<cr1901> I can't always control how much data an embedded HAL driver slurps up within an interrupt, or it might do more than one read()
<re_irc> <therealprof> The nice thing about the combination interrupt + blocking is: When the interrupt fires you know that your blocking API will not actually block but fetch the data and then your main program can do whatever it wants to do the rest of the time. nb only adds overhead to that.
<re_irc> <thejpster> So, Futures are a huge cognitive load, and blocking APIs are not helpful in many applications. So I liked nb as it was simple.
<re_irc> <adamgreig> dirbaio: but maybe with a bunch of extra complexity and verbosity?
<re_irc> <adamgreig> I wonder if we could have some crate that provided a wrapper around the async traits to make them basically the nb traits? so a HAL implements async and users could use the wrapper to get nb, as it were?
<re_irc> <dirbaio> sure, but that complexity can be solved with wrappers
<re_irc> <dirbaio> "nb" still needed wrappers to be usable, see "nb::block!"
<re_irc> <dirbaio> * "nb::block!()"
<re_irc> <adamgreig> yea, sure
<cr1901> I'm not really against wrappers
<re_irc> <adamgreig> DIYing the wrapper inside your own project every time is much more annoying than just using an nb trait, but maybe a simple wrapper around the async ones could give basically the same API
<re_irc> <therealprof> thejpster: I don't quite see it. nb only provides a benefit if you had combinators, allowing you to make progress on multiple things at once. If you have that, you have basically reinvented async; if you don't you degraded your non-blocking use into a blocking one with extra overhead.
<cr1901> therealprof: I'm gonna be honest, my concerns are "what if" right now. I haven't found them in practice, and I haven't had an application (or bandwidth to make one) where I needed to think about "using embedded HAL driver in an interrupt". 1/2
<cr1901> I'm just worried "what if I _did_ need it, and now it's too late"?
<re_irc> <thejpster> So my use case is usually โ€œhereโ€™s a buffer, read what you can and tell me how muchโ€
<re_irc> <thejpster> I never cared for the byte-by-byte API.
<re_irc> <adamgreig> do you need combinators for nb? I'd just have like a main loop that checks each thing in turn and each one might be ready or not, which is a really common embedded pattern
<re_irc> <adamgreig> (don't tell me that's basically a combinator :P)
<re_irc> <thejpster> Yeah, that.
<re_irc> <thejpster> And I know everyone loves async, but having your functions rewritten into a state machine is a big cognitive load and is not a universal answer.
<re_irc> <thejpster> Especially not for embedded people coming from C.
<re_irc> <dirbaio> nb is so "simple" that it's not fit for many use cases IMO
<re_irc> - Can't use DMA (the api only borrows the buffer while being polled)
<re_irc> - Can't do low-power since you don't know when you have to poll (no wakers), you essentially have to poll in a loop
<re_irc> - Can't store "state" between polls (no Future struct), this means doing anything more complex than "read a byte" or "write a byte" is not possible. For example there's no "nb" i2c trait.
<re_irc> <therealprof> adamgreig: You mean "select" is not a combinator? ๐Ÿ˜‰
<cr1901> Tbf, Rust is about limiting the amount of valid programs that a CPU runs :P
<re_irc> <dirbaio> - Can't abstract interrupts, drivers still need a "pls call this on interrupt" function, while with async you don't need it
<re_irc> <thejpster> You poll nb until it says there is no more. Then you do something else.
<cr1901> >while with async you don't need it <-- because the runtime provides it
<cr1901> dirbaio: And I've asked several times for a "pls call this on interrupt" function in the embedded HAL
<cr1901> And each time I was told "just use async"
<re_irc> <therealprof> thejpster: Huh? async is a pile of state machines internally with the point being you don't have to reinvent the wheel all the time. Writing a statemachine to deal with multiple nb things at once is exactly the main thing async avoids.
<re_irc> <dirbaio> cr1901: there's no easy way to do such an "interrupt" trait, it's too hardware-specific. There's one issue discussing this I believe
<re_irc> <thejpster> But I donโ€™t write a state machine. I wait for a flag to be set and then I while let until the serial FIFO is empty. Then I go back to sleep. What I canโ€™t say is how many bytes are in the serial port buffer.
<re_irc> <thalesfragoso> thejpster: The compiler does the state machine for you though
<cr1901> In C world, a solution is "provide a function the user is expected to put in their interrupt handler to drive the software stack to completion"
<re_irc> <thejpster> So I canโ€™t say read (n) and I never want read() to block.
<cr1901> Which is "#ifdef" based on MCU
<cr1901> There's no equivalent in the HAL trait
<re_irc> <thejpster> Yes the compiler doing the state machine is precisely the problem. I have to replicate that work in my head to get a model of how my code is going to run. And the code on screen bears little relevance to the disassembly. Which is still a thing for embedded, as good as Rust is.
<re_irc> <newam> thejpster: wouldn't a wrapper around "read([u8; 1])" achieve the same thing? It sounds pretty custom either way.
<re_irc> <thalesfragoso> thejpster: That seems like poll_read
<re_irc> <dirbaio> you don't have to "mentally replicate" the state machine, you just write blocking-looking code that runs top-to-bottom: "do this, then do that", except with some ".awaits" in places, and it Just Works
<cr1901> If you have the flash space for the runtime, and you don't need to ever delve into the assembly of the runtime
<re_irc> <thejpster> Please stop telling me how async works and please stop trying to invalidate my experience.
<re_irc> <adamgreig> I think it's unavoidable that many embedded users won't want to use an async runtime for a variety of reasons
explore has quit [Quit: Connection closed for inactivity]
<re_irc> <therealprof> Which is fine but besides the point.
<re_irc> <adamgreig> but my question is whether the embedded-hal async traits can still be enough that those users could (via some wrapper) get something that feels like the nb traits
<re_irc> <thalesfragoso> adamgreig: I mean, you don't even need a runtime, like I said, they are N poll methods
<re_irc> <adamgreig> (there are existing poll methods?)
<cr1901> >can still be enough that those users could (via some wrapper) get something that feels like the nb traits <-- My guess is "yes"
<re_irc> <thalesfragoso> adamgreig: Yeah, just look at the Read and Write traits of any Future crate
<re_irc> <newam> thalesfragoso: Can they be used without nightly? That's the biggest reason I see for not wanting to go into async land.
<re_irc> <thalesfragoso> newam: Yep, poll methods are completely sync
<re_irc> <dirbaio> async traits require nightly yep
<re_irc> <thejpster> > Where possible must not be tied to a specific asynchronous model. The API should be usable in blocking mode, with the futures model, with an async/await model or with a callback model.
<re_irc> <dirbaio> async itself _doesn't_, only async traits with TAIT do
<re_irc> <adamgreig> the async traits can't be used on stable yet anyway, but since we expect they should be stable fairly soon, now's the time to consider not having nb in e-h 1.0
<re_irc> <thejpster> I stand by that statement in the e-Hal docs
<cr1901> thejpster, I sympathize with you deeply here, but that thing you just quoted was written years ago by japaric
<cr1901> and prob doesn't reflect rust-embedded's stance now
<re_irc> <thalesfragoso> dirbaio: But the defacto traits today for basic stuff, like IO, doesn't
<re_irc> <dirbaio> thejpster: the "nb" traits fail at being usable with futures/async
<re_irc> <adamgreig> thejpster: yea, I agree, but it seems like possibly we can just have two sets of traits - one blocking, one using async/futures - and still have all the same flexibility for users
<re_irc> <dirbaio> * futures/async, because they lack wakers
<re_irc> <adamgreig> if so, that's less work for HALs to implement, a simpler trait hierarchy/api, simpler for drivers
<re_irc> <dirbaio> so that statement is already false tosay
<re_irc> <dirbaio> * today
<cr1901> Is it false inherently, or because "nobody wanted to try to make it work b/c of async fever"?
<re_irc> <thalesfragoso> adamgreig: However, Pin can be a pain
<re_irc> <dirbaio> there's one api (blocking traits) usable blocking ONLY
<re_irc> there's another api (nb traits) usable with manual nb polling ONLY, and can't be wrapped on futures or async/await
<re_irc> <adamgreig> it was written long before rust had async and futures, right? so I think it just turned out nb didn't end up meshing well enough to what futures does
<re_irc> <adamgreig> thalesfragoso: yea, Pin and fake Wakers and stuff like that is what concerns me I guess, I don't know how much they can all be hidden by some wrapper
<re_irc> <thalesfragoso> adamgreig: Fake wakers is easy, Pin on the other hand...
<re_irc> <newam> dirbaio: I think it should be revised... That statement caused a lot of confusion for me back when I was learning embedded rust ๐Ÿ˜…
<re_irc> <thejpster> Ok, look. I do a whole bit in my training about how amazing iterators are with only one next method.
<re_irc> <adamgreig> I think that statement was aspirational at the time
<re_irc> <therealprof> thejpster: Are they though? A lot of people would disagree. ๐Ÿ˜…
<re_irc> <thejpster> If the UART read trait only required read() -> Option you could build the rest around it
<re_irc> <thalesfragoso> adamgreig: But I would expect most of the drivers implementing the io async traits to be Unpin
<re_irc> <thalesfragoso> But if they aren't
<re_irc> <thalesfragoso> +...
<re_irc> <dirbaio> thejpster: you can't do low-power, you can't do DMA
<re_irc> <thejpster> Correct, you could not - with the default impl of the โ€œfancierโ€ functions
<re_irc> <adamgreig> if it's read() -> Future that you can then poll() -> Option, is that satisfactory?
<re_irc> <thejpster> And you can do low power.
<re_irc> <adamgreig> yea, low power seems easy still, just enable the peripheral interrupt and wfi(), even if it's not through the HAL
<re_irc> <thejpster> Option is much easier to explain in a training course than Future.
<re_irc> <adamgreig> when you wake up you can check all the things you might be looking for
<re_irc> <thalesfragoso> adamgreig: But then you wake for everything, and has to check everything again
<re_irc> <thalesfragoso> * have
<re_irc> <adamgreig> you do, it might not be a big hit especially though (and you could always have a flag set from the interrupt I guess?)
<re_irc> <dirbaio> thejpster: you sort of can with a "pls call this on interrupt" function, by the time you do that you lose the abstraction of traits, for example on Linux there's no "interrupts"
<re_irc> <thejpster> Yeah. Itโ€™s fine. I made an rf tag thanks ran 10 years on a cell doing exactly that.
<re_irc> <adamgreig> for a training course I'd have thought just using blocking API would be simpler though, maybe? depends who you're training I guess
<re_irc> <dirbaio> the point of embedded-hal traits is to allow writing hardware-independent drivers. They're not hardware-independent if they need a "pls call on interrupt" callback
<re_irc> <richarddodd> I'll just throw in my 2c. I think async maps really well to embedded. Rather than needing to write interrupts and do synchronization, you can write code that is sequential, just that there are points where there might be a pause.
<re_irc> Just using the "poll" method isn't an option IIUC because "poll" is also responsible for stashing a waker and waking it when the future can make progress, and you need a runtime to schedule this.
<re_irc> <eldruin> thalesfragoso: which might be fine if you are only waiting on like 2 things. which is the point of having something really simple like nb
<re_irc> <thejpster> You canโ€™t block if you donโ€™t know in advance how much data there is. In std::io this is read vs read-exact.
<re_irc> <adamgreig> yea
<cr1901> >Just using the "poll" method isn't an option IIUC because "poll" is also responsible for stashing a waker and waking it when the future can make progress, and you need a runtime to schedule this.
<re_irc> <dirbaio> richarddodd: you can supply a noop waker then poll in a hot loop, you don't need a runtime
<cr1901> Shit. I forgot about this
<re_irc> <therealprof> And for everything else there's RTIC...
<cr1901> I don't want to poll in a hot loop either
<re_irc> <thalesfragoso> richarddodd: Not really, wakers are runtime specific, but futures usually aren't
<re_irc> <newam> My problems with nb in EH would largely be resolved if there were some docs explaining when to use blocking/nb/async traits.
<re_irc> Based on this conversation it seems like there are valid usecases for nb, but it also seems that blocking/async are what majority of people will want to use, and that isn't communicated very well.
<re_irc> <adamgreig> we're nearly out of time, it seems to me like we should explore how ergonomic it might be to use an async trait as though it were nb, with some kind of wrapper, and see how close we can get?
<cr1901> I'm alright with this
<re_irc> <thalesfragoso> eldruin: You can still call poll/poll_read/poll_write manually
<re_irc> <dirbaio> cr1901: you can supply a noop waker, then poll in a "wfi" loop as well :)
<re_irc> <adamgreig> I guess nb does have the advantage that it's usually trivial to provide the blocking implementation on top of it :P
<re_irc> <James Munns> btw, https://docs.rs/cassette/latest/cassette/struct.Cassette.html and the "poll on" function is basically that
<re_irc> <James Munns> put a future in a struct, it has a "poll" on function that eventually becomes Some
<cr1901> dirbaio: That's fine then
<re_irc> <thalesfragoso> adamgreig: Agreed, I like futures, but I'm also worried about these wrappers, Pin can be a pita
<re_irc> <dirbaio> HALs could ensure the futures are Unpin maybe
<re_irc> <adamgreig> yea, I worry, and I guess you'd have to store the future _somehow_, unlike with nb where you don't store anything except the HAL struct itself?
<re_irc> <dirbaio> then you can move them around freely...
<re_irc> <thalesfragoso> dirbaio: There goes generators
<re_irc> <therealprof> thejpster: You mean because there's no "Result"?
<re_irc> <adamgreig> I guess you couldn't/shouldn't just take a future and poll it immediately and throw it away if it's not ready
<re_irc> <dirbaio> embassy doesn't do that today though, and it'd be a pain
<re_irc> <thalesfragoso> adamgreig: The HAL struct would usually implement poll_read though, but it seems libstd won't go that route for the async io traits
<re_irc> <thalesfragoso> So worth thinking
<re_irc> <richarddodd> If there are people who have a strong preference for keeping nb, why not keep it? Like has been said you can make a blocking API super easy by just hot-looping.
<re_irc> <richarddodd> So it doesn't add to maintenance burden too much I don't think
<re_irc> <adamgreig> the downside is you have way more e-h traits, so HAL authors and driver authors need to deal with more things, and the nb ones aren't compatible with the async ones even though they nominally serve a similar purpose
<re_irc> <dirbaio> fragments the ecosystem
<re_irc> <richarddodd> hmm
<re_irc> <adamgreig> makes it more likely a HAL would only support, say, blocking and async, but your driver needs nb, or whatever
<re_irc> <dirbaio> for example embassy will never impl the "nb" traits (they don't work with DMA, and the whole embassy nonblocking story is based on DMA)
<re_irc> <thalesfragoso> I would say keep it, implementing async traits in a HAL isn't that easy with the current HALs
<re_irc> <thejpster> fragments the ecosystem == accommodates people who refuse to touch async/await
<re_irc> <dirbaio> then drivers using "nb" won't work on embassy
<re_irc> <dirbaio> and drivers using async won't work on HALs that impl "nb" and not async
<re_irc> <richarddodd> You can guide people with documentation if you think it's appropriate, for example by making beginner docs point to async.
<cr1901> I barely touch DMA as it is
<re_irc> <newam> richarddodd: EH is a very visible crate for newcomers. "nb" traits are a bit of a trap IMO, since most people probably want async or blocking instead.
<re_irc> Guiding with docs would solve this issue though.
<re_irc> <richarddodd> And you could put a note on saying you need to pick one set of traits and stick to it - you can't mix and match them
<re_irc> <adamgreig> richarddodd: yea, and we'll definitely be doing this if we keep all 3, but you don't get much choice in the matter if the driver you want to use needs one trait and the HAL provides another
<re_irc> <adamgreig> (keeping all 3 is the default position, to be clear, it's just recently the idea of removing the nb ones has come about)
<re_irc> <richarddodd> Yes for me I find it enjoyable to translate drivers, but not everyone has the time or inclination I know
<re_irc> <thalesfragoso> adamgreig: A problem is that HALs don't usually implement async traits, that can change, but it might take time, they don't even handle interrupt right now
<cr1901> I'm not allergic to bumping the major version to 2, if it becomes truly clear the nb versions of the traits aren't usable as is
<re_irc> <thejpster> Is the blocking API expected to sleep internally?
<re_irc> <dirbaio> also look at the hacks "nrf-hal" had to do to impl the "nb" traits on uart
<re_irc> <thejpster> Or do a hot loop?
<re_irc> <adamgreig> we haven't even released the async traits yet, so that's fair enough :P
<re_irc> <eldruin> I think we should explore how much mix and match is actually possible. like, you can use nb in a blocking way with block!, it kind of defeats the point, but it is usable. same thing with blocking stuff in async
<re_irc> <thalesfragoso> adamgreig: Storing and waking a waker in the right time is not really trivial
<re_irc> <richarddodd> thejpster: That's a good question.
<re_irc> <adamgreig> I certainly don't think that's "expected"
<re_irc> <adamgreig> afaik nothing does that?
<re_irc> <adamgreig> could you implement the blocking api on top of the async api ๐Ÿค”
<re_irc> <therealprof> thalesfragoso: It's really hard to figure out whether you're for or against async HAL... ๐Ÿ˜„
<re_irc> <James Munns> adamgreig: Yes, pretty much by wrapping the future from the async api in a cassette block_on call
<re_irc> <James Munns> (or even futures::block_on, for that matter)
<re_irc> <adamgreig> cr1901: yea, this remains an option too, but then you make all the existing HALs and drivers incompatible :/
<re_irc> <richarddodd> If you wanted to sleep you'd need to have access to interrupts. So you'd need to know you don't have multiple things wanting to use the same interrupt.
<re_irc> <thejpster> Weโ€™re over the hour and I have to go. Take care.
<re_irc> <thalesfragoso> therealprof: I'm all in, but I've been there and done that, is great, but you need some ground to step one
<re_irc> <therealprof> eldruin: However that is exactly how nb is used typically.
<re_irc> <adamgreig> seeya thejpster, thanks for dropping by!
<re_irc> <thalesfragoso> * on
<re_irc> <richarddodd> I personally would always go for async, because you get scheduling and sleep on idle for free.
<re_irc> <thalesfragoso> adamgreig: It's fine if the user doesn't need the future after the function call, which I think is true most of the time
<re_irc> <adamgreig> one trait to rule them all :D
<re_irc> <dirbaio> eh that was tried with "nb" and didn't work
<re_irc> <richarddodd> with casette you lose the auto-sleep
<re_irc> <adamgreig> dirbaio: what was?
<re_irc> <adamgreig> just having the nb traits and write the blocking ones on top?
<re_irc> <dirbaio> adamgreig: have HALs impl just "nb" and then autoimpl the blocking traits from that
<re_irc> <dirbaio> I think with async it'd work "less badly" because you can still use dma etc, but you'd still be "wasting" the waker
<re_irc> <adamgreig> is that worse than just having the blocking implementation in the hal?
<re_irc> <James Munns> richarddodd: Currently yes, but it could have some interface (if you know you will get an interrupt) to ping pong between poll and wfi
<re_irc> <James Munns> or "nb-async" or whatever could have that.
<re_irc> <richarddodd> In the non-async world there has been a lot of talk about a 'default' runtime. And consensus is/was difficult because a simple runtime is going to be suboptimal in a lot of situations.
<re_irc> <dirbaio> adamgreig: ๐Ÿค” perhaps the issues were just due to the "Default" magic marker trait to opt into the auto-impl
<re_irc> it'd give weird errors if you used it wrong and made the blocking impls less discoverable in the docs
<re_irc> <richarddodd> * non-embedded
<re_irc> <richarddodd> oops my comment makes more sense now
<re_irc> <James Munns> As someone who has very recently went through the "learn enough async to be dangerous" process, I'm very sympathetic to both dirbaio and thejpster's view points, from what I understand
<re_irc> <James Munns> it is a WAY better way to do things, once you've passed the learning curve
* re_irc eldruin has to go. nice discussion everybody!
<re_irc> <James Munns> BUT, the learning curve (if you ever want to look under the hood - not at the user level), is bizzarely vertical
<re_irc> <richarddodd> James Munns: I guess it is like Rust generally - steep learning curve but makes sense once you've 'got it'.
<re_irc> <James Munns> and often involves understanding how ALL the parts click together
<re_irc> <dirbaio> I think the learning curve is fine if you're just using it (ie not implementing an executor or a HAL)
<re_irc> <James Munns> Yeah, I agree, but that puts you in a "don't look at the man behind the curtain" place
<re_irc> <James Munns> which is NOT where you want to be if you are debugging something.
<re_irc> <adamgreig> I think there are always going to be embedded projects/developers who want to have a closer-to-the-metal approach and/or hard hard-real-time enough that they want some non-async stuff, but like, it's a question of what we support and what we expect from HAL authors too
<re_irc> <richarddodd> I did a few impls of traits in embassy and I have zero understanding of the executor. You just need to call "waker.wake" from the interrupt.
<re_irc> <thalesfragoso> I have to go too. As a last note, I think async is great and that would be the traits I would use. However, if you want to have nb wrappers, I recommend trying them before
<re_irc> <dirbaio> and "things have learning curve when you look under the hood" applies to pretty much everything
<re_irc> <adamgreig> especially so to async, perhaps?
<re_irc> <richarddodd> I think mixing async for non-realtime and higher-priority interrupts for realtime is an exciting avenue for the future
<re_irc> <James Munns> dirbaio: Yeah, but I don't consider myself dumb, and it took me two weeks with basically you and mycoliza coaching me through the whole thing to really "get it"
<re_irc> <dirbaio> even a blocking HAL impl has lots of stuff going on
<re_irc> <James Munns> like, and I was already familiar with how async (from a userspace perspective) is supposed to work
<re_irc> <dirbaio> and with "nb"/interrupts, you're pretty much FORCED to look under the hood
<re_irc> <adamgreig> personally I am comfortable with what a blocking HAL has going on, and it basically all comes from the mcu's reference manual, whereas you need to know all that and also all of rust async on top to have a complete handle of what's going on with async
<re_irc> <dirbaio> critical sections to share data with interrupt, infinite irq loops due to forgetting clearing "IF" flags...
<re_irc> <thalesfragoso> Oh, adamgreig and svd2rust people, not sure if you saw it but there is a PR to remove derefereable from &UnsafeCell
<re_irc> <James Munns> I guess what I'm trying to say is, there is no right or perfect choice for embedded
<re_irc> <adamgreig> but personally I write my own drivers and hals and don't really use embedded-hal, so...
<re_irc> <James Munns> and it's always going to be a fight
<re_irc> <adamgreig> thalesfragoso: oooh do you have a link?
<re_irc> <James Munns> IMO, we should fracture e-h, e-h-async, and e-h-nb
<re_irc> <James Munns> if someone wants to pick one or more, cool
<re_irc> <James Munns> if someone wants to write a working but sub-optimal conversion layer between any two or more? cool
<re_irc> <James Munns> like, people are going to work on what they want to work on
<re_irc> <richarddodd> yep that sounds good - also call e-h e-h-blocking
<re_irc> <James Munns> dirbaio has support for a ton of boards in embassy-hal, and will likely push adoption of that
<re_irc> <adamgreig> James Munns: at that point might as well have all three traits in the one embedded-hal crate probably, but my worry is you end up much less likely to find drivers that Just Work with your HAL
<re_irc> <James Munns> there's a ton of established e-h-blocking
<re_irc> <James Munns> sure, three modules, three crates, all works for me
<re_irc> <James Munns> just, they are different, and we don't need to make a perfect one
<re_irc> <James Munns> just nudge the N to be as small as possible
<re_irc> <James Munns> being clear about what they are and aren't good for will be educational enough
<re_irc> <adamgreig> but you'd keep nb, even though it seems to have fewest users and is in many ways just an inferior async?
<re_irc> <thalesfragoso> adamgreig: Sorry, a bit busy now, but it's 98017 iirc
<re_irc> <James Munns> "want simple? choose blocking. Want non-blocking but no async? choose nb. Want async? choose async."
<re_irc> <James Munns> adamgreig: That's sort of my desire to make it three crates
<re_irc> <James Munns> they just move at the speed of the people that give it life
<re_irc> <adamgreig> https://github.com/rust-lang/rust/pull/98017 thanks thalesfragoso
<cr1901> I like this
<re_irc> <James Munns> people who want it move it forward
<re_irc> <richarddodd> ask the people who want nb to write the blurb for it. will also force them to articulate their motivation, which will be good for understanding of everyone
<re_irc> <dirbaio> motivation was "do async before async existed"
<re_irc> <dirbaio> +for "nb"
<re_irc> <James Munns> we can't force jp to like async
<re_irc> <James Munns> and we can't force dirbaio to like nb
<re_irc> <thalesfragoso> adamgreig: Curious that I don't think that applies to &mut, I wouldn't expect people to get &mut to regblocks, but they sure can...
<re_irc> <James Munns> so like, give people the space to put in the work they want to put in
<re_irc> <James Munns> if it dies out, it dies out.
<re_irc> <James Munns> people will move on and port to oene or the other.
<cr1901> There's an overwhelming async bias in Rust
<re_irc> <James Munns> We have no LTS
<re_irc> <James Munns> if no one shows up to maintain it, it will suck, and people will move to other things.
<re_irc> <James Munns> it can go to e-h-community to retire.
<re_irc> <James Munns> but if people maintain it and use it everywhere? Great!
<re_irc> <adamgreig> that approach works fine for a single crate but this is basically an entire ecosystem of trait impls/users
<re_irc> <James Munns> who am I to judge their motivations.
<re_irc> <James Munns> We're a loose federation
<re_irc> <adamgreig> maybe we can make some smart decisions today and save a lot of HAL and driver authors a lot of time and effort later, though
<re_irc> <adamgreig> or maybe not, dunno yet, but seems worth considering
<re_irc> <James Munns> we don't have the power or time to make everything consistent
<re_irc> <James Munns> just make suggestions to the federation to follow or not
<re_irc> <James Munns> more tastemakers, than bosses, imo.
<re_irc> <adamgreig> like, we're not going to roll out embedded-hal-semiblocking or whatever
<re_irc> <James Munns> adamgreig: Or we can argue about it for two years and everyone does their own thing.
<re_irc> <adamgreig> we should believe in nb enough to actually think it's useful to have as an option, if we're going to publish it, I think
<re_irc> <adamgreig> hah, sure, that's a fun option too
<re_irc> <therealprof> semiblocking... ๐Ÿ˜„
<re_irc> <adamgreig> I don't think we're delaying e-h-a here though
<re_irc> <dirbaio> I suggest splitting it into "embedded-hal-nb" then, so we don't release something whose future is uncertain in EH1.0
<re_irc> <adamgreig> I expect that will come out and take off and be popular either way
<re_irc> <James Munns> I dunno, I'm not on the core team, I just run the twitter :D
<re_irc> <James Munns> take my advice for what you paid for it
<re_irc> <James Munns> but like, we aren't zephyr or mbed
<re_irc> <adamgreig> oh, it's a bit late but the core team was on my agenda in my head too, maybe next week
<re_irc> <Lachlan> Is nb actually useful for complex systems?
<re_irc> <James Munns> there's no "one ecosystem", there are just a bunch of people who see the value in things working together whenever possible.
<re_irc> <Lachlan> I couldn't figure out how to use it for more than super simple drivers
<re_irc> <James Munns> Lachlan: That's an flamebait question
<re_irc> <James Munns> yes, to some people
<re_irc> <James Munns> no, to others.
<cr1901> is async useful for simple systems?
<cr1901> (where you can read the asm and understand everything going on)
<re_irc> <James Munns> bout the same threshold as rtic, imo
<re_irc> <James Munns> if you can get away with a single busy loop, do that
<re_irc> <Lachlan> James Munns: You're right sorry, did not mean to phase it that way
<re_irc> <James Munns> Lachlan: Sorry, didn't mean to chastise, just saying you're unlikely to get a useful answer to that question, IMO :D
<re_irc> <James Munns> "is ketchup on mac n cheese good"?
<cr1901> some ppl might be unhappy w/ the amount of disagreement today, but honestly I think it's good
<cr1901> it's healthy/shows that ppl are thinking
<re_irc> <James Munns> We've been arguing for years :D
<re_irc> <dirbaio> IME async has never been a problem for "look at the asm" for me :S
<re_irc> <dirbaio> I pretty much never look at the ASM ๐Ÿ˜‚
<re_irc> <James Munns> Honestly there's been a lot less of it lately
<re_irc> <adamgreig> probably why you like async so much ๐Ÿ”ฅ
<re_irc> <James Munns> but there's a history of "spirited discussions" lol
<re_irc> <dirbaio> and when I do it's to optimize some hot loop computation, which is never async, so the ASM reads fine
<re_irc> <adamgreig> (in seriousness I agree though, the overwhelming bulk of code you never need to check the asm, it's the few weird spots...)
<re_irc> <dirbaio> or to troubleshoot bootloader / startup stuff maybe? which again, not async even if you use async in the rest of the code
<cr1901> Or when Rust misoptimizes panic code AGAIN
<cr1901> And brings in all the dead formatting crap
<re_irc> <James Munns> I'll be honest
<re_irc> <dirbaio> cr1901: it never does with panic_immediate_abort :P
<re_irc> <James Munns> I can't even read ASM
<re_irc> <Lumpio> Lachlan: If you want my 2 cents it feels like it's better for blocking than non-blocking. I think the docs at least used to talk about how you could use it for implementing async on top of it etc but so far nobody has managed to do so afaict.
<re_irc> <therealprof> adamgreig: Arduino people don't look at ASM code so seems totally overrated.
<cr1901> panic_immediate_abort should not exist
<re_irc> <dirbaio> agreed ๐Ÿ˜ญ
<cr1901> panic=abort should optimize down to the same down thing
<re_irc> <adamgreig> therealprof: what do you mean?
<re_irc> <Lumpio> Does the async asm not boil down to the same state machine you'd have to write anyways? Granted it wouldn't map very well to the actual source code but
<re_irc> <dirbaio> with "panic=abort", panics compile to calling the panic handler, which does have fmt stuff as args
<re_irc> <adamgreig> I think it's usually a more complicated state machine than most people would bother writing by hand :P
<re_irc> <Lumpio> mmmm complicated state machines
<re_irc> <dirbaio> yeah, async compiles to state machines. the "poll()" fn usually loads a "state" integer from the future, then does a switch based on that to "continue" executing from the last await, etc
<re_irc> <therealprof> adamgreig: I mean we're caring so much about things a lot of people don't even know exists. ๐Ÿ˜‰
<re_irc> <adamgreig> sure, but as usual the people who do know about it care about it vocally
<re_irc> <adamgreig> having people coming from arduino go straight into async is probably great
<re_irc> <adamgreig> but if someone's coming from a career of writing misra c or worse, they probably want something else
<re_irc> <James Munns> "when you are faced with a hard thing, you have two choices: you can avoid the hard thing to make it happen as rarely as possible, or do the hard thing as often as possible, to make it less hard"
<re_irc> <James Munns> (e.g. the linux vs bsd approach to breaking userspace)
<re_irc> <therealprof> Lumpio: If you're lucky. Problem is it's not quite obvious what it will compile to. As always with black magic, you might pull a rabbit or a rat of the hat...
<re_irc> <James Munns> I wouldn't really call it black magic
<re_irc> <James Munns> like, it's no more magic than any other dependency.
<re_irc> <James Munns> yes, it might take some time to figure out why it is doing something
<re_irc> <adamgreig> it's definitely more understandable than the black magic of like, an optimising compiler
<re_irc> <James Munns> but it's not inventing complication just for funsies, unless a library you use is.
<re_irc> <Lumpio> I think asynv is going to be what drives Rust home as the next embedded language so shouldn't be shy to use it extensively. What other language does such things efficiently and with static memory usage?
<re_irc> <James Munns> don't get me wrong, there's a lot to understand about async
<re_irc> <Lumpio> * async
<re_irc> <James Munns> and it will seem like greek at first
<re_irc> <James Munns> but like
<re_irc> <James Munns> we can avoid it entirely
<re_irc> <therealprof> Oh, it's understandable sure, but if you write async code you will have no idea on whether that's going to be lean and mean code or a big fat state machine.
<cr1901> Lumpio: what other language w/o GC has reached critical mass?
<re_irc> <James Munns> or slam hard into it and make sure we have a happy path for people coming from arduino or misra c
<re_irc> <James Munns> like we have with everything else in embedded rust
<re_irc> <dirbaio> async is THE reason I adopted Rust ๐Ÿ˜‚
<re_irc> <James Munns> therealprof: You could say the same about iterators
<re_irc> <therealprof> Heh!
<re_irc> <James Munns> Turns out, a lot of the time the compiler does BETTER than you do, as long as you aren't in opt level 0
<re_irc> <dirbaio> going from "state machine spaghetti in C" to "state machine spaghetti in Rust" wasn't worth rewriting my entire firmware (for already-shipping products!)
<re_irc> going from "state machine spaghetti in C" to "async Rust" was :D
<re_irc> <Lumpio> cr1901: C? Heh
<re_irc> <richarddodd> I think rust async + embedded is so compelling it allows people like me who have never had any electronic engineering training to program embedded systems without any help, essentially.
<re_irc> <adamgreig> (I'm beyond late, thanks everyone! back later...)
<re_irc> <richarddodd> And get something that is pretty close to optimal
<re_irc> <James Munns> yeah, dropping off for dinner
<re_irc> <richarddodd> ๐Ÿ‘‹
<re_irc> <therealprof> Well, async code generation is a multistep problem... First the compiler is trying hard to not create a massive state machine and then it's trying hard to optimize the rest into efficient maschine code.
<cr1901> Look my priority is "to get Rust to fit anywhere C can within reason". It's one of the things that gives me joy when programming, and I'm not giving up that goal w/o a fight. Requiring an async runtime for anything more complicated than toy apps _may or may not_ be a threat to that.
<re_irc> <richarddodd> I guarantee the compiler is better at it than me
<re_irc> <James Munns> did you ever use protothreads therealprof?
<re_irc> <James Munns> an async runtime can fit in like 500 bytes
<re_irc> <dirbaio> and async does not require a runtime!!!!
<re_irc> <James Munns> like, that might not work for all the msp430s
<re_irc> <James Munns> but like, I fit it in my 2K stm32g0 bootloader
<re_irc> <James Munns> it requires an executor :D
<cr1901> This is why I said "for anything more complicated than toy apps" :P
<re_irc> <dirbaio> you can just "loop { poll() }", it'll give you similar code size as code using "nb"
<re_irc> <Lachlan> It seems to me like any complicated app that doesn't use async is going to have an implementation of something very similar, just more manual
<re_irc> <richarddodd> If you're writing complicated apps, I'd be willing to bet that you don't to much better than embassy even if you hand-code everything down to MMIO
<re_irc> <Lachlan> You have interrupts, state machines, etc
<re_irc> <dirbaio> and if "loop { poll() }" is not suitable for non-toy apps, then "nb" is not suitable for non-toy apps! :D
<re_irc> <dirbaio> because you can ONLY do "loop { poll() }" with "nb", vs with async you can do that or use a multitask excecutor
<re_irc> <richarddodd> If you're writing simple apps, then nothing stops you from doing MMIO
<re_irc> <James Munns> okay really going to dinner
<re_irc> <dirbaio> * executor
<re_irc> <therealprof> James Munns: I have not. Anything with "thread" in the name is an automatic turnoff for me. ๐Ÿ˜ฌ
<re_irc> <Lumpio> Wonder when we'll have stable GATs (and async traits)
<re_irc> <James Munns> therealprof: oh man, you really missed out on a cursed thing
<re_irc> <James Munns> it's basically a duff's device state machine
<re_irc> <Christopher Hunt> For me, async translates to energy efficiency while still being able to reason my code. :-)
<re_irc> <James Munns> but fundamentally, rust's async/await is just a safe version of protothreads
<re_irc> <dirbaio> I think I've seen the protothreads thing it was horrible C macro abuse right?
<re_irc> <dirbaio> * thing,
<re_irc> <James Munns> (both are cooperative multitasking)
<re_irc> <therealprof> Now you're turning me on...
<cr1901> >and if "loop { poll() }" is not suitable for non-toy apps, then "nb" is not suitable for non-toy apps! :D <-- yes, that's probably true...
<re_irc> <James Munns> only in protothreads, you can't use local variables, because of cursed impl details
<re_irc> <dirbaio> oh yeah macros with unbalanced braces, yay
<re_irc> <James Munns> you have to use statics ๐Ÿคฃ
<cr1901> You still need to bring in embassy for the leaf-futures to inject a noop waker, correct?
<re_irc> <dirbaio> so glad rust forbids that
<cr1901> or does the HAL constructor take the waker?
<re_irc> <dirbaio> cr1901: no, you can just create one https://github.com/jamesmunns/cassette/blob/main/src/lib.rs#L227-L240
<re_irc> <dirbaio> and pass that to future's "poll()" fns
<re_irc> <dirbaio> actually if the HAL stores that, it probably won't get optimized away
<re_irc> <dirbaio> I wonder if it'd be worth it for async HALs to have an "ignore wakers" feature flag
<re_irc> <dirbaio> so they don't store the waker, don't wake them on interrupt
<re_irc> <dirbaio> then it _would_ all get optimized out
<cr1901> erm, I thought future::poll takes no args
<re_irc> <richarddodd> James Munns: Just skimming that doc - it's very interesting and well worth a read.
<re_irc> <dirbaio> it takes a "Context" which contains the waker
<re_irc> Contiki OS used to be based on it
<re_irc> <James Munns> richarddodd: It's a fun blast from the past.
<cr1901> Also, Pin<> automatically gets added to method calls?
<re_irc> <richarddodd> It builds a state machine as a massive "switch" statement :)
<re_irc> <dirbaio> if the future is Unpin you just do "Pin::new(&mut fut)", if it isn't you pin it with "pin_mut!(fut)"
<re_irc> <Lachlan> And in a few versions, you can just to "let pinned = pin!(fut)"
<re_irc> <dirbaio> pinning is perhaps the one ergonomics "downside" vs nb
<re_irc> <therealprof> Async is great for a lot of applications, although it is rather undeterministic which is bad for a lot of other applications, especially in the embedded realm.
cr1901_ has joined #rust-embedded
<re_irc> <richarddodd> therealprof: In cases where you need determinism for part of your app, I think you could run that part on a higher priority, then for stuff where you don't run that on an async executor.
<re_irc> <therealprof> richarddodd: I meant also wrt to the amount of code generated.
<re_irc> <dirbaio> you can still do priority-based preemption with async https://github.com/embassy-rs/embassy/blob/master/examples/nrf/src/bin/multiprio.rs
<cr1901_> How does Rust know to "convert" the first argument to poll() back to Pin<> after doing the Deref?
<re_irc> <therealprof> A small change in code can have a huge impact in performance and size.
<re_irc> <dirbaio> async code is bigger than the equivalent blocking code yup
<re_irc> <Lumpio> I'm still trying to figure out if my async idea of "directly awaiting interrupts with priorities" would work in practice. Maybe I should try it since I have free time next month...
<re_irc> <dirbaio> I wouldn't say it's that unpredictable though, it's still roughly proportional to the amount of lines of code
<re_irc> <dirbaio> also, fully hand-written state machines would also have bigger code size than the equivalent blocking code
<re_irc> <dirbaio> i'd say async vs handwritten-state-machine code size is similar
<re_irc> <dirbaio> after all it's what the compiler is doing for you
<re_irc> <therealprof> dirbaio: Sure, but that's often not necessary.
cr1901 has quit [Ping timeout: 272 seconds]
<re_irc> <richarddodd> I think that if you're problem is simple enough that you can reason about without async, then there's nothing wrong with doing that.
<re_irc> <richarddodd> But that gets harrrrrd
<re_irc> <dirbaio> Lumpio: embassy had a while ago an "interruptFuture" that was "wait for interrupt to trigger", it didn't work out in practice, and was removed
<re_irc> or you mean something else?
<re_irc> <therealprof> dirbaio: Not really. I've seen functions where an added async call has squared the size of the functiion.
dc740 has quit [Remote host closed the connection]
<re_irc> <therealprof> * compiled function.
<re_irc> <therealprof> Butterfly effect...
<re_irc> <dirbaio> perhaps due to inlining heuristics changing opinion?
<re_irc> <dirbaio> adding an async call can cause storing/loading things to the future that could be kept on stack previously, so it's expected it might increase code size, but it should still be linear...
<re_irc> <therealprof> dirbaio: Maybe, but I suspect the state machine generator not figuring out the proper scoping and generating an unnecessarily complex state machine.
<re_irc> <dirbaio> if it's really quadratic it'd be worth reporting!
<re_irc> <dirbaio> i've never seen that personally though
<re_irc> <dirbaio> also async codegen is much better now than when it came out
<re_irc> <therealprof> I think there're even tests on the rust perf for such worst case scenario.
<re_irc> <dirbaio> :D
<re_irc> <therealprof> dirbaio: Oh definitely.
starblue has quit [*.net *.split]
gsalazar has quit [*.net *.split]
limpkin has quit [*.net *.split]
rektide_ has quit [*.net *.split]
agg has quit [*.net *.split]
GenTooMan has quit [*.net *.split]
Abhishek_ has quit [*.net *.split]
darknighte has quit [*.net *.split]
sauce has quit [*.net *.split]
Ekho has quit [*.net *.split]
tafa has quit [*.net *.split]
Rahix has quit [*.net *.split]
emerent has quit [*.net *.split]
funsafe has quit [*.net *.split]
Shell has quit [*.net *.split]
xnor has quit [*.net *.split]
dequbed has quit [*.net *.split]
cyrozap has quit [*.net *.split]
starblue has joined #rust-embedded
GenTooMan has joined #rust-embedded
emerent has joined #rust-embedded
funsafe has joined #rust-embedded
gsalazar has joined #rust-embedded
rektide_ has joined #rust-embedded
Abhishek_ has joined #rust-embedded
darknighte has joined #rust-embedded
agg has joined #rust-embedded
sauce has joined #rust-embedded
limpkin has joined #rust-embedded
Shell has joined #rust-embedded
xnor has joined #rust-embedded
Ekho has joined #rust-embedded
Rahix has joined #rust-embedded
cyrozap has joined #rust-embedded
tafa has joined #rust-embedded
dequbed has joined #rust-embedded
<re_irc> <Lumpio> dirbaio: How'd it not work out?
<re_irc> <dirbaio> when writing drivers you usually don't want to await an entire interrupt, you want to await individual "events"
<re_irc> <dirbaio> for example on uart rx/tx, if the driver allows "splitting", you can have one task awaiting rx, another awaiting tx
<re_irc> <Lumpio> In that case you'd await the interrupt and then read the event flags
<re_irc> <dirbaio> and you want rx to only wake the rx task
<re_irc> <Lumpio> Oh hm, peripherals huh
<re_irc> <dirbaio> and even if you're OK with the interrupt waking both tasks, it's not trivial to do because now you have to allow associating N wakers to an interrupt (so multiple tasks can await the same interrupt concurrently), so you now need a dynamically-sized Vec, or specifying the size upfront which can overflow at runtime
<re_irc> <Lumpio> +split
<re_irc> <Lumpio> That's a fair point
<re_irc> <dirbaio> even more with complex peripherals like usb :)
<re_irc> <Lumpio> Just put them in a lock free linked list :-)
<re_irc> <Lumpio> What could possibly go wrong
<re_irc> <dirbaio> thanks to Pin it's possible to do intrusive linked lists within the futures yup
<re_irc> <dirbaio> that deregister themselves on drop. the "futures-intrusive" crate does this a lot
<re_irc> <dirbaio> but it's very complex, which causes subpar code size
<re_irc> <dirbaio> also stm32 LOVES to share a single irq between multiple peripherals
<re_irc> I was reading this - does this even use the UART interrupts or just DMA?
<re_irc> <dirbaio> yeah stm32 uart uses the DMA irqs only
<re_irc> <Lumpio> The DMA interrupts don't seem to be shared between channels
<re_irc> <Lumpio> (Although I guess you could plausibly want to share the channel)
<re_irc> <Lumpio> Oh it works like this, I guess this isn't that bad
<re_irc> <Lumpio> At least the number of hardware interrupts never changes at runtime
<re_irc> <dirbaio> on some stm32s, dma channels do share irqs
dc740 has joined #rust-embedded
<re_irc> <Lumpio> ah, I just checked a couple of reference manuals
<re_irc> <dirbaio> currently embassy-stm32 handles shared dma irqs with build.rs codegen (and doesn't handle shared irqs for normal peripherals at all, that's TODO)
<re_irc> <dirbaio> worst offenders are cortex-m0s which are limited to 32 irqs
<re_irc> <Lumpio> mm
<re_irc> <dirbaio> my favourite is stm32g0c1, with gems like "DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR", "USART3_4_5_6_LPUART1", "TIM6_DAC_LPTIM1" :D
<Lumpio-> oo that sounds very nice
<re_irc> <Lachlan> i bet there's some mcu out there where every event is on the same irq
<re_irc> <James Munns> x86
<re_irc> <James Munns> also, nrc IMMEDIATELY subtweeting me: https://twitter.com/nick_r_cameron/status/1541886270879711232
<re_irc> <James Munns> (not really, but funny timing)
<re_irc> <adamgreig> Hah
<re_irc> <adamgreig> Tingling spidey sense perhaps
<re_irc> <James Munns> I actually know what nrc is talking about (or at least one aspect of it - primarily the discussion around GATs), but maybe it's a gut check for me.
dc740 has quit [Remote host closed the connection]
<re_irc> <dirbaio> lol tha GATs discussion is still going
<re_irc> <dirbaio> lol tha GATs discussion is still going
<re_irc> <dirbaio> I honestly don't get the argument GATs increase complexity
<re_irc> <dirbaio> Without GATs: types can have generics, except if they're associated types of traits
<re_irc> With GATs: types can have generics
<re_irc> <dirbaio> adding GATs feels almost like fixing a bug to me ๐Ÿ˜‚
<re_irc> <chrysn (@chrysn:matrix.org)> There's the "where Self: 'a" wart, but it's kind of logical from some PoV, and there can still be smoother ways to express that.
<re_irc> <dirbaio> * a bugfix to me instead of a feature
<re_irc> <dirbaio> yeah that's a bit warty, imo that where clause shouldn't be forced like it is now ...
<re_irc> <dirbaio> like, you should get to choose whether to have it or not. Very rarely you don't need it
<re_irc> <dirbaio> it shouldn't be implicit, there's already enough weird implicit bounds in rust
<re_irc> <chrysn (@chrysn:matrix.org)> Thing is, without the clause it's hard to really say "for any "'a", including static" ... maybe "<'a + ?static>" could be an alternative, but I don't think that that alone should stop GATs
<re_irc> <dirbaio> * enough weird implicit bounds in rust already
<re_irc> <dirbaio> not sure what you mean, "Foo<'a>" already allows "'static"
<re_irc> <chrysn (@chrysn:matrix.org)> Yes, so if people preferred to not write "where Self: 'a" to limit what "'a" can be (but want "not longer-living than the type" to be the default), that'd be an alternative.
<re_irc> <chrysn (@chrysn:matrix.org)> I'm fine with the defaults as they are ("for every" just means "for every"), but if that were the showstopper there'd be alternatives.
<re_irc> <dirbaio> hmm
<re_irc> <dirbaio> currently GATs require "where Self: 'a" because they couldn't decide whether to make it implicit or not
<re_irc> <dirbaio> so they settled on _not_ making it implicit, but making it _mandatory_ to have according to some weird heuristic
<re_irc> <dirbaio> so that making it implicit in the future wouldn't be a breaking chane
<re_irc> <dirbaio> * change
<re_irc> <dirbaio> (all existing code would have it explicitly, so by making it implicit you wouldn't be changing the meaning of any existing code
<re_irc> <newam> My only concern with GATs is that I don't know enough type theory to know if the current implementation has any significant limitations, I'll leave that to the experts ๐Ÿ™ƒ
<re_irc> <dirbaio> * code)
<re_irc> <dirbaio> but
<re_irc> <dirbaio> there ARE cases where you don't want the "where Self: 'a" clause
<re_irc> <dirbaio> if the GAT borrows from somewhere other than self
<re_irc> <dirbaio> it's rare, but it's allowed
<re_irc> <chrysn (@chrysn:matrix.org)> ... or whatever produces it consumes self
<re_irc> <dirbaio> yeah, but in that case I think the heuristic doesn't trigger, not sure
<re_irc> <dirbaio> or also if the gat borrows _part_ of self, not the entire thing
<re_irc> <dirbaio> so IMO the clause shouldn't be mandated like now, but shouldn't be implicit either
<re_irc> <dirbaio> if you need it you write it, if you don't need it you don't write it
<re_irc> <thalesfragoso> Lachlan: I don't think it works with the let, the macro has to shadow the old variable
<re_irc> <Lachlan> thalesfragoso: https://doc.rust-lang.org/std/pin/macro.pin.html
<re_irc> <Lachlan> Makes pin way more usable
<re_irc> <thalesfragoso> Lachlan: Hmm, I thought that lifetime extension only worked with temporaries. Almost all examples uses a temporary, but there's one without it
<re_irc> <Lachlan> Yep, it's apparently only possible with macros 2.0
<re_irc> <thalesfragoso> Lachlan: The macro is simple, the difference is that libstd can access the field of Pin
<re_irc> <thalesfragoso> But I can't make it work here, won't that have a weird effect with Copy types ?
<re_irc> <Lachlan> Why would it?
<re_irc> <thalesfragoso> Lachlan: It would copy the future instead of moving it to the inner scope and extend it as a temporary
<re_irc> <thalesfragoso> e.g.
<re_irc> <dirbaio> it'd copy then pin the copy, which is fine because you can only call pin-requiring methods on the copy, which is pinned
<re_irc> <thalesfragoso> dirbaio: Yeah, sure, but that is quite unexpected
<re_irc> <James Munns> Accidentally-Copy futures would be hard to do, IMO
<re_irc> <dirbaio> !Unpin stuff is usually not Copy
<re_irc> <James Munns> you'd have to make the future-impl'ing type explicitly "#[derive(Copy)]"?
<re_irc> <thalesfragoso> James Munns: I didn't say make a future accidentally Cipy, I meant accidentally copy a Copy future
<re_irc> <thalesfragoso> * Copy,
<re_irc> <James Munns> have you ever seen a "Copy" future?
<re_irc> <dirbaio> futures from "async" blocks/fns are never Copy
<re_irc> <James Munns> like, the "Output" might be "Copy", but I don't think I've ever seen an "impl Future" that was copy
<re_irc> <James Munns> totally could happen! And would lead to weird results.
<re_irc> <James Munns> I just don't think I've seen one.
<re_irc> <thalesfragoso> James Munns: Maybe timer futures
<re_irc> <dirbaio> you can make a Copy future if you manually-implement it
<re_irc> <dirbaio> but if you also need it to be !Unpin then chances are whatever you're doing is unsound
<re_irc> <thalesfragoso> dirbaio: Or just weird
<re_irc> <thalesfragoso> The thing is, you will also use the macro in genercis
<re_irc> <thalesfragoso> Generics*
<re_irc> <dirbaio> I think you can't copy a "Copy" thing once it's pinned
<re_irc> <dirbaio> so you can only copy it before pinning it
<re_irc> <thalesfragoso> I just found the behavior very unexpected
<re_irc> <dirbaio> so maybe yes, you could write sound "Copy + !Unpin" futures... but the question is WHY WOULD YOU DO THAT
<re_irc> <dirbaio> sooooo cursed :D
<re_irc> <thalesfragoso> dirbaio: Maybe for "sleep_until" futures
<re_irc> <dirbaio> yeah but why
<re_irc> <dirbaio> why do you want it to be Copy
<re_irc> <dirbaio> vs just creating two of them
<re_irc> <dirbaio> ๐Ÿคฃ
<re_irc> <thalesfragoso> dirbaio: Why not
<re_irc> <dirbaio> because it's so oddddddd
<re_irc> <dirbaio> your users won't take advantage of it because they'll assume it's not copy because it's a fut
<re_irc> <thalesfragoso> I mean, the macro is going in libstd and be there forever, it better be good
<re_irc> <dirbaio> also it being copy dosn't make it more ergeonomic
<re_irc> <dirbaio> thalesfragoso: yeahhhhh I dunno, I think it's fine. It behaves exactly like a function call
<re_irc> <dirbaio> like. "Box::pin()" also has the exact same behavior with Copy types
<re_irc> <dirbaio> so I think it's fine
<re_irc> <thalesfragoso> Hmm, maybe I got to used with the semantics of the current pin!
<re_irc> <dirbaio> stockholm syndrome? :D
<re_irc> <thalesfragoso> dirbaio: I mean, the std one is a bit better, but not by far
<re_irc> <thalesfragoso> It ends up with the same problem with select loops and whatnot
<re_irc> <dirbaio> still makes the lifetime "too long"? aww
<re_irc> <dirbaio> +:(
<re_irc> <thalesfragoso> dirbaio: Well, it moves the value instead of shadowing, but still, same result
<re_irc> <dirbaio> ๐Ÿ‘ป
<re_irc> <thalesfragoso> Actually, pin_mut also moves the thing
<re_irc> <thalesfragoso> But just to test it's owned
<re_irc> <thalesfragoso> Do we have a select macro like the one from tokio in no_std ?
<re_irc> <dirbaio> there's "futures::select_biased!"
<re_irc> <thalesfragoso> dirbaio: I mean, that doesn't require Unpin
<re_irc> <thalesfragoso> The select macro from tokio takes ownership of the future, instead of borrowing
<re_irc> <dirbaio> it doesn't? :o
<re_irc> <thalesfragoso> If you want borrow you pin it yourself and use as_mut, just like you would do in the futures one
<re_irc> <dirbaio> also I've never used the tokio one
<re_irc> <thalesfragoso> I thought it was the same too, but it's a tad different
<re_irc> <dirbaio> ah so it's kind of like "futures"'s "fn select()"?
<re_irc> <dirbaio> it takes owned Unpin, but in practice to make futs Unpin you "pin_mut!" them which makes them borrowed instead of owned
<re_irc> <dirbaio> with the new "pin!" macro you can do "select(pin!(fut1()), pin!(fut2()))" though
<re_irc> <dirbaio> and these _do_ get dropped earlier
<re_irc> <dirbaio> it's not like "pin_mut!" which forced you to do a binding, which would always live until end of scope (?)
<re_irc> <dirbaio> and these _do_ get dropped earlier, at end of statement
<re_irc> <dirbaio> it's not like "pin_mut!" which forced you to do a binding, which would always live until end of parent scope (?)
<re_irc> <thalesfragoso> dirbaio: But it doesn't need Unpin, because it doesn't give it back
<re_irc> <dirbaio> ahhhhhh okay
<re_irc> <dirbaio> it pins it internally, I see
<re_irc> <dirbaio> sounds much more usable than "futures"'s then