| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
…epts features of C++20 for Spi, SpiMaster and SpiNorFlash. The 'interface' of the drivers are defined in `Pinetime::Drivers::Interface::Spi`, `Pinetime::Drivers::Interface::SpiMaster` and `Pinetime::Drivers::Interface::SpiNorFlash`. The actual implementation will be injected via the template parameter `T`. T is required to conform to the corresponding concepts `IsSpi`, `IsSpiMaster` and `IsFlashMemory`. This implementation should be practically free in terms of memory and cpu resource usage, but it provides way to implement multiple variations of the same driver. It also describes very clearly the interface of the drivers thanks to the concepts. This will allow us to support more easily other hardwares : different SoC, different sensors and memory chips. It'll also make the implemenation of InfiniSim much easier.
There was a problem hiding this comment.
I like the usage of the impl reference, as I was wary of the compile time overhead if so much code must be in the headers as templates require. But with the impl reference this should be ok as the heavy lifting is in the impl.cpp file
Sorry, something went wrong.
| uint8_t pinMOSI; | ||
| uint8_t pinMISO; | ||
| template <typename T> | ||
| concept IsSpiMaster = |
There was a problem hiding this comment.
(outsider suggestion) (ditto for other places) Concepts should be named like adjectives rather than predicates, i.e. SpiMaster rather than IsSpiMaster. This convention is established in the standard (same_as, derived_from, destructible, regular, ...) and is in line with how other languages treat traits and trait-like features
Sorry, something went wrong.
There was a problem hiding this comment.
I agree with this. However... this rule is not easy to apply here :
Renaming the concept from IsSpiMaster to SpiMaster will conflict with SpiMaster in InfiniTime.h. I'm open to suggestions regarding the naming of the types and namespaces, though!
Sorry, something went wrong.
…ate types name with more explicit names
In theory (and according to my experiments with compiler explorer), those calls to impl.xxx() should be optimized by the compiler, since the type of impl is known at compile time. Looks like the new cmake Unity build feature does not like some of those changes... |
Sorry, something went wrong.
SpiNorFlash : use the generic SPI driver instead of the NRF52 one.
|
I think this PR is ready for review. In this PR, I added a new concept and proxy object for the following low-level drivers:
The goals of these changes are:
The current naming of the classes / namespaces are: namespace Pinetime {
namespace Drivers {
template <typename DeviceImpl>
concept IsDevice = { /* ... */ };
namespace Interface {
template <class T>
requires IsDevice<T>
class Device {
public:
explicit Device(T& impl) : impl {impl} {}
/* ... */
private:
T& impl;
};
}
using Device = Interface::Device<ActualDevice>;
}
}Basically:
TODO:
|
Sorry, something went wrong.
|
It seems to me like there's no framework to choose the drivers yet, unless I'm missing something. How would you make two drivers selectable with CMake? |
Sorry, something went wrong.
The selection of the driver can be done by editing files in src/port (should I name this folder ports?). Currently, InfiniTime only supports the PineTime, so this is the only option available. There are a few variant that are supported (MOY-TFK5 MOY-TIN5 MOY-TON5 MOY-UNK) but they only need different clock settings, which was not in the scope of this PR. I implemented this mecanism in InfiniSim in this PR. See this file, for example I think, for example, that some Colmi P8 are using another touch panel. The file src/port/TouchPanel.h could be edited this way: #pragma once
#include "drivers/TouchPanel.h"
#ifdef TARGET_DEVICE_PINETIME
#include <drivers/touchpanels/Cst816s.h>
#endif
namespace Pinetime {
namespace Drivers {
#ifdef TARGET_DEVICE_PINETIME
using TouchPanel = Interface::Touchpanel<Pinetime::Drivers::TouchPanels::Cst816S>;
#elif TARGET_DEVICE_COLMI_P8
using TouchPanel = Interface::Touchpanel<Pinetime::Drivers::TouchPanels::TheOtherTouchPanelDriver>;
#else
#error "No target device specified!"
#endif
}
}
This is done using the variable TARGET_DEVICE, which is set to PINETIME by default. In the future, I think we could also move the initialization of the objects (that are currently created and initialized in the global scope in main.cpp) so that we can apply different parameter values to the constructors of the driver (ex : to apply a different display mirroring). But I figured that this could be done in a future PR, once this design is approved. |
Sorry, something went wrong.
|
What about main.cpp, where the touchPanel is hard coded to be constructed with the Cst816S implementation? |
Sorry, something went wrong.
Yes, right. I didn't encounter this issue in InfiniSim since it has its own main.cpp file, that is distinct from the one in InfiniTime. |
Sorry, something went wrong.
|
@Riksu9000 Unless there's any doubt that this design is correct and that we'll be able to actually support multiple hardware with it ? |
Sorry, something went wrong.
|
Reading the linked discussion it seems like avoiding runtime polymorphism might not make a big difference, but it makes the architecture more confusing. I don't really understand why it wouldn't get optimized without the proxy, but I think it could be worth the tradeoff to make the code significantly simpler. |
Sorry, something went wrong.
|
This abstraction scheme as-is is unable to handle multiple differing implementations of the same concept being needed at runtime, for example if trying to port to a device that has two different SPI flashes on board, or (if this is extended to GPIOs), if some things are wired up to GPIOs from the SoC and some are going through an I2C GPIO expander chip. Handling these cases would require either explicitly templating the user on the peripheral type, or introducing virtual functions. |
Sorry, something went wrong.
This is an interesting discussion! Let me (try to) explain the goals of this new design. The first goal of this design is not optimizations. Instead, it's more about "semantics" : I want the design to be as close as the hardware as possible. InfiniTime is a firmware for smartwatches. To the InfiniTime point of view, a smartwatch is a device with a display, a touchpanel, a motion sensor, a heart rate sensor, a button, an external memory,... This list of peripherals is know and does not change : every PineTime has the exact same hardware. So the idea is to provide a design that explicitly builds one and only one instance of each driver : the one that will be needed at runtime for a specific hardware. Also, the concept brings a nice way to describe the interface of a driver, in my opinion. In the end, I wanted this design to be as close as what we currently have in InfiniTime : everything is statically resolved and allocated. BUT... you said that this new implementation is confusion, and that is certainly something I would like to avoid so I guess I still need to work a bit on that. Using dynamic polymorphism and trust the compiler to optimize everything nicely might actually work! This is something I need to think about and experiment (compare).
Is this really an issue? Up to now, I've I've never seen a smartwatch with multiple memory cheap (let alone 2 different ones), or multiple displays, multiple HR sensors,... Do you think there are reasonable reason to need to instantiate 2 different implementations of the same "category" ? |
Sorry, something went wrong.
To be fair, in most cases I can't think of any applicable to InfiniTime, apart from potentially the aforementioned GPIO expanders vs SoC GPIO banks. I thought it still might be worth mentioning. |
Sorry, something went wrong.
I tend to agree that it's unlikely to be an issue for InfiniTime any time soon. |
Sorry, something went wrong.
|
Thanks everyone for your feedback on this PR. I'll think a bit more about this new hardware abstraction since the additional complexity brought by the new design might not be needed after all. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This PR improves the hardware abstraction by integrating the Concepts features of C++20 for Spi, SpiMaster and SpiNorFlash.
My goal is to provide a design that abstracts the hardware nicely and clearly. I want to avoid preprocessor (#ifdef), CMake black magic and code generation as much as possible. I also want the abstraction to have the lowest possible overhead : most if not all of the abstraction work should be done at compile time. And I think that new features from C++20 will make this possible.
The 'interface' of the drivers are defined in Pinetime::Drivers::Interface::Spi, Pinetime::Drivers::Interface::SpiMaster and Pinetime::Drivers::Interface::SpiNorFlash. The actual implementation will be injected via the template parameter T. T is required to conform to the corresponding concepts IsSpi, IsSpiMaster and IsFlashMemory.
This implementation should be practically free in terms of memory and cpu resource usage, but it provides way to implement multiple variations of the same driver. It also describes very clearly the interface of the drivers thanks to the concepts.
This will allow us to support more easily other hardware : different SoC, different sensors and memory chips. It'll also make the implementation of InfiniSim much easier.
Note that the C++ standard was upgraded from 14 to 20.
This is still a work in progress, other drivers must still be ported to this new design.
The corresponding PR for InfiniSim : InfiniTimeOrg/InfiniSim#73.
Here is a shorter version of the design in Compiler Explorer : https://godbolt.org/z/zTTPTPvEv
But, as this is the first time I'm using concepts, this PR is also a request for comments : what do you think of this "hardware abstraction" design?