Skip to content

Commit

Permalink
tests: benchmark: multicore: Test PWM with low power modes
Browse files Browse the repository at this point in the history
Add test that confirms correct operation of PWM
when target enters and leaves low power mode.

Signed-off-by: Sebastian Głąb <sebastian.glab@nordicsemi.no>
  • Loading branch information
nordic-segl committed Sep 20, 2024
1 parent dac867a commit 4c06f99
Show file tree
Hide file tree
Showing 17 changed files with 419 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ CMakeLists* @nrfconnect/ncs-co-build-system
/tests/ @nrfconnect/ncs-co-verification @katgiadla
/tests/benchmarks/multicore/ @carlescufi
/tests/benchmarks/multicore/idle/ @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/bluetooth/tester/ @carlescufi @nrfconnect/ncs-paladin
Expand Down
19 changes: 19 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
15 changes: 15 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/Kconfig
Original file line number Diff line number Diff line change
@@ -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"
10 changes: 10 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -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"
72 changes: 72 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.. _multicore_idle_gpio_test:

Multicore idle GPIO 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.
Original file line number Diff line number Diff line change
@@ -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>;
};
4 changes: 4 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_PRINTK=y
CONFIG_LOG=y
CONFIG_PWM=y
CONFIG_PWM_LOG_LEVEL_DBG=n
17 changes: 17 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/prj_s2ram.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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_CLOCK_CONTROL=n

CONFIG_PWM=y
CONFIG_PWM_LOG_LEVEL_DBG=n
12 changes: 12 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/remote/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
15 changes: 15 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/remote/Kconfig
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

/ {
aliases {
pwm-led0 = &pwm_gpio;
};

pwmleds {
compatible = "pwm-leds";
/*
* LEDs are connected to GPIO Port 9 - pins 0-3. There is no valid hardware
* configuration to pass PWM signal on pis 0 and 1. First valid config is P9.2.
* Signal on PWM130's channel 0 can be passed directly on GPIO Port 9 pin 2.
* PWM131 can't access GPIO Port 9, thus use GPIO Port 0, pin 7.
*/
pwm_gpio: pwm_gpio {
pwms = <&pwm131 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
};
};
};

&pinctrl {
/omit-if-no-ref/ pwm131_default: pwm131_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 7, 0)>;
};
};

/omit-if-no-ref/ pwm131_sleep: pwm131_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 7, 0)>;
low-power-enable;
};
};
};

&pwm131 {
status = "okay";
pinctrl-0 = <&pwm131_default>;
pinctrl-1 = <&pwm131_sleep>;
pinctrl-names = "default", "sleep";
memory-regions = <&cpurad_dma_region>;
};
4 changes: 4 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/remote/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_PRINTK=y
CONFIG_LOG=y
CONFIG_PWM=y
CONFIG_PWM_LOG_LEVEL_DBG=n
12 changes: 12 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/remote/prj_s2ram.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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_CLOCK_CONTROL=n

CONFIG_PWM=y
CONFIG_PWM_LOG_LEVEL_DBG=n
97 changes: 97 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(idle_with_pwm, LOG_LEVEL_INF);

#include <zephyr/kernel.h>
#include <zephyr/pm/pm.h>
#include <zephyr/drivers/pwm.h>

#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;
}
22 changes: 22 additions & 0 deletions tests/benchmarks/multicore/idle_with_pwm/sysbuild.cmake
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SB_CONFIG_REMOTE_BOARD="nrf54h20dk/nrf54h20/cpurad"
Loading

0 comments on commit 4c06f99

Please sign in to comment.