-
Notifications
You must be signed in to change notification settings - Fork 2.1k
periph/gpio: new expandable GPIO API #14602
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
base: master
Are you sure you want to change the base?
Conversation
d36d760
to
daf5f11
Compare
Could you also provide numbers when compiled with LTO? |
Sure, I will provide them later today. I have them already for STM32, but for ATmega, collect2 of RIOT toolchain crashes. Should I include the number in the table above or should I use a separate table? |
1d35ec4
to
70db5f7
Compare
9b5b336
to
9024fba
Compare
I really would like to see this finally getting upstream. Maybe it is easiest to split this effort into some smaller PRs to get this less scary. How about this:
Does this sound reasonable? I could help with reviewing and/or coding parts of this. |
Me too 😉 Unfortunatly, I'm completly busy due to the second Corona online semester. That's why I didn't contribute anything to RIOT in the last months. All my contributions are currently on hold. This will probably remain so for the next two months. But I'm willing to resume my work after that. Maybe the PR seems to be too fat, but it isn't. The core of this PR are just a small number of files.
That's it. All other changes in the source code are just small modifications to make the code compilable with the new The new GPIO API is completely transparent for existing applications and the existing high-level API with one exception, comparision functions have to be used instead of value comparisons due to the structured
As already mentioned, the current approach doesn't require that all platforms implement the new GPIO API at the same time. It can be done platform by platform. A platform that supports the new GPIO API just provides the feature
The critical point seems to be the new structured GPIO type which consists of a port of type If I understand your approach correctly, you would split |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
AIU there's the whole gpio_abc PR tree depending on this (#12015 for ground work up to #12020 that adds WS281x support for more platforms). @maribu, can you you process Gunar's last comments? All involved, other than questions of whether to split this into one or two components, is there any reason this should generally not be done? (I don't think so, just trying to paint the roadmap here). |
I currently need a ws281x driver for the nRF52 family and reconsidered the approach. Basically, a fast (1-2 CPU cycles) GPIO API that doesn't rely on LTO and parameters being correctly identified as compile time constants would allow ignoring the overhead of the GPIO API for the bit-banging timing. Adding a separate API for precise busy-waiting for short durations (maybe using the SysTick timer on Cortex-M rather than counting CPU cycles to also work for Cortex M7 and not having to experimentally the number of CPU cycles per loop iteration) would do the trick. I'm currently working on a new low-level peripheral GPIO API that is addressing this and a couple of other limitations of the current GPIO API, which is loosely based on this PR. It will however not take GPIO extenders into account. The current pin-oriented API can easily be implemented on top of that at one place - and likely most users will stick with the current API. It should be way easier to hook GPIO extenders into the pin-oriented GPIO API once there is only a single, hardware independent implementation of it. The low-level port-oriented GPIO API that I'm working on is IMO not the right place for hooking in GPIO extenders, as this would affect timing. If we want to do precise bit-banging as required for speaking to the ws281x, we are limited to using the peripheral GPIOs and depend on reliable timing of GPIO accesses. Use-cases not depending on this will likely prefer the pin-based API anyway and will work just fine for external GPIOs. |
Just as a data point, that was relatively easy to achieve also based on #12015 which is still based on the current GPIO API. |
Well, GPIO ABC is a new API just for bit-banging that is only needed because the current API is too slow. And we still are not able to bit-bang parallel protocols with it, as this would inherently need a port based API. (And also: The current approach of counting CPU cycles in GPIO ABC relies on the same code taking the same amount of CPU cycles. But with the dynamic branch predictor the delay loop on STM32F7 takes easier 1 CPU cycles for the two instructions (using the dual issue feature), or something in the order of 10 CPU cycles if the pipeline stalls. So we need to come up with something else for at least some boards anyway.) Also the GPIO initialization API IMO sucks, as we cannot expose features commonly present on modern MCUs easily. E.g. if an MCU as strong and weak internal pull resistors and can not only pull and down, but also keep the input at bus level (pull to whatever the input was last, so that it needs to be externally driven to the opposite level) we would get a lot of So there are actually plenty of reasons I dislike the current API. The ws281x driver is just an excuse to finally scratch that itch. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
Contribution description
Please note: Even if this PR should be compilable with the current master, the new API provided with this PR cannot be tested standalone. At least one MCU implementation is required for testing. Follow-up PRs with two MCU implementations are PR #14610 for STM32 and PR #14607 for ATmega.
This PR provides a new GPIO API that
GPIO_PIN(port,pin)
macroIt is the result of the discussion in POC/RFC PR #12877. The new GPIO API consists of a high-level API and a low-level API:
Implementation Details
GPIO pins
A GPIO pin of type
gpio_t
is now defined as a structure of a GPIO port of typegpio_port_t
and a pin number of typegpio_pin_t
.gpio_t
can't be overridden anymore.The number of pins per GPIO port is defined by the width of type
gpio_mask_t
which in turn is determined by modulegpio_mask_8bit
,gpio_mask_16bit
orgpio_mask_32bit
. Each driver for GPIO ports including the MCU implementation has to enable the according module. The width of typegpio_mask_t
is then the maximum width of all different GPIO ports used in the system.GPIO ports
The GPIO port of type
gpio_port_t
and can be eithergpio_dev_t
in case of a GPIO expander port, orThe port number is a sequential number that goes from 0 to
GPIO_EXP_PORT-1
for MCU GPIO ports. For GPIO expanders, the port number is derived fromGPIO_EXP_PORT
and their index in the GPIO device tablegpio_devs
as offset.In the case of GPIO expander ports, the port is defined as a reference to a device descriptor of type
gpio_dev_t
. This device descriptor containsgpio_driver_t
which contains the references to the low-level GPIO API functions of the associated device driver.Device descriptors of type
gpio_dev_t
are stored for all existing GPIO expander ports in a device table for GPIO Portsgpio_devs
, where each entry contains a device descriptor for exactly one GPIO expander port. If a GPIO expander provides multiple ports, each port has to be defined as separate device of typegpio_dev_t
. The device table for GPIO expander portsgpio_devs
is automatically generated from theGPIO_EXP_PORTS
macro definition which has to be defined either by the board definition or by the application in a separate filegpio_exp_conf.h
.In the case of MCU GPIO ports, the port defines just the register address. The mapping to the functions of the low-level API is realized by the special driver interface instance
gpio_cpu_driver
which is created automatically from the MCU low-level functionsgpio_cpu_*
.MCU Low-level API Implementation
The MCU implementation of the low-level API has to define
gpio_cpu_*
,gpio_mask_8bit
,gpio_mask_16bit
orgpio_mask_32bit
,GPIO_CPU_PORT
andGPIO_CPU_PORT_NUM
GPIO_CPU_PORT_BASE
andGPIO_CPU_PORT_MASK
or alternativelyGPIO_CPU_PORT_IS
periph_gpio_exp
.How the new GPIO API works
If the MCU implementation does not support the new GPIO API, that is the
periph_gpio_exp
feature is not provided by the MCU, the legacy GPIO API is used. GPIO expanders can then only be used via the device-specific driver interface.If the MCU implementation supports the new GPIO API, that is the
periph_gpio_exp
feature is provided by the MCU, the GPIO API works as follows:Default case where GPIO expanders are not used (
gpio_exp
module is not enabled).In this case, high-level API functions call the MCU low-level functions
gpio_cpu_*
directly. There is no MCU driver interfacegpio_cpu_driver
and no device table for GPIO expander portsgpio_devs
. So only slightly more RAM than with the legacy GPIO API is needed for the port member in the GPIO pin variables. The performance is the same as with the old GPIO API or even better, depending on the MCU low-level implementation, such as for ATmega and STM32.GPIO expanders are used (
gpio_exp
module is not enabled)The low-level API functions are called indirectly via a driver interface. For this purpose, the high-level API first determines with the macro
GPIO_CPU_PORT_IS
whether the specified port is an MCU port and uses either the driver interfacegpio_cpu_driver
to call the MCU low-level functions indirectly or the driver interface of the corresponding GPIO expander from the device tablegpio_devs
. In this case, on Harvard architectures such as ATmega, additional RAM is required for the MCU driver interfacegpio_cpu_driver
and the device table for GPIO expander portsgpio_devs
. Of course the performance is lower than with the old GPIO API due to the additional checks and the indirect call of the low-level API functions. But for ATmega it is still better because of the more efficient implementation of the low-level functions.This approach is implemented as following:
This means that if GPIO expanders are used (module
gpio_exp
is enabled), the low-level API functions are always called indirectly, also for the MCU. A slight improvement in performance with slightly higher ROM requirements could be achieved if the test for the MCU port were performed separately in each function, see the approach below. In this case the RAM for the MCU driver interfacegpio_cpu_driver
could also be saved. But, in the default case where GPIO expanders are not used, this doesn't matter. Therefore, in my opinion the best compromise between performance, RAM/ROM requirements and code readability is the approach above.Benchmarks
Performance test using
tests/periph_gpio
and commandbench 0 4
:gpio_exp
used)gpio_exp
used)ROM/RAM requirements for
tests/pkg_semtech-loramac
:sx127x_params
in the device variable (8 pins a 4 byte forgpio_port_t
)gpio_exp
used)16 bytes for static const pin definitions in the Harvard architecture (8 pins a 2 byte for
gpio_port_t
)16 bytes because of a complete copy of
sx127x_params
in the device variable (8 pins a 2 byte forgpio_port_t
)gpio_exp
used)gpio_cpu_driver
(9 function references a 2 bytes)ROM/RAM requirements for
tests/driver_sdcard_spi
:sx127x_params
in the device variable (5 pins a 4 byte forgpio_port_t
)gpio_exp
used)10 bytes for static const pin definitions in the Harvard architecture (5 pins a 2 byte for
gpio_port_t
)10 bytes because of a complete copy of
sx127x_params
in the device variable (5 pins a 2 byte forgpio_port_t
)gpio_exp
used)gpio_cpu_driver
(6 function references a 2 bytes)The additional RAM requirements expose a problem of many, if not most, of our driver implementations. They keep their own copy of the parameters in the device variable instead of simply pointing to the static constant parameters in ROM. I'm not sure if this is always the best compromise between RAM/ROM size and performance.
Testing procedure
Compilation should succeed. Tests should still work.
The new GPIO API cannot be tested standalone. The test application
tests/periph_gpio_exp
, which implements virtual GPIO expanders and demonstrates how to use singleport and multiport GPIO expanders, requires an MCU implementation. Such an implementation is available for ATmega in PR #14607 and for STM32 in PR #14607. Use these PRs for testing.Issues/PRs references
Replaces PR #12877.
PR #12612 and #13925 will affect this PR and all follow-ups.