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

cpu/native: add gpio-mock #20431

Merged
merged 4 commits into from
Mar 29, 2024
Merged

cpu/native: add gpio-mock #20431

merged 4 commits into from
Mar 29, 2024

Conversation

gdoffe
Copy link
Contributor

@gdoffe gdoffe commented Feb 24, 2024

Contribution description

Modify gpio-mock to allow arbitrary GPIO emulation.

Testing procedure

Build:

$ make -C tests/periph/gpio BOARD=native -j
make : on entre dans le répertoire « /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/tests/periph/gpio »
Building application "tests_gpio" for "native" with CPU "native".

"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/boards/common/init
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/boards/native
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/core
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/core/lib
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/cpu/native
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/drivers
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/boards/native/drivers
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/drivers/periph_common
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/auto_init
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/benchmark
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/frac
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/isrpipe
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/libc
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/preprocessor
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/shell
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/shell/cmds
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/test_utils/interactive_sync
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/test_utils/print_stack_usage
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/tsrb
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/sys/ztimer
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/cpu/native/periph
"make" -C /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/cpu/native/stdio_native
   text	   data	    bss	    dec	    hex	filename
  49771	    844	  58120	 108735	  1a8bf	/home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/tests/periph/gpio/bin/native/tests_gpio.elf
make : on quitte le répertoire « /home/gdo/Developpement/Informatique/Personnel/cogip/RIOT/tests/periph/gpio »

Test:

$ tests/periph/gpio/bin/native/tests_gpio.elf
RIOT native interrupts/signals initialized.
RIOT native board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2024.04-devel-390-gebf95d-native_fixes)
GPIO peripheral driver test

In this test, pins are specified by integer port and pin numbers.
So if your platform has a pin PA01, it will be port=0 and pin=1,
PC14 would be port=2 and pin=14 etc.

NOTE: make sure the values you use exist on your platform! The
      behavior for not existing ports/pins is not defined!
> init_in 0 10
init_in 0 10
> read 0 10
read 0 10
GPIO_PIN(0.10) is LOW
> set 0 10
set 0 10
> read 0 10
read 0 10
GPIO_PIN(0.10) is HIGH
> bench 0 10
bench 0 10

GPIO driver run-time performance benchmark

                 nop loop:        46us  ---   0.000us per call  ---  2173913043 calls per sec
                 gpio_set:       346us  ---   0.003us per call  ---  289017341 calls per sec
               gpio_clear:       346us  ---   0.003us per call  ---  289017341 calls per sec
              gpio_toggle:       387us  ---   0.003us per call  ---  258397932 calls per sec
                gpio_read:       329us  ---   0.003us per call  ---  303951367 calls per sec
               gpio_write:       382us  ---   0.003us per call  ---  261780104 calls per sec

 --- DONE ---
> clear 0 10
clear 0 10
> read 0 10
read 0 10
GPIO_PIN(0.10) is LOW
> init_out 0 10
init_out 0 10
> read 0 10
read 0 10
GPIO_PIN(0.10) is LOW
> set 0 10
set 0 10
> read 0 10
read 0 10
GPIO_PIN(0.10) is HIGH
> 

@gdoffe gdoffe requested a review from gschorcht as a code owner February 24, 2024 00:37
@github-actions github-actions bot added Platform: native Platform: This PR/issue effects the native platform Area: drivers Area: Device drivers Area: cpu Area: CPU/MCU ports Area: sys Area: System labels Feb 24, 2024
Copy link
Member

@chrysn chrysn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good on a high-level and line-by-line level, setting the appropriate review flags.

Allowing everything in gpio_mock makes sense -- there is a strategy of doing a mock where things would be strict, but then they'd need to be consistently so (and right now this is more an "we drive with zero strength and the GPIOs are wired to GND" implementation without any checking whatsoever).

Some remarks inline.

