Git Product home page Git Product logo

Comments (23)

diondokter avatar diondokter commented on June 28, 2024 2

There has been some discussion about a flash/memory API here. I think you can skip the usize is most of those methods, since the slice will already tell you how long it is. Here is an example of a generic EEPROM driver.

The usize is probably the start address.

from device-driver.

hargoniX avatar hargoniX commented on June 28, 2024 2

It would also be pretty cool if we could have a separation of registers and protocols used to control said registers, for example OpenBSD has an IC directory containing register definitions https://github.com/openbsd/src/tree/master/sys/dev/ic as well as several bus specific directories such as I2C, SPI, PCI etc. in the directory above the IC one. I think this separation of bus and registers might be a pretty interesting thing to have, of course for code re-usage but also because if we have the register definition strictly separate from the bus specific implementation writing mock tests would be a lot easier for the developer.

from device-driver.

hargoniX avatar hargoniX commented on June 28, 2024 1

There is also this https://github.com/jonas-schievink/spi-memory which might be a good playground to try some approaches

from device-driver.

ryankurte avatar ryankurte commented on June 28, 2024 1

hey this is a super interesting project! i would love to have a better mechanism for describing registers / fields / etc. than the current write-it-all-out-by-hand approach. it's probably a bunch more primitive than what is proposed here but, i have been playing with similar abstractions across the few radio driver crates i have, trying to extract commonality and smooth the driver writing / maintenance situation.

