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
richardeoin has quit [Ping timeout: 246 seconds]
richardeoin has joined #rust-embedded
sigmaris has quit [Quit: ZNC - https://znc.in]
sigmaris has joined #rust-embedded
<JamesMunns[m]> As promised, first episode of my new podcast, "Self-Directed Research", is now live!
lehmrob has joined #rust-embedded
GuineaWheek[m] has quit [Quit: Idle timeout reached: 172800s]
lehmrob has quit [Quit: Konversation terminated!]
lehmrob has joined #rust-embedded
KevinLynagh[m] has joined #rust-embedded
<KevinLynagh[m]> I'm using the probe-rs debugger via the VS Code extension for the first time --- is it possible to add a breakpoint on any exception? I'm trying to track down a mysterious freeze in an Embassy program running on stm32f103 that somehow bypasses the hardfault handler and leads to all of my CPU registers becoming 0x2100_0000.
<KevinLynagh[m]> (I've never really used a debugger on embedded, so please forgive me if this is a dumb question.)
<KevinLynagh[m]> I diagnosed the register state via STM32CubeProgrammer but wasn't able to figure out what was happening. I'm thinking if I catch the problem sooner via a debugger breakpoint, I might have better luck.
<KevinLynagh[m]> A friend tried to help me do this with GDB, but I've never really used that either and we weren't able to get gdb multiarch built and running on my mac m1. Apparently gdb doesn't support it. I do have lldb, but figured VS Code might be easier since I'm a debugger n00b =D I'm open to whatever suggestions y'all might have though.
<KevinLynagh[m]> Aight, if anyone wants to help hunt this demon down, I've posted a repo with minimal reproducible example and register dumps: https://github.com/lynaghk/repro-stm32f103-rust-embassy-freeze
<KevinLynagh[m]> * Aight, if anyone wants to help hunt this demon down, I've posted a repo with minimal reproducible example and register dumps. Works on a "blue pill" dev board. https://github.com/lynaghk/repro-stm32f103-rust-embassy-freeze
GuineaWheek[m] has joined #rust-embedded
<GuineaWheek[m]> what's a good way to measure runtime stack usage, assuming the program does not have recursion?
<ryan-summers[m]> https://github.com/japaric/cargo-call-stack might be a good starting point, but AFAIK it is unmaintained and doesn't work with latest rust
<thejpster[m]> you can also paint the stack (ideally using an assembly language routine, hooked to run before main()) and then walk the stack to see where the paint has been rubbed off.
<ryan-summers[m]> Yeah this is something I do a lot with medical devices, where you put a specific pattern at some "high water mark" and then check if the pattern ever gets modified to detect after-the-fact corruption
<ryan-summers[m]> But average run-time stack usage vs worst-case theoretical analysis are oftentimes different
<thejpster[m]> You can also use the MPU to give an access fault if the region beyond the stack is accessed, or use flip-link as your linker, which puts the stack at the bottom of RAM (therefore going beyond the available space is a hard fault). But these tools only tell you "you went too far" not "how close you are to going too far". Which is why I like painting the whole stack first.
cyrozap_ has quit [Quit: ZNC 1.8.2+deb3.1+deb12u1 - https://znc.in]
cyrozap has joined #rust-embedded
<ryan-summers[m]> There's also the case that theoretical analysis is often times not necessarily accurate. Just because call stack traces show that an execution path is possible does not mean that it's ever actually possible given the logic contained within the code
<ryan-summers[m]> So basically, what you do depends on your level of concern raised by an actual stack overflow
<ryan-summers[m]> * stack overflow. Flip-link is indeed quite a nice way of solving these problems
<GeorgesP[m]> Hello, I am struggling trying to implement embedded_io_async::ReadReady for tokio::net::TcpStream (doing so in a custom adapter copied from embedded_io_adapters::tokio_1). The required fn to impl is not async (read_ready()) but TcpStream provide poll_read_ready() which need a Context as argument. How can I get the context from within my adapter? Thanks for your help
<ryan-summers[m]> GeorgesP[m]: I suspect https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.ready may be a more appropriate method to use
<GeorgesP[m]> <ryan-summers[m]> "I suspect https://docs.rs/tokio..."; <- ready() is async and blocking... The whole purpose of using ReadReady trait is to poll the readiness without blocking.
<thejpster[m]> people keep asking about stack painting, so here's a PR for cortex-m that paints the stack on startup: https://github.com/rust-embedded/cortex-m/pull/548
<thejpster[m]> looking for tide marks in the paint is an exercise left for the reader (because it depends on whether you have a heap or not).
<ryan-summers[m]> I really like that. Would be quite nice to have a cortex-m function to check the watermark as well. Is there no predefined heap end variable? I thought the heap had a predefined size
<dirbaio[m]> there's no linker section for the heap. the embedded-alloc examples make it a static which ends up going in .bss
<tamme[m]> thejpster[m]: oh is this then the right moment to discuss about the color of the <del>bikeshed</del>stack?
<ryan-summers[m]> Ah maybe I'm thinking of C-isms around predefined stack size then
<ryan-summers[m]> * around predefined ~stack, * stack~, * heap size then
<ryan-summers[m]> s/stack/heap/
<thejpster[m]> sbrk - the stack break function - dynamically moves the boundary between the heap and the stack
<thejpster[m]> tamme[m]: we can definitely talk about that new clippy lint that hates you cortex-m-rt fails to indent text after a bullet point
cyrozap has quit [Quit: ZNC 1.8.2+deb3.1+deb12u1 - https://znc.in]
<tamme[m]> oh weird, did clippy complain about the missing indents?
<tamme[m]> or did cargo doc just not render them?
<thejpster[m]> also, the CI seems to be broken and I'm pretty sure I didn't break it.
<ryan-summers[m]> I think the latest stable broke a ton of clippy checks and whatnot. I saw this while touching some repos lately
<ryan-summers[m]> * latest stable rustc broke a
<ryan-summers[m]> * latest stable rust version broke a
<thejpster[m]> also it's weird that I'm getting errors about things I have just fixed.
<thejpster[m]> it's like it didn't see my latest push, or it's not looking at my branch at all.
<thejpster[m]> It's also weird that an admin didn't have to approve the CI run, because I'm not on the team and you should not trust me.
<thejpster[m]> I might have pushed a PR that reprogrammed the voltage regulator on your HIL hardware, and then sent all your keys to pastebin.
<ryan-summers[m]> Have you contributed to cortex-m before? Once you have one contribution, it doesn't require an approval (if its configured as such in the repo)
<ryan-summers[m]> And it looks like you have already contributed once, so that seems like its working as-intended
<thejpster[m]> oh, once trusted always trusted.
cyrozap has joined #rust-embedded
cyrozap has quit [Quit: ZNC 1.8.2+deb3.1+deb12u1 - https://znc.in]
cyrozap has joined #rust-embedded
cyrozap has quit [Client Quit]
cyrozap has joined #rust-embedded
<M9names[m]> <KevinLynagh[m]> "Aight, if anyone wants to help..." <- how frequently is this reproducing for you? i've given it a lot of hell and the best i got (once) is that the debugger stopped being able to talk to the core.
<M9names[m]> if the debugger can't talk to the hardware you'll have no idea whether it hit a hardfault or not. If your problem is the same, that would explain the bogus register values via stm's debugger.
<KevinLynagh[m]> M9names[m]: > <@9names:matrix.org> how frequently is this reproducing for you? i've given it a lot of hell and the best i got (once) is that the debugger stopped being able to talk to the core.
<KevinLynagh[m]> > if the debugger can't talk to the hardware you'll have no idea whether it hit a hardfault or not. If your problem is the same, that would explain the bogus register values via stm's debugger.
<KevinLynagh[m]> Thanks for giving it a shot! I'm still able to read out peripheral registers with Cube (i.e., watching the DMA update the ADC's DR register). So doesn't that mean the debugger is still working? Or is talking to the core separate from talking to the other registers on the MCU?
<KevinLynagh[m]> For me it's reproducing regularly. E.g., I just re-flashed with the firmware that's pushed up to that repo, power cycled everything, and started the frontend read. it froze up within 10s
<M9names[m]> if you can still see peripheral registers updating the debug core connection is still fine, so it's something different
<KevinLynagh[m]> I should add a note that I'm on MacOS and running this through two external USB hubs. (because M1 laptop ports...)
lehmrob has quit [Quit: Konversation terminated!]
lehmrob has joined #rust-embedded
<dirbaio[m]> you should check whether you have a fake stm32f1
<dirbaio[m]> I'm not sure how, but this is a common problem with F1s if you source them from dubious sources
<dirbaio[m]> where did you get the chip/board from?
<KevinLynagh[m]> Yeah, I did that yesterday when you suggested. Sorry if I wasn't clear about it. Most of the tests I've run have been on a custom board assembled by JLCPCB, but I also repro'd on a blue pill dev board I had laying around. Both yielded the same info when I connected to them via Cube MX Programmer
<KevinLynagh[m]> If you have any stm32f103 board laying around that you know is genuine, you should be able to repro yourself in 5 minutes via this repo https://github.com/lynaghk/repro-stm32f103-rust-embassy-freeze/
<dirbaio[m]> i'm not sure what fakes report in cubemxprog, it's possible they report "yep i'm a totally legit stm32f1"
<dirbaio[m]> I don't think JLCPCB would give you a fake tho? so maybe it's not this
cyrozap has quit [Quit: ZNC 1.8.2+deb3.1+deb12u1 - https://znc.in]
cyrozap has joined #rust-embedded
lehmrob has quit [Quit: Konversation terminated!]
<KevinLynagh[m]> <M9names[m]> "how frequently is this reproduci..." <- > <@9names:matrix.org> how frequently is this reproducing for you? i've given it a lot of hell and the best i got (once) is that the debugger stopped being able to talk to the core.
<KevinLynagh[m]> > if the debugger can't talk to the hardware you'll have no idea whether it hit a hardfault or not. If your problem is the same, that would explain the bogus register values via stm's debugger.
<KevinLynagh[m]> Did you reproduce the freeze? Regardless of the debugger, did running that frontend program which printed the ADC data eventually freeze up, or did it keep going for a few minutes?
lehmrob has joined #rust-embedded
<KevinLynagh[m]> If it goes for more than 30 seconds I quit the frontend program, power cycle the device manually (i.e., unplug, wait 2 seconds, plug in again ) to clear and reset USB bus state. Then I fire up the frontend program again and it'll usually freeze up in ~15 seconds.
lehmrob has quit [Quit: Konversation terminated!]
lehmrob has joined #rust-embedded
lehmrob has quit [Client Quit]
lehmrob has joined #rust-embedded
lehmrob has quit [Client Quit]
sourcebox[m] has joined #rust-embedded
<sourcebox[m]> I've done some more testing with my USB code based on usb-device and I think that moving the poll function into an interrupt handler will improve some things. But I think I need to pass a closure to the handler that captures things like the class list, so how can I get this into some construct with mutexes to get it static?
flippette[m] has quit [Quit: Idle timeout reached: 172800s]
<JamesMunns[m]> yeah, it's important to note that `fn()` - a function pointer, isn't the same as Fn/FnMut/FnOnce traits.
<JamesMunns[m]> Both a function pointer and a closure impl the traits, but a closure can't become a fn pointer
<JamesMunns[m]> the issue with storing a closure in a static is that closures are "un-nameable" types
<JamesMunns[m]> and (without nightly features) statics must have the types named
<JamesMunns[m]> typically, the only way around this would be to have a `Box<dyn FnMut + 'static>` or so to say:
<JamesMunns[m]> * this boxed item (the closure), which contains only owned data (or `'static references`), can be used for whatever purpose
<sourcebox[m]> I have alloc available but not used yet.
<sourcebox[m]> Are there any other alternatives to handle the USB poling in an interrupt?
<sourcebox[m]> Because the timing is not that good otherwise for obvious reasons if you do more in your main loop than just a bit of example code.
<sourcebox[m]> I'm using async, so possibly running it in an interrupt executor is an option, but then also a waker is needed.
<JamesMunns[m]> There are other options, for example embassy-usb has impls for different HALs, which bring their own interrupt management IIRC
<JamesMunns[m]> I'm not sure how else you would do it, portably, using usb-device. Maybe others have an idea!
<sourcebox[m]> I know, but my use case here with ESP32 and isochronous transfers is not really covered yet.
<JamesMunns[m]> gotcha! I don't know how to bridge that gap! Just sharing what I know about calling closures from free functions.
<ryan-summers[m]> Out of curiosity, why is your main loop blocking the USB ISR?
<sourcebox[m]> At least I now know that it's not just my stupidity that makes it hard to solve.
<ryan-summers[m]> If its because you're accessing the USB device data and processing the ISOC transfers, maybe you could process them immediately in the USB ISR handler and move them immediately into an MPMC queue or other fifo
<sourcebox[m]> My main loop is not blocking the ISR. The ISR is not used atm and it's exactly what I want to make work.
<ryan-summers[m]> Ah why is that a problem? From what I understand you don't need a closure to do that
<ryan-summers[m]> We have an RTIC app where the USB device is used in both an ISR and in the main loop
<ryan-summers[m]> Or is that the behavior youre trying to emulate without using a framework like RTIC?
<sourcebox[m]> No, I just want to move the poll function and processing of results into the ISR.
<ryan-summers[m]> For clarity, you're not using a framework like RTIC or embassy?
<sourcebox[m]> I'm using embassy within esp-hal.
<sourcebox[m]> But I'm not using the USB part of embassy.
<ryan-summers[m]> Could you not just spawn an async task that owns the whole UsbDevice and move that into the ISR somehow? I guess that's probably what you're trying to do, but embassy has the ISR definition somewhere under the hood? Sorry I'm not super familiar with embassy and ISR management, I've only used it with toy projects
<sourcebox[m]> This is part of esp-hal.
<ryan-summers[m]> But it seems like it should be relatively easy to somehow move the UsbDevice into the ISR to use it there exclusively, not sure where this boxing of closures etc is coming from
<ryan-summers[m]> Ah okay, so you're trying to write this all abstractly?
<ryan-summers[m]> Thats indeed harder...
<sourcebox[m]> This ISR part of course only has to do the polling, not the setup of the device, so you can't just move it in completely.
<ryan-summers[m]> So basically what you want to do is ask the user to provide some closure that does the polling + processing and you'll move that closure into the ISR directly and call it, but since you're writing this for the HAL, you don't have any concrete types as to what that closure actually does, since its app dependent, right?
<sourcebox[m]> Yes, that's basically it.
<ryan-summers[m]> This is kinda more reminiscent of allowing users to define their own ISR handlers. Maybe there's some kind of overload process that could work instead? In C, you could do this with weak references
<ryan-summers[m]> Does Rust support anything like that? Has any other project essentially exposed a dynamic ISR binding mechanism?
<ryan-summers[m]> Maybe you could expose a feature flag in the HAL and allow the user to implement the ISR within their app directly and provide some convenience functions to help with moving the data around etc
<sourcebox[m]> I'm noz really doing this in the HAL, it's the board crate that abstracts these things away from the app.
<sourcebox[m]> s/noz/not/
<ryan-summers[m]> But what James mentioned about storing closures in static types is indeed true, it's very hard and something I fought with when writing up an MQTT generic handler lib. I was trying to keep a hash table of (key, callback). What you _could_ do is make a static with something like `Option<fn(UsbDevice)>`, since that has a known size at comp time
<ryan-summers[m]> I.e. don't use the Fn* trait objects, but instead use the raw function pointer definition, which is a concrete type that impls the Fn* traits
<sourcebox[m]> I can try if that is working, the device is already a complex type I guess.
<ryan-summers[m]> That doesn't require any type of allocation or dynamically sized types, so users wouldn't need alloc. It _is_ more restrictive on the actual function signature though, so you couldn't capture anything more than just the `UsbDevice`. You could provide something like `fn<Context>(UsbDevice, Context)` to allow for other data too
<ryan-summers[m]> Ah wait, having generics in UsbDevice and making some static Option will also cause problems
<sourcebox[m]> With using alloc this throws an error:... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/hPAGqHhphPfIcIuiuxWctByB>)
<dirbaio[m]> dyn FnMut() + Send 'static
<dirbaio[m]> * dyn FnMut() + Send + 'static
<sourcebox[m]> Thx, works.
<sourcebox[m]> This is the kind of stuff i will probably never understand.
<dirbaio[m]> Rust hates callbacks, my advice would be to not try. Instead of taking a closure, expose some fn on_irq() that the user has to call, and let the user register the irq handler themselves
<dirbaio[m]> and the user has to make their own Mutex<RefCell<T>> to share stuff between main and irq
<dirbaio[m]> which has the benefit that they'll most likely know the concrete type, so it won't need dyn nor alloc.
<sourcebox[m]> I'm trying to understand what that means. So no static at all?
<dirbaio[m]> no static in the HAL
<dirbaio[m]> the user defines the irq handle and their own statics
<thejpster[m]> cs should have a MutexCell type because everyone puts a RefCell in the cs Mutex.
<sourcebox[m]> Ok, so what the HAL/BSP should offer is simply this?... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/PMedAsiswMnQYInpNQKAXRqy>)
<dirbaio[m]> or make the user call interrupt::bind_interrupt directly/
<dirbaio[m]> s/bind_interrupt/bind\_interrupt/, s///?/
<dirbaio[m]> that's the standard way to register irq handlers in esp-hal I guess?
<sourcebox[m]> I think so, I never used it before. Have to check if the whole thing works as expected anyway.
<sourcebox[m]> At least the handler is called, this I have checked.
zeenix[m] has quit [Quit: Idle timeout reached: 172800s]
<sourcebox[m]> Of course now, on the user side, stuff gets annoying because you have to know the exact types and trait bounds to put them into a struct that then can be shared with the handler. The types of the classes are complex, they include the bus.
<sourcebox[m]> No closures, no type inference.
<dirbaio[m]> yea.. btu this will always be the case when you put things in a static
<dirbaio[m]> if you don't use dyn+alloc
<dirbaio[m]> no way around it
<dirbaio[m]> it used to be possible wit TAIT but they nerfed it a bit
<sourcebox[m]> Especially the trait bounds drive me crazy. No idea how to figure them out from the inner depths without going completely insane.
<dirbaio[m]> usb-device types are really cursed yep
mightypork has quit [Quit: ZNC - https://znc.in]
<sourcebox[m]> Honestly, I give up on this now. After getting the types right, lifetime issues arise because the classes hold references to the bus and other things.
<dirbaio[m]> try embassy-usb 🙈
<sourcebox[m]> If you get the isochronous stuff right, then we can talk about it. Haven't found anything about it in the sources.
<dirbaio[m]> is it for USB Audio? there's a PR https://github.com/embassy-rs/embassy/pull/3212
<sourcebox[m]> There's still the incomplete transfer handling I did some PR for on the ESP synopsys fork.
<sourcebox[m]> ATM I don't really want to swap out the USB code.
<sourcebox[m]> I can try to change the USB task using an interrupt executor, but that requires a waker to be implemented.
<sourcebox[m]> And that waker needs to be triggered by the ISR somehow, I guess.
<sourcebox[m]> But the ISR has to handle the poll itself, otherwise it triggers over and over because of not having the flags cleared.
<dirbaio[m]> before embassy-usb existed, embassy had an async wrapper over usb-device
<dirbaio[m]> I ... would not recommend
<dirbaio[m]> It got very gnarly for the same reasons it's gnarly to use it with interrupts (complex types, poll has to borrow all classes, classes are generic on the driver type)
<dirbaio[m]> these are mainly the reasons embassy-usb exists 🥲
<dirbaio[m]> maybe if you use async just for scheduling it, instead of doing a full async wrapper, you'll have more luck, dunno.
<sourcebox[m]> What I currently have works to a certain degree, means it runs for hours, maybe even days but at some point it crashes. Hard to investigate why. But the timing by having the poll in a regular executor while doing a shitload of other things and an audio task executor that delays it completely is probably the cause.
<sourcebox[m]> What I can try instead of using a waker is to use embassy-sync::signal to wake up the processing inside an interrupt executor driven task. The IRQ handler needs to disable itself to avoid to get stuck with retriggering. The async task then re-enables it after each loop iteration. Not sure if that works, bit of hacky approach.
<ryan-summers[m]> Oof, the crowd strike vulnerability root cause was an out of bounds memory access.
<thejpster[m]> has anyone ever tried svd2rust with a register that has `<size>24</size>`?
<thejpster[m]> I see it generating a 32-bit type to hold it, but also adding 1 byte padding after it. Which makes 5.
K900 has quit [Quit: Idle timeout reached: 172800s]
FreeKill[m] has quit [Quit: Idle timeout reached: 172800s]
<balbi[m]> <thejpster[m]> "I see it generating a 32-bit..." <- don't see that, gives me a `u32` as expected. Do you have a minimum repro SVD somewhere? Here's what I used:... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/thUgymBuRzomJLTllOPRAkdk>)
<balbi[m]> s/WSSI-//, s/WSSI//
Dherse[m] has quit [Quit: Idle timeout reached: 172800s]
rmsyn[m] has joined #rust-embedded
<rmsyn[m]> @thejpster: not a <size>24</size>, but have used <size>32</size> with a single 24-bit field. the field generates a 32-bit type, haven't seen the extra padding byte issue you're talking about
<rmsyn[m]> assuming svd2rust is using different logic for the field and register types?
<rmsyn[m]> can take a look, is there a tracking issue?
<rmsyn[m]> ^ if an extra pair of eyes is needed, that is
<GuineaWheek[m]> <ryan-summers[m]> "So basically, what you do..." <- i'm already using flip-link but i do want something more fine-grained -- while i know static analyzers will never be 100% accurate (given the halting problem) i do think it would be nice to have some sort of worst-case analysis
TomB[m] has joined #rust-embedded
<TomB[m]> <GuineaWheek[m]> "i'm already using flip-link..." <- Enable the MPU to get stack overflow exceptions, apply a word pattern and monitor the stack for when it ends while running periodically to get peak stack usage with typical firmware usage patterns, try and analyze the call graph by parsing dwarf info
<GuineaWheek[m]> i'm not running into memory usage issues yet but i guess i'll probably do stack painting
<TomB[m]> That’s it, the dwarf call analysis is limited and heuristics based as there can of course be dynamic indirects (function pointers) or cycles
<GuineaWheek[m]> side note but recursion on embedded seems like a bad idea; wouldn't you just want to use a proper bounded queue and iteration?
<TomB[m]> Rust might be better than C or definitely C++ here as most things are static call paths, you could check for and remove recursions
<TomB[m]> Maybe not all recursions are removable :) or worth the effort to remove anyways
<TomB[m]> There’s also stack allocated variables based on a dynamic parameter that could cause hassles
Foxyloxy_ has quit [Read error: Connection reset by peer]
<GuineaWheek[m]> that's true in C but i don't remember that existing in rust?
<TomB[m]> E.g. stack array for doing some formatting, could likely find and root those out too with dwarf analysis perhaps?
Foxyloxy has joined #rust-embedded
<GuineaWheek[m]> like stack VLAs don't exist in rust iirc
<GuineaWheek[m]> at least not via constructs like [u8; _]
<TomB[m]> Interesting! That’s helpful, I imagine there’s escapes somewhere for alloca like behavior
<TomB[m]> Seems less likely to be used if it’s not easy though with the array declaration though thankfuy
<TomB[m]> s/thankfuy/thankfully /
GrantM11235[m] has joined #rust-embedded
<GrantM11235[m]> Rust doesn't support dynamically sized locals. There is an unfinished nightly feature for it, but it looks like it has stalled https://doc.rust-lang.org/nightly/unstable-book/language-features/unsized-locals.html
<cr1901> Also, recursion is sometimes unavoidable in deps- e.g. serde uses it internally and I doubt miniserde (w/o recursion) is ever going to get the same amount of support.
<cr1901> traits also can't assume that recursion can't occur (without being marked unsafe- I have examples in my IRC logs, but paging dirbaio[m] to give an example when he's around)
m5zs7k has quit [Quit: m5zs7k]
m5zs7k has joined #rust-embedded
<dirbaio[m]> Wut?