sys/newlib_syscalls_default/syscalls.c Outdated Show resolved Hide resolved
@@ -63,6 +59,10 @@ extern "C" {
*/
#define GPIO_PIN(port, pin) (gpio_t)((port << GPIO_PORT_SHIFT) | pin)

/* GPIO configuration only if the module is available (=Linux) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure this is the right approach here:

The mock implementation is distinct from the native implementation, so reusing the same settings for both is confusing.

What is wrong with GPIO_PIN(1,3) == GPIO_PIN(0, 3)? The hardware that is being mocked could just only have one port. Even with the current defines, GPIO_PIN(0, 1<<24) would be identical to GPIO_PIN(1, 0); aliasing is only avoided if the mock module either established some boundaries or chose to use a two-int sized (eg. struct) type as its gpio_t (which I'd welcome as a test to see whether any piece of the code tacitly assumes that gpio_t can be cast around).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each GPIO "identifier" should be unique.
If I want to mock STM32 GPIOs for instance, GPIO_PIN(PORT_A, 1) is not the same than GPIO_PIN(PORT_B, 1). If we want to mock GPIOs, it should be close to real world.
But it is hard to define some boundaries as it is hard to predict what will be mocked.

Moreover, if you want to perform a switch case, if the macro returns twice same value it leads to such kind of error:

pf_positional_actuators.cpp: In function ‘void* cogip::pf::actuators::positional_actuators::_gpio_handling_thread(void*)’:
pf_positional_actuators.cpp:218:9: error: duplicate case value
  218 |         case pin_limit_switch_left_arm_lift_bottom:
      |         ^~~~
pf_positional_actuators.cpp:202:9: note: previously used here
  202 |         case pin_limit_switch_central_lift_bottom:
      |         ^~~~
pf_positional_actuators.cpp:233:9: error: duplicate case value
  233 |         case pin_sensor_pump_left:
      |         ^~~~
pf_positional_actuators.cpp:214:9: note: previously used here
  214 |         case pin_limit_switch_right_arm_lift_top:
      |         ^~~~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GPIO pin and port arguments, though theoretically generic, are both integer, and thus the only way to avoid equality of valid GPIOs are to either decide to subset them at check time (say, use 16 bits for pin and 16 bit for port number, and enforce that no other pins are valid), or to use a larger than default gpio_t.

To some extent, the bug here is that arbitrary pins are created in the first place -- any time input to GPIO_PIN is provided, the application needs to know that that pin exists (or rely on the verify function, which in the mock case is not accurately representing that there is really just one port). Usually the pins are configurable per board, and for native they would just all be mapped to the 0 port.

If the gpio_mock module is supposed to be used for arbitrary pin setup emulation (as is suggested by the presence of the weak attributes; a comment similar to the one on the i2c side could clarify that), I think that the best route would be to typedef struct { unsigned int port; unsigned in pin; bool is_undef; } gpio_t; and #define GPIO_PIN accordingly (possibly through a static inline function because C is odd about where struct literals can be used).
Alternatively, copy what is done in the gpio_linux case and pick some grouping of the bits what is for registers and what is for pins. But in that case, that choice (including the concrete shift) becomes part of the public API for creating the per-application gpio_… mock functions, and they need to pick their arguments apart again using the same shift.

Semi-relatedly, I'm not sure doing a switch-case on pins is futureproof -- there exists an gpio_is_equal function that may change its implementation in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrysn I now understand what you mean and I agree. 👍
However, it's not entirely straightforward, because if I change gpio_t to something other (struct, pointer) than the default uint32_t type, it results in a build error if I mix with a pcf875x gpio expander for example.

I think it's not as simple as I thought at first and it would take more time to get it right.
And it is always possible to define my own GPIO_PIN macro in the context of the application or my own board, in periph_conf.h for instance.

I therefore propose to abandon this commit. Are you OK with that ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uh pcf857x should not (ab)use gpio_t like that. It expects a plain integer (index of the GPIO), using gpio_t here will just lead to chaos.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/** conversion of (port x : pin y) to a pin number */
#define PCF857X_GPIO_PIN(x,y)  ((gpio_t)((x << 3) | y))

All the pcf857x API is modeled on periph_gpio. As mainly using stm32 and native, planets were aligned but following @chrysn implementation, I confirm, it leads to chaos in my build due to pcf857x. 😆

int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode);

It seems more safe to drop this commit for now, just let me know.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ben fixed this already in #20435, so the option of doing the struct would be on the table again. Abandoning this is fine, but if you happen to have already some code around where you tried the struct approach, I'd love to see it here.

Copy link
Contributor Author

@gdoffe gdoffe Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done @chrysn . You can check the last commit.

