Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Wrap nrf52::Peripherals and nrf52::CorePeripherals #7

Open
hannobraun opened this issue Sep 12, 2018 · 0 comments
Open

[RFC] Wrap nrf52::Peripherals and nrf52::CorePeripherals #7

hannobraun opened this issue Sep 12, 2018 · 0 comments

Comments

@hannobraun
Copy link
Contributor

This issue was first opened in the old nrf52-hal repository. Original discussion: jamesmunns/nrf52-hal#6


The entry points to this crate's API are currently mostly extension traits: For a peripheral that this crate provides an API for, an extension trait is defined and implemented. Extension traits are implemented for the peripheral proxys from the nrf52 crate. An extension trait defines a method like constrain or split, which consumes the raw peripheral proxy and returns the HAL API for that peripheral.

That style of API is popular among HAL APIs (I think mostly because most HAL crates start out by copying other HAL crates), but I believe it is flawed. The critical problem is, that the HAL can't control the initial hardware state, making it impossible to give certain guarantees without significant runtime overhead. If we knew for a fact that no one had been messing with the hardware before initializing the HAL API, we could give much stronger safety guarantees, without the additional runtime overhead.

So far this is a rather general argument, but there is one concrete problem that would be weird to solve with the extension trait style: On the nRF52, several groups of peripherals share the same address space, and only one peripheral in each group can be used at a time (see nRF52832 Product Specification, section 15.2).

If we controlled the initial hardware state, we could statically guarantee that the user can enable only one of the peripherals in a given address space at a time. With the the current architecture, I can only think of solutions that are weird (enabling one peripheral in the group consumes the others) or less efficient (check hardware status at runtime.

I propose the following architecture:

  • We add a new struct, Peripherals, which wraps nrf52::Peripherals and nrf52::CorePeripherals.
  • Peripherals has two methods that can be used to get an instance, take and steal.
  • Under the hood, take and steal use the take and steal methods of nrf52::Peripherals/nrf52::CorePeripherals to provide the same guarantees that those methods provide.
  • Each nRF52 peripheral is available as a field of Peripherals. For peripherals that don't have a HAL API implemented yet, the peripheral proxy from nrf52 is provided directly.
  • Each peripheral API provides a free method that returns the peripheral proxy from nrf52. This allows the user to fall back to the raw register API, if the HAL API doesn't fulfill their needs.
  • This allows us to solve the problem of shared address spaces cleanly: We add singleton structs that represent the shared address spaces to Peripherals. Those are consumed by the HAL APIs of the shared peripherals when enabling them.

Summary: By following this architecture, we know that the user hasn't modified the hardware at the point when the HAL API is initialized (unless they have used unsafe means to do so, at which point all bets are off). This allows us to provide safety guarantees that we otherwise couldn't provide easily, or only with additional runtime overhead.

Further reading: I've been using this technique in lpc82x-hal for a while now, and I'm very happy with it. One major problem it solves is pin function assignment. lpc82x-hal statically guarantees that conflicting pin functions can't be assigned to a single pin. Without control over the initial hardware conditions, this could not be achieved without checking every single function assignment register (nRF52 has the same problem, by the way).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant