risc-v/espressif: Add support for Tickless mode

Signed-off-by: Gustavo Henrique Nihei <gustavo.nihei@espressif.com>
This commit is contained in:
Gustavo Henrique Nihei 2023-03-30 11:53:40 -03:00 committed by Petro Karashchenko
parent 62ff3f484e
commit ac746fd87f
7 changed files with 655 additions and 1 deletions

View file

@ -19,6 +19,7 @@ config ESPRESSIF_ESP32C3
select ARCH_HAVE_MPU
select ARCH_HAVE_RESET
select ARCH_HAVE_RNG
select ARCH_HAVE_TICKLESS
select LIBC_ARCH_ATOMIC
select LIBC_ARCH_MEMCPY
select LIBC_ARCH_MEMCHR
@ -46,6 +47,7 @@ config ESPRESSIF_ESP32C6
select ARCH_HAVE_MPU
select ARCH_HAVE_RESET
select ARCH_HAVE_RNG
select ARCH_HAVE_TICKLESS
select LIBC_ARCH_MEMCPY
select LIBC_ARCH_MEMCHR
select LIBC_ARCH_MEMCMP
@ -71,6 +73,7 @@ config ESPRESSIF_ESP32H2
select ARCH_HAVE_MPU
select ARCH_HAVE_RESET
select ARCH_HAVE_RNG
select ARCH_HAVE_TICKLESS
select LIBC_ARCH_ATOMIC
select LIBC_ARCH_MEMCPY
select LIBC_ARCH_MEMCHR

View file

@ -36,7 +36,13 @@ CHIP_ASRCS = esp_vectors.S
CHIP_CSRCS = esp_allocateheap.c esp_start.c esp_idle.c
CHIP_CSRCS += esp_irq.c esp_gpio.c esp_libc_stubs.c
CHIP_CSRCS += esp_lowputc.c esp_serial.c
CHIP_CSRCS += esp_timerisr.c esp_systemreset.c
CHIP_CSRCS += esp_systemreset.c
ifeq ($(CONFIG_SCHED_TICKLESS),y)
CHIP_CSRCS += esp_tickless.c
else
CHIP_CSRCS += esp_timerisr.c
endif
ifeq ($(CONFIG_WATCHDOG),y)
CHIP_CSRCS += esp_wdt.c

View file