$ tests/periph/gpio/bin/native/tests_gpio.elf
RIOT native interrupts/signals initialized.
RIOT native board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2024.04-devel-332-g721330-native_fixes)
GPIO peripheral driver test

In this test, pins are specified by integer port and pin numbers.
So if your platform has a pin PA01, it will be port=0 and pin=1,
PC14 would be port=2 and pin=14 etc.

NOTE: make sure the values you use exist on your platform! The
      behavior for not existing ports/pins is not defined!
> init_out 0 1
init_out 0 1
> set 0 1
set 0 1
> read 0 1
read 0 1
GPIO_PIN(0.01) is HIGH
> read 0 0
read 0 0
GPIO_PIN(0.00) is LOW
> toggle 0 0
toggle 0 0
> read 0 0
read 0 0
GPIO_PIN(0.00) is HIGH
> toggle 0 0
toggle 0 0
> read 0 0
read 0 0
GPIO_PIN(0.00) is LOW
> bench 0 2
bench 0 2

GPIO driver run-time performance benchmark

                 nop loop:        92us  ---   0.000us per call  ---  1086956521 calls per sec
                 gpio_set:       346us  ---   0.003us per call  ---  289017341 calls per sec
               gpio_clear:       346us  ---   0.003us per call  ---  289017341 calls per sec
              gpio_toggle:       387us  ---   0.003us per call  ---  258397932 calls per sec
                gpio_read:       329us  ---   0.003us per call  ---  303951367 calls per sec
               gpio_write:       383us  ---   0.003us per call  ---  261096605 calls per sec

 --- DONE ---
> 

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrysn what do you think about that proposal for gpio mock in last commit ?

@chrysn chrysn added Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Feb 25, 2024
@riot-ci
Copy link

riot-ci commented Feb 25, 2024

Murdock results

✔️ PASSED

ebf95d2 cpu/native: enhance GPIO mocking with 2D array representation

Success Failures Total Runtime
10009 0 10009 10m:28s

Artifacts

@benpicco
Copy link
Contributor

The _init() and _fini() functions are already defined into the C RunTime (crti.o) object file provided by the arm-none-eabi toolchain. o avoid multiple definition error, set them as 'weak'.

What toolchain are you using and how is this related to native?

@github-actions github-actions bot removed the Area: sys Area: System label Feb 26, 2024
@gdoffe gdoffe force-pushed the native_fixes branch 4 times, most recently from 8204b94 to 55cfe0c Compare February 28, 2024 13:48
@gdoffe
Copy link
Contributor Author

gdoffe commented Feb 28, 2024

The _init() and _fini() functions are already defined into the C RunTime (crti.o) object file provided by the arm-none-eabi toolchain. o avoid multiple definition error, set them as 'weak'.

What toolchain are you using and how is this related to native?

it was not relative to native, second reason to have dropped this commit. 😅

But just for information:

$ arm-none-eabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-none-eabi-gcc
COLLECT_LTO_WRAPPER=/home/gdo/Applications/toolchain/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin/../libexec/gcc/arm-none-eabi/13.2.1/lto-wrapper
Target: arm-none-eabi
Configured with: /data/jenkins/workspace/GNU-toolchain/arm-13/src/gcc/configure --target=arm-none-eabi --prefix=/data/jenkins/workspace/GNU-toolchain/arm-13/build-arm-none-eabi/install --with-gmp=/data/jenkins/workspace/GNU-toolchain/arm-13/build-arm-none-eabi/host-tools --with-mpfr=/data/jenkins/workspace/GNU-toolchain/arm-13/build-arm-none-eabi/host-tools --with-mpc=/data/jenkins/workspace/GNU-toolchain/arm-13/build-arm-none-eabi/host-tools --with-isl=/data/jenkins/workspace/GNU-toolchain/arm-13/build-arm-none-eabi/host-tools --disable-shared --disable-nls --disable-threads --disable-tls --enable-checking=release --enable-languages=c,c++,fortran --with-newlib --with-gnu-as --with-headers=yes --with-gnu-ld --with-native-system-header-dir=/include --with-sysroot=/data/jenkins/workspace/GNU-toolchain/arm-13/build-arm-none-eabi/install/arm-none-eabi --with-multilib-list=aprofile,rmprofile --with-pkgversion='Arm GNU Toolchain 13.2.rel1 (Build arm-13.7)' --with-bugurl=https://bugs.linaro.org/
Thread model: single
Supported LTO compression algorithms: zlib
gcc version 13.2.1 20231009 (Arm GNU Toolchain 13.2.rel1 (Build arm-13.7))

