diff --git a/CODEOWNERS b/CODEOWNERS index f152e6045c28..a9922b8836ef 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -342,6 +342,7 @@ /tests/benchmarks/multicore/ @carlescufi /tests/benchmarks/multicore/idle/ @adamkondraciuk @nrfconnect/ncs-low-level-test /tests/benchmarks/multicore/idle_gpio/ @adamkondraciuk @nrfconnect/ncs-low-level-test +/tests/benchmarks/multicore/idle_with_pwm/ @nrfconnect/ncs-low-level-test /tests/benchmarks/i2c_endless/ @nrfconnect/ncs-low-level-test /tests/benchmarks/spi_endless/ @nrfconnect/ncs-low-level-test /tests/benchmarks/peripheral_load/ @nrfconnect/ncs-low-level-test diff --git a/tests/benchmarks/multicore/idle_with_pwm/CMakeLists.txt b/tests/benchmarks/multicore/idle_with_pwm/CMakeLists.txt new file mode 100644 index 000000000000..ed397cdd75a8 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +if (NOT SYSBUILD) + message(WARNING + " This is a multi-image application that should be built using sysbuild.\n" + " Add --sysbuild argument to west build command to prepare all the images.") +endif() + +project(idle_with_pwm) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/benchmarks/multicore/idle_with_pwm/Kconfig b/tests/benchmarks/multicore/idle_with_pwm/Kconfig new file mode 100644 index 000000000000..e58444e7b7bd --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/Kconfig @@ -0,0 +1,15 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config TEST_SLEEP_DURATION_MS + int "Amount of time (in miliseconds) the core is sleeping" + default 1000 + help + Set sleep duration to TEST_SLEEP_DURATION_MS miliseconds. + Based on value of 'min-residency-us' specified for each power state defined in the DTS, + core will enter lowest possible power state. + +source "Kconfig.zephyr" diff --git a/tests/benchmarks/multicore/idle_with_pwm/Kconfig.sysbuild b/tests/benchmarks/multicore/idle_with_pwm/Kconfig.sysbuild new file mode 100644 index 000000000000..0898eb292938 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/Kconfig.sysbuild @@ -0,0 +1,10 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +source "${ZEPHYR_BASE}/share/sysbuild/Kconfig" + +config REMOTE_BOARD + string "The board used for remote target" diff --git a/tests/benchmarks/multicore/idle_with_pwm/README.rst b/tests/benchmarks/multicore/idle_with_pwm/README.rst new file mode 100644 index 000000000000..3ff4060705c5 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/README.rst @@ -0,0 +1,72 @@ +.. _multicore_idle_with_pwm_test: + +PWM in low power states test +############################ + +.. contents:: + :local: + :depth: 2 + +The test benchmarks the idle behavior of an application that runs on multiple cores and uses PWM driver to light up LED. + +Test scenario +Repeat forever: +- gradually light up LED (this lasts for ~1 second), +- disable PWM (set period to 0); This will turn off LED, +- sleep for 1 second. + +Requirements +************ + +The test supports the following development kits: + +.. table-from-rows:: /includes/sample_board_rows.txt + :header: heading + :rows: nrf54h20dk_nrf54h20_cpuapp + +Overview +******** + +The test demonstrates use of PWM with low power modes. + +Code stored in :file:`main.c` is compiled for Application and Radio cores. +Application core uses pwm130 to generate PWM signal on LED2. +Radio core uses pwm131 to generate PWM signal on GPIO Port 0 pin 7. + +There are three test configurations in :file:`testcase.yaml`. + +#. 'benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.s2ram' + This configuration uses KConfigs that enable entering low power modes. However, logging is disabled here. + Core sleeps for time sufficient to enter "suspend-to-ram" power state. +#. 'benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.sleep' + This configuration uses KConfigs that enable entering low power modes. However, logging is disabled here. + Core sleeps for time sufficient to enter "suspend-to-idle" power state. +#. 'benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.no_sleep' + This configuration can be used for debug purposes. It has enabled logging while Power Moding is disabled. + +Building and running +******************** + +.. |test path| replace:: :file:`tests/benchmarks/multicore/idle_with_pwm` + +.. include:: /includes/build_and_run_test.txt + +To build the test, use configuration setups from :file:`testcase.yaml` using the ``-T`` option. +See the following examples: + +nRF54H20 DK + You can build the test for application and radio cores as follows: + + .. code-block:: console + + west build -p -b nrf54h20dk/nrf54h20/cpuapp -T benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.s2ram . + +Testing +======= + +After programming the test to your development kit, complete the following steps to test it: + +#. Connect the PPK2 Power Profiler Kit or other current measurement device. +#. Reset the kit. +#. Observe the LED2 brightness (or use oscilloscope to collect PWM signal on Port 0 pin 7). +#. When LED2 is OFF power profiler shall indicate low current consumption resulting from both cores being in low power mode. diff --git a/tests/benchmarks/multicore/idle_with_pwm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/benchmarks/multicore/idle_with_pwm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 000000000000..a1e708928fc2 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + power-states { + idle: idle { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + min-residency-us = <100000>; + }; + + s2ram: s2ram { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-ram"; + min-residency-us = <800000>; + }; + }; +}; + +&cpu { + cpu-power-states = <&idle &s2ram>; +}; diff --git a/tests/benchmarks/multicore/idle_with_pwm/prj.conf b/tests/benchmarks/multicore/idle_with_pwm/prj.conf new file mode 100644 index 000000000000..bbe2b1d251b5 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/prj.conf @@ -0,0 +1,3 @@ +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_PWM=y diff --git a/tests/benchmarks/multicore/idle_with_pwm/prj_s2ram.conf b/tests/benchmarks/multicore/idle_with_pwm/prj_s2ram.conf new file mode 100644 index 000000000000..a1e7eba93d69 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/prj_s2ram.conf @@ -0,0 +1,15 @@ +CONFIG_PM=y +CONFIG_PM_S2RAM=y +CONFIG_POWEROFF=y +CONFIG_PM_S2RAM_CUSTOM_MARKING=y + +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y + +CONFIG_CONSOLE=n +CONFIG_UART_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_BOOT_BANNER=n +CONFIG_NRFS_MRAM_SERVICE_ENABLED=n + +CONFIG_PWM=y diff --git a/tests/benchmarks/multicore/idle_with_pwm/remote/CMakeLists.txt b/tests/benchmarks/multicore/idle_with_pwm/remote/CMakeLists.txt new file mode 100644 index 000000000000..3f79e49afb26 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/remote/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(remote) + +target_sources(app PRIVATE ../src/main.c) diff --git a/tests/benchmarks/multicore/idle_with_pwm/remote/Kconfig b/tests/benchmarks/multicore/idle_with_pwm/remote/Kconfig new file mode 100644 index 000000000000..e58444e7b7bd --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/remote/Kconfig @@ -0,0 +1,15 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config TEST_SLEEP_DURATION_MS + int "Amount of time (in miliseconds) the core is sleeping" + default 1000 + help + Set sleep duration to TEST_SLEEP_DURATION_MS miliseconds. + Based on value of 'min-residency-us' specified for each power state defined in the DTS, + core will enter lowest possible power state. + +source "Kconfig.zephyr" diff --git a/tests/benchmarks/multicore/idle_with_pwm/remote/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/benchmarks/multicore/idle_with_pwm/remote/boards/nrf54h20dk_nrf54h20_cpurad.overlay new file mode 100644 index 000000000000..5fcd71d3a39c --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/remote/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + aliases { + pwm-led0 = &pwm_gpio; + }; + + pwmleds { + compatible = "pwm-leds"; + pwm_gpio: pwm_gpio { + pwms = <&pwm131 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>; + }; + }; +}; + +&pinctrl { + /omit-if-no-ref/ pwm131_default: pwm131_default { + group1 { + psels = ; + }; + }; + + /omit-if-no-ref/ pwm131_sleep: pwm131_sleep { + group1 { + psels = ; + low-power-enable; + }; + }; +}; + +&pwm131 { + status = "okay"; + pinctrl-0 = <&pwm131_default>; + pinctrl-1 = <&pwm131_sleep>; + pinctrl-names = "default", "sleep"; + memory-regions = <&cpurad_dma_region>; +}; diff --git a/tests/benchmarks/multicore/idle_with_pwm/remote/prj.conf b/tests/benchmarks/multicore/idle_with_pwm/remote/prj.conf new file mode 100644 index 000000000000..bbe2b1d251b5 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/remote/prj.conf @@ -0,0 +1,3 @@ +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_PWM=y diff --git a/tests/benchmarks/multicore/idle_with_pwm/remote/prj_s2ram.conf b/tests/benchmarks/multicore/idle_with_pwm/remote/prj_s2ram.conf new file mode 100644 index 000000000000..0345ed9c78e1 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/remote/prj_s2ram.conf @@ -0,0 +1,10 @@ +CONFIG_PM=y +CONFIG_POWEROFF=y + +CONFIG_CONSOLE=n +CONFIG_UART_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_BOOT_BANNER=n +CONFIG_NRFS_MRAM_SERVICE_ENABLED=n + +CONFIG_PWM=y diff --git a/tests/benchmarks/multicore/idle_with_pwm/src/main.c b/tests/benchmarks/multicore/idle_with_pwm/src/main.c new file mode 100644 index 000000000000..1eeb067cd12f --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/src/main.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +LOG_MODULE_REGISTER(idle_with_pwm, LOG_LEVEL_INF); + +#include +#include +#include + +#if IS_ENABLED(CONFIG_SOC_NRF54H20_CPUAPP) +/* Alias pwm-led0 = &pwm_led2 */ +static const struct pwm_dt_spec pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0)); +#elif IS_ENABLED(CONFIG_SOC_NRF54H20_CPURAD) +/* Alias pwm-led0 = &pwm_led3 */ +static const struct pwm_dt_spec pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0)); +#else +#error "Invalid core selected. " +#endif + +#define PWM_STEPS_PER_SEC (50) + +int main(void) +{ + int ret; + unsigned int cnt = 0; + uint32_t pwm_period; + uint32_t pulse_min; + uint32_t pulse_max; + int32_t pulse_step; + uint32_t current_pulse_width; + + if (!pwm_is_ready_dt(&pwm_led)) { + LOG_ERR("Device %s is not ready.", pwm_led.dev->name); + return -ENODEV; + } + + /* + * In case the default pwm_period value cannot be set for + * some PWM hardware, decrease its value until it can. + */ + pwm_period = PWM_USEC(200U); + LOG_INF("Testing PWM period of %d us for channel %d", + pwm_period, pwm_led.channel); + while (pwm_set_dt(&pwm_led, pwm_period, pwm_period) != 0) { + pwm_period /= 2U; + LOG_INF("Decreasing period to %u", pwm_period); + } + + /* + * There is no distinct change in LED brightness for high PWM duty cycles. + * Thus limit duty cycle to [0; max/2]. + */ + pulse_min = 0; + pulse_max = pwm_period / 2; + pulse_step = (pulse_max - pulse_min) / PWM_STEPS_PER_SEC; + LOG_DBG("Period is %u nsec", pwm_period); + LOG_DBG("PWM pulse width varies from %u to %u with %u step", + pulse_min, pulse_max, pulse_step); + + LOG_INF("Multicore idle_with_pwm test on %s", CONFIG_BOARD_TARGET); + LOG_INF("Core will sleep for %d ms", CONFIG_TEST_SLEEP_DURATION_MS); + + while (1) { + LOG_INF("Multicore idle_with_pwm test iteration %u", cnt++); + + /* Light up LED */ + current_pulse_width = pulse_min; + for (int i = 0; i < PWM_STEPS_PER_SEC; i++) { + /* set pulse width */ + ret = pwm_set_dt(&pwm_led, pwm_period, current_pulse_width); + if (ret) { + LOG_ERR("pwm_set_dt(%u, %u) returned %d", + pwm_period, current_pulse_width, ret); + return ret; + } + current_pulse_width += pulse_step; + k_msleep(1000 / PWM_STEPS_PER_SEC); + } + + /* Disable PWM / LED OFF */ + ret = pwm_set_dt(&pwm_led, 0, 0); + if (ret) { + LOG_ERR("pwm_set_dt(%u, %u) returned %d", + 0, 0, ret); + return ret; + } + + /* Sleep 1 second */ + k_msleep(CONFIG_TEST_SLEEP_DURATION_MS); + } + + return 0; +} diff --git a/tests/benchmarks/multicore/idle_with_pwm/sysbuild.cmake b/tests/benchmarks/multicore/idle_with_pwm/sysbuild.cmake new file mode 100644 index 000000000000..7af563e1ccc6 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/sysbuild.cmake @@ -0,0 +1,22 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +if("${SB_CONFIG_REMOTE_BOARD}" STREQUAL "") + message(FATAL_ERROR "REMOTE_BOARD must be set to a valid board name") +endif() + +# Add remote project +ExternalZephyrProject_Add( + APPLICATION remote + SOURCE_DIR ${APP_DIR}/remote + BOARD ${SB_CONFIG_REMOTE_BOARD} + BOARD_REVISION ${BOARD_REVISION} + ) + +# Add a dependency so that the remote image will be built and flashed first +add_dependencies(idle_with_pwm remote) +# Add dependency so that the remote image is flashed first. +sysbuild_add_dependencies(FLASH idle_with_pwm remote) diff --git a/tests/benchmarks/multicore/idle_with_pwm/sysbuild/nrf54h20dk_nrf54h20_cpurad.conf b/tests/benchmarks/multicore/idle_with_pwm/sysbuild/nrf54h20dk_nrf54h20_cpurad.conf new file mode 100644 index 000000000000..dd863e78d993 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/sysbuild/nrf54h20dk_nrf54h20_cpurad.conf @@ -0,0 +1 @@ +SB_CONFIG_REMOTE_BOARD="nrf54h20dk/nrf54h20/cpurad" diff --git a/tests/benchmarks/multicore/idle_with_pwm/testcase.yaml b/tests/benchmarks/multicore/idle_with_pwm/testcase.yaml new file mode 100644 index 000000000000..62af8ca65501 --- /dev/null +++ b/tests/benchmarks/multicore/idle_with_pwm/testcase.yaml @@ -0,0 +1,46 @@ +common: + sysbuild: true + tags: ci_build ci_tests_benchmarks_multicore + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp + +tests: + benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.no_sleep: + extra_args: + SB_CONF_FILE=sysbuild/nrf54h20dk_nrf54h20_cpurad.conf + harness: console + harness_config: + type: multi_line + ordered: true + regex: + - "Multicore idle_with_pwm test on" + - "Multicore idle_with_pwm test iteration 0" + - "Multicore idle_with_pwm test iteration 1" + + benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.idle: + tags: ppk_power_measure + extra_args: + SB_CONF_FILE=sysbuild/nrf54h20dk_nrf54h20_cpurad.conf + idle_with_pwm_CONF_FILE=prj_s2ram.conf + remote_CONF_FILE=prj_s2ram.conf + idle_with_pwm_CONFIG_TEST_SLEEP_DURATION_MS=500 + remote_CONFIG_TEST_SLEEP_DURATION_MS=500 + harness: pytest + harness_config: + fixture: ppk_power_measure + pytest_root: + - "${CUSTOM_ROOT_TEST_DIR}/test_measure_power_consumption.py::test_measure_and_data_dump_pwm_and_idle" + + benchmarks.multicore.idle_with_pwm.nrf54h20dk_cpuapp_cpurad.s2ram: + tags: ppk_power_measure + extra_args: + SB_CONF_FILE=sysbuild/nrf54h20dk_nrf54h20_cpurad.conf + idle_with_pwm_CONF_FILE=prj_s2ram.conf + remote_CONF_FILE=prj_s2ram.conf + harness: pytest + harness_config: + fixture: ppk_power_measure + pytest_root: + - "${CUSTOM_ROOT_TEST_DIR}/test_measure_power_consumption.py::test_measure_and_data_dump_pwm_and_s2ram"