-
Notifications
You must be signed in to change notification settings - Fork 156
Description
Memory mapped IO is a completely different animal than the normal memory. It may mask bits when reading/writing, it oftens changes values of registers "asynchronously", may be inherently stateful and may even trigger hard faults (or similar) when a read is attempted. It's not given how rusts notion of safety should be extended or extrapolated into something meaningful when hardware access is thrown into the mix.
Code generated with svd2rust currently exposes two interfaces to the hardware. One of them is the static Peripheral
structs. These give a strong feeling of interior mutability and lets you safely (no unsafe) borrow one in exchange for a CriticalSection
token. This is great! Except for when it isn't. Which unfortunately is a lot of the time. What I'm referring to is the composability problem of
- Being allowed to safely access IO in a critical section
- Only being allowed to safely access IO in a critical section.
Let's look at number two first. Imagine a token representing that a clock source has been activated for a peripheral. To use the peripheral you would have to present such token with a longer lifetime than the peripheral. The first problem is that, with safe rust, the peripheral would have lifetime of the CriticalSection
and so would the ClockSource
token and the actual peripheral as well. This would force you to either reinitialize everything the next time you're going to use the unit (which makes the library *useless in practice) or always do everything in a critical section all the time (which makes the library *useless in practice).
The other way to access IO is by generating a struct with references to all io. With this method, you don't have a present a CriticalSection
token. This is obviously unsafe for the following reasons:
- You can generate a gazillion structs which all reference the same data
- You can change the data by using non-mutable references without "feeling like" interior mutability
- Even if you only give out one struct and doesn't alias the reference, the only thing an external module has to do is presenting a
CriticalSection
badge and they will be able to mess up the hw module without calling unsafe code.
My first question is. Why do we generate these Peripherals that will safely trade a CriticalSection
for register access? It seems like they are of limited use in practice and by allowing this as "safe behavior" destroys composability by making it safe for a library to change the state of io registers as long as they have a critical section.
Is this not a threat to composability? What is the drawback of removing these constants? What is the drawback of making them unsafe to use?
The next problem I see is that there is no way to get a mutable reference to the registers. Let's look at the clock gating example again. If several peripherals share a common clock source it would make sense to require a read reference so they can't be changed while the peripheral is in use. This only holds as long as you need to use a mutable reference to mutate the registers. Due to never being able to get a mutable reference It's currently impossible to write the drivers in such way (without using transmute
).
What would be the drawback of the Peripheral
struct containing mutable references instead of non-mutable references?
I see that "state altering" calls in std
often doesn't require mutable references, this in turns requires more error handling capabilities (no functions are assumed "not fail" and thus have to return a Result
). An example is std::net::TcpStream
. I guess it could be achievable by checking all relevant configurations before doing anything, but i don't think it will be worth it.
(I'm aware that you can currently create several Peripheral
structs. It's not ideal, but due to requiring unsafe code it's ok. And in the future, mechanisms for ensuring that only one mutable reference exists to each register can be implemented, the app!
macro from rtfm seems like a step in the right direction)
*(Exaggeration for artistic purposes. The library is not useless, it's actually great! The safe constant definitions is a bit weird though.)