it's all a little cursed due to the outstanding transactional SPI PR but, some links if you're interested: trying to abstract over underlying SPI / UART protocols [1] (though i haven't yet implemented the UART one), as well as common driver functions [2] [3] to simplify driver type bounds, providing blocking [4] and nonblocking [5] accessors over classical / c-style do/poll/done drivers, and hal abstractions to simplify writing utilities to test and interact with drivers [6].

from device-driver.

TheZoq2 avatar TheZoq2 commented on June 28, 2024 1

Great news 🎉 I'll look into this when I find time :)

Also, I don't have a lot of Open Source experience. So I'm open for process/style suggestions as well slightly_smiling_face

From my f103_hal experience, keeping a structured changelog is really good. For every new feature or fix, add a note to the changelog. Generally, this is much easier to look through than commit messages and makes upgrading particularly breaking changes much simpler.

This is the format we use: https://keepachangelog.com/en/0.3.0/

from device-driver.

TheZoq2 avatar TheZoq2 commented on June 28, 2024

Great initiative, the things you showed in your talk looked super interesting!

/// NOTE Using this can conflict with high-level functionality. Make sure not to break any assumptions that the crate makes.

What does this mean?

What do you think is better? Macro's, generation or maybe something else?

Both have merits. Macros would probably be a bit less expressive (maybe), may increase compile time(?), and often breaks autocompletion in rls/rust-analyser. On the other hand, a file based approach would need to generate a new crate, be run separately from the compiler, probably. It would make it easier to re-use the data for other purposes though...

The API to access registers can be the same as the API used in the PAC crates for the Arm MCU's. (See embedded book)

Quick thought: the PAC interface is neat, but the high amount of zero cost abstraction can be annoying in debug mode. I believe there is a project for writing another PAC-like interface that uses less "expensive" zero cost abstraction, but I can't remember the name right now, peripheral something something.

Might be worth looking into though.

from device-driver.

tl8roy avatar tl8roy commented on June 28, 2024

Memory space API

I am thinking of writing a storage embedded hal trait. The current interface in my head is read(usize) -> Word, write(usize, Word), read_slice(usize, [Word]), write_slice(usize,[Word]). So I would think the memory space API would want to use something similar.

Streaming data API

I think a generic Streaming API is probably in the too hard basket, I think there is more value in dealing with the actual data like ATAT does for AT devices or a GPS format crate.

Sleep

Some kind of Sleep trait would be nice too. But it is also very device specific.

from device-driver.

hargoniX avatar hargoniX commented on June 28, 2024

I don't really like the high level traits here to be honest, for example the power trait.

/// This device can be powered on and off
pub trait PowerManagedDevice : Device {
    fn power_off(&mut self) -> Result<(), Self::Error>;
    fn power_on(&mut self) -> Result<(), Self::Error>;
}

forces the developer away from using type states for this transition, in case a certain set of functionality is only supported for the chip as long as it is powered down/up. While we can represent that with type states and PhantomData without this trait in order to enforce that this driver is not misused by the consumer when we handle this sort of state transition via this trait that seems impossible to me. Same goes for stuff like the reset() function of

/// General Device trait
pub trait Device {
    type Interface;
    type Error;

    /// Create a new instance of the device with the given interface
    fn new(interface: Self::Interface) -> Result<Self, Self::Error>
        where Self: Sized;
        
    fn reset(&mut self) -> Result<(), Self::Error>;
}

Maybe some people would instead like to change a type state that indicates the chip is in its reset state instead of , also impossible with this API.

from device-driver.

ryan-summers avatar ryan-summers commented on June 28, 2024

I have to agree with @hargoniX on this - when we force device driver crates to conform to some strictly defined interface, it can make it hard to adopt. For example, often-times, a device driver may want to take in use-once GPIO when instantiating a device (e.g. tied to reset lines or asynchronous edge trigger signals) to bring the device up from a cold boot. With the proposed Device::new(), that's not really possible. I think in general, we should try to implement this while imposing the minimal amount of requirements on the driver developer.

/// This device can be powered on and off
pub trait PowerManagedDevice : Device {
    fn power_off(&mut self) -> Result<(), Self::Error>;
    fn power_on(&mut self) -> Result<(), Self::Error>;
}

/// This device can be put to sleep
pub trait SleepManagedDevice : Device {
    type SleepMode;

    fn sleep(&mut self, mode: Self::SleepMode) -> Result<(), Self::Error>;
    fn wakeup(&mut self) -> Result<(), Self::Error>;
}

In regards to power states, devices can often have a multitude of powerstates, such as a radio that is in a low-power polling mode (WOR), but is not completely powered off. I think power state management is a bit complex here. I would also start off simple here and start with just the register API and work up from there.

What do you think is better? Macro's, generation or maybe something else?

I would go for the macro approach personally, due to the following:

  • Easy to update the definitions when a mistake is found (don't have to run some external tool to do the codegen each change)
  • Stores the register definitions directly next to the code that uses it, which increases visibility.
  • Reduces the development overhead (only one single source file required)
  • Simpler to develop - just a rust macro instead of file definitions + external tooling

from device-driver.

eldruin avatar eldruin commented on June 28, 2024

I think it is worth mentioning register-rs here.

from device-driver.

diondokter avatar diondokter commented on June 28, 2024

To summarize what I've heard so far:

  • There's a slight preference to using macros for low level generation.
  • Maybe the generation parts should be separate from this effort?
  • There are concerns about the restrictiveness of using traits to define functionality.
  • People really like to do typestate.

/// NOTE Using this can conflict with high-level functionality. Make sure not to break any assumptions that the crate makes.

What does this mean?

The high level code may set a register to a certain value and expect it to stay that way. If you then change it without the high level code knowing about it, then it could start malfunctioning. I do feel you'd still want access, though, because not all high-level layers are complete and you may need other functionality that hasn't been implemented.


Quick thought: the PAC interface is neat, but the high amount of zero cost abstraction can be annoying in debug mode. I believe there is a project for writing another PAC-like interface that uses less "expensive" zero cost abstraction, but I can't remember the name right now, peripheral something something.

You can easily optimize the crates you're using from debug mode by adding this to your Cargo.toml:

[profile.dev.package.my-chip-driver]
opt-level = 3

Memory space API

I am thinking of writing a storage embedded hal trait. The current interface in my head is read(usize) -> Word, write(usize, Word), read_slice(usize, [Word]), write_slice(usize,[Word]). So I would think the memory space API would want to use something similar.

It could be nice for this library to use that trait. Then any library expecting some storage could use devices that use this toolkit.


Streaming data API

I think a generic Streaming API is probably in the too hard basket, I think there is more value in dealing with the actual data like ATAT does for AT devices or a GPS format crate.

You're probably right. I'm hoping someone has a good idea for this. Otherwise, these kind of devices may simply be out of scope to provide low level api generation for.


Sleep

Some kind of Sleep trait would be nice too. But it is also very device specific.

In regards to power states, devices can often have a multitude of powerstates, such as a radio that is in a low-power polling mode (WOR), but is not completely powered off. I think power state management is a bit complex here.

Yes, very specific. But I think it's possible to generalize. If anything more specific is needed, then the user would simply not call it through the trait API, but via the crate's custom high-level layer.


I have to agree with @hargoniX on this - when we force device driver crates to conform to some strictly defined interface, it can make it hard to adopt. For example, often-times, a device driver may want to take in use-once GPIO when instantiating a device (e.g. tied to reset lines or asynchronous edge trigger signals) to bring the device up from a cold boot. With the proposed Device::new(), that's not really possible. I think in general, we should try to implement this while imposing the minimal amount of requirements on the driver developer.

I agree as well. The traits proposed here are only to give an idea of the direction I see this going. Defining good ones will be the most challenging part of this crate. For example, there should be a way with extra pins like the reset pin.

from device-driver.

adamgreig avatar adamgreig commented on June 28, 2024

Quick thought: the PAC interface is neat, but the high amount of zero cost abstraction can be annoying in debug mode. I believe there is a project for writing another PAC-like interface that uses less "expensive" zero cost abstraction, but I can't remember the name right now, peripheral something something.

I think you're thinking of https://github.com/adamgreig/stm32ral which provides a very lightweight and quick to compile macro-based interface which looks like:

use stm32ral::{modify_reg, gpio};
let gpioa = gpio::GPIOA::take().unwrap();
modify_reg!(gpio, gpioa, MODER, MODER1: Input, MODER2: Output, MODER3: Input);

The crate provides modules for each peripheral and register which contains constants for values you might write to the fields. The macro is expanded into a simple mask-and-shift expression, so it produces small and fast code even without any compiler optimisations turned on, which makes debugging a lot easier too. Personally I prefer the syntax compared to the closure-with-method-chaining of svd2rust, too.

from device-driver.

tl8roy avatar tl8roy commented on June 28, 2024

Memory space API

I am thinking of writing a storage embedded hal trait. The current interface in my head is read(usize) -> Word, write(usize, Word), read_slice(usize, [Word]), write_slice(usize,[Word]). So I would think the memory space API would want to use something similar.

It could be nice for this library to use that trait. Then any library expecting some storage could use devices that use this toolkit.

I will try to make some progress in the next week or 2 then.

from device-driver.

eldruin avatar eldruin commented on June 28, 2024

There has been some discussion about a flash/memory API here. I think you can skip the usize is most of those methods, since the slice will already tell you how long it is. Here is an example of a generic EEPROM driver.

from device-driver.

eldruin avatar eldruin commented on June 28, 2024

Thanks. My brain immediately saw a pointer+length like in C :)

from device-driver.

regexident avatar regexident commented on June 28, 2024

A device trait like this would assume that a device can only use one interface:

pub trait Device {
    type Interface;
    // ...
}

In order to nicely allow for providing multiple interfaces the trait would have to look more like this:

pub trait Device<Interface> {
    // ...
}

impl Device<I2c> for Foo42 {}

impl Device<Spi> for Foo42 {}

(Your talk's code matches the latter, for what it's worth)

from device-driver.

diondokter avatar diondokter commented on June 28, 2024

(Your talk's code matches the latter, for what it's worth)

You're quite right! Didn't think of that.

from device-driver.

therealprof avatar therealprof commented on June 28, 2024

Something like a proc macro creating the interface from a yaml (or similar) description would be nice. 😅

from device-driver.

hargoniX avatar hargoniX commented on June 28, 2024

For generating from yaml files I'd like to point at https://github.com/google/cyanobyte, as of now the "standard" they have is just I2C but since the file format is essentially only a YAML defined via a JSON Schema we should be able to fork it and add required changes (maybe even upstream them as cyanobyte was planning to look into SPI etc anyways?). Plus we'd already have a nice set of files in that repo we can easily create test drivers with and try them out against real hardware while developing this crate.

from device-driver.

hargoniX avatar hargoniX commented on June 28, 2024

As it seems like the acivity on this issue regarding new features we'd like to have has stopped for now. What I get from this is that there is definitely a desire to have 2 parts to this crate

  1. Low level APIs inspired by PAC design that provide a unified way of accessing the information (in whatever form it should be) on the chip the driver is being written for. This lower level API should definitely be auto generated as well, just like a PAC. The biggest questions remaining I can see are:
  • Should we generate it with a (proc-)macro or from a file instead (like svd2rust for example)
  • We can agree on the PAC like interface for register based chips but how do we handle streaming and memory based API? (would be nice to see some actual code proposed for this @tl8roy mentioned he wanted to get something going a bit ago, did something drop out of that?)
  1. Higher Level APIs
    The main point for the traits proposed here was "not generic enough" if I understood everything correctly.

In order to discuss these questions it might be reasonable to split the current thread up into a few more issues so we can discuss everything that's separate separately?

(If something is missing from the questions I collected feel free to bring it up of course)

from device-driver.

diondokter avatar diondokter commented on June 28, 2024

Yes that's what I got as well from the responses.
I've been working on and off to get some basic thing going to get feedback on.

For now it will be a normal macro that will help you create a device with registers.
The high level stuff can wait for I think.
For defining memory space, I'd like to use this when it eventually lands: rust-embedded/embedded-hal#241

Don't know how soon I can finish it, but I'll have some time off in September which I hope proves to be productive for this.

from device-driver.

emosenkis avatar emosenkis commented on June 28, 2024

What about async vs blocking?

from device-driver.

diondokter avatar diondokter commented on June 28, 2024

I've released a first version!
It only does registers for now.

https://crates.io/crates/device-driver

Try it out and look at the example if you want. If you have ideas, then please post them here!
For specific problems, please open a new issue. For now I'll keep this open for general discussions.

Also, I don't have a lot of Open Source experience. So I'm open for process/style suggestions as well 🙂

from device-driver.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.