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

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


The following commit(s) were added to refs/heads/master by this push:
     new 3b7a6ae  xtensa/esp32s3: Add support for Tickless kernel using Systimer
3b7a6ae is described below

commit 3b7a6ae31160b1579072a398350ba2196a09076e
Author: Gustavo Henrique Nihei <[email protected]>
AuthorDate: Fri Feb 25 18:13:23 2022 -0300

    xtensa/esp32s3: Add support for Tickless kernel using Systimer
    
    Signed-off-by: Gustavo Henrique Nihei <[email protected]>
---
 arch/xtensa/src/esp32s3/Kconfig                    |   6 +
 arch/xtensa/src/esp32s3/Make.defs                  |   9 +-
 arch/xtensa/src/esp32s3/esp32s3_tickless.c         | 484 +++++++++++++++++++++
 .../esp32s3-devkit/configs/tickless/defconfig      |  48 ++
 4 files changed, 545 insertions(+), 2 deletions(-)

diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig
index e0bb068..33edeac 100644
--- a/arch/xtensa/src/esp32s3/Kconfig
+++ b/arch/xtensa/src/esp32s3/Kconfig
@@ -483,6 +483,12 @@ config ESP32S3_ONESHOT
 
 endmenu # Timer/Counter Configuration
 
+config ESP32S3_TICKLESS
+       bool "Enable Tickless OS"
+       default n
+       select ARCH_HAVE_TICKLESS
+       select SCHED_TICKLESS
+
 menu "Application Image Configuration"
 
 choice
diff --git a/arch/xtensa/src/esp32s3/Make.defs 
b/arch/xtensa/src/esp32s3/Make.defs
index d70c4a3..5c33b35 100644
--- a/arch/xtensa/src/esp32s3/Make.defs
+++ b/arch/xtensa/src/esp32s3/Make.defs
@@ -64,9 +64,8 @@ endif
 # Required ESP32-S3 files (arch/xtensa/src/esp32s3)
 
 CHIP_CSRCS  = esp32s3_irq.c esp32s3_clockconfig.c esp32s3_region.c
-CHIP_CSRCS += esp32s3_timerisr.c esp32s3_user.c esp32s3_allocateheap.c
+CHIP_CSRCS += esp32s3_systemreset.c esp32s3_user.c esp32s3_allocateheap.c
 CHIP_CSRCS += esp32s3_wdt.c esp32s3_gpio.c esp32s3_lowputc.c esp32s3_serial.c
-CHIP_CSRCS += esp32s3_systemreset.c
 
 # Configuration-dependent ESP32-S3 files
 
@@ -74,6 +73,12 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y)
 CHIP_CSRCS += esp32s3_idle.c
 endif
 
+ifeq ($(CONFIG_SCHED_TICKLESS),y)
+CHIP_CSRCS += esp32s3_tickless.c
+else
+CHIP_CSRCS += esp32s3_timerisr.c
+endif
+
 ifeq ($(CONFIG_ESP32S3_TIMER),y)
 CHIP_CSRCS += esp32s3_tim.c
 ifeq ($(CONFIG_TIMER),y)
diff --git a/arch/xtensa/src/esp32s3/esp32s3_tickless.c 
b/arch/xtensa/src/esp32s3/esp32s3_tickless.c
new file mode 100644
index 0000000..7e188b3
--- /dev/null
+++ b/arch/xtensa/src/esp32s3/esp32s3_tickless.c
@@ -0,0 +1,484 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s3/esp32s3_tickless.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void sched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <arch/board/board.h>
+#include <arch/irq.h>
+
+#include "xtensa.h"
+#include "chip.h"
+#include "esp32s3_irq.h"
+#include "hardware/esp32s3_systimer.h"
+#include "hardware/esp32s3_system.h"
+#include "hardware/esp32s3_soc.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ESP32S3_SYSTIMER_TICKS_PER_SEC  (16 * 1000 * 1000)
+
+#define CTICK_PER_SEC         (ESP32S3_SYSTIMER_TICKS_PER_SEC)
+#define CTICK_PER_USEC        (CTICK_PER_SEC / USEC_PER_SEC)
+
+#define SEC_2_CTICK(s)        ((s) * CTICK_PER_SEC)
+#define USEC_2_CTICK(us)      ((us) * CTICK_PER_USEC)
+#define NSEC_2_CTICK(nsec)    (((nsec) * CTICK_PER_USEC) / NSEC_PER_USEC)
+
+#define CTICK_2_SEC(tick)     ((tick) / CTICK_PER_SEC)
+#define CTICK_2_USEC(tick)    ((tick) / CTICK_PER_USEC)
+#define CTICK_2_NSEC(tick)    ((tick) * 1000 / CTICK_PER_USEC)
+
+#define CPU_TICKS_MAX         (UINT32_MAX / 4 * 3)
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static inline uint64_t tickless_getcounter(void);
+static inline uint64_t tickless_getalarmvalue(void);
+static void IRAM_ATTR tickless_setcounter(uint64_t ticks);
+static int IRAM_ATTR tickless_isr(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static bool g_timer_started; /* Whether an interval timer is being started */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tickless_getcounter
+ *
+ * Description:
+ *   Return the total ticks of system since power-on.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Total system ticks.
+ *
+ ****************************************************************************/
+
+static inline uint64_t tickless_getcounter(void)
+{
+  uint32_t lo;
+  uint32_t lo_start;
+  uint32_t hi;
+  uint64_t counter;
+
+  /* Set the "update" bit and wait for acknowledgment */
+
+  modifyreg32(SYSTIMER_UNIT0_OP_REG, 0, SYSTIMER_TIMER_UNIT0_UPDATE);
+  while ((getreg32(SYSTIMER_UNIT0_OP_REG) &
+          SYSTIMER_TIMER_UNIT0_VALUE_VALID_M) !=
+         SYSTIMER_TIMER_UNIT0_VALUE_VALID_M);
+
+  /* Read LO, HI, then LO again, check that LO returns the same value.
+   * This accounts for the case when an interrupt may happen between reading
+   * HI and LO values, and this function may get called from the ISR.
+   * In this case, the repeated read will return consistent values.
+   */
+
+  lo_start = getreg32(SYSTIMER_UNIT0_VALUE_LO_REG);
+  do
+    {
+      lo = lo_start;
+      hi = getreg32(SYSTIMER_UNIT0_VALUE_HI_REG);
+      lo_start = getreg32(SYSTIMER_UNIT0_VALUE_LO_REG);
+    }
+  while (lo_start != lo);
+
+  counter = ((uint64_t) hi << 32) | lo;
+
+  return counter;
+}
+
+/****************************************************************************
+ * Name: tickless_getalarmvalue
+ *
+ * Description:
+ *   Return the remaining ticks in the currently running timer.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Remaining ticks.
+ *
+ ****************************************************************************/
+
+static inline uint64_t tickless_getalarmvalue(void)
+{
+  uint32_t hi = getreg32(SYSTIMER_TARGET0_HI_REG);
+  uint32_t lo = getreg32(SYSTIMER_TARGET0_LO_REG);
+  uint64_t ticks = ((uint64_t) hi << 32) | lo;
+
+  return ticks;
+}
+
+/****************************************************************************
+ * Name: tickless_setcounter
+ *
+ * Description:
+ *   Set the new value for the timer counter.
+ *
+ * Input Parameters:
+ *   ticks - Ticks for a timer operation.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void IRAM_ATTR tickless_setcounter(uint64_t ticks)
+{
+  uint64_t alarm_ticks = tickless_getcounter() + ticks;
+
+  /* Select alarm mode */
+
+  modifyreg32(SYSTIMER_TARGET0_CONF_REG, SYSTIMER_TARGET0_PERIOD_MODE, 0);
+
+  /* Set alarm value */
+
+  putreg32(alarm_ticks & 0xffffffff, SYSTIMER_TARGET0_LO_REG);
+  putreg32((alarm_ticks >> 32) & 0xfffff, SYSTIMER_TARGET0_HI_REG);
+
+  /* Apply alarm value */
+
+  putreg32(SYSTIMER_TIMER_COMP0_LOAD, SYSTIMER_COMP0_LOAD_REG);
+
+  /* Enable alarm */
+
+  modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TARGET0_WORK_EN);
+
+  /* Enable interrupt */
+
+  modifyreg32(SYSTIMER_INT_CLR_REG, 0, SYSTIMER_TARGET0_INT_CLR);
+  modifyreg32(SYSTIMER_INT_ENA_REG, 0, SYSTIMER_TARGET0_INT_ENA);
+}
+
+/****************************************************************************
+ * Name: tickless_isr
+ *
+ * Description:
+ *   Called as the IRQ handler for timer expiration.
+ *
+ * Input Parameters:
+ *   irq           - CPU interrupt index.
+ *   context       - Context data from the ISR.
+ *   arg           - Opaque pointer to the internal driver state structure.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. A negated errno value is returned on
+ *   failure.
+ *
+ ****************************************************************************/
+
+static int IRAM_ATTR tickless_isr(int irq, void *context, void *arg)
+{
+  g_timer_started = false;
+
+  modifyreg32(SYSTIMER_INT_CLR_REG, 0, SYSTIMER_TARGET0_INT_CLR);
+
+  nxsched_timer_expiration();
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_gettime
+ *
+ * Description:
+ *   Return the elapsed time since power-up (or, more correctly, since
+ *   up_timer_initialize() was called).  This function is functionally
+ *   equivalent to:
+ *
+ *      int clock_gettime(clockid_t clockid, struct timespec *ts);
+ *
+ *   when clockid is CLOCK_MONOTONIC.
+ *
+ *   This function provides the basis for reporting the current time and
+ *   also is used to eliminate error build-up from small errors in interval
+ *   time calculations.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the location in which to return the up-time.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   Called from the normal tasking context.  The implementation must
+ *   provide whatever mutual exclusion is necessary for correct operation.
+ *   This can include disabling interrupts in order to assure atomic register
+ *   operations.
+ *
+ ****************************************************************************/
+
+int IRAM_ATTR up_timer_gettime(struct timespec *ts)
+{
+  uint64_t ticks;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  ticks = tickless_getcounter();
+  ts->tv_sec  = CTICK_2_SEC(ticks);
+  ts->tv_nsec = CTICK_2_NSEC(ticks % CTICK_PER_SEC);
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_cancel
+ *
+ * Description:
+ *   Cancel the interval timer and return the time remaining on the timer.
+ *   These two steps need to be as nearly atomic as possible.
+ *   nxsched_timer_expiration() will not be called unless the timer is
+ *   restarted with up_timer_start().
+ *
+ *   If, as a race condition, the timer has already expired when this
+ *   function is called, then that pending interrupt must be cleared so
+ *   that up_timer_start() and the remaining time of zero should be
+ *   returned.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Location to return the remaining time.  Zero should be returned
+ *        if the timer is not active.  ts may be zero in which case the
+ *        time remaining is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int IRAM_ATTR up_timer_cancel(struct timespec *ts)
+{
+  uint64_t alarm_value;
+  uint64_t counter;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ts != NULL)
+    {
+      if (!g_timer_started)
+        {
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+      else
+        {
+          alarm_value = tickless_getalarmvalue();
+          counter = tickless_getcounter();
+          if (alarm_value <= counter)
+            {
+              alarm_value = 0;
+            }
+          else
+            {
+              alarm_value -= counter;
+            }
+
+          ts->tv_sec  = CTICK_2_SEC(alarm_value);
+          ts->tv_nsec = CTICK_2_NSEC(alarm_value % CTICK_PER_SEC);
+        }
+    }
+
+  g_timer_started = false;
+
+  modifyreg32(SYSTIMER_CONF_REG, SYSTIMER_TARGET0_WORK_EN, 0);
+  modifyreg32(SYSTIMER_INT_ENA_REG, SYSTIMER_TARGET0_INT_ENA, 0);
+  modifyreg32(SYSTIMER_INT_CLR_REG, SYSTIMER_TARGET0_INT_CLR, 0);
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_start
+ *
+ * Description:
+ *   Start the interval timer.  nxsched_timer_expiration() will be
+ *   called at the completion of the timeout (unless up_timer_cancel
+ *   is called to stop the timing.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the time interval until nxsched_timer_expiration() is
+ *        called.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int IRAM_ATTR up_timer_start(const struct timespec *ts)
+{
+  uint64_t cpu_ticks;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (g_timer_started)
+    {
+      up_timer_cancel(NULL);
+    }
+
+  cpu_ticks = SEC_2_CTICK((uint64_t)ts->tv_sec) +
+              NSEC_2_CTICK((uint64_t)ts->tv_nsec);
+
+  tickless_setcounter(cpu_ticks);
+  g_timer_started = true;
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   Initializes all platform-specific timer facilities.  This function is
+ *   called early in the initialization sequence by up_initialize().
+ *   On return, the current up-time should be available from
+ *   up_timer_gettime() and the interval timer is ready for use (but not
+ *   actively timing.
+ *
+ *   Provided by platform-specific code and called from the architecture-
+ *   specific logic.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  int cpuint;
+
+  g_timer_started = false;
+
+  cpuint = esp32s3_setup_irq(0, ESP32S3_PERIPH_SYSTIMER_TARGET0, 1,
+                             ESP32S3_CPUINT_LEVEL);
+
+  DEBUGASSERT(cpuint >= 0);
+
+  /* Attach the timer interrupt. */
+
+  irq_attach(ESP32S3_IRQ_SYSTIMER_TARGET0, tickless_isr, NULL);
+
+  /* Enable the allocated CPU interrupt. */
+
+  up_enable_irq(ESP32S3_IRQ_SYSTIMER_TARGET0);
+
+  /* Enable timer clock */
+
+  modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, SYSTEM_SYSTIMER_CLK_EN);
+  modifyreg32(SYSTEM_PERIP_RST_EN0_REG, SYSTEM_SYSTIMER_RST, 0);
+  modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_CLK_EN);
+
+  /* Stall systimer 0 when CPU stalls, e.g., when using JTAG to debug */
+
+  modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TIMER_UNIT0_CORE0_STALL_EN);
+}
+
+#endif /* CONFIG_SCHED_TICKLESS */
diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/tickless/defconfig 
b/boards/xtensa/esp32s3/esp32s3-devkit/configs/tickless/defconfig
new file mode 100644
index 0000000..5c6d5af
--- /dev/null
+++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/tickless/defconfig
@@ -0,0 +1,48 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+# CONFIG_ARCH_LEDS is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_CMDPARMS is not set
+CONFIG_ARCH="xtensa"
+CONFIG_ARCH_BOARD="esp32s3-devkit"
+CONFIG_ARCH_BOARD_ESP32S3_DEVKIT=y
+CONFIG_ARCH_CHIP="esp32s3"
+CONFIG_ARCH_CHIP_ESP32S3=y
+CONFIG_ARCH_CHIP_ESP32S3WROOM1=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARCH_XTENSA=y
+CONFIG_BOARD_LOOPSPERMSEC=16717
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_ESP32S3_TICKLESS=y
+CONFIG_ESP32S3_UART0=y
+CONFIG_FS_PROCFS=y
+CONFIG_HAVE_CXX=y
+CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_IDLETHREAD_STACKSIZE=3072
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INTELHEX_BINARY=y
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_LINELEN=64
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=114688
+CONFIG_RAM_START=0x20000000
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_START_DAY=6
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2011
+CONFIG_SYSTEM_NSH=y
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_USEC_PER_TICK=10000

Reply via email to