<re_irc>
<@lavanya10010010:matrix.org> Hi all, I recently started my embedded rust journey. I'm working with ATSAM4LC8CA (ATSAM4L Xplained Pro board) and I was wondering if we have any HAL support for this board or we have to use PAC only?
<re_irc>
<@lavanya10010010:matrix.org> Hi all, I have recently started my embedded rust journey. I'm currently working with ATSAM4LC8CA (ATSAM4L Xplained Pro board) and I was wondering if we have any HAL support for this board or we have to use PAC only?
<re_irc>
<@jamesmunns:beeper.com> (if you _do_ want it to not be no-std either when running tests, or when some feature flag (like "use-std") is active)
<re_irc>
<@jamesmunns:beeper.com> err, some unbalanced parens there, but you get the idea :D
<re_irc>
<@d3zd3z:matrix.org> Is it always ok to use "alloc::Vec" whether a crate is nostd or not? In this instance, I'm currently needing alloc.
<re_irc>
<@jamesmunns:beeper.com> yes, but you'll need to add "extern crate alloc;"
<re_irc>
<@d3zd3z:matrix.org> Although, I do remember seeing a Vec-like trait that was implemented for both alloc::Vec and for a crate implementing a non-allocating Vec-like (with a cap) struct. I could also use that, but some of my other dependencies do need alloc, so I'm not too worried in this instance.
<re_irc>
<@jamesmunns:beeper.com> I believe it! I've written some things like that, but they don't work _great_. If you need alloc, probably better to use it.
<re_irc>
<@d3zd3z:matrix.org> Yeah, I've discovered too many instances where a buffer should have been enough, only to find some use case where things don't fit.
<re_irc>
<@d3zd3z:matrix.org> Then again, one still has to decide on the heap size, which kind of comes back to the same problem, but only in one place, rather than all over.
<re_irc>
<@jamesmunns:beeper.com> yeah, that's a thing, but also trying to abstract over two different types is... not always pleasant :D
<re_irc>
I run into this a lot with postcard "wire types", where you want one side (like embedded) to use "&[u8]" or "heapless::Vec<u8, N>", and the host side to be "Vec<u8>" so you can have owned data.
dc740 has quit [Remote host closed the connection]
dc740 has joined #rust-embedded
IlPalazzo-ojiisa has quit [Remote host closed the connection]
<re_irc>
<@shakencodes:matrix.org> I'm trying to pass a heapless::String<nn> over Postcard and was a bit surprised to find Serialize was not a Trait of heapless::String. How are strings normally handed into Postcard for serialization?
<re_irc>
<@shakencodes:matrix.org> +I am embedding the heapless::String into a larger structure and the structure has the #[derive(Serializable)], and it is this declaration that is throwing: "error[E0277]: the trait bound "String<32>: Serialize" is not satisfied"
<Shell>
heapless::String does implement Serialize with the serde feature flag.
<re_irc>
<@jamesmunns:beeper.com> Yeah, if heapless String works for you, it's definitely a good choice!
<re_irc>
For larger sizes (>100 chars for log lines) is potentially not _great_ to have on the stack
<re_irc>
<@jamesmunns:beeper.com> It gets _weird_ for even slightly deeply nested types tho
dc740 has quit [Remote host closed the connection]
<re_irc>
<@shakencodes:matrix.org> Does anyone know how to setup the Rust toolchain so that you use nightly for Test and stable for Build? I'm presuming you can do something with rust-toolchain.toml... but have not been able to figure it out.
<re_irc>
<@dngrs:matrix.org> : Can you use "cargo +nightly test"?
<re_irc>
<@diondokter:matrix.org> I don't think you can. But you can just use nightly for the normal build. I've been using nightly for like 3 years and it's only been 3 or 4 times that there was an actual issue. And then you can just go back to the previous nightly too
<re_irc>
<@diondokter:matrix.org> cargo +nightly will work yeah
<re_irc>
<@diondokter:matrix.org> But I don't think you can make it the default for testing while having the default be stable for building
<re_irc>
<@dkhayes117:matrix.org> Too bad you couldn't specify it under a cargo toml profile
<re_irc>
<@dirbaio:matrix.org> so, it seems it's official, "vcell::VolatileCell" is unsound and there's no plans to change opsem to make it sound
<re_irc>
<@dirbaio:matrix.org> the only sound way to do MMIO today is with raw pointers
<re_irc>
<@dirbaio:matrix.org> "cortex-m", "riscv", all PACs generated with svd2rust, all other crates that use "vcell" or "volatile_register" are unsound 🔥
<re_irc>
<@adamgreig:matrix.org> Unsound, or might insert spurious reads when used with a hypothetical non-llvm backend?
<re_irc>
<@dirbaio:matrix.org> i'd say "compiler can insert spurious reads" is "unsound" :D
<re_irc>
<@dirbaio:matrix.org> other than that, i'm not sure...
<re_irc>
<@adamgreig:matrix.org> I thought the concern there was more about the intended semantics for rust vs what the llvm backend can currently do, which currently would make them not dereferenceable
<re_irc>
<@adamgreig:matrix.org> But I mean, for a very long time it did still mark them dereferenceable, soo
<re_irc>
<@dirbaio:matrix.org> but that's not guaranteed to be the case, according to rust's opsem
<cr1901>
>the only sound way to do MMIO today is with raw pointers <-- sounds like something they have to fix on their end :)
<re_irc>
<@dirbaio:matrix.org> you can do mmio just fine with raw pointers
<cr1901>
do raw pointers imply "do not f***ing optimize this away"? Or is this also based on "vibes"/what LLVM does?
<cr1901>
Well, in any case, hopefully it's not too much work to fix svd2rust
<re_irc>
<@grantm11235:matrix.org> > Volatile operations are intended to act on I/O memory, and are guaranteed to not be elided or reordered by the compiler across other volatile operations.
<re_irc>
<@dirbaio:matrix.org> rust references have a "must be valid" guarantee, raw pointers don't
<re_irc>
<@dirbaio:matrix.org> so the compiler is allowed to insert speculative reads for references, not for raw pointers
<re_irc>
<@dirbaio:matrix.org> (this is what actual opsem says, not llvm implementation details)
<cr1901>
insert speculative reads for references <-- well, except those wrapped in a Cell*?
<cr1901>
Anyways, pity. I was hoping MMIO could play nice w/ references, but guess not
<re_irc>
<@dirbaio:matrix.org> > except those wrapped in a Cell
<re_irc>
no, really any reference
<cr1901>
why? Cells are the shared AND mutable type. How can you be sure that the contents of a RefCell won't change in each iteration of a loop?
<cr1901>
To use yesterdays example
<re_irc>
<@dirbaio:matrix.org> the compiler will not insert the speculative read if it can't prove no other code _from the current threads_ mutates the value inside the cell
<re_irc>
<@dirbaio:matrix.org> for code _from another thread_ the compiler is indeed allowed to assume no other thread is modifying the cell
<re_irc>
<@dirbaio:matrix.org> the compiler will not insert the speculative read if it can't prove no other code _from the current thread_ mutates the value inside the cell
<re_irc>
<@dirbaio:matrix.org> and it's your job to synchronize memory accesses across threads, with atomics/barriers with the right ordering
<re_irc>
<@dirbaio:matrix.org> these tell the compiler "hey, careful, other threads migh've modified the value in the unsafecell, don't cache reads here"
<re_irc>
<@dirbaio:matrix.org> if you don't then it's a data race, which is UB
<cr1901>
you can use a Mutex<Refcell<>> with a cs, and modify a var from one thread in a loop safely while also reading from it in another. And Mutex also uses UnsafeCell internally and returns a &shared
<cr1901>
that &shared can't be speculatively moved out of the loop; I thought UnsafeCell was the marker that said "this can be modified elsewhere"
<re_irc>
<@dirbaio:matrix.org> that's what prevents the compiler from caching values across inside/outside the CS
<re_irc>
<@dirbaio:matrix.org> it's not the UnsafeCell
<re_irc>
<@dirbaio:matrix.org> > I thought UnsafeCell was the marker that said "this can be modified elsewhere"
<re_irc>
yes, within the same thread
<re_irc>
<@dirbaio:matrix.org> across threads you need fences/atomics
<re_irc>
<@dirbaio:matrix.org> UnsafeCell tells the compiler "hey, maybe some other code in the current thread is modifying this. If you're 100% sure no other code in this thread is doing that, then go ahead and optimize all you want"
<cr1901>
So it's not "no, really any reference". If you mark it w/ UnsafeCell, it is _possible_ that a speculative read could be suppressed (but not guaranteed)
<re_irc>
<@dirbaio:matrix.org> it is
<re_irc>
<@dirbaio:matrix.org> the compiler CAN insert speculative reads from ANY reference
<re_irc>
<@dirbaio:matrix.org> (as long as it can prove the optimization is correct)
<re_irc>
<@dirbaio:matrix.org> and UnsafeCell doesn't prevent speculative reads, it just adds another hurdle to "prove the optimization is correct"
<re_irc>
<@dirbaio:matrix.org> "fun" reads "x" only if "n!=0", but the optimized code reads "x" always. This is allowed because rustc can assume all references are valid
<re_irc>
<@dirbaio:matrix.org> and unsafecell doesn't prevent this
<cr1901>
Interesting...
<re_irc>
<@dirbaio:matrix.org> vs with "fun2", the same optimization isn't possible
<re_irc>
<@dirbaio:matrix.org> because there's no guarantee that raw pointers must be valid
<re_irc>
<@dirbaio:matrix.org> calling "fun2(null, 0)" is perfectly fine, the compiler is not allowed to optimize that into code that reads from a null pointer
<cr1901>
Well, if the raw pointer is invalid, you have UB, but interesting that code is generated to avoid deferencing NULL and crashing your program.
<cr1901>
if the raw pointer is invalid _and you read from it_*
<re_irc>
<@dirbaio:matrix.org> yep
<cr1901>
fun2(null, 0) is the only non-UB-causing call you can make where the first arg is null
<re_irc>
<@dirbaio:matrix.org> with raw pointers, it is only UB if you _read_ from an invalid pointer
<re_irc>
vs with references, an invalid reference just _existing_ is already UB
<re_irc>
<@grantm11235:matrix.org> (I switched to opt=s because opt=3 did some gnarly unrolling)
<cr1901>
So you need volatile for MMIO, and trying to do MMIO using references, even with Cells, is insufficient, because ppl working on opsem have decided that references by themselves can have speculative reads, and that bypasses the volatility of reading the underlying pointer
<cr1901>
Something like that?
<re_irc>
<@dirbaio:matrix.org> exactly
<cr1901>
"even with Cells that mark this value might've been modified since last read from despite being &shared*"
<cr1901>
^which is a precursor to MMIO as well
<cr1901>
MMIO would probably be too restrictive without the "shared and mutable" guarantee provided by cells or raw pointers, but I can't think of a good code example offhand where that's required other than "just vibes"
<re_irc>
<@grantm11235:matrix.org> There is also a difference between the compiler inserting a speculative read and actually using the value that it read
<cr1901>
What do you mean? Referring to MMIO read side effects, or...?
<re_irc>
<@grantm11235:matrix.org> For example, if i write "some_kind_of_fence(); let bar = *foo;", the compiler wouldn't be allowed to change that to "let bar = *foo; some_kind_of_fence();"
<re_irc>
<@grantm11235:matrix.org> But it could still do `let _ = *foo; some_kind_of_fence(); let bar = *foo;
<re_irc>
<@grantm11235:matrix.org> * *foo;`
<re_irc>
<@grantm11235:matrix.org> * "let _ = *foo; some_kind_of_fence(); let bar = *foo;"
<cr1901>
I see the difference (and how from an MMIO perspective that's be undesirable, especially in a non-contrived use of *foo before some_kind_of_fence()). What was your "there is also a difference between..." message in reply to?
<re_irc>
<@grantm11235:matrix.org> I mean that the fence means "I want you to read the value after the fence", it doesn't mean "you aren't allowed to do a read before the fence"
<cr1901>
That example seems a bit contrived (nothing to gain by doing the extra read?)
<cr1901>
But I feel like the important takeaway is that "the number of reads you put into the source code and the reads the compiler do" are not one-to-one when a speculative read is possible (either more or less reads)
<cr1901>
Btw, would like to add: As of right now, read_volatile on the contents of an UnsafeCell generates the same code as w/ a volatile read to ptr: https://godbolt.org/z/6eedK39G4
<re_irc>
<@grantm11235:matrix.org> It is contrived, but when it comes to reasoning about opsem, you need to consider what the compiler is _allowed_ to do, not what it currently does for your compiler version, target, options, etc
<cr1901>
In my godbolt link, is the fact that "I have to do a get() before I get to the raw pointer to do read_volatile" the theoretical grounds of "this could be speculatively read from", thereby destroying a one-to-one correspondence w/ ptr reads in the loop to "what the compiler generates"?
<cr1901>
(fun4)
<re_irc>
<@grantm11235:matrix.org> fun4 will always have exactly n volatile reads because that is what you asked for and the compiler isn't allowed to change that
<re_irc>
<@grantm11235:matrix.org> The compiler is allowed to insert additional (non-volatile) reads, but there is no benefit to doing so in this case