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