<thejpster[m]>
wrapping string log functions with defmt doesn't make a lot of sense anyway - defmt relies on macros to intern the strings so they don't end up in flash or RAM. If your C code has strings in flash/ram, you'll probably just want to render the string to a byte buffer and then defmt that.
<thejpster[m]>
I was sure there was a workaround where the C program was (char * fmt, ...) and you made a va_list object and passed that to Rust.
<ragarnoy[m]>
the logs are private, as in they're in the proprietary part of the library i'm using, the hal is the only way to get these logs to print basically
<ragarnoy[m]>
<adamgreig[m]> "https://doc.rust-lang.org/beta/..." <- if possible i'd like to not have to use that feature since all I want to do is pipe the string from the library , check the log level and print through defmt
<Socke>
hey guys! my esp32c3 does not show up as a usb device when pin GPIO18 is high (~2V). am I doing something wrong?
<Socke>
I connected a voltage divider (with a photo resistor but thats not important) between 3.3V and GND to this pin and whenever the voltage gets too high, the usb device disappears
<Socke>
shall I use a different pin?
<Socke>
oh I see from the docs that this pin is used for jtag :/
<Socke>
wasn't mentioned in the pinout picture that I saw -.- damnit
Noah[m]1 has joined #rust-embedded
<Noah[m]1>
Socke doing their mischief again :P
<Socke>
:(
<thejpster[m]>
ragarnoy: yeah that should be fine. Although note if you pass 256 to snprintf, it might fill all the bytes and leave it non-null terminated. Also you need to expressly null-initialise buffer `= {0}`;
<thejpster[m]>
so I would zero-init and then pass sizeof(buffer) - 1 to snprintf as the length.
<adamgreig[m]>
snprintf already subtracts one and adds a null terminator, doesn't it?
Guest7282 has joined #rust-embedded
<thejpster[m]>
> The functions snprintf() and vsnprintf() write at most size bytes (including the terminating null byte ('\0')) to str.
<thejpster[m]>
Hmm, maybe it does and I'm used to an older version.
<thejpster[m]>
I definitely had a C library on a platform that would not add the NUL if it ran out of space. Fun times.
<thejpster[m]>
> sprintf is like printf, except that output is directed to the buffer str, and a terminating NUL is output. Behavior is undefined if more output is generated than the buffer can hold.
<Socke>
it uses espflash flash --monitor as the runner command
<FreeKill[m]>
<thejpster[m]> "I definitely had a C library..." <- That is how the behaviour is defined i believe ๐๐
<FreeKill[m]>
Eh maybe not... Though I have also used an implementation which did, which is grand
Guest7282 has left #rust-embedded [Error from remote client]
<vollbrecht[m]>
Socke: yes you do something wrong, gpio 18/19 is the usb + / - in. So the complete usb-acm/jtag thing runs over that two pins. So you can use this two pins for flashing/debugging/serial monitoring.
<vollbrecht[m]>
are you currently using that connection also for flashing/monitoring or are you using gpio20/21 over an uart/usb converter?
<vollbrecht[m]>
if you use the traditional setup via an external uart/usb converter, there is an option to use pin 18/19 as regular gpio pins. But by default they are usb/jtag
<Socke>
yeah I switched to GPIO6 that works fine
Guest7282 has joined #rust-embedded
marmrt[m] has quit [Quit: Idle timeout reached: 172800s]
notgull has quit [Ping timeout: 260 seconds]
jessebraham[m] has quit [Quit: Idle timeout reached: 172800s]
notgull has joined #rust-embedded
<ragarnoy[m]>
Hey, I have a C function such as
<ragarnoy[m]>
```c
korken89[m] has joined #rust-embedded
<korken89[m]>
Does anyone know if it's possible to get the size of an anonymous type? I'd like to know the size of the future returned by an async function without first constructing the future.
<korken89[m]>
What I want to do is too get the size of the futures in RTIC to add support for allocating them all before init
<thejpster[m]>
ragarnoy: you might want want to check the pointer is non-null. You should also try and avoid static mut variables (see docs.rs/grounded for some ideas or docs.rs/static-cell)
lehmrob has joined #rust-embedded
<Noah[m]1>
<korken89[m]> "What I want to do is too get the..." <- doesn't embassy do this?
<JamesMunns[m]>
It does on nightly
<JamesMunns[m]>
That's why they switched to the pool allocator on stable. To get the size, you need nightly features, like TAIT or one or two others
<korken89[m]>
I want the size, so RTIC can work on stable
<korken89[m]>
Else i don't know how to allocate space for the future
<JamesMunns[m]>
So does embassy, but afaik, you can't on stable.
<korken89[m]>
As I understand the task allocator is specifically for stable support
<JamesMunns[m]>
Maybe we're talking past each other: there's no stable+static way to get the size. It's pick one right now
<JamesMunns[m]>
So the pool allows you to get the size at runtime and allocate that.
<korken89[m]>
I don't need a static way, only some way to get the size :)
<korken89[m]>
I want to do the allocations before I we jump to init
<JamesMunns[m]>
Probably core::mem::size_of_val or something
<JamesMunns[m]>
But yeah you could look at how embassy does it.
<korken89[m]>
So users get nice errors before an app starts, if the allocator does not have enough memory
<korken89[m]>
I'm digging :)
<Noah[m]1>
<JamesMunns[m]> "That's why they switched to..." <- ohh, TIL!
<korken89[m]>
So embassy constructs the future when it's supposed to be spawned and allocates then
<JamesMunns[m]>
Noah[m]1: Yeah sorry I should have said "to get the size **statically** you need nightly features"
<korken89[m]>
Indeed, this is what RTIC does today
<Noah[m]1>
JamesMunns[m]: no worries, I got you just right :)
<korken89[m]>
And one could do the alloc at spawn-time, but then you don't get any guarantees that your allocation pool is large enough or not
<Noah[m]1>
I did not know they switched to get stable support. Somehow I thought all the required features were stabilized even though I never saw an announcement lol :D
<ragarnoy[m]>
<thejpster[m]> "ragarnoy: you might want want..." <- i can't seem to be able to call write on the spi when it's behind the static cell :/
<JamesMunns[m]>
Yeah, async fn in trait was the highest blocker, the rest was workaroundable
<JamesMunns[m]>
There's like 2-3 different nightly features that let you calculate the size, TAIT is the most direct or most likely to stabilize iirc
<JamesMunns[m]>
Dirbaio will know the real answer :D
<dirbaio[m]>
Yeah in the case of rtic thanks to the macro it feels like you should be able to allocate everything in initย
<korken89[m]>
I feel so as well
<korken89[m]>
Just trying to conjure the future enough to get its size and alignment xD
<dirbaio[m]>
So OOM can only happen right at boot, never later. Which is really nice, and not possible in embassy.ย
<korken89[m]>
Soooo, the asm if you just yolo MaybeUninit::uninit().assume_init() does give the correct answer xD
<dirbaio[m]>
Yea but you have to somehow be able to name the typeย
<JamesMunns[m]>
korken89[m]: This is pretty much always ub
<JamesMunns[m]>
The docs specifically say "don't do that"
<korken89[m]>
Yeah, it's not something I'd recommend
<korken89[m]>
But I am getting desperate :P
<JamesMunns[m]>
You don't need to assume init
<JamesMunns[m]>
Maybe uninit T is guaranteed to be the same size as T
<korken89[m]>
Oh?
<dirbaio[m]>
actually
<JamesMunns[m]>
It's repr transparent
<dirbaio[m]>
you can just stack-allocate everything on init
<dirbaio[m]>
can't you?
<dirbaio[m]>
no unsafe hacks required
<JamesMunns[m]>
dirbaio[m]: Yeah, and then use size_of_val by ref like I mentioned :p
<ragarnoy[m]>
<thejpster[m]> "ragarnoy: you might want want..." <- could you explain how one is supposed to get a &mut T back after initializing a staticcell ?
<dirbaio[m]>
if you stack-allocate it and leave it on the stack forever throughout the execution you don't even need the size
<korken89[m]>
dirbaio[m]: Hmm, not quite sure how I'd do this. It would require the same where I know the size of the future right?
<dirbaio[m]>
yea but you can use generics in that case to know it
<korken89[m]>
Oh
<dirbaio[m]>
ah maybe you need unstable feature(unboxed_closures) to do F::Output or something
<dirbaio[m]>
uhh
<korken89[m]>
Hmm
<korken89[m]>
<JamesMunns[m]> "Maybe uninit T is guaranteed..." <- But how to get the future when the values are uninit? If I transmute them to T it seems just as UB to me as `assume_init`, no?
<JamesMunns[m]>
korken89[m]: I'm not sure I understand. I thought you just needed the size to move it to a static allocation pool or something
<korken89[m]>
Yes, that is correct
<korken89[m]>
But to do that I need input parameters to the future, which I do not have
<JamesMunns[m]>
Like your code creates the future on the stack, you allocate space, you move from stack to uninit allocation using ptr::write
<korken89[m]>
So I need to conjure them soehow
<korken89[m]>
s/soehow/somehow/
<korken89[m]>
As I want the size of the future before a user creates the same future
<korken89[m]>
So I can pre-allocate it before init
<korken89[m]>
I can allocate them at call time from the user, but I really want the feature of making sure the allocators pool size is large enough at init
<JamesMunns[m]>
I'm not sure I understand, it seems like you would need to calculate it statically then. But maybe I'm just not imaginative enough.
<korken89[m]>
Statically give the answer indeed (this is what RTIC does today), but I'm fine with run-time as long as I can conjure the inputs so size_of_val can be used
<korken89[m]>
The UB way does give the right answer, but I'm looking for some other way that is at least technically not UB :)
<dirbaio[m]>
I think the erase/un-erase should be good? in both cases they're the same type
<dirbaio[m]>
miri doesn't complain
<dirbaio[m]>
and
<dirbaio[m]>
as long as main doesn't return, the stack-allocated Task lives forever, so it's fine to store a pointer in a static.
<dirbaio[m]>
and RTIC's macro controls main, so it should be easy to enforce it never returns I guess
<korken89[m]>
That was a really neat way of doing it
<dirbaio[m]>
it feels a bit ugly
<dirbaio[m]>
but it's the least ugly way I can think of so... ๐คท
<korken89[m]>
It gave me inspiration for more heinous crimes to the compiler :D
<dirbaio[m]>
lol
<dirbaio[m]>
btw, there's talks to stabilize ATPIT soon (associated-type-position impl Trait), before the full TAIT
<korken89[m]>
Oh
<dirbaio[m]>
it's TAIT but only in associated types: `impl Foo for Bar { type Future = impl Future<Output=...>; }`
<dirbaio[m]>
which seems unlikely to have issues getting stabilized, because ATPIT is not affected by the defining scope issues the full TAIT has
<dirbaio[m]>
* not affected as much by the
<dirbaio[m]>
and
<dirbaio[m]>
you can almost always convert code from TAIT to ATPIT, you just need a dummy trait and dummy struct ๐คฎ
<dirbaio[m]>
but that's not an issue for macros :D
<dirbaio[m]>
so I think just stable ATPIT would work for RTIC or embassy-executor
<dirbaio[m]>
so I hope all these hax will become obsolete soon ๐
<korken89[m]>
That's actually true!
<korken89[m]>
I did not think ATPIT was enough
<dirbaio[m]>
make a dummy trait, impl it for a dummy struct returning the fut in the ATPIT, then you can name it as `<DummyStruct as DummyTrait>::Future` in the static ๐ฉ
<korken89[m]>
Generate the SizeOf for all sizes needed in the macro
<dirbaio[m]>
hah
<korken89[m]>
But if once can allocate on the stack is neater hough
<korken89[m]>
s/hough/though/
<korken89[m]>
* But if once can allocate on the stack of main is neater though
<dirbaio[m]>
if you go the allocator route don't forget about align too
<korken89[m]>
Indeed, just align_of there :)
<dirbaio[m]>
i'm not sure what's best though.
<dirbaio[m]>
in the stack the user doesn't need to tune the arena size, it'll grow as needed. but if it grows too much it'll stack-overflow which is UB
<dirbaio[m]>
with an arena, the user has to manually tune the size which is annoying, but you can give a nice error message if it fills up
<dirbaio[m]>
* fills up, instead of UB
<korken89[m]>
Yeaha
<korken89[m]>
Tbh I'm not sure there. If the tuning is easy I'd see it as a win as wll
<korken89[m]>
s/wll/well/
<korken89[m]>
E.g. be able to access the_amount_of_space_actually_used_by_the_allocations()
<JamesMunns[m]>
I wonder if you could make it tunable by having the structs be actual powers of two. Like, one feature for each bit. So if you want 192 bytes you could select the 128 feature and the 64 feature
<dirbaio[m]>
lol!
<dirbaio[m]>
just convert the size you want to binary, then enable the features corresponding to the bits
<dirbaio[m]>
easy peasy
<dirbaio[m]>
then you got two crates depending on embassy-executor in your tree, and now the arena size is accidentally the bitwise OR of them ๐
<korken89[m]>
:D
<JamesMunns[m]>
:D :D
<JamesMunns[m]>
truly additive features
<JamesMunns[m]>
<ragarnoy[m]> "could you explain how one is..." <- btw, you just call `init()` and it gives you back a `&'static mut T`?
<korken89[m]>
Thanks for the discussions! I think I'll make one "main as allocator" and one "real allocator" test implementation for RTIC to see where they land
<ragarnoy[m]>
JamesMunns[m]: this initializes the cell, but how do i get a mutable reference to its content ?
<ragarnoy[m]>
but what if you want to use it else where ? and you can't pass the reference by argument
<JamesMunns[m]>
then you need to make it something like a `StaticCell<Mutex<..., u32>>`
<JamesMunns[m]>
(or RefCell, or otherwise). It's two concerns: StaticCell makes it "live forever", Mutex allows it to be shared (if it needs to be mutable in multiple places)
<dirbaio[m]>
ragarnoy[m]: StaticCell gives you a `&'static mut T` the instant you initialize it, then nothing else. you have to pass that `&'static mut T` to the places you need it, for example as task arguments. If you need to pass it to multiple places you can convert it to `&'static T` first, so you can copy it first.
<dirbaio[m]>
you can not get a second reference to the contents after you have initialized it
<dirbaio[m]>
this is what allows StaticCell to work with !Send, !Sync data, and allows it to give you a `&'static mut T`. if you could get more refs out of it, it'd be unsound
<dirbaio[m]>
if this doesn't work for you then you want something else
<dirbaio[m]>
like a OnceCell
<dirbaio[m]>
OnceCell allows you to get many references out multiple times, but only `&'static`, not `&'static mut`
<dirbaio[m]>
* a OnceCell or OnceLock
<dirbaio[m]>
and if you need to get multiple references, and get mut references, then you need a full Mutex
PhilMarkgraf[m] has quit [Quit: Idle timeout reached: 172800s]
<ragarnoy[m]>
dirbaio[m]: I'm working with a Rust library that requires a custom hardware abstraction layer (HAL) implementation in C. This implementation includes a transfer function as part of the HAL interface, the signature is inflexible but internally it needs to perform SPI transfers
<dirbaio[m]>
maybe you want to use the actual HAL SPI type instead of dyn
<dirbaio[m]>
what HAL are you using? stm32?
<ragarnoy[m]>
dirbaio[m]: stm32, but in theory the antenna can be put on any type of processor
<ragarnoy[m]>
hence the SpiDriver
<dirbaio[m]>
ah you're writing a hardware-independent driver, so using only embedded-hal
<dirbaio[m]>
I see
<dirbaio[m]>
yeah :(
lehmrob has quit [Ping timeout: 256 seconds]
<ragarnoy[m]>
i guess i'm kind of screwed lol
<GrantM11235[m]>
Are you sure you don't want to just rewrite the whole c library in rust? ๐
<JamesMunns[m]>
ragarnoy[m]: You have certainly chosen a fairly challenging set of requirements
<JamesMunns[m]>
like, it's probably not impossible to do, decently well. It's just not a very fun + easy challenge, especially for volunteer effort lol
<GrantM11235[m]>
btw, I don't think you need to use dyn, but it is probably the easiest option. You can probably optimize it a bit more later if you want to
<ragarnoy[m]>
GrantM11235[m]: well, i'm making the layer between the hal and the phy implementation, which is private, what i'm using is the api that they give you
<ragarnoy[m]>
GrantM11235[m]: what's the alternative ?
<GrantM11235[m]>
doing fun tricks with generics
<JamesMunns[m]>
ragarnoy[m]: I can't think of any if you want it to be generic over any impl AND has to go through statics like that
<JamesMunns[m]>
GrantM11235[m]: if you can name those fun tricks I'd be interested.
<dirbaio[m]>
ragarnoy[m]: it's less "risky", you can't "forget to put it back"
<dirbaio[m]>
with Cell it's easy to forget, if the function returns early, for example with an error you propagate with ?
<Ecco>
What's the equivalent of CMSIS' "__set_PRIMASK" in Rust?
<ragarnoy[m]>
<dirbaio[m]> "with Cell it's easy to forget..." <- yeah, last question, `SpiDevice<u16, Error = Infallible>` is infallible correct here or what should I put for the library users ?
<Ecco>
I found a way to *read* primask in the cortex-m crate, but no way to set it
<dirbaio[m]>
ragarnoy[m]: yeah it's infallible, but most HALs's SPIs are *not* infallible. So the user will have to "adapt" from "fallible SPI" to "infallible SPI" to use your driver...
<dirbaio[m]>
I don't see any way around it. with `dyn` you have to specify the error type, just `dyn SpiDevice<u16>` doesn't work.
<dirbaio[m]>
so yeah... perhaps you can provide the "infallible adapter" to make things easier for the user.
<dirbaio[m]>
* yeah it's infallible, but most HALs's SPIs are _not_ infallible. So the user will have to "adapt" from "fallible SPI" to "infallible SPI" to use your driver...
<dirbaio[m]>
I don't see any way around it. with `dyn` you have to specify the error type, just `dyn SpiDevice<u16>` doesn't work.
<dirbaio[m]>
so yeah... perhaps you can provide the "infallible adapter" yourself to make things easier for the user.
<dirbaio[m]>
PRIMASK is the "disable all interrupts" bit
<dirbaio[m]>
so cortex_m::interrupt::disable(), cortex_m::interrupt::enable()
<GrantM11235[m]>
ragarnoy[m]: mem_alloc always returns a pointer to the first byte of MEM
<GrantM11235[m]>
GrantM11235[m]: if there is more than one allocation, they will all be at the same address
<GrantM11235[m]>
GrantM11235[m]: And it is only byte-aligned, which might not be enough
<GrantM11235[m]>
GrantM11235[m]: and mem_free just zeros all the bytes *before* the pointer to be freed, which is a no-op since the pointer is always at the start of MEM
<Socke>
hmm. whenever I (accidentally) press the enter key in the espflash monitor window, it hangs up and I can't even terminate it using ctrl-c
<Socke>
(does not happen when running "espflash monitor" but only when running it through cargo run, which runs espflash flash --monitor)
<Socke>
is this a known issue or am I doing something wrong?
<Socke>
wait. last info was wrong. also happens with "espflash monitor"
<ragarnoy[m]>
<GrantM11235[m]> "and mem_free just zeros all..." <- oh yeah i didn't expect this to be this annoying actually
<ragarnoy[m]>
ragarnoy[m]: that free function is non trivial
<dirbaio[m]>
it forwards c's malloc/free to rust's global alloc, yes
<dirbaio[m]>
so you do need an alloc on the rust side
<ragarnoy[m]>
<dirbaio[m]> "if you need something fancier..." <- and on that end, i can't really find how to implement a decent free function
holo[m] has quit [Quit: Idle timeout reached: 172800s]
<vollbrecht[m]>
<Socke> "hmm. whenever I (accidentally..." <- i am not sure, best is to create a small issue on github for it.
<GrantM11235[m]>
<ragarnoy[m]> "and on that end, i can't..." <- Allocators are complicated. [linked-list-allocator](https://github.com/rust-osdev/linked-list-allocator) is one of the simplest possible implementations, and it is about 1000 lines
emerent has quit [Killed (zinc.libera.chat (Nickname regained by services))]
emerent has joined #rust-embedded
<dirbaio[m]>
๐ฆ
<firefrommoonligh>
<dirbaio[m]> "or more idiomatic cortex_m::..." <- A note for anyone reading: It seems this is being deprecated in favor of `critical_section::with`. So, if writing new code, consider that. Adv: It's more cross-MCU maybe
<firefrommoonligh>
(The ecosystem churn is real, but worth it to make stuff better)
<dirbaio[m]>
lol churn ๐ฅ
<adamgreig[m]>
I don't think cortex_m::interrupt::free will be removed, because it still makes sense to provide "execute a closure with interrupts disabled"
<adamgreig[m]>
but I think it will stop giving out a CriticalSection token, that's what critical_section::with is for
<adamgreig[m]>
but yea, if what you want is a critical section where you can be sure only one instance of it is running at a time, use the critical_section crate, and as a bonus it's architecture-independent