Skip to content

Do the current semantics for referencing registers interfere with composability? #151

@kjetilkjeka

Description

@kjetilkjeka

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

  1. Being allowed to safely access IO in a critical section
  2. 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:

  1. You can generate a gazillion structs which all reference the same data
  2. You can change the data by using non-mutable references without "feeling like" interior mutability
  3. 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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions