This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 53ff941e3 examples/pmstress: Add a power management current staircase.
53ff941e3 is described below

commit 53ff941e3026694013663a604ea0265fe6840fd3
Author: Marco Casaroli <[email protected]>
AuthorDate: Wed Oct 7 18:03:06 2026 +0200

    examples/pmstress: Add a power management current staircase.
    
    Walk the PM states in a fixed, timed sequence (active, idle, standby,
    then the deepest state for ever), with a 1 s full-speed burst between
    the steps, so that a logging supply can record the current of each
    state with no debugger or console attached.
    
    On the rp23xx, "pmstress suspend <seconds>" also suspends to RAM with
    the BOARDIOC_RP23XX_SUSPEND boardctl() command, and prints the wake
    source.  The wake is an RTC alarm that it sets with RTC_SET_RELATIVE,
    not an rp23xx timer, so it needs RTC_DRIVER and RTC_ALARM.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 examples/pmstress/CMakeLists.txt  |  35 ++++
 examples/pmstress/Kconfig         |  52 ++++++
 examples/pmstress/Make.defs       |  25 +++
 examples/pmstress/Makefile        |  36 ++++
 examples/pmstress/pmstress_main.c | 380 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 528 insertions(+)

diff --git a/examples/pmstress/CMakeLists.txt b/examples/pmstress/CMakeLists.txt
new file mode 100644
index 000000000..6693e4126
--- /dev/null
+++ b/examples/pmstress/CMakeLists.txt
@@ -0,0 +1,35 @@
+# 
##############################################################################
+# apps/examples/pmstress/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+# license agreements.  See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership.  The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may 
not
+# use this file except in compliance with the License.  You may obtain a copy 
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+# 
##############################################################################
+
+if(CONFIG_EXAMPLES_PMSTRESS)
+  nuttx_add_application(
+    NAME
+    ${CONFIG_EXAMPLES_PMSTRESS_PROGNAME}
+    PRIORITY
+    ${CONFIG_EXAMPLES_PMSTRESS_PRIORITY}
+    STACKSIZE
+    ${CONFIG_EXAMPLES_PMSTRESS_STACKSIZE}
+    MODULE
+    ${CONFIG_EXAMPLES_PMSTRESS}
+    SRCS
+    pmstress_main.c)
+endif()
diff --git a/examples/pmstress/Kconfig b/examples/pmstress/Kconfig
new file mode 100644
index 000000000..e290d3c6b
--- /dev/null
+++ b/examples/pmstress/Kconfig
@@ -0,0 +1,52 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config EXAMPLES_PMSTRESS
+       tristate "Power management current staircase"
+       default n
+       depends on PM
+       ---help---
+               Walk the power management states in a fixed, timed sequence, so
+               that a logging supply can record the current of each state with
+               nothing attached to the board.  The deepest state comes last and
+               is held until the board is powered off.
+
+               On the rp23xx, "pmstress suspend <seconds>" suspends to RAM 
until
+               an RTC alarm (RP23XX_PM_SUSPEND, BOARDCTL_IOCTL, RTC_DRIVER and
+               RTC_ALARM).
+
+if EXAMPLES_PMSTRESS
+
+config EXAMPLES_PMSTRESS_PROGNAME
+       string "Program name"
+       default "pmstress"
+
+config EXAMPLES_PMSTRESS_PRIORITY
+       int "Task priority"
+       default 100
+
+config EXAMPLES_PMSTRESS_STACKSIZE
+       int "Stack size"
+       default DEFAULT_TASK_STACKSIZE
+
+config EXAMPLES_PMSTRESS_STEP_SECS
+       int "Seconds to hold each state"
+       default 30
+
+config EXAMPLES_PMSTRESS_DISCONNECT_SECS
+       int "Seconds to wait before starting"
+       default 30
+       ---help---
+               Time to unplug the debugger and the console before the run.  A
+               debugger keeps power domains on.
+
+config EXAMPLES_PMSTRESS_LEDPATH
+       string "User LED device"
+       default "/dev/userleds"
+       depends on USERLED_LOWER
+       ---help---
+               Device used to turn the LEDs off.
+
+endif
diff --git a/examples/pmstress/Make.defs b/examples/pmstress/Make.defs
new file mode 100644
index 000000000..04fb5cd09
--- /dev/null
+++ b/examples/pmstress/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/examples/pmstress/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifneq ($(CONFIG_EXAMPLES_PMSTRESS),)
+CONFIGURED_APPS += $(APPDIR)/examples/pmstress
+endif
diff --git a/examples/pmstress/Makefile b/examples/pmstress/Makefile
new file mode 100644
index 000000000..ec62238a3
--- /dev/null
+++ b/examples/pmstress/Makefile
@@ -0,0 +1,36 @@
+############################################################################
+# apps/examples/pmstress/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+# Power management staircase built-in application info
+
+PROGNAME = $(CONFIG_EXAMPLES_PMSTRESS_PROGNAME)
+PRIORITY = $(CONFIG_EXAMPLES_PMSTRESS_PRIORITY)
+STACKSIZE = $(CONFIG_EXAMPLES_PMSTRESS_STACKSIZE)
+MODULE = $(CONFIG_EXAMPLES_PMSTRESS)
+
+# Power management current staircase
+
+MAINSRC = pmstress_main.c
+
+include $(APPDIR)/Application.mk
diff --git a/examples/pmstress/pmstress_main.c 
b/examples/pmstress/pmstress_main.c
new file mode 100644
index 000000000..c8f22d75b
--- /dev/null
+++ b/examples/pmstress/pmstress_main.c
@@ -0,0 +1,380 @@
+/****************************************************************************
+ * apps/examples/pmstress/pmstress_main.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Walk the power management states in a fixed, timed sequence, so that a
+ * logging supply can record the current of each state with nothing
+ * attached to the board.  A full-speed burst separates the steps, so the
+ * trace shows where each one starts.  The last step is the deepest state.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/boardctl.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <nuttx/power/pm.h>
+#include <nuttx/leds/userled.h>
+#include <nuttx/timers/rtc.h>
+
+#ifdef CONFIG_ARCH_CHIP_RP23XX
+#  include <arch/chip/pm.h>
+#endif
+
+#if defined(BOARDIOC_RP23XX_SUSPEND) && defined(CONFIG_RTC_DRIVER) && \
+    defined(CONFIG_RTC_ALARM)
+#  define HAVE_SUSPEND
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MARKER_MS      1000    /* Length of the burst between steps */
+#define PM_DOMAIN      0       /* PM_IDLE_DOMAIN */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct pmstress_step_s
+{
+  FAR const char *name;
+  int             state;    /* State to hold the domain at, -1 for none */
+  int             seconds;  /* How long, 0 for ever */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct pmstress_step_s g_steps[] =
+{
+  {
+    "active  (full speed, busy)", PM_NORMAL,
+    CONFIG_EXAMPLES_PMSTRESS_STEP_SECS
+  },
+  {
+    "idle    (full speed, wfi)", PM_NORMAL,
+    CONFIG_EXAMPLES_PMSTRESS_STEP_SECS
+  },
+  {
+    "standby (clocks gated)", PM_STANDBY,
+    CONFIG_EXAMPLES_PMSTRESS_STEP_SECS
+  },
+  {
+    "sleep   (deepest state)", -1, 0
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pmstress_control
+ ****************************************************************************/
+
+static int pmstress_control(int action, int state)
+{
+  struct boardioc_pm_ctrl_s ctrl;
+
+  memset(&ctrl, 0, sizeof(ctrl));
+  ctrl.action = action;
+  ctrl.domain = PM_DOMAIN;
+  ctrl.state  = state;
+
+  return boardctl(BOARDIOC_PM_CONTROL, (uintptr_t)&ctrl);
+}
+
+/****************************************************************************
+ * Name: pmstress_hold
+ *
+ * Description:
+ *   Hold the idle domain at a state, or release it with -1.  The governor
+ *   does not go below a state that has a wakelock.  *held is the state
+ *   held now, or -1.
+ *
+ ****************************************************************************/
+
+static void pmstress_hold(FAR int *held, int state)
+{
+  if (*held >= 0)
+    {
+      pmstress_control(BOARDIOC_PM_RELAX, *held);
+      *held = -1;
+    }
+
+  if (state >= 0)
+    {
+      if (pmstress_control(BOARDIOC_PM_STAY, state) == OK)
+        {
+          *held = state;
+        }
+      else
+        {
+          printf("pmstress: failed to hold state %d: %d\n", state, errno);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: pmstress_busy
+ *
+ * Description:
+ *   Run at full speed, with no idle time, for the given time.
+ *
+ ****************************************************************************/
+
+static void pmstress_busy(long ms)
+{
+  struct timespec start;
+  struct timespec now;
+  volatile uint32_t sink = 0;
+  long elapsed;
+  int i;
+
+  clock_gettime(CLOCK_MONOTONIC, &start);
+
+  do
+    {
+      for (i = 0; i < 10000; i++)
+        {
+          sink = sink + i;
+        }
+
+      pmstress_control(BOARDIOC_PM_ACTIVITY, 0);
+
+      clock_gettime(CLOCK_MONOTONIC, &now);
+      elapsed = (now.tv_sec - start.tv_sec) * 1000 +
+                (now.tv_nsec - start.tv_nsec) / 1000000;
+    }
+  while (elapsed < ms);
+}
+
+/****************************************************************************
+ * Name: pmstress_leds_off
+ *
+ * Description:
+ *   Turn the user LEDs off.  A LED draws milliamps, more than a low power
+ *   state.
+ *
+ ****************************************************************************/
+
+static void pmstress_leds_off(bool verbose)
+{
+#ifdef CONFIG_USERLED_LOWER
+  userled_set_t ledset = 0;
+  int fd;
+
+  fd = open(CONFIG_EXAMPLES_PMSTRESS_LEDPATH, O_WRONLY);
+  if (fd < 0)
+    {
+      if (verbose)
+        {
+          printf("pmstress: no %s, LEDs left as they are: %d\n",
+                 CONFIG_EXAMPLES_PMSTRESS_LEDPATH, errno);
+        }
+
+      return;
+    }
+
+  if (ioctl(fd, ULEDIOC_SETALL, (unsigned long)ledset) < 0)
+    {
+      if (verbose)
+        {
+          printf("pmstress: could not clear the LEDs: %d\n", errno);
+        }
+    }
+  else if (verbose)
+    {
+      printf("pmstress: LEDs driven off\n");
+    }
+
+  close(fd);
+#else
+  if (verbose)
+    {
+      printf("pmstress: no user LED driver, check the LED is not lit\n");
+    }
+#endif
+}
+
+#ifdef HAVE_SUSPEND
+/****************************************************************************
+ * Name: pmstress_suspend
+ *
+ * Description:
+ *   Suspend to RAM until an RTC alarm in the given time.  The alarm always
+ *   returns the board, even if no other wake source is set up.
+ *
+ ****************************************************************************/
+
+static int pmstress_suspend(int seconds)
+{
+  struct rp23xx_suspend_s suspend;
+  struct rtc_setrelative_s alarm;
+  int ret;
+  int fd;
+
+  pmstress_leds_off(true);
+
+  printf("pmstress: suspending to RAM for %d s\n", seconds);
+  fflush(stdout);
+
+  /* Let the console drain: the UART loses its registers */
+
+  sleep(1);
+
+  memset(&alarm, 0, sizeof(alarm));
+  alarm.event.sigev_notify = SIGEV_NONE;
+  alarm.reltime            = seconds;
+
+  fd = open("/dev/rtc0", O_RDONLY);
+  if (fd < 0)
+    {
+      printf("pmstress: cannot open /dev/rtc0: %d\n", errno);
+      return EXIT_FAILURE;
+    }
+
+  ret = ioctl(fd, RTC_SET_RELATIVE, (unsigned long)&alarm);
+  close(fd);
+
+  if (ret < 0)
+    {
+      printf("pmstress: cannot set the RTC alarm: %d\n", errno);
+      return EXIT_FAILURE;
+    }
+
+  suspend.wake_ms     = 0;
+  suspend.wake_source = 0;
+
+  ret = boardctl(BOARDIOC_RP23XX_SUSPEND, (uintptr_t)&suspend);
+
+  printf("pmstress: back, returned %d, wake source 0x%08" PRIx32 "\n",
+         ret < 0 ? -errno : ret, suspend.wake_source);
+
+  return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+  size_t i;
+  int held = -1;
+  int total;
+
+#ifdef HAVE_SUSPEND
+  if (argc > 1 && strcmp(argv[1], "suspend") == 0)
+    {
+      return pmstress_suspend(argc > 2 ? atoi(argv[2]) : 10);
+    }
+#endif
+
+  printf("\npmstress: power management current staircase\n\n");
+
+  pmstress_leds_off(true);
+
+  /* Print the schedule now: the console is unplugged during the run */
+
+  total = 0;
+  printf("pmstress: schedule, %d ms full-speed burst between each step\n",
+         MARKER_MS);
+
+  for (i = 0; i < nitems(g_steps); i++)
+    {
+      if (g_steps[i].seconds > 0)
+        {
+          total += g_steps[i].seconds + (MARKER_MS / 1000);
+          printf("  t+%4ds  %s, %ds\n", total - g_steps[i].seconds,
+                 g_steps[i].name, g_steps[i].seconds);
+        }
+      else
+        {
+          total += MARKER_MS / 1000;
+          printf("  t+%4ds  %s, until power off\n", total,
+                 g_steps[i].name);
+        }
+    }
+
+  printf("\npmstress: starting in %d seconds, disconnect now\n\n",
+         CONFIG_EXAMPLES_PMSTRESS_DISCONNECT_SECS);
+  fflush(stdout);
+
+  /* Hold PM_NORMAL during the wait: the deepest state can stop the system
+   * tick, and then the sleep would not end.
+   */
+
+  pmstress_hold(&held, PM_NORMAL);
+  sleep(CONFIG_EXAMPLES_PMSTRESS_DISCONNECT_SECS);
+
+  for (i = 0; i < nitems(g_steps); i++)
+    {
+      pmstress_hold(&held, PM_NORMAL);
+      pmstress_busy(MARKER_MS);
+
+      pmstress_hold(&held, g_steps[i].state);
+
+      /* A board LED callback can turn the LED on again in PM_NORMAL */
+
+      pmstress_leds_off(false);
+
+      if (g_steps[i].seconds == 0)
+        {
+          for (; ; )
+            {
+              sleep(3600);
+            }
+        }
+      else if (i == 0)
+        {
+          pmstress_busy(g_steps[i].seconds * 1000L);
+        }
+      else
+        {
+          sleep(g_steps[i].seconds);
+        }
+    }
+
+  return EXIT_SUCCESS;
+}

Reply via email to