GenTooMan has quit [Ping timeout: 250 seconds]
<re_irc> <> Maybe that could be used to charge a capacitor or battery used to burst radio transmissions
<re_irc> <> Does embedded-hal have a SPI slave trait?
<re_irc> <> Nope
fabic has joined #rust-embedded
GenTooMan has joined #rust-embedded
<re_irc> <> But somewhere was a good proposal
dcz has joined #rust-embedded
cr1901 has quit [Ping timeout: 240 seconds]
<re_irc> <> There is an I2C peripheral trait discussion here:
fabic has quit [Ping timeout: 252 seconds]
<re_irc> <> How do I access my peripherals in an interrupt routine, like if i want to reset the interrupt flag in the routine that it does not get called again as soon as it is finished? #[interrupt] part is outside main and inside there i declare my peripherals (see here:
<re_irc> <> static mut is difficult bc I dont know how to give it a Type for the peripheral
<re_irc> <> when you are doing this "raw", you need to put the peripheral somewhere where you can access it everywhere but safe, e.g. a Mutex
<re_irc> <> for example:
<re_irc> <> use core::{cell::RefCell, ops::{DerefMut}};
<re_irc> <> use cortex_m::interrupt::{free, Mutex};
<re_irc> <> static MUTEX_RTC: Mutex<RefCell<Option<stm32f3xx_hal::stm32::RTC>>> = Mutex::new(RefCell::new(None));
<re_irc> <> static MUTEX_ADC1: Mutex<RefCell<Option<stm32f3xx_hal::stm32::ADC1>>> = Mutex::new(RefCell::new(None));
<re_irc> <> static MUTEX_DMA1: Mutex<RefCell<Option<stm32f3xx_hal::stm32::DMA1>>> = Mutex::new(RefCell::new(None));
<re_irc> <> then wherever you want to use it, you do something like: `let adc1 = MUTEX_ADC1.borrow(cs).borrow();`
<re_irc> <> at least that's how I did it before moving to RTIC...
<re_irc> <> also, you can only do `mkl25z4::Peripherals::take()` once
<re_irc> <> you would do that, and then put the periphs in the Mutexes
<re_irc> <> that's the safe way I found without frameworks
<re_irc> <> you could also go unsafe as long as you really know what you're doing
<re_irc> <> and just do:
<re_irc> <> #[interrupt]
<re_irc> <> fn RTC_WKUP() {
<re_irc> <> free(|_cs| {
<re_irc> <> unsafe {
<re_irc> <> makes sense?
<re_irc> <> let me see if I got it right:
<re_irc> <> For the "quick and dirty": this `*stm32::RTC::ptr()` just points to the location where usually my peripherals were taken and I do it like I would do in C code where i just "throw bits around"
<re_irc> <> For the mutex solution: Do I have to free it every time I use it, even in main?
<re_irc> <> the mutex I'm using there is this one:
<re_irc> <> which you use within a critical section
<re_irc> <> so you get the resource as long as you are within the cs
<re_irc> <> bear in mind that I wrote this my first days with embedded rust, then I found RTIC and went that way, so this might well be a very cumbersome way to do it
<re_irc> <> maybe the more experienced people have better ways :)
<re_irc> <> and yes, the quick and dirty is just pointers to the registers, which is what the PAC does underneath
tokomak has joined #rust-embedded
<re_irc> <> thank you I guess I will just "hack that away" for now :)
<re_irc> <> personally, I wouldn't recommend it
<re_irc> <> you just lose the benefits of writing Rust
<re_irc> <> if you keep writing unsafe everythere using pointers to the registers
<re_irc> <> thats true.
<re_irc> <> if you know you're the only one accessing a register, there is nothing wrong with raw pointer access.
<re_irc> <> it's definitely safer to use a lock if you don't know, or don't want to care about this though.
<re_irc> <> it's pretty easy to have collisions in read-modify-write if you do though, so it should not be your default position
Amadiro has quit [Quit: Leaving]
Amadiro has joined #rust-embedded
cr1901 has joined #rust-embedded
Amadiro has quit [Remote host closed the connection]
Amadiro has joined #rust-embedded
<re_irc> <> If have been trying to learn rust in the embedded context for a while. One thing I do not get my head around are mutable statics in a no heap environment. Coming from C/C++ I have all my classes/objects/structs as static in some factory function and they are mutable and life for the whole program. As it is single threaded (for all data not used from interrupt) I give all parts of the program a reference which..
<re_irc> ... need to work with that struct/class. All embedded rust examples are "small" main function only examples were all resources life on the stack at beginning of main. How do you handle mutable statics in embedded context? Can you point me to some "real" embedded application not only consisting of a main with one or two showcase functionalities? Not sure if I phrased my problem correct :)
<re_irc> <> the main problem is that because interrupts _could_ access statics at the same time as other interrupts or the main thread, rust requires an `unsafe` block to access mutable statics
<re_irc> <> so there's one very simple answer, i.e. you continue to make your global shared state `static mut`, and wherever you need to access them, you use an `unsafe` block, and then everything works just like C/C++
<re_irc> <> (conceptually you can imagine the entire C/C++ program as being inside an `unsafe` block)
<re_irc> <> the bigger question is then: how can you avoid needing unsafe, in other words, how can you ensure that the access to shared memory is synchronised (in other words, cannot race between interrupts and main thread)
<re_irc> <> there's some discussion of it here:
<re_irc> <> one of the popular options today is something like RTIC ( which works out at compile-time which resources can never be raced (for example, because they are only visible to one interrupt, or to interrupts at the same priority level) and then gives you safe access accordingly
<re_irc> <> an easy option is something like the Mutex from the cortex-m crate, which enters a critical section for the duration of the access to the shared resource, which also ensures there's no race, but impacts interrupt processing time and so on
<re_irc> <> sometimes you can just put everything on the stack though, and it makes everything much easier
<re_irc> <> if your data is not actually shared between interrupts, putting it on the stack in main and pass it through to where it's needed removes all these problems
<re_irc> <> and it's the same memory, it's not like you'll run out of stack but would have been OK with it statically allocated
<re_irc> <> (the downside is it can be annoying to pass all this stuff around, but maybe you can use a 'shared state' struct that contains everything you need or something like that)
<re_irc> <> I am used to thinking in like bss section and stack as somewhat different
<re_irc> <> so putting "all" on the stack
<re_irc> <> seems odd
<re_irc> <> inside an operating system the stack is often limited in size while you can make arbitrary heap allocations
<re_irc> <> so it makes sense
<re_irc> <> but on embedded the heap (if any), statically allocated objects, and the stack all generally share the same continuous section of RAM
<re_irc> <> yeah they do
fabic has joined #rust-embedded
<re_irc> <> but still coming from C I think about them differently I guess
<re_irc> <> (there are exceptions, some MCUs have multiple RAM sections and you might specifically put stacks on one and statics on another and so on)
<re_irc> <> In my typical use case I believe naively that I know which resources are shared with interrupts and which not
<re_irc> <> yea. in C it's very easy to use translation-unit-local statics for shared state that doesn't need to be passed in every time, and so long as you're careful to never access them from different contexts (interrupt/thread/etc) it's OK
<re_irc> <> it's nicer to put stuff in .bss if you can, it allows you to see how much RAM you're using
<re_irc> <> I would like a way to tell the compiler: yes its static and mutable but I know for sure its fine put it in bss and let me access it safe
<re_irc> <> in rust I think it's probably nicer to have a struct that holds that state, and methods that operate on it, instead of a bunch of free state in the C translation unit and functions inside that operate on it
<re_irc> <> BigFive: that's exactly what `unsafe` does :P
<re_irc> <> it tells the compiler "I know you can't prove this, but I'm sure it's fine"
<re_irc> <> it is precisely the way to tell the compiler that
<re_irc> <> Unsafe feels and looks so wrong
<re_irc> <> maybe that is something I need to get used to in the beginning
<re_irc> <> until I have learned other more rusty ways
<re_irc> <> like I said, just imagine your entire C program is wrapped in it, because effectively it is :P
<re_irc> <> it's well worth looking at RTIC, which handles this for you in a really nice way
<re_irc> <> in c/c++ everything is implicitly unsafe
<re_irc> <> in rust you choose which parts are unsafe
<re_irc> <> we could debate if `unsafe` was the best word for "I promise the compiler this code is OK even though it can't tell"
<re_irc> <> but it's the word we've got
<re_irc> <> it feels "wrong" to write `unsafe{}` but it is actually *good*, it means the rest of the code is safe
<re_irc> <> s/unsafe/trust_me_I_got_this/
<re_irc> <> hold_my_beer { }
<re_irc> <> yolo { }
<re_irc> <> but still, it _is_ good to try and avoid having unsafe everywhere, and that's why you want nice abstractions that can prove or check it is OK, and provide safe access
<re_irc> <> and you can easily audit a codebase by doing "control+F unsafe"
<re_irc> <> so if a typical embedded application would be ported to rust without some kind of framework like RTIC it would be full of unsafe
<re_irc> <> which is what RTIC does
<re_irc> <> not _necessarily_ but it does depend on how it implements shared resources
<re_irc> <> for example you can use `cortex_m::interrupt::Mutex` safely
<re_irc> <> and it provides safe mutable access to statics
<re_irc> <> but it does so by disabling all interrupts for the duration of the access, which ensures no races, but might be troublesome for hard realtime systems
<re_irc> <> in `cortex_m_rt`, any `static mut` declared at the top of an interrupt or main function is transformed into a safe `&mut`, because cortex-m-rt can tell that function can never be called by user code, only as an interrupt entry point, and so all access must be synchronised
<re_irc> <> Is there any "bigger" embedded application without a framework which uses a different approach? As you seed wrapping everything in a Mutex when 95% of the resources do not need that is not satisfying
<re_irc> <> but the cortex-m-rt transform is a bit surprising/weird so might go away...
<re_irc> <> still thanks for all the answers I will look at the stuff you posted
<re_irc> <> putting all on the stack seems for me the best way but I do not like to not a clear bss section for RAM estimation
<re_irc> <> there's other ways to do safe abstractions for statics
<re_irc> <> for example a "Forever" that lets you "intialize a static once" and gives you a `&'static mut T`:
<re_irc> <> or a "ThreadModeMutex" that lets you access the static only from thread mode, so it's safe while still not needing to block interrupts:
<re_irc> <> you can also use `unsafe` once to get an &mut, and then use that for the rest of the code
<re_irc> <> fn foo(x: &mut u32) {
<re_irc> <> ```rust
<re_irc> <> *x += 1;
<re_irc> <> }
<re_irc> <> this seems interesting will take a look at embassy
<re_irc> <> so e.g. make the state struct static and then pass the references into the functions that need it
<re_irc> <> hm okay this reduces unsafe quite alot
<re_irc> <> okay I got to go - thanks very much that helped a lot
<re_irc> <> 👍️
<re_irc> <> dirbaio: do you have ThreadModeMutex for but each ISR priority level too?
<re_irc> <> no... but it could totally be done, yup!
<re_irc> <> not sure if there's any gotchas
<re_irc> <> pretty bad if the thread changes prio during the mutex and then gets pre-empted I guess lol
<re_irc> <> for example, how to get the "curren tprio level?
<re_irc> <> read current IRQn, then read the prio reg for that IRQn?
<re_irc> <> guess so
<re_irc> <> maybe do it per-ISR instead of per-priority
<re_irc> <> how does NVIC handle changing an irq prio while that irq is running? I guess the current prio stays at the "old" prio... but then if you make the prio higher, can an irq handler preempt itself? 🤣
<re_irc> <> I guess that's more useful anyway, to lock a static resource to a single ISR, the sort of thing c-m-rt does it
<re_irc> <> yea.. needing to access some data *only* from a single ISR is super rare in my experience though :S
<re_irc> <> hm, I'm sure I've had it a bunch of times for state machines in the isr
<re_irc> <> also embassy's owned irqs allow changing the priority without unsafe
<re_irc> <> now that I think about it, this breaks the CMRT static mut transform lol
<re_irc> <> does that break rtic?
<re_irc> <> does it?
<re_irc> <> so long as the isr can't preempt itself I think it's OK, since no other interrupt can see the static anyway
<re_irc> <> not sure if it breaks RTIC, probably not because to get the owned irq embassy has to register the irq handler, which would conflict with rtic's
<re_irc> <> ah, ok, it's not just using your singleton interrupts but also registers the handler? that should be OK then
<re_irc> <> yea
<re_irc> <> it forces the IRQ to be either fully managed by embassy or not managed at all
<re_irc> <> ah actually it doesn't break the cmrt's static mut transform because it makes it so you can't register the handler with cmrt's `#[interrupt]` either
<re_irc> <> otherwise it'd definitely break it
<re_irc> <> say you have some type `NotSync` which is not Sync
<re_irc> <> then a `&NotSync` is not Send
<re_irc> <> you could have IrqA, IrqB at prio 1
<re_irc> <> create a `NotSync` from IrqA, get a `&NotSync`, move it to IrqB (OK because same prio)
<re_irc> <> then change IrqB from prio 1 to prio 2
<re_irc> <> now you have a `&NotSync` at both prio 1 and prio 2
<re_irc> <> which you can then use to poke at the `NotSync` from different prio levels 💣️
<re_irc> <> "not Send" really means "you may not send it to a different prio level"
<re_irc> <> or wait
<re_irc> <> if we say it means "you may not send it to another ISR" then yes, it'd be safe to change the prio
<re_irc> <> me reading this -> 🤯
<re_irc> <> but then the "prio level mutex" thing wouldn't be sound
<re_irc> <> IMO the "!Send = you may not send it to a different prio level" model is the most useful one, it's what I've been following for embassy
<re_irc> <> is this "officially decided" somewhere?
<re_irc> <> if I understand correctly, the basic problem is we still do not know the exact definition of Sync and Send in embedded context
<re_irc> <> ummm
<re_irc> <> the "official definition" of Send/Sync is
<re_irc> <> - Send = you may send T between different "execution contexts"
<re_irc> <> - Sync = you may have &T in different "execution contexts" simultaneously
<re_irc> <> with that to me it sounds like !Send should not allow to pass data between any ISR
<re_irc> <> where "execution context" means "code in different execution contexts may preempt each other, code in the same execution context may not"
<re_irc> <> in std, "execution context" = "thread"
<re_irc> <> I fully understand the scenario you described above now
<re_irc> <> in embedded, we're on our own to define what an "execution context" is
<re_irc> <> so if you allow to change IRQ levels dynamically each ISR should be considered a "execution context"
<re_irc> <> for fixed IRQ prio levels you can model it as one single "execution context" per prio
<re_irc> <> kinda makes sense
<re_irc> <> I see 2 ways to define "execution context":
<re_irc> <> - execution context = prio level: possible execution contexts are thread mode, irq prio 0, irq prio 1, ...
<re_irc> <> - execution context = isr number: possible execution contexts are thread mode, irq A, irq B, irq C...
<re_irc> <> both definitions fulfill the "code in different execution contexts may preempt each other, code in the same execution context may not" requirement
<re_irc> <> so both are "correct"...?
<re_irc> <> james had some notes about this a while ago in
<re_irc> <> yeah... that doesn't pick one of the 2 definitions though :D
<re_irc> <> no :P
<re_irc> <> it'd be cool to pick an "official" one
<re_irc> <> if you mix crates using "context = prio level" and "context = irq number" you could have a bad time
<re_irc> <> CMRT is *almost* picking "context = irq number" due to the static mut transform, except that changing prio is unsafe, so it's still correct according to the "context = prio level" one too, lol
<re_irc> <> same for CMIM, lol
<re_irc> <> In my mind I have something like that
<re_irc> <> where each resource is an entry in the matrix
<re_irc> <> RTIC and embassy pick "context = prio level"
<re_irc> <> one model would group a column as context, the other a row
<re_irc> <> changing priority would move the resource within the column
<re_irc> <> yup, exactly :D
<re_irc> <> sending between ISRs would change the row
<re_irc> <> you have to forbid either
<re_irc> <> or have a better model on how to move resources within that matrix
<re_irc> <> that's a nice visualization
<re_irc> <> a cell is a possible "mode" code can run in: irq X at prio Y
<re_irc> <> code within a row can't preempt each other
<re_irc> <> code within a column can't preempt each other
<re_irc> <> so you can either define "context = row" or "context = column"
<re_irc> <> wait, does RTIC require resources to be Send?
<re_irc> <> > In the context of RTIC the Send trait is only required where it's possible to transfer a value between tasks that run at different priorities
<re_irc> <> exactly, yup! so rtic follows the "context = prio level"
<re_irc> <> uh so if "context = prio level" is the more widely accepted definition, isn't this another reason for removing the cmrt static mut transform?
<re_irc> <> you could say cmrt is _both_, i.e. the context is row+col and you can't move between them?
<re_irc> <> or rather, that cmrt is more conservative and would work with libraries using either?
<re_irc> <> hmm if a lib assumes "context = prio level" and exposes a safe API to change irq prio level, that breaks CMRT's static muts
<re_irc> <> I mean
<re_irc> <> if we choose "context = prio level", then we can have "static mut transform" XOR "safe prio changing", having both is unsound
<re_irc> <> but I guess "safe prio changing" breaks more stuff, like rtic for example
<re_irc> <> unless you pair it with "owned irqs" like embassy's
<re_irc> <> so, uh
<re_irc> <> I guess the status quo is OK?
<re_irc> <> OK in that nothing is broken but it would still be nice to write this up at least
<re_irc> <> yeah
<re_irc> <> cortex-m is what makes prio changing unsafe, right? c-m-rt doesn't even depend on that
<re_irc> <> at the very least, something like
<re_irc> <> "the REWG's official position is that "context = prio level", not "context = irq number". If you make a crate that uses "context = irq number" then you're on your own, your crate will be unsound when used together with the rest of the ecosystem"
<re_irc> <> aaaaand actually
<re_irc> <> as a consequence
<re_irc> <> using CMIM + embassy owned irqs IS unsound
<re_irc> <> boom
<re_irc> <> ah, no, because CMIM requires T: Send
<re_irc> <> Rust is lovely for reasons so much more than memory safety!
<re_irc> <> real sum types (enums)
<re_irc> <> I miss them so hard when I write Go now...
<re_irc> <> Go error handling sucks
<re_irc> <> they do `fn foo() -> (u32, error)` instead of `fn foo() -> Result<u32, Error>`
<re_irc> <> so calling code can read the u32 just fine, even if foo failed 😭
<re_irc> <> all due to lack of sum types
<re_irc> <> err, d = thing(); if err != nil { return err; }
<re_irc> <> the boilerplate sucks, but the criminal thing is you can read the "ok" result when the function failed and the compiler doesn't catch it
<re_irc> <> i've had bugs due to that
<re_irc> <> fully agree
<re_irc> <> there's tons of stuff i miss when i go back to c++
<re_irc> <> the only thing i miss coming back is consteval
<re_irc> <> I'm incapable of writing c++ anymore lol
<re_irc> <> too spoiled by go/rust
<re_irc> <> i'm not incapable, it just makes me sad
<re_irc> <> i feel like i should learn go but there's literally no place that i'd use it
<re_irc> <> Go seems like a nice enterprise language, ie its value is simplifying working with others
<re_irc> <> I do a lot of C#, for every version they're bringing in more and more of the good stuff from F#, so now i'm mostly missing the sum types :)
<re_irc> <> Thoughts on Julia?
<re_irc> <> 🌟Job posting 🌟
<re_irc> <> I work at a small quantum computing startup in Oxford, UK. We're hiring for embedded software engineers, and expect to be using a lot of Rust very soon. More info here and the rest of our job postings here
<re_irc> <> Do feel free to ask me if you have any specific questions
<re_irc> <> Are contractors any use to you, or are you just looking for full-time staff?
fabic has quit [Ping timeout: 245 seconds]
<re_irc> <> thejpster: I'll have to ask, and I imagine it depends on some factors like how long a contract you'd want. I have asked :) -- sorry for not thinking that through before we posted the job adverts!
<re_irc> <> I work at, so whilst I'm not looking for a new job, my services are available in exchange for currency.
<re_irc> <> code reviews, writing specific modules, whatever would be useful. If it's of interest, I can get someone to reach out.
<re_irc> <> Thanks, I'll let you know :) I used to go to your Rust meetups in Cambridge, good to hear from you!
<re_irc> <> oh! hi!
<re_irc> <> Nice to think we didn't scare people away entirely.
<re_irc> <> Ah, thanks for posting here too lochsh :D
<re_irc> <> Thanks for RTing!
<re_irc> <> I was just coming to remind folks that I put out a "we retweet job postings" on the r-e twitter, so for folks in this room:
<re_irc> <> * Keep an eye on the twitter if you're looking for a rust job! Folks usually tweet in waves
<re_irc> <> * If you're hiring/looking for rust-embedded devs, also post it and tag `rustembedded` so I see it and retweet it
<re_irc> <> Also, if you're looking for a Rust gig (and can post publicly about it), tag r-e twitter too!
<re_irc> <> A couple of the most widely shared job postings from the last round were in response to folks saying they were looking for rust jobs
<re_irc> <> Hi folks! I'm working on adding eMMC support to the Rust embedded world, starting with `stm32f4` devices.
<re_irc> <> I've written up a proposal here regarding organization of SDIO data structure definitions. It'd be great to get feedback from anyone interested.
<re_irc> <>
fabic has joined #rust-embedded
<re_irc> <> thalesfragoso: didn't you do some SDIO work on embassy?
fabic has quit [Ping timeout: 252 seconds]
starblue has joined #rust-embedded
<re_irc> <> Yes, for the H7, I used sdio-host iirc
tokomak has quit [Ping timeout: 252 seconds]
dcz has quit [Ping timeout: 252 seconds]
<re_irc> <> I'd suggest caution with the SD Card terminology. SDIO is a specific thing for putting non disk devices in SD Card slots. As far as I understand it, the non-SPI bus mode for SD cards is the "SD Card Host Interface". I know everyone calls it SDIO, but they are different.
<re_irc> <> it's baffling that ST call it SDIO
<re_irc> <> Sorry, it's "SD Bus Interface". I made an error.
<re_irc> <> I guess their point is their peripheral can do SDIO and therefore can also do all SD card functions
<re_irc> <> ah, the newer stm32s call it SDMMC actually
<re_irc> <> SDIO requires the SD Bus Interface, yes. So I can sort of see their point.
<re_irc> <> (it supports MMC/eMMC in addition to SD and SDIO)
<re_irc> <> Anyway. Let's not call crates SDIO when they do more than SDIO.
<re_irc> <> fair to call a driver for the ST SDIO peripheral SDIO though?
<re_irc> <> but maybe not the higher-level abstractions, eya
cr1901 has quit [Read error: Connection reset by peer]
<re_irc> <> should the position of the SPI MOSI line at the start of a transfer have any bearing on the transfer? My analyzer considers both cases the same but the display I use doesn't work if the data line was high before the CS assert (it does get set low before the clock ticks in too!)
<re_irc> <> this works:
<re_irc> <> this doesn't:
<re_irc> <> in general it shouldn't matter, but it could on specific devices (for example I feel like I've seen some that swap between i2c and spi based on value of DIN wen CS goes low maybe?)
<re_irc> <> there's no single 'standard' spi, but generally the data pin state should only matter when it's clocked
<re_irc> <> there's also the DC line but it's low way before in both cases (command mode)
<re_irc> <> I'm testing SPI sharing between 2 devices, in this case the other one isn't even connected, I send anything with LSB 0 on that and the main device (the display) keeps on working. If I send LSB 1 at the end for the dummy, the display goes broke. Analyzer considers the data on SPI_display line the same in both cases so I'm really confused
<re_irc> <> the CS lines assert ok, the display CS is high throughout the dummy send
<re_irc> <> adamgreig: The fact the the SD association requires licensing to use their trademark or to implement a card host but not to implement SDIO devices or host probably has something to do with that.
<re_irc> <> that could well explain it!
<re_irc> <> almindor: maybe check the datasheet timing diagram for the display? in principle it really shouldn't matter but....
<re_irc> <> adamgreig: You're right, it's the init. DISPON requires some time and it seems even with CS de-assert sending data when it's "turning on" causes it to bork