From 10897a5965d073acb48f57b2c50849e64d08dde0 Mon Sep 17 00:00:00 2001 From: George Beckstein Date: Tue, 9 Mar 2021 12:11:16 -0500 Subject: [PATCH 1/3] Introduce section on driver polymorphism This commit introduces documentation for the experimental driver polymorphism being added to Mbed OS. See related issue there: ARMmbed/mbed-os#13209 --- docs/api/drivers/drivers.md | 132 ++++++++++++++++++++ docs/images/driver_interface_diagram.png | Bin 0 -> 12276 bytes docs/images/driver_polymorphism_example.png | Bin 0 -> 16109 bytes 3 files changed, 132 insertions(+) create mode 100644 docs/images/driver_interface_diagram.png create mode 100644 docs/images/driver_polymorphism_example.png diff --git a/docs/api/drivers/drivers.md b/docs/api/drivers/drivers.md index 5c47538cc3..77b0665374 100644 --- a/docs/api/drivers/drivers.md +++ b/docs/api/drivers/drivers.md @@ -21,3 +21,135 @@ For example, you can attach a callback function on a [Serial](serial.html) objec One important thing to note is that when you attach callbacks with this function, the driver calls them in interrupt context. Interrupt context runs at a higher priority than any thread, which means that any code called from the attach callback must be interrupt safe. This excludes any blocking APIs such as blocking drivers, malloc, and mutexes. Or you risk preventing other high-priority interrupts from running, which may break other parts of the OS. When you need to call code that’s not interrupt safe from interrupt context, you must first defer from the interrupt context to a thread. The easiest way to defer to a thread is to signal a waiting thread through a [Semaphore](semaphore.html). In more complex use cases, you can use an [EventQueue](eventqueue.html) to manage multiple events at once. + +## Experimental: Driver Interfaces and Polymorphism + +**Please note: Polymorphic driver interfaces are still experimental and must be manually enabled by adding the `EXPERIMENTAL_API` feature to your project in your application configuration.** + +### Motivation and Overview + +Mbed driver APIs are intended to provide a solid foundation upon which to build cross-platform third-party libraries and higher level device drivers. With this goal in mind, an experimental feature has been introduced into Mbed's driver API hierarchy: **interfaces and polymorphism** + +Previously, polymorphism and virtual inheritance have been avoided in Mbed's core hardware driver APIs due to the potential memory and speed implications. Virtual inheritance adds virtual tables (vtables) to each inheriting class; this results in a slightly larger memory footprint for each instance of a virtual class. Not only this, but when a method is being called on a pointer to a virtual class (and therefore the concrete type of the underlying object is not known at compile time), there is an additional runtime step involved where the correct virtual method must be looked up in the class's vtable. In some high speed use cases, this added latency can be unacceptable. + +With the introduction of C++11, the language now has features to help optimize the above situtation. Namely, the `final` keyword. + +Subclasses inheriting from virtual base classes may now be marked `final`, which seals them from further inheritance. This allows the compiler to optimize calls to virtual methods in some cases, including inlining and removing the vtable lookup entirely. It must be stressed that this _can_ eliminate the vtable lookup and associated latency, but ultimately it is up to the compiler to do so. + +Unfortunately, at time of writing, toolchain support for pruning unused vtables is not widely supported and so the memory usage increase associated with virtual inheritance is unavoidable. This is the primary reason Mbed's developers have decided to make this feature experimental and disabled by default. For some very memory-constrained targets this memory usage increase would be unacceptable. + +### Using Driver Polymorphism in Your Code + +Even though this feature is still experimental, you can start writing code that takes advantage of driver polymorphism now. + +As an example use case, consider the scenario where your target has a limited number of internal GPIO available. You have run out of GPIO but need to control an RGB LED for user feedback. Now, your hardware team is very smart, and they've solved the problem for you: they've added an external GPIO expander IC that's accesible over I2C! You've already developed a very well tested and complex LED animation library but it has a structure like so: + +``` +class LEDAnimator { + + LEDAnimator(PinName red, PinName green, PinName blue) :_red(red), _green(green), _blue(blue) { ... } + +private: + mbed::DigitalOut _red; + mbed::DigitalOut _green; + mbed::DigitalOut _blue; + +} +``` + +As it is now, your library only supports using internal GPIO. This obviously won't work with the external GPIO expander IC! + +Previously, your only options were: + +1. Rewrite your animation library to use the I2C-based GPIO expander. This creates an entirely separate fork of your library to maintain in the best case. +2. Add extra code to conditionally handle the case where your library is using an external I2C-connected GPIO expander rather than internal GPIO. This complicates your library and makes the code much less readable. Additionally, what happens when your hardware team uses a SPI-connected GPIO expander in the next design? + +With driver polymorphism, the problem is much easier to solve. + +When enabled, supported drivers will inherit from a driver **interface**: + +![](../../images/driver_interface_diagram.png) + +Note that some behavior _is_ defined in the interface itself, namely, some operators. These operators are defined here to make it intuitive to use _all_ implementations of a given driver interface, even when mixing them together. + +You can always evaluate a `mbed::interface::DigtialOut` as an `int` (which returns the state of the output), and you can always assign the value of one `mbed::interface::DigitalOut` to another `mbed::interface::DigitalOut` in an intuitive manner using the assignment "=" operator. + +Let's get back to the above example. What you can do now, with driver polymorphism, is make it possible for your external I2C-connected GPIO expander to _act like_ a `DigitalOut` and _be compatible_ with any API that uses `mbed::interface:DigitalOut`: + +![](../../images/driver_polymorphism_example.png) + +This hierarchy minimizes the changes you need to make to your existing library. + +The recommended pattern for designing 3rd-party libraries that take advantage of driver polymorphism is to use _pointers_ to the _interface_ of the driver you're using. For example, we can refactor the `LEDAnimator` class to the following: + +``` +class LEDAnimator { + + /** + * Optional constructor to maintain backwards compatibility. This constructor + * dynamically allocates its own DigitalOut instances internally. + */ + LEDAnimator(PinName red, PinName green, PinName blue) : _internally_created(true) { + _red = new mbed::DigitalOut(red); + _green = new mbed::DigitalOut(green); + _blue = new mbed::DigitalOut(blue); + } + + LEDAnimator(mbed::interface::DigitalOut *red, mbed::interface::DigitalOut *green, mbed::interface::DigitalOut *blue) : _red(red), _green(green), _blue(blue), _internally_created(false) { + ... + } + + ~LEDAnimator() { + // Make sure to delete the instances if they were dynamically allocated! + if(_internally_created) { + delete _red; + delete _green; + delete _blue; + } + } + +private: + mbed::interface::DigitalOut *_red; + mbed::interface::DigitalOut *_green; + mbed::interface::DigitalOut *_blue; + + bool _internally_created; +} +``` + +Now it is possible for your existing library to use the external GPIO expander without any knowledge that it is external. As long as your `my_company::I2CExpander::DigitalOut` class implements the `mbed::interface::DigitalOut` interface properly, your code will work as it did before. You can even have some of the GPIO be internal or external if your design calls for it! + +### Backwards Compatibility + +Since driver polymorphism is still experimental, it is disabled by default. Therefore, Mbed's developers have been careful to make sure any code using the existing, non-polymorphic driver classes is not affected by the introduction of this new class hierarchy. + +The inheritance of a driver from the new driver interface is conditional upon `FEATURE_EXPERIMENTAL_API` being added to your project's configuration. However, it is still possible for you to develop APIs now that are compatible with both polymorphic and non-polymorphic drivers. + +When `FEATURE_EXPERIMENTAL_API` is disabled, the following type alias is made instead: + +``` +using mbed::interface::DigitalOut = mbed::DigitalOut; +``` + +This allows your new code library to be compiled with or without driver polymorphism enabled, since `mbed::interface::DigitalOut` will just fall back to the familiar, unpolymorphic `mbed::DigitalOut`. + +### Design Considerations and Caveats + +As mentioned before, virtual inheritance and polymorphism _do_ have an impact on the resulting binary. For extremely constrained targets, you can optimize away a bit of memory consumption by disabling driver polymorphism (which is currently the default). Then there is the impact on execution speed that can result from virtual inheritance. + +In some cases, such as a software ("bit-banged") UART implementation or similar, a small change in execution speed can have disastrous effects on your code's timing. + +Fortunately, you can still take advantage of driver polymorphism _and_ maintain high speed operation when you need it. + +When driver polymorphism is enabled, Mbed's driver implementations are marked `final`. This accomplishes two things: + +1. It prevents any further inheritance from the `final` class. +2. It hints to the compiler that a pointer or reference to this class will never point to anything but an instance of this class. + +The latter effect allows us to minimize the impact of virtual inheritance on execution speed in some cases. If the compiler knows the concrete type of the driver object at runtime, it _may_ eliminate or inline the vtable redirection. In our testing, we have seen this optimization result in _no speed hit_ when toggling a pin on and off in a while loop. Your results may vary depending on your target's HAL implementation, however. + +To improve the compiler's ability to optimize calls to a polymorphic driver in this way, you should use the following guidelines when designing your 3rd-party libraries: + +When your library **does not** have any high speed requirements, you should use a pointer to the _driver interface_ (eg: `mbed::interface::DigitalOut*`). This gives your library maximum flexibility by allowing it to use polymorphic subtypes of the given interface. + +When your library **does** have high speed requirements (such as the "bit-banged" example mentioned previously), you should use a pointer/reference to Mbed's driver implementation type _directly_ (eg: `mbed::DigitalOut*`). Since the compiler knows the concrete type is Mbed's implementation (which is `final`), it _may_ inline calls to that type, maintaining the execution speed possible with polymorphism disabled. diff --git a/docs/images/driver_interface_diagram.png b/docs/images/driver_interface_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..2b367b882e6faa766a5356814210971c75ee68c1 GIT binary patch literal 12276 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

