Closed
Description
Folks who want to write drivers and embedded code using Rust need to have a way to guarantee exactly-once access to certain memory locations. Today, the embedded wg makes extensive use of @japaric's VolatileCell
crate, along with RegisterBlock structures containing VolatileCell
wrappers around each field of the register block, and a function to provide a single access to the register block at a fixed address. The API exposed in the the stdm32f103xx
crate and similar only expose *const RegisterBlock
values (example) from the overall Peripherals
object. This then requires unsafe code to access and mutate any particular field.
Asks:
- Is this pattern sufficient to guarantee that the number of writes to IO-mapped memory will exactly match the number of calls to
unsafe { (*x.volatile_cell_field).set(...) }
, and that the number of reads will exactly match the number of calls tounsafe { (*x.volatile_cell_field).get(...) }
? it seems like it should be. - Is it possible to provide the same guarantee while exposing the register block via a safe reference type such as
&
? It would be possible to provide a customRegisterRef<'a, T>
that consisted of a raw pointer internally as well as a custom derive for projecting this to fields of the register block, but this seems unfortunately complicated and unergonomic.
Complicating factors:
- LLVM's precise definition of "volatile" is a bit shakey. It says that optimizers must not change the number of volatile operations or change their order of execution relative to other volatile operations. However, it doesn't seem to specify that non-volatile operations can't be inserted-- this is something we need to prevent, but which LLVM might insert in an attempt to pre-load a value (as allowed by the "dereferencable" attribute that we apply to references). Can we make sure that LLVM doesn't do such a thing? If we fail in that, could we potentially make the compiler understand that
VolatileCell
is special, similar toUnsafeCell
, and cannot have "dereferenceable" applied to references to it (and objects that contain it), in order to prevent this misoptimization? This seems potentially more complicated and intrusive, but IMO still worth considering.