@gdoffe gdoffe requested review from chrysn and benpicco March 1, 2024 16:26
@github-actions github-actions bot removed the Area: drivers Area: Device drivers label Mar 6, 2024
@gdoffe
Copy link
Contributor Author

gdoffe commented Mar 6, 2024

Just removed 2 commits already applied in #20430

/**
* @brief Mocked GPIO array
*/
static gpio_mock_t __attribute__((unused)) gpio_mock[GPIO_PORT_MAX][GPIO_PIN_MAX];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this array needs to go into the c file and this needs to be

Suggested change
static gpio_mock_t __attribute__((unused)) gpio_mock[GPIO_PORT_MAX][GPIO_PIN_MAX];
extern gpio_mock_t gpio_mock[GPIO_PORT_MAX][GPIO_PIN_MAX];

else we have an array per compilation unit

which is probably not intended

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@kfessel
Copy link
Contributor

kfessel commented Mar 13, 2024

please fix the pr description

Signed-off-by: Gilles DOFFE <g.doffe@gmail.com>
gdoffe added 2 commits March 15, 2024 20:27
If the gpio is initialized as an input or interruptable pin, the
gpio_mock driver returns -1 leading to failed initialization.
However that is not because nothing can change the GPIO state that it
has to be an error.
Return 0 in all cases.

Signed-off-by: Gilles DOFFE <g.doffe@gmail.com>
This allows developpers to override gpio behavior into their
applications.

Signed-off-by: Gilles DOFFE <g.doffe@gmail.com>
@@ -18,60 +18,85 @@
*/

#include "periph/gpio.h"
#include <stdio.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused include

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@kfessel kfessel changed the title cpu/native: various native architecture fixes. cpu/native: add gpio-mock Mar 18, 2024
@gdoffe gdoffe changed the title cpu/native: add gpio-mock cpu/native: update gpio-mock Mar 18, 2024
@@ -96,6 +96,52 @@ typedef enum {
} gpio_flank_t;

/** @} */
#else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#else
#else /* MODULE_PERIPH_GPIO_LINUX | DOXYGEN */

but shouldn't there be some GPIO_MOCK module to activate this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done (see next comment)

This commit introduces a more robust GPIO mocking mechanism by utilizing
a 2-dimensional array. Each element of the array holds a gpio_mock_t
structure describing a pin's attributes such as value, mode, flank,
interruption callback, and callback argument.

This enhancement allows for the arbitrary simulation of GPIOs across
various microcontroller architectures using the current API, while
maintaining consistency through the use of the GPIO_PIN macro.

Additionally, it should be noted that only the maximum number of ports
and maximum number of pins can be altered according to the context.

The implemented API in gpio_mock.c remains rudimentary, providing no
validation but fulfilling the required functions. However, it remains
customizable as all its functions are marked as weak.

Signed-off-by: Gilles DOFFE <g.doffe@gmail.com>
@kfessel
Copy link
Contributor

kfessel commented Mar 25, 2024

@gdoffe please fill the testin procedure section of the PR-message (include test ressults if possible like #20431 (comment))

does this realy depend on #20430 ( or is it the other way around)

Copy link
Contributor

@kfessel kfessel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code looks good to go

@kfessel kfessel changed the title cpu/native: update gpio-mock cpu/native: add gpio-mock Mar 25, 2024
@gdoffe
Copy link
Contributor Author

gdoffe commented Mar 25, 2024

@gdoffe please fill the testin procedure section of the PR-message (include test ressults if possible like #20431 (comment))

does this realy depend on #20430 ( or is it the other way around)

Sorry, done. It depends previously of #20430 but not the case anymore.

@kfessel kfessel added this pull request to the merge queue Mar 28, 2024
Merged via the queue into RIOT-OS:master with commit 3e2ab59 Mar 29, 2024
29 checks passed
@MrKevinWeiss MrKevinWeiss added this to the Release 2024.04 milestone Apr 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: cpu Area: CPU/MCU ports CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: native Platform: This PR/issue effects the native platform Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants