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

Implement polymorphism for DigitalIn/Out/InOut #13209

Merged
merged 5 commits into from
Mar 22, 2021

Conversation

AGlass0fMilk
Copy link
Member

@AGlass0fMilk AGlass0fMilk commented Jun 30, 2020

Summary of changes

In some IO-constrained applications, it is necessary to use a GPIO expander like the I2C-based MCP23008. To maintain compatibility with existing libraries that require a DigitalIn/Out/InOut handle, it would be beneficial to be able to subclass one of these classes and retarget the read/write, etc calls. This would enable an MCP23008 driver to provide DigitalOut-like objects that abstract away the intermediate I2C transfers. These objects can then be passed to older APIs using DigitalOut pointers.

For this to work properly, some of the DigitalIn/Out/InOut class methods must be made virtual. This PR accomplishes this.

I would encourage Mbed to consider "virtualizing" many of the other hardware drivers for similar use cases.

See #12387 for a similar discussion concerning retargetting Mbed's CAN API to an external SPI-based CAN controller/transceiver.

Impact of changes

None. This should not affect current users of these APIs in any functional way. Code size may be slightly increased due to the addition of virtual tables and new functions.

Migration actions required

None

Documentation

None

Pull request type

[] Patch update (Bug fix / Target update / Docs update / Test update / Refactor)
[x] Feature update (New feature / Functionality change / New API)
[] Major update (Breaking change E.g. Return code change / API behaviour change)

Test results

[] No Tests required for this change (E.g docs only update)
[X] Covered by existing mbed-os tests (Greentea or Unittest)
[] Tests / results supplied as part of this PR

Reviewers


@0xc0170 @ithinuel @bulislaw

@AGlass0fMilk AGlass0fMilk force-pushed the virtual-gpio-methods branch from ce285f9 to feea424 Compare June 30, 2020 20:49
@AGlass0fMilk
Copy link
Member Author

@AGlass0fMilk AGlass0fMilk force-pushed the virtual-gpio-methods branch from feea424 to a332084 Compare June 30, 2020 20:51
@ciarmcom ciarmcom requested review from 0xc0170, bulislaw, ithinuel and a team June 30, 2020 21:00
@ciarmcom
Copy link
Member

@AGlass0fMilk, thank you for your changes.
@bulislaw @0xc0170 @ithinuel @ARMmbed/mbed-os-core @ARMmbed/mbed-os-maintainers please review.

@kjbracey
Copy link
Contributor

kjbracey commented Jul 1, 2020

This could have a noticeable performance impact for bitbangers. The DigitalIn/Out classes are set up as one of the very few HAL APIs that can be fully inlined all the way down to a register read/write, and polymorphism defeats that.

Although on the other hand, I guess in most cases people accessing DigitalIn/Out are doing it as direct objects, eg class members, rather than via a pointer or reference, so the polymorphism should be skipped in those cases?

I've been involved in quite a bit of "devirtualisation" work - trying to minimise the number of uses of virtual in Mbed OS to get code size down.

We haven't yet come up with a totally satisfactory pattern that gives you the best of both worlds - no polymorphism overhead when you have exactly one such type in a system, and pay for it only when you need it. The toolchains would ideally be able to figure that out themselves (LTO?)

Maybe you could have a config option for virtualised drivers - either global or per-type?

@AGlass0fMilk
Copy link
Member Author

AGlass0fMilk commented Jul 1, 2020

This could have a noticeable performance impact for bitbangers. The DigitalIn/Out classes are set up as one of the very few HAL APIs that can be fully inlined all the way down to a register read/write, and polymorphism defeats that.

Definitely don't want to mess with that.

Although on the other hand, I guess in most cases people accessing DigitalIn/Out are doing it as direct objects, eg class members, rather than via a pointer or reference, so the polymorphism should be skipped in those cases?

For software my company writes, I'm trying to make it standard practice to lean towards using references rather than direct objects. This allows drivers written in the past to accept abstracted objects in the future if ever necessary.

In my specific case we have an external SPI-based chip and the CS pin is tied to a GPIO on an MCP23008 I/O expander. The driver I originally wrote for the SPI-based chip sometimes does multiple accesses during one function call so controlling the CS pin external to the driver code isn't possible. The best solution for me was to create a subclass of DigitalOut and virtual-ize the functions in this PR.

This is one of the benefits of using C++ in embedded. While I accept the increase in latency from the vtable redirection may be unacceptable in some cases, the flexibility provided by polymorphism in C++ is why I like using Mbed in the first place. It's one of the only embedded platforms pushing modern C++ onto micros. Therefore I don't think polymorphism should be completely removed from Mbed's base drivers.

I've been involved in quite a bit of "devirtualisation" work - trying to minimise the number of uses of virtual in Mbed OS to get code size down.

We haven't yet come up with a totally satisfactory pattern that gives you the best of both worlds - no polymorphism overhead when you have exactly one such type in a system, and pay for it only when you need it. The toolchains would ideally be able to figure that out themselves (LTO?)

Maybe you could have a config option for virtualised drivers - either global or per-type?

I don't love the idea of yet another obscure configuration option but I don't see another path forward to satisfy both use cases simultaneously. Perhaps we can add a new macro to mbed_toolchain.h -- MBED_VIRTUAL or something and conditionally apply the virtual keyword based on the app configuration? What would be the default in this case?

EDIT: my suggestion above ignores the possibility of enabling/disabling virtual on a per-module basis. We could introduce a configuration option for each module that can be configured to have virtual members.

To avoid conflicting macro definitions we could:
1.) Use long, unique, ugly names on a per-file basis. eg: MBED_VIRTUAL_DIGITALIN
2.) Put a single macro definition for each module in mbed_toolchain.h. eg: MBED_VIRTUAL_IO
3.) Create a new header -- mbed_virtual.h -- and put these module-level definitions there.

@kjbracey
Copy link
Contributor

kjbracey commented Jul 1, 2020

I don't love the idea of yet another obscure configuration option but I don't see another path forward to satisfy both use cases simultaneously. Perhaps we can add a new macro to mbed_toolchain.h -- MBED_VIRTUAL or something and conditionally apply the virtual keyword based on the app configuration? What would be the default in this case?

Basically my thoughts too. It would be something a bit more specific like MBED_DRIVER_VIRTUAL. It shouldn't be too obscure, in that as long as you use override you would get a somewhat clear error if the config isn't set right - the derived class would be in an "#if` checking that it was okay.

I've been involved a bit too much in micro-optimisation recently, spending days to shave another few hundred bytes off an image, which is somewhat skewing my view :) And that makes me inclined to have per-class virtual options, rather than a single one.

There's another significant image size problem in that neither GCC nor ARMC6 can eliminate unused virtual functions (ARMC5 and IAR can). Which means all virtual functions get included into an image. Big effect.

@0Grit
Copy link

0Grit commented Jul 1, 2020

More and more my problems end up in the compiler bin...

@adbridge
Copy link
Contributor

@AGlass0fMilk I've re-classified this as a feature update as you would be introducing new callable functions...

@mergify
Copy link

mergify bot commented Aug 5, 2020

This PR cannot be merged due to conflicts. Please rebase to resolve them.

@0xc0170
Copy link
Contributor

0xc0170 commented Oct 19, 2020

@donatieng @bulislaw What shall we do with this PR ? Please review

@hugueskamba
Copy link
Collaborator

@AGlass0fMilk Please rebase your branch if you still intend it to be merged.

@AGlass0fMilk
Copy link
Member Author

@AGlass0fMilk Please rebase your branch if you still intend it to be merged.

I will rebase this week.

@ciarmcom ciarmcom added the stale Stale Pull Request label Jan 20, 2021
@ciarmcom
Copy link
Member

This pull request has automatically been marked as stale because it has had no recent activity. @AGlass0fMilk, please carry out any necessary work to get the changes merged. Thank you for your contributions.

@AGlass0fMilk
Copy link
Member Author

Hi @hugueskamba, I have rebased and implemented the discussed configuration options for "virtualizing" each class.

@ciarmcom ciarmcom removed the stale Stale Pull Request label Jan 21, 2021
@AGlass0fMilk
Copy link
Member Author

Another use case for this feature is linked to above:

EmbeddedPlanet/ep-oc-mcu#42

@0xc0170 I'd like to know your thoughts on that PR above... It's a simple problem with an infinite number of solutions...

@AGlass0fMilk
Copy link
Member Author

@0xc0170 Please review

@ciarmcom ciarmcom added the stale Stale Pull Request label Feb 1, 2021
@AGlass0fMilk
Copy link
Member Author

@0xc0170 @kjbracey-arm I have created a small blinky example showing a simple use case for this polymorphism:

https://github.com/EmbeddedPlanet/mbed-os-example-inverted-digitalout

It compiles and works as expected on an NRF52840_DK.

Let me know if there's anything else to get this pushed forward.

@AGlass0fMilk AGlass0fMilk force-pushed the virtual-gpio-methods branch from ce29f89 to 1bc96c9 Compare March 9, 2021 18:21
@AGlass0fMilk AGlass0fMilk force-pushed the virtual-gpio-methods branch from 1bc96c9 to 78cfad9 Compare March 9, 2021 18:22
AGlass0fMilk added a commit to AGlass0fMilk/mbed-os-5-docs that referenced this pull request Mar 9, 2021
This commit introduces documentation for the experimental driver polymorphism being added to Mbed OS. See related issue there: ARMmbed/mbed-os#13209
@0xc0170
Copy link
Contributor

0xc0170 commented Mar 12, 2021

Reviewers, is this ready for integration ? The old approvals were cleared after the recent updates, is there anything outstanding?

@AGlass0fMilk Thanks for the example and doc PR, I'll check them

@AGlass0fMilk
Copy link
Member Author

Please move this along! I put quite a bit of work into the documentation and PRs for this...

@0xc0170
Copy link
Contributor

0xc0170 commented Mar 15, 2021

CI started

@mergify mergify bot added needs: CI and removed needs: review labels Mar 15, 2021
@0xc0170
Copy link
Contributor

0xc0170 commented Mar 18, 2021

I'll restart CI, this should be ready once we release 6.9 today

@0xc0170 0xc0170 removed the stale Stale Pull Request label Mar 18, 2021
@mbed-ci
Copy link

mbed-ci commented Mar 18, 2021

Jenkins CI Test : ✔️ SUCCESS

Build Number: 3 | 🔒 Jenkins CI Job | 🌐 Logs & Artifacts

CLICK for Detailed Summary

jobs Status
jenkins-ci/mbed-os-ci_unittests ✔️
jenkins-ci/mbed-os-ci_cmake-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_cmake-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-ARM ✔️
jenkins-ci/mbed-os-ci_build-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-ARM ✔️
jenkins-ci/mbed-os-ci_cmake-example-test ✔️
jenkins-ci/mbed-os-ci_greentea-test ✔️

@AGlass0fMilk
Copy link
Member Author

Let's get that merge!

@0xc0170 0xc0170 merged commit 7ef27d9 into ARMmbed:master Mar 22, 2021
@mergify mergify bot removed the ready for merge label Mar 22, 2021
ChrisGooch-Arm added a commit to ARMmbed/mbed-os-5-docs that referenced this pull request Aug 19, 2021
* 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

* Add PlantUML sources for driver polymorphism diagrams

* Apply suggestions from code review

Co-authored-by: Vincent Coubard <[email protected]>

Co-authored-by: George Beckstein <[email protected]>
Co-authored-by: George Beckstein <[email protected]>
Co-authored-by: Vincent Coubard <[email protected]>
ChrisGooch-Arm pushed a commit to ARMmbed/mbed-os-5-docs that referenced this pull request Aug 19, 2021
* 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

* Add PlantUML sources for driver polymorphism diagrams

* Apply suggestions from code review

Co-authored-by: Vincent Coubard <[email protected]>

Co-authored-by: Vincent Coubard <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.