@ -0,0 +1,433 @@
/****************************************************************************
* arch/risc-v/src/espressif/esp_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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <stdint.h>
#include <time.h>
#include <arch/board/board.h>
#include <arch/irq.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <nuttx/spinlock.h>
#include "chip.h"
#include "esp_irq.h"
#include "esp_attr.h"
#include "hal/systimer_hal.h"
#include "hal/systimer_ll.h"
#include "periph_ctrl.h"
#include "systimer.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if SOC_SYSTIMER_INT_LEVEL
# define SYSTIMER_TRIGGER_TYPE ESP_IRQ_TRIGGER_LEVEL
#else
# define SYSTIMER_TRIGGER_TYPE ESP_IRQ_TRIGGER_EDGE
#endif /* SOC_SYSTIMER_INT_LEVEL */
/****************************************************************************
* Private Data
****************************************************************************/
/* Systimer HAL layer object */
static systimer_hal_context_t systimer_hal;
/* Whether an interval timer is being started */
static bool g_timer_started;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: esp_tickless_isr
*
* Description:
* Handler to be executed by the Systimer ISR.
*
* Input Parameters:
* irq - IRQ associated to that interrupt.
* context - Interrupt register state save info.
* arg - A pointer to the argument provided when the interrupt
* was registered.
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int esp_tickless_isr(int irq, void *context, void *arg)
{
g_timer_started = false;
systimer_ll_clear_alarm_int(systimer_hal.dev,
SYSTIMER_ALARM_OS_TICK_CORE0);
nxsched_timer_expiration();
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_get_idletime
*
* Description:
* This function returns the idle time.
*
* Input Parameters:
* None.
*
* Returned Value:
* The time in system ticks remaining for idle.
* Zero means system is busy.
*
****************************************************************************/
uint32_t up_get_idletime(void)
{
uint32_t us;
uint64_t alarm_value;
uint64_t counter;
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
if (!g_timer_started)
{
spin_unlock_irqrestore(NULL, flags);
return 0;
}
alarm_value = systimer_hal_get_alarm_value(&systimer_hal,
SYSTIMER_ALARM_OS_TICK_CORE0);
counter = systimer_hal_get_counter_value(&systimer_hal,
SYSTIMER_COUNTER_OS_TICK);
if (alarm_value > counter)
{
us = systimer_hal.ticks_to_us(alarm_value - counter);
}
else
{
us = 0;
}
spin_unlock_irqrestore(NULL, flags);
return us;
}
/****************************************************************************
* Name: up_step_idletime
*
* Description:
* Add system time by idletime_us.
*
* Input Parameters:
* idletime_us - Idle time in microseconds.
*
* Returned Value:
* None.
*
****************************************************************************/
void up_step_idletime(uint32_t idletime_us)
{
irqstate_t flags;
DEBUGASSERT(g_timer_started);
flags = spin_lock_irqsave(NULL);
systimer_hal_counter_value_advance(&systimer_hal, SYSTIMER_COUNTER_OS_TICK,
idletime_us);
spin_unlock_irqrestore(NULL, flags);
}
/****************************************************************************
* 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 time_us;
irqstate_t flags = spin_lock_irqsave(NULL);
time_us = systimer_hal_get_time(&systimer_hal, SYSTIMER_COUNTER_OS_TICK);
ts->tv_sec = time_us / USEC_PER_SEC;
ts->tv_nsec = (time_us % USEC_PER_SEC) * NSEC_PER_USEC;
spin_unlock_irqrestore(NULL, 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)
{
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
if (ts != NULL)
{
if (!g_timer_started)
{
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
else
{
uint64_t alarm_ticks;
uint64_t alarm_us;
uint64_t counter;
uint64_t ticks_mod;
counter = systimer_hal_get_counter_value(&systimer_hal,
SYSTIMER_COUNTER_OS_TICK);
alarm_ticks = systimer_hal_get_alarm_value(&systimer_hal,
SYSTIMER_ALARM_OS_TICK_CORE0);
if (alarm_ticks <= counter)
{
alarm_ticks = 0;
}
else
{
alarm_ticks -= counter;
}
alarm_us = systimer_hal.ticks_to_us(alarm_ticks);
ticks_mod = (alarm_ticks - systimer_hal.us_to_ticks(alarm_us));
ts->tv_sec = alarm_us / USEC_PER_SEC;
ts->tv_nsec = systimer_hal.ticks_to_us(ticks_mod) * NSEC_PER_USEC;
}
}
g_timer_started = false;
systimer_ll_enable_alarm(systimer_hal.dev,
SYSTIMER_ALARM_OS_TICK_CORE0, false);
systimer_ll_enable_alarm_int(systimer_hal.dev,
SYSTIMER_ALARM_OS_TICK_CORE0, false);
systimer_ll_clear_alarm_int(systimer_hal.dev,
SYSTIMER_ALARM_OS_TICK_CORE0);
spin_unlock_irqrestore(NULL, 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 target_us;
uint64_t alarm_ticks;
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
if (g_timer_started)
{
up_timer_cancel(NULL);
}
target_us = (uint64_t)ts->tv_sec * USEC_PER_SEC +
(uint64_t)(ts->tv_nsec / NSEC_PER_USEC);
alarm_ticks = systimer_hal_get_counter_value(&systimer_hal,
SYSTIMER_COUNTER_OS_TICK);
alarm_ticks += systimer_hal.us_to_ticks(target_us);
systimer_hal_select_alarm_mode(&systimer_hal,
SYSTIMER_ALARM_OS_TICK_CORE0,
SYSTIMER_ALARM_MODE_ONESHOT);
systimer_hal_set_alarm_target(&systimer_hal,
SYSTIMER_ALARM_OS_TICK_CORE0,
systimer_hal.ticks_to_us(alarm_ticks));
systimer_hal_enable_alarm_int(&systimer_hal,
SYSTIMER_ALARM_OS_TICK_CORE0);
g_timer_started = true;
spin_unlock_irqrestore(NULL, flags);
return OK;
}
/****************************************************************************
* Name: up_timer_initialize
*
* Description:
* This function is called during start-up to initialize the timer
* interrupt.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
void up_timer_initialize(void)
{
g_timer_started = false;
periph_module_enable(PERIPH_SYSTIMER_MODULE);
systimer_hal_init(&systimer_hal);
systimer_hal_tick_rate_ops_t ops =
{
.ticks_to_us = systimer_ticks_to_us,
.us_to_ticks = systimer_us_to_ticks,
};
systimer_hal_set_tick_rate_ops(&systimer_hal, &ops);
systimer_ll_set_counter_value(systimer_hal.dev,
SYSTIMER_COUNTER_OS_TICK,
0);
systimer_ll_apply_counter_value(systimer_hal.dev,
SYSTIMER_COUNTER_OS_TICK);
systimer_hal_connect_alarm_counter(&systimer_hal,
SYSTIMER_ALARM_OS_TICK_CORE0,
SYSTIMER_COUNTER_OS_TICK);
systimer_hal_counter_can_stall_by_cpu(&systimer_hal,
SYSTIMER_COUNTER_OS_TICK, 0,
true);
systimer_hal_enable_counter(&systimer_hal, SYSTIMER_COUNTER_OS_TICK);
esp_setup_irq(SYSTIMER_TARGET0_EDGE_INTR_SOURCE,
ESP_IRQ_PRIORITY_DEFAULT,
SYSTIMER_TRIGGER_TYPE);
/* Attach the timer interrupt. */
irq_attach(ESP_IRQ_SYSTIMER_TARGET0_EDGE, (xcpt_t)esp_tickless_isr, NULL);
/* Enable the allocated CPU interrupt. */
up_enable_irq(ESP_IRQ_SYSTIMER_TARGET0_EDGE);
}