x!}z66B(G_%nlOKUH`#b^el(dzp-E$M1*!st8%> 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 Date: Tue, 9 Mar 2021 13:47:23 -0500 Subject: [PATCH 2/3] Add PlantUML sources for driver polymorphism diagrams --- .../uml-src/driver_interface_diagram.txt | 18 +++++++++++++ .../uml-src/driver_polymorphism_example.txt | 25 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 docs/images/uml-src/driver_interface_diagram.txt create mode 100644 docs/images/uml-src/driver_polymorphism_example.txt diff --git a/docs/images/uml-src/driver_interface_diagram.txt b/docs/images/uml-src/driver_interface_diagram.txt new file mode 100644 index 0000000000..bf7be76d7d --- /dev/null +++ b/docs/images/uml-src/driver_interface_diagram.txt @@ -0,0 +1,18 @@ +@startuml +class mbed::DigitalOut implements mbed::interface::DigitalOut + +abstract mbed::interface::DigitalOut { + {abstract} void write(int value) + {abstract} int read() + {abstract} int is_connected() + DigitalOut &operator=(int value) + DigitalOut &operator=(DigitalOut &rhs) + operator int() +} + +class mbed::DigitalOut { + void write(int value) + int read() + int is_connected() +} +@enduml diff --git a/docs/images/uml-src/driver_polymorphism_example.txt b/docs/images/uml-src/driver_polymorphism_example.txt new file mode 100644 index 0000000000..5158abede7 --- /dev/null +++ b/docs/images/uml-src/driver_polymorphism_example.txt @@ -0,0 +1,25 @@ +@startuml +class mbed::DigitalOut implements mbed::interface::DigitalOut +class my_company::I2CExpander::DigitalOut implements mbed::interface::DigitalOut + +abstract mbed::interface::DigitalOut { + {abstract} void write(int value) + {abstract} int read() + {abstract} int is_connected() + DigitalOut &operator=(int value) + DigitalOut &operator=(DigitalOut &rhs) + operator int() +} + +class mbed::DigitalOut { + void write(int value) + int read() + int is_connected() +} + +class my_company::I2CExpander::DigitalOut { + void write(int value) + int read() + int is_connected() +} +@enduml From 1cae544b08c7419f9891de625904a37264023afb Mon Sep 17 00:00:00 2001 From: George Beckstein Date: Thu, 18 Mar 2021 10:23:05 -0400 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Vincent Coubard --- docs/api/drivers/drivers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/drivers/drivers.md b/docs/api/drivers/drivers.md index 77b0665374..412b6d1ec4 100644 --- a/docs/api/drivers/drivers.md +++ b/docs/api/drivers/drivers.md @@ -82,7 +82,7 @@ This hierarchy minimizes the changes you need to make to your existing library. The recommended pattern for designing 3rd-party libraries that take advantage of driver polymorphism is to use _pointers_ to the _interface_ of the driver you're using. For example, we can refactor the `LEDAnimator` class to the following: -``` +```c++ class LEDAnimator { /** @@ -127,7 +127,7 @@ The inheritance of a driver from the new driver interface is conditional upon `F When `FEATURE_EXPERIMENTAL_API` is disabled, the following type alias is made instead: -``` +```c++ using mbed::interface::DigitalOut = mbed::DigitalOut; ``` @@ -150,6 +150,6 @@ The latter effect allows us to minimize the impact of virtual inheritance on exe To improve the compiler's ability to optimize calls to a polymorphic driver in this way, you should use the following guidelines when designing your 3rd-party libraries: -When your library **does not** have any high speed requirements, you should use a pointer to the _driver interface_ (eg: `mbed::interface::DigitalOut*`). This gives your library maximum flexibility by allowing it to use polymorphic subtypes of the given interface. +When your library **does not** have any high speed requirements, you should use a pointer/reference to the _driver interface_ (eg: `mbed::interface::DigitalOut*`). This gives your library maximum flexibility by allowing it to use polymorphic subtypes of the given interface. When your library **does** have high speed requirements (such as the "bit-banged" example mentioned previously), you should use a pointer/reference to Mbed's driver implementation type _directly_ (eg: `mbed::DigitalOut*`). Since the compiler knows the concrete type is Mbed's implementation (which is `final`), it _may_ inline calls to that type, maintaining the execution speed possible with polymorphism disabled.