Open Finomnis opened 7 months ago
I may be in the minority about the "give users low-level control" design decisions
I personally don't like this, because it introduces ways for the user to break the internal state of the driver. I kind of consider this a functional unsoundness. I found the existing driver very hard to use because it requires me to understand the peripheral, which I as a user don't want to have to understand. I felt I would have to write a driver for the driver to actually use it in a project.
I would personally rather go the other route - closing it off completely and then step by step adding features as required. But that's just my personal opinion.
Here's a rough outline of what it might look like:
Let me think about this. You might be onto something.
Be aware that:
I think it’s cool that you are adding support for eh 1.0. I’d note that interrupts are not free of cost and sometimes it really is faster to poll. I saw this in a few spi peripherals in Zephyr that I’ve modified/reviewed. At times interrupts can cause slowdowns. Especially true when doing XIP or on cores with longer pipelines with icache/dcache involved like the M7
That might be true!
If we
flush
call is acceptableThen we can go with @mciantyre's architecture proposal.
The biggest open question I still have is error handling. But I have to clarify:
I found the existing driver very hard to use because it requires me to understand the peripheral, which I as a user don't want to have to understand.
We support embedded-hal for hiding the peripheral's complexity. We still need to give the user APIs for driver configurations, since embedded-hal doesn't help us here. But after you configure your driver and pass ownership into your embedded-hal-using component, it should become difficult to break the driver's internal state.
I'm sure there's use-cases I'm not considering, but that's the thinking for giving users the lower-level control.
there will always be a benefit from using interrupts, even in the dma case. For error handling and for the flush call.
We could require the user to transform InterruptTransmit
into DmaTransmit
, for example. Once DmaTransmit
takes ownership of InterruptTransmit
, it could select the I/O behaviors, wait for flush, check errors.
I’d note that interrupts are not free of cost and sometimes it really is faster to poll.
Good point. We're free to spin in async code if we find it profitable. We block the executor and other tasks.
But after you configure your driver and pass ownership into your embedded-hal-using component, it should become difficult to break the driver's internal state.
I agree. So we should maybe look into something like a builder pattern?
@mciantyre Might have to take a step back over the next couple of days/weeks. If someone has an idea, be free to use my code as a base/reference. There are a couple of things in there that took a while to figure out, like the proper settings of the timing registers and the clock multiplier calculation, which I think are improvements over the original version in a couple of details.
If I do continue (which I will if nobody else wants to take over), I might start incorporating a couple of improvements into the existing driver if you think it would be better to start with the existing one as a low-level backend. We could then add a high-level driver which takes ownership of the low-level one that then can do Embedded Hal Async stuff. Although @mciantyre I would need some guidance on the naming of things, because I feel like both the low level and the high level driver deserve the name Lpspi
.
Moved discussions over to #147.
I'm uncertain what you mean, to be honest. Which delays?
If you are taking about the example, the delay there is not part of our driver code, it comes from embedded-hal 1.0