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
emerent has quit [Ping timeout: 255 seconds]
emerent has joined #rust-embedded
<BenPye[m]> dirbaio I see that `MakeRegisterArray` has an `allow_cursed` for register arrays with non-even spacing, but is there any good way to represent arrays with missing registers? In this case I have DMA address registers 0-3,5-7...
<dirbaio[m]> there's no way, no
<dirbaio[m]> with "cursed" arrays you'd get
<dirbaio[m]> indices 0-3 go to address 0-3
<dirbaio[m]> indices 4-6 go to address 5-7
<waveguide[m]> "allow_cursed" hehe
<dirbaio[m]> that's not possible to do
<BenPye[m]> Yeah, that's what it looked like. The weirdness is that DMA buffer 0 and 4 share an address (placed contiguously) and so there is no register for 4. Having [4] -> 5 seems like overly surprising behaviour
<BenPye[m]> I guess leaving it as seperate registers isn't the worst thing in the world
<dirbaio[m]> or make an array of 8 and rely on the HAL to not access the unimplemented index
<dirbaio[m]> I usually do that
<dirbaio[m]> because not arrayifying usually forces the HAL to have ugly macros
<BenPye[m]> Hm, just adding a fake register for the extra index?
<dirbaio[m]> yea
<BenPye[m]> Yeah that might make sense. I think I'm gonna have to depend on svdtool anyway since there are also no enumeratedValues elements
<BenPye[m]> * enumeratedValues elements, so in that case it's pretty trivial
<dirbaio[m]> ah you're maintaining the patches
<dirbaio[m]> in stm32-data we just extract once from svd, use the patches to accelerate cleanup, then check in the result yamls to git
<dirbaio[m]> because the SVDs are a disaster, maintaining the patches is a lot of work, vs just directly editing the yaml to do the fixes
<dirbaio[m]> ST rarely updates the SVDs and when they do they often fix mistakes that have already been discovered and fixed
<dirbaio[m]> that's why there's no transforms for stuff that can easily be done by hand, like "add a register"
<dirbaio[m]> but if you want to add them, PRs welcome
<BenPye[m]> Hm, I guess editing the YAML is also fine - I doubt the peripherals will really change. I guess I just kind of like having a set of steps to go from source material -> usable result. But yes, the SVDs are a disaster
<BenPye[m]> For stm32-data how do you deal with common peripherals, you just end up with one SVD as your source of truth and then future versions are derived?
<BenPye[m]> For example this USBFS peripheral, I know there is another device with basically the same peripheral - except it has the missing DMA reigster because of course it does
<dirbaio[m]> the repo has one yaml per peripheral version: {peri}_{version}.yaml https://github.com/embassy-rs/stm32-data/tree/main/data/registers
<dirbaio[m]> then there's a bunch of code that extracts more data from XML files used by STM32CubeMX
<dirbaio[m]> that can be used to sort of figure out which chip has which version
<dirbaio[m]> others are mapped manually
<dirbaio[m]> that generates files like this, where each periph has its version https://github.com/embassy-rs/stm32-data-generated/blob/main/data/chips/STM32H755ZI.json#L7079-L7080
<dirbaio[m]> and the PAC is generated from these
<dirbaio[m]> some versions do small additions/changes
<dirbaio[m]> some versions change absolutely everything
<waveguide[m]> I swear st has the weirdest peripheral IP block set...
<dirbaio[m]> so in the end it was easier to check in a yaml per version
<dirbaio[m]> even if there's duplication
<waveguide[m]> I wish there was ST part packaging with NXP peripherals
M9names[m] has joined #rust-embedded
<M9names[m]> By nxp peripherals do you mean ex-freescale peripherals or ex-philips peripherals?
<M9names[m]> Maybe a series name would help :)
<waveguide[m]> that's a good question, I'm not too familiar with what came when, but for example LPSPI is a really a nice peripheral
<waveguide[m]> as is flexcomm
<waveguide[m]> so maybe a mix of both
<M9names[m]> Thats ex-freescale, so I'm in agreement :)
<waveguide[m]> the lpc parts I've looked at less, I've played more with the kinetis and now imxrt line
<M9names[m]> The lpc parts are not all bad, there's just no consistency at all and the C hals are woefully incomplete and buggy.
<M9names[m]> Maybe this has changed for the newer chips post-merger, but it's hard to give up a grudge
<waveguide[m]> heh, yeah I can understand
notgull has quit [Ping timeout: 255 seconds]
notgull has joined #rust-embedded
crabbedhaloablut has joined #rust-embedded
duderonomy has joined #rust-embedded
Henk[m] has joined #rust-embedded
<Henk[m]> Hi all, does anyone know of a good example of using ADC with DMA on a stm32(f7)? I'd like to get a bit of an overview of how this works so I can understand the reference a bit better
<Henk[m]> <Henk[m]> "Hi all, does anyone know of a..." <- I'd like to do it using the stm32f7 PAC (or the stm32f7xx-hal, but that doesn't have DMA support for ADC yet, it seems)
wucke13[m] has joined #rust-embedded
<wucke13[m]> Henk: Is RTIC an option?
IlPalazzo-ojiisa has joined #rust-embedded
<FlixtheNewbie[m]> I have a problem: I want to put several 12kB arrays on the stack: if I put 2, my binary works, if I put 3, it doesn't work anymore. What are the alternatives? having them as static mut so that they're not on the stack? What are the gotchas when doing that?
<Darius> put them on the heap?
<Darius> (ie call malloc)
<Lumpio-> How big if your chip's RAM then
<Lumpio-> Technically nothing wrong with having big things on your stack in main() or something essentially just using it as an allocator, however there have been issues where depending on how you initialize those arrays it might temporarily allocate many timer more stack than you actually need
<FlixtheNewbie[m]> Lumpio-: Oh, that may be the issue: each array is returned from a function to build another piece of data, itself returned from another function. Maybe it eats more RAM than necessary. I'll try and inline them.
<Lumpio-> times* even
<Lumpio-> Yes returning big arrays can run into issues like that
<FlixtheNewbie[m]> Hey, thanks! after spreading #[inline(always)] everywhere, it works, and the binary is only bigger by ~150B
<FlixtheNewbie[m]> Eh, when I put a 4th array, it overflows again tho 😢
<Lumpio-> So how big's your RAM
<FlixtheNewbie[m]> Up to 40 Kbytes of SRAM, from the doc. Yeah, I cant put all of these arrays on the stack. I need to streamline them somehow :/
<FlixtheNewbie[m]> I wonder how the official firmware is able to work with that amount of ram
<Lumpio-> Well yeah, 12kB * 4 is more than 40kB
<Lumpio-> What is the official firmware doing then
<FlixtheNewbie[m]> It's a keyboard.
<FlixtheNewbie[m]> QMK
<FlixtheNewbie[m]> I used to have like 10layers, and it worked correctly. I only have 4 and I'm out of RAM 😢
<FlixtheNewbie[m]> s/10layers/10 layers/
Guest7221 has quit [Ping timeout: 255 seconds]
<FlixtheNewbie[m]> I didn't imagine I'd have as little ram as that
<waveguide[m]> Why not statically allocate?
<FlixtheNewbie[m]> I think that with how I built the library, I cannot do that: I have a big struct holding all the data, and I call a method to get the HID reports: my_big_struct.reports(). So to call that method, I'd need to load the object in RAM anyway, right?
<Lumpio-> I refuse to believe a keyboard layout takes 12kB
<FlixtheNewbie[m]> Each action takes 24B because there is one variant which is big. The 2nd bigger is like 8B.
<Lumpio-> You have 512 keys?
<Lumpio-> If it's just read-only data you could put it in flash only as a const
<FlixtheNewbie[m]> Welp, you're right, the numbers don't add up 🤔
<FlixtheNewbie[m]> It's not read-only, they hold an internal state
<Lumpio-> I dunno if it's worth the effort in your case but it is possible to arrange things so that readonly stuff stays in flash and only the mutable stuff goes into RAM
<FlixtheNewbie[m]> I have 84 keys, so it should be 84*24 = 2KB. Why is there 12KB more in the binary when I add a layer???
<FlixtheNewbie[m]> It's literally an array
<Lumpio-> What told you "there's 12kB more in the binary"
<FlixtheNewbie[m]> Both ls -l and dfu-util when I download it.
<FlixtheNewbie[m]> Like, I just added a single array:... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/QIRWieaUNLpLEEyIZUnTSVCK>)
<FlixtheNewbie[m]> It's not exactly 12K since I inlined the function calls, but it's close enough
<Lumpio-> Hard to say what's going on without seeing all the code
<dirbaio[m]> binary size is flash size, not ram size
<FlixtheNewbie[m]> There's a huge library with the keyboard logic, there's like 4k lines of convoluted logic :P
<Lumpio-> Maybe the inlining is not working quite the way you expect it to
<FlixtheNewbie[m]> Lumpio-: Well, there is a problem with the array size anyway. With 2KB per layer, I shouldn't eat all the RAM
<FlixtheNewbie[m]> I'll need to figure that out
<FlixtheNewbie[m]> And I'm sure of the size, because I have a test:... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/lZYwbyjQqiquiIZmuzcsSklW>)
<FlixtheNewbie[m]> * And I'm sure of the size, because I have a test:... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/bAgMjvsvQeaksBKzeJKXokql>)
<FlixtheNewbie[m]> <dirbaio[m]> "binary size is flash size..." <- I know that, but when I add a new array having a theoretical size of 2K, I trigger an overflow. There's something wrong in my code
jessebraham[m] has quit [Quit: Idle timeout reached: 172800s]
diondokter[m] has quit [Quit: Idle timeout reached: 172800s]
vollbrecht[m] has quit [Quit: Idle timeout reached: 172800s]
TomB[m] has quit [Quit: Idle timeout reached: 172800s]
Guest7221 has joined #rust-embedded
eZioPan[m] has quit [Quit: Idle timeout reached: 172800s]
Foxyloxy_ has joined #rust-embedded
Foxyloxy has quit [Ping timeout: 272 seconds]
FreeKill[m] has joined #rust-embedded
<FreeKill[m]> Did you say you're putting like 30kB on the stack? That's a lot, I'm not surprised you'd get a stack overflow
kevlar700KevinCh has joined #rust-embedded
<kevlar700KevinCh> FreeKill[m]: Really, why? I can put the whole available ram on the stack in embedded Ada. Linux imposes annoying limits on stack use though.
<FreeKill[m]> It's not a lot if you accommodate it in the link. But lots of default linker maps pick a fairly small size for the stack. I don't actually know what the popular rust crates do
<FreeKill[m]> I have seen auto-generated projects with 16kB stack, for example
aerasto[m] has joined #rust-embedded
<aerasto[m]> A bit new to embedded Rust, so just curious, how have y'all found it to be compared to C? Rust is great with the memory safety stuff but seems like you have to do a lot of unsafe to do embedded stuff
ryan-summers[m] has joined #rust-embedded
<ryan-summers[m]> aerasto[m]: The unsafe all gets hidden behind sound abstractions, so it ends up making it super easy to write high performance, low footprint code. I have an MQTT stack running fully memory safely with much smaller footprints than C with like 13KB of flash or so :)
<ryan-summers[m]> Its also much easier to know that something will work if it compiles compared to C. In general, if rust is compiling, things are usually correct because of some solid typestating etc.
<dngrsspookyvisio> aerasto[m]: Not much unsafe in my experienc, but it depends what you're working on. Frameworks like RTIC and embassy help a lot with resource management so you can avoid static mut globals
<dngrsspookyvisio> s/experienc/experience/
<dngrsspookyvisio> I'm loving the type safety and abstraction capabilities anyway
<aerasto[m]> ryan-summers[m]: Yeah this is true, the compiler checks all the safe stuff, so all that matters is that you implement the unsafe stuff right
<ryan-summers[m]> And the unsafe generally is only with:
<ryan-summers[m]> 2. Raw register access, which is usually rarely needed because the HALs and PACs handle the safety for you
<ryan-summers[m]> 1. Static muts, which frameworks handle for you
<ryan-summers[m]> Then the rest of the unsafe is pretty rare and you can reason about it thoroughly
<ryan-summers[m]> I.e. I tend to use it when resetting a microcontroller to DFU mode or something where I no longer care about memory ownership because we're rebooting
fu5ha[m] has joined #rust-embedded
<fu5ha[m]> Is there a reason that a lot of drivers that use multi-device busses (SPI/I2C) try to take ownership of the entire bus device on creation? Why is the convention not to just pass in `&mut Spi` or whatever when it's actually needed to write to the bus instead? The current convention makes it extremely cumbersome to share a bus across multiple devices which is in theory the whole point x)
diondokter[m] has joined #rust-embedded
<diondokter[m]> <fu5ha[m]> "Is there a reason that a lot..." <- The new embedded hal 1.0 will fix this issue
<fu5ha[m]> diondokter[m]: 👀 what's preventing such a design with the current version? Afaict the EH traits take &mut self where needed
<diondokter[m]> Because with Rust we tend to build using objects. Sometimes a driver needs more info like some sort of configured address.
<diondokter[m]> It also doesn't work with drivers that implement other traits. Say I implement a tcp trait on an Ethernet driver. That trait doesn't know anything about spi and doesn't have an interface that allows you to pass it. So it also doesn't compose well
<diondokter[m]> So now you'd have to keep hold of your driver object and pass it a spi bus every time you want to use it. It simply doesn't scale well.
<diondokter[m]> That's why we take ownership of some kind of spi. In the next version of EH this is actually acknowledged and a distinction is made between the spi bus and a device that van access the spi bus
<dngrsspookyvisio> In the meantime this might help:
<diondokter[m]> s/van/can/
<fu5ha[m]> > So now you'd have to keep hold of your driver object and pass it a spi bus every time you want to use it. It simply doesn't scale well.... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/jBxqmqZlnLskTZrmnFHiiLAu>)
<fu5ha[m]> dngrsspookyvisio: > <@dngrs:matrix.org> In the meantime this might help:... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/ecWrmeFepOKdiwtZgolpqDND>)
<dngrsspookyvisio> Yw!
<dirbaio[m]> note that shared_bus doesn't handle SPI CS pins correctly
<dirbaio[m]> i'd recommend using embedded-hal 1.0 SpiDevice
<dirbaio[m]> if your driver uses embedded-hal 0.2, you have to port it
<dirbaio[m]> there's no correct way to do SPI bus sharing with embedded-hal 0.2, because it leaves the CS pin at the driver's will
<dirbaio[m]> (like, it's not really a shared-bus bug, it's an eh0.2 limitation. there's no way to fix shared-bus as long as it uses eh0.2)
<FlixtheNewbie[m]> <FreeKill[m]> "Did you say you're putting like..." <- TBH, I'm not sure it's a stack overflow, I don't have a debugging device. But if I add a new layer, it crashes. And after inlining functions, I could add one more layer: all points to a SO.
<fu5ha[m]> dirbaio[m]: I will look into it :)
<FreeKill[m]> FlixtheNewbie[m]: Yep that certainly sounds believable. If you really can't get any debug info (even serial?) then you could do static stack analysis
<fu5ha[m]> <dirbaio[m]> "there's no correct way to do SPI..." <- Feels like this isn't really a soundness issue and more just a logic error because "eh 0.2 drivers are used to owning the whole bus, therefore they can happily always leave the chip select active"
<FlixtheNewbie[m]> FreeKill[m]: I can send messages through serial, but I don't really know what could help here. Also, I have a problem with the fact that messages are lost during the time needed to read the socket
<FreeKill[m]> FlixtheNewbie[m]: Well you have your linker map I guess, so you know where the see stack is. You can log the stack pointer at various points
<FlixtheNewbie[m]> How do you manage with the time needed to cat the file? Most of the messages I send at boot are lost
<FreeKill[m]> I don't know what your setup is. But given you know it's going to crash anyway, can you just delay it to give yourself time?
<FlixtheNewbie[m]> Well, that's an idea...
<FreeKill[m]> Or you could use one less object so it *wont* crash, and then look at the stack pointer in the call
<FreeKill[m]> If you see you have 1k left of stack then you know why the next 2k kills you
<FlixtheNewbie[m]> It's supposed to be 2k, but I suspect it's more for some reason. As someone said earlier, there is no way such a small program causes an overflow.
<FlixtheNewbie[m]> This is the init function: where can I safely put the pause? https://gitlab.com/Boiethios/moonlander-firmware/-/blob/new-keebit/src/main.rs?ref_type=heads#L66
<FreeKill[m]> Hold on I am not at my PC
<FlixtheNewbie[m]> I don't really know the step where the host can detect it communicates through serial, if you know what I mean
<FlixtheNewbie[m]> Unlike everyone here, I'm discovering embedded programming, so I lack the most basic knowledge, and since I learn in Rust, I don't know where to find it
<FreeKill[m]> i will message you not in the main chat so we dont spam
<FlixtheNewbie[m]> Thanks
Guest7221 has left #rust-embedded [Error from remote client]
<adamgreig[m]> <kevlar700KevinCh> "Really, why? I can put the whole..." <- It's a lot when the entire RAM is 40kB! Which is the case for this particular chip.
<adamgreig[m]> For typical cortex-m projects on Rust, all ram not statically allocated is available for stack
ragarnoy[m] has joined #rust-embedded
<ragarnoy[m]> Hey, kind of a loaded question but what do you guys do to test the behaviour of an embedded no-std crate on a host ? As in I have a crate that is generic over any board that has a lora antenna and I'd like to test the serialization/deserialization mechanism on my host (x86-64) but apparently doing cargo test --target=x86-.... makes the linker freak out
mameluc[m] has joined #rust-embedded
<mameluc[m]> you only want to test the serialization/deserialization mechanism?
<ragarnoy[m]> no not only that
<ragarnoy[m]> like, testing the message reception deserialization, add it to the message queue etc
<mameluc[m]> anything not hardware related is perfectly fine to run on you computer. I think the easiest is to separate that into a separate crate and run tests normally
<mameluc[m]> no_std is perfectly fine to run as a "regular" rust binary
Guest44 has joined #rust-embedded
<ragarnoy[m]> so it has to be in a separate crate ?
Guest44 has left #rust-embedded [#rust-embedded]
<ragarnoy[m]> What if i need to unit test private structs/methods ?
<mameluc[m]> well it doesn't have to be it is just easier if you can separate stuff. you can put no_std behind a feature too iirc. Somebody who knows better can chime in
<mameluc[m]> I think somebody talked about this earlier today on this channel
<mameluc[m]> here is one way to do it
<mameluc[m]> my repo but I did not write the tests 😅
<mameluc[m]> here is some info too
<ragarnoy[m]> alright i'm gonna go read that
<ragarnoy[m]> seems they still use a different crate though
<JamesMunns[m]> Yeah, personally I would say your life will be easiest for testing if you have a separate lib and bin crate, and keep all the "target specific" stuff in the bin, and all the "portable logic" in the lib, in a way you can test the lib crate on the host.
<ragarnoy[m]> i mean that's already the case and in theory there's no dependency on armv7 on the library but the linker still wont work for some reason (linking with link.exe failed: exit code: 1120)
<ragarnoy[m]> maybe one of the crates prevents me from compiling the tests? Because otherwise I can cargo build just fine, the tests wont though
<ragarnoy[m]> Is there anything to be careful about when using defmt as far as dependencies go ?
<FreeKill[m]> <adamgreig[m]> "For typical cortex-m projects on..." <- For the curiosity of others - it was an 8K structure being instantiated and copied a few times, causing a 27K stack pointer bump
<FlixtheNewbie[m]> Thanks a lot again for your help, FreeKill
<FlixtheNewbie[m]> He told me to use that: https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html
IlPalazzo-ojiisa has quit [Remote host closed the connection]
IlPalazzo-ojiisa has joined #rust-embedded
crabbedhaloablut has quit []
duderonomy has quit [Quit: Textual IRC Client: www.textualapp.com]
<ragarnoy[m]> So I kind of found out what my issue was... somehow, it's windows that doesn't like linking certain binaries that have defmt in them
<ragarnoy[m]> it works fine on linux
Guest7221 has joined #rust-embedded
Guest7221 has left #rust-embedded [Error from remote client]
<ruabmbua[m]> I am not sure, can the llvm linker in the rust tool chain be used under windows?
<ruabmbua[m]> AHH just read the full thread. I agree you should probably put the stuff you want to test in a crate detached from the main crate. At least that is how I approached it, and it works well
IlPalazzo-ojiisa has quit [Remote host closed the connection]
<fu5ha[m]> dirbaio: According to embedded-sdmmc docs, the sdmmc spec requires sometimes performing reads on the bus without asserting the chip's chip select (see https://docs.rs/embedded-sdmmc/latest/embedded_sdmmc/sdcard/struct.SdCard.html). It seems this isn't possible with the eh 1.0 `SpiDevice` trait/transactions at the moment. What do you think is the best solution for such a use case? It seems like it could potentially be useful to allow
<fu5ha[m]> a transaction to define whether it wants the CS pin to be asserted or not, seems there's no harm in allowing it to do that as long as we know nobody else is active on the bus at that time.
<adamgreig[m]> haha, wow, that's annoying
<adamgreig[m]> I was going to say it's sort of a compatibility hazard with things like linux where you can't really choose not to assert cs, but actually I think it is possible if you configure the spidev device just so
<adamgreig[m]> I wonder if there's some way to do this "flushing" without requiring clocking without cs set though
<adamgreig[m]> like, in the stupidest case, you could make a second spidevice with a different fake cs pin
<adamgreig[m]> and just clock a few bytes to that one instead
<adamgreig[m]> or otherwise, maybe sdcard needs to take an spibus instead of an spidevice
<JamesMunns[m]> ragarnoy it's probably the defmt symbols
<JamesMunns[m]> macos has that problem too
<fu5ha[m]> > I wonder if there's some way to do this "flushing" without requiring clocking without cs set though
<fu5ha[m]> Yeah, I'm also having a hard time finding this process mentioned in docs elsewhere. I'll try to dive in the code and see where it's actually used to find something to cross reference tomorrow (and/or send the crate owner an issue about it)
<JamesMunns[m]> we name sections like .defmt.{ JSON GOES HERE }
<JamesMunns[m]> * defmt names sections like .defmt.{ JSON GOES HERE }
<JamesMunns[m]> this happens to work on linux, but IIRC windows and macos hate it
<JamesMunns[m]> projects like smoltcp and embassy have a macro that hacks around this (you can pick log, defmt, or none), but it's not super pleasant
<fu5ha[m]> Ok so in the sd/mmc spi saga, it seems that basically the spec says that the card doesn't go idle as soon as the CS is de-asserted but once CS is de-asserted and then another 8 clocks pass (i.e. another byte is sent). During those 8 clocks, it still sends on its tx line (i.e. MISO)... So this definitely is needed functionality if sharing the bus