View file

@ -0,0 +1,69 @@
/****************************************************************************
* arch/risc-v/src/espressif/esp_tickless.h
*
* 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.
*
****************************************************************************/
#ifndef __ARCH_RISCV_SRC_ESPRESSIF_ESP_TICKLESS_H
#define __ARCH_RISCV_SRC_ESPRESSIF_ESP_TICKLESS_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: up_get_idletime
*
* Description:
* This function returns the idle time.
*
* Input Parameters:
* None.
*
* Returned Value:
* The time in system ticks remaining for idle.
* Zero means system is busy.
*
****************************************************************************/
uint32_t up_get_idletime(void);
/****************************************************************************
* Name: up_step_idletime
*
* Description:
* Add system time by idletime_us.
*
* Input Parameters:
* idletime_us - Idle time in microseconds.
*
* Returned Value:
* None.
*
****************************************************************************/
void up_step_idletime(uint32_t idletime_us);
#endif /* __ARCH_RISCV_SRC_ESPRESSIF_ESP_TICKLESS_H */

View file

@ -0,0 +1,47 @@
#
# 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_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="risc-v"
CONFIG_ARCH_BOARD="esp32c3-generic"
CONFIG_ARCH_BOARD_COMMON=y
CONFIG_ARCH_BOARD_ESP32C3_GENERIC=y
CONFIG_ARCH_CHIP="espressif"
CONFIG_ARCH_CHIP_ESPRESSIF=y
CONFIG_ARCH_INTERRUPTSTACK=1536
CONFIG_ARCH_RISCV=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARD_LOOPSPERMSEC=15000
CONFIG_BUILTIN=y
CONFIG_DEV_ZERO=y
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_NSH_STRERROR=y
CONFIG_PREALLOC_TIMERS=0
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_BACKTRACE=y
CONFIG_SCHED_TICKLESS=y
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=29
CONFIG_START_MONTH=11
CONFIG_START_YEAR=2019
CONFIG_SYSTEM_DUMPSTACK=y
CONFIG_SYSTEM_NSH=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_UART0_SERIAL_CONSOLE=y
CONFIG_USEC_PER_TICK=10000

View file

@ -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_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="risc-v"
CONFIG_ARCH_BOARD="esp32c6-generic"
CONFIG_ARCH_BOARD_COMMON=y
CONFIG_ARCH_BOARD_ESP32C6_GENERIC=y
CONFIG_ARCH_CHIP="espressif"
CONFIG_ARCH_CHIP_ESPRESSIF=y
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_RISCV=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARD_LOOPSPERMSEC=15000
CONFIG_BUILTIN=y
CONFIG_DEV_ZERO=y
CONFIG_ESPRESSIF_ESP32C6=y
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_NSH_STRERROR=y
CONFIG_PREALLOC_TIMERS=0
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_BACKTRACE=y
CONFIG_SCHED_TICKLESS=y
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=29
CONFIG_START_MONTH=11
CONFIG_START_YEAR=2019
CONFIG_SYSTEM_DUMPSTACK=y
CONFIG_SYSTEM_NSH=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_UART0_SERIAL_CONSOLE=y
CONFIG_USEC_PER_TICK=10000

View file

@ -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_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="risc-v"
CONFIG_ARCH_BOARD="esp32h2-generic"
CONFIG_ARCH_BOARD_COMMON=y
CONFIG_ARCH_BOARD_ESP32H2_GENERIC=y
CONFIG_ARCH_CHIP="espressif"
CONFIG_ARCH_CHIP_ESPRESSIF=y
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_RISCV=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARD_LOOPSPERMSEC=15000
CONFIG_BUILTIN=y
CONFIG_DEV_ZERO=y
CONFIG_ESPRESSIF_ESP32H2=y
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_NSH_STRERROR=y
CONFIG_PREALLOC_TIMERS=0
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_BACKTRACE=y
CONFIG_SCHED_TICKLESS=y
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=29
CONFIG_START_MONTH=11
CONFIG_START_YEAR=2019
CONFIG_SYSTEM_DUMPSTACK=y
CONFIG_SYSTEM_NSH=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_UART0_SERIAL_CONSOLE=y
CONFIG_USEC_PER_TICK=10000