freertos(esp32s3): SysTick uses systimer

pull/7386/head
Konstantin Kondrashov 2021-08-04 20:33:44 +08:00 zatwierdzone przez Zim Kalinowski
rodzic b5f9149399
commit 29f581fc70
23 zmienionych plików z 315 dodań i 121 usunięć

Wyświetl plik

@ -151,16 +151,17 @@ extern const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI;
// The lowest RAM address used for IDs (pointers) // The lowest RAM address used for IDs (pointers)
#define SYSVIEW_RAM_BASE (SOC_DROM_LOW) #define SYSVIEW_RAM_BASE (SOC_DROM_LOW)
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 #ifdef CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER
#if CONFIG_FREERTOS_CORETIMER_0 #if CONFIG_FREERTOS_CORETIMER_0
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER0_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF) #define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER0_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif #endif
#if CONFIG_FREERTOS_CORETIMER_1 #if CONFIG_FREERTOS_CORETIMER_1
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF) #define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif #endif
#elif CONFIG_IDF_TARGET_ESP32C3
#define SYSTICK_INTR_ID (ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF) #elif CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
#endif #define SYSTICK_INTR_ID (ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE)
#endif // CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER
// SystemView is single core specific: it implies that SEGGER_SYSVIEW_LOCK() // SystemView is single core specific: it implies that SEGGER_SYSVIEW_LOCK()
// disables IRQs (disables rescheduling globally). So we can not use finite timeouts for locks and return error // disables IRQs (disables rescheduling globally). So we can not use finite timeouts for locks and return error

Wyświetl plik

@ -854,7 +854,7 @@ static void reset_task(void* arg)
ESP_LOGI(TAG, "Start reset task"); ESP_LOGI(TAG, "Start reset task");
while (!cmd_stop_reset_task) { while (!cmd_stop_reset_task) {
esp_efuse_utility_reset(); esp_efuse_utility_reset();
vTaskDelay(1); vTaskDelay(2);
} }
vTaskDelete(NULL); vTaskDelete(NULL);
} }

Wyświetl plik

@ -31,7 +31,7 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#if __XTENSA__ #if CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
#include "freertos/xtensa_timer.h" #include "freertos/xtensa_timer.h"
#include "xtensa/core-macros.h" #include "xtensa/core-macros.h"
#endif #endif
@ -68,7 +68,7 @@
#define MHZ (1000000) #define MHZ (1000000)
#if __XTENSA__ #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work /* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
* for the purpose of detecting a deadlock. * for the purpose of detecting a deadlock.
*/ */
@ -78,7 +78,7 @@
* than this. This is to prevent setting CCOMPARE below CCOUNT. * than this. This is to prevent setting CCOMPARE below CCOUNT.
*/ */
#define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000 #define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000
#endif #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/* When light sleep is used, wake this number of microseconds earlier than /* When light sleep is used, wake this number of microseconds earlier than
* the next tick. * the next tick.
@ -184,7 +184,7 @@ static const char* s_mode_names[] = {
}; };
#endif // WITH_PROFILING #endif // WITH_PROFILING
#if __XTENSA__ #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU. /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
* Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU. * Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
*/ */
@ -197,7 +197,7 @@ static uint32_t s_ccount_div;
static uint32_t s_ccount_mul; static uint32_t s_ccount_mul;
static void update_ccompare(void); static void update_ccompare(void);
#endif // __XTENSA__ #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
static const char* TAG = "pm"; static const char* TAG = "pm";
@ -425,7 +425,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
esp_timer_private_update_apb_freq(apb_ticks_per_us); esp_timer_private_update_apb_freq(apb_ticks_per_us);
} }
#if __XTENSA__ #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
#ifdef XT_RTOS_TIMER_INT #ifdef XT_RTOS_TIMER_INT
/* Calculate new tick divisor */ /* Calculate new tick divisor */
_xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC; _xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC;
@ -462,7 +462,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
s_ccount_div = 0; s_ccount_div = 0;
ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id); ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id);
} }
#endif // __XTENSA__ #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
} }
/** /**
@ -484,7 +484,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
portEXIT_CRITICAL_ISR(&s_switch_lock); portEXIT_CRITICAL_ISR(&s_switch_lock);
return; return;
} }
#if __XTENSA__ #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
if (s_need_update_ccompare[core_id]) { if (s_need_update_ccompare[core_id]) {
s_need_update_ccompare[core_id] = false; s_need_update_ccompare[core_id] = false;
} }
@ -529,7 +529,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
portEXIT_CRITICAL_ISR(&s_switch_lock); portEXIT_CRITICAL_ISR(&s_switch_lock);
} }
#if __XTENSA__ #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/** /**
* @brief Calculate new CCOMPARE value based on s_ccount_{mul,div} * @brief Calculate new CCOMPARE value based on s_ccount_{mul,div}
* *
@ -550,7 +550,7 @@ static void IRAM_ATTR update_ccompare(void)
} }
} }
} }
#endif // __XTENSA__ #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
static void IRAM_ATTR leave_idle(void) static void IRAM_ATTR leave_idle(void)
{ {
@ -656,7 +656,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
/* Adjust RTOS tick count based on the amount of time spent in sleep */ /* Adjust RTOS tick count based on the amount of time spent in sleep */
vTaskStepTick(slept_ticks); vTaskStepTick(slept_ticks);
#if __XTENSA__ #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/* Trigger tick interrupt, since sleep time was longer /* Trigger tick interrupt, since sleep time was longer
* than portTICK_PERIOD_MS. Note that setting INTSET does not * than portTICK_PERIOD_MS. Note that setting INTSET does not
* work for timer interrupt, and changing CCOMPARE would clear * work for timer interrupt, and changing CCOMPARE would clear
@ -666,7 +666,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) { while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) {
; ;
} }
#elif __riscv #else
portYIELD_WITHIN_API(); portYIELD_WITHIN_API();
#endif #endif
} }
@ -810,7 +810,7 @@ void IRAM_ATTR esp_pm_impl_isr_hook(void)
* from happening in this section, since they will also call into esp_pm_impl_isr_hook. * from happening in this section, since they will also call into esp_pm_impl_isr_hook.
*/ */
uint32_t state = portENTER_CRITICAL_NESTED(); uint32_t state = portENTER_CRITICAL_NESTED();
#if __XTENSA__ && (portNUM_PROCESSORS == 2) #if defined(CONFIG_FREERTOS_SYSTICK_USES_CCOUNT) && (portNUM_PROCESSORS == 2)
if (s_need_update_ccompare[core_id]) { if (s_need_update_ccompare[core_id]) {
update_ccompare(); update_ccompare();
s_need_update_ccompare[core_id] = false; s_need_update_ccompare[core_id] = false;
@ -819,7 +819,7 @@ void IRAM_ATTR esp_pm_impl_isr_hook(void)
} }
#else #else
leave_idle(); leave_idle();
#endif // portNUM_PROCESSORS == 2 #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT && portNUM_PROCESSORS == 2
portEXIT_CRITICAL_NESTED(state); portEXIT_CRITICAL_NESTED(state);
ESP_PM_TRACE_EXIT(ISR_HOOK, core_id); ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
} }

Wyświetl plik

@ -110,7 +110,7 @@ void IRAM_ATTR esp_restart_noos(void)
// Reset timer/spi/uart // Reset timer/spi/uart
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG, SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG,
SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST); SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST | SYSTEM_SYSTIMER_RST);
REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0); REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0);
// Reset dma // Reset dma
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);

Wyświetl plik

@ -94,7 +94,7 @@ void IRAM_ATTR esp_restart_noos(void)
// Reset timer/spi/uart // Reset timer/spi/uart
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG, SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG,
SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST); SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST | SYSTEM_SYSTIMER_RST);
REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0); REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0);
// Reset dma // Reset dma
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);

Wyświetl plik

@ -103,7 +103,7 @@ void IRAM_ATTR esp_restart_noos(void)
// Reset timer/spi/uart // Reset timer/spi/uart
SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG, SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN0_REG,
SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST); SYSTEM_TIMERS_RST | SYSTEM_SPI01_RST | SYSTEM_UART_RST | SYSTEM_SYSTIMER_RST);
REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0); REG_WRITE(SYSTEM_PERIP_RST_EN0_REG, 0);
// Reset dma // Reset dma

Wyświetl plik

@ -133,7 +133,7 @@ esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
&timer_alarm_isr, NULL, &s_timer_interrupt_handle); &timer_alarm_isr, NULL, &s_timer_interrupt_handle);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (%#x)", err); ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%x)", err);
goto err_intr_alloc; goto err_intr_alloc;
} }
@ -155,7 +155,7 @@ esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
err = esp_intr_enable(s_timer_interrupt_handle); err = esp_intr_enable(s_timer_interrupt_handle);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_EARLY_LOGE(TAG, "esp_intr_enable failed (%#x)", err); ESP_EARLY_LOGE(TAG, "esp_intr_enable failed (0x%x)", err);
goto err_intr_en; goto err_intr_en;
} }
return ESP_OK; return ESP_OK;

Wyświetl plik

@ -6,8 +6,7 @@ endif()
idf_build_get_property(target IDF_TARGET) idf_build_get_property(target IDF_TARGET)
# should test arch here not target, TODO ESP32-C3 IDF-1754 if(CONFIG_IDF_TARGET_ARCH_XTENSA)
if(NOT "${target}" STREQUAL "esp32c3" AND NOT "${target}" STREQUAL "esp32h2")
set(srcs set(srcs
"port/xtensa/port.c" "port/xtensa/port.c"
"port/xtensa/portasm.S" "port/xtensa/portasm.S"
@ -25,10 +24,10 @@ if(NOT "${target}" STREQUAL "esp32c3" AND NOT "${target}" STREQUAL "esp32h2")
include/freertos include/freertos
port/xtensa/include/freertos port/xtensa/include/freertos
port/xtensa port/xtensa
port/priv_include
.) .)
set(required_components app_trace esp_timer) elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
else() # RISC-V
set(srcs set(srcs
"port/riscv/port.c" "port/riscv/port.c"
"port/riscv/portasm.S") "port/riscv/portasm.S")
@ -41,13 +40,14 @@ else() # RISC-V
include/freertos include/freertos
port/riscv/include/freertos port/riscv/include/freertos
port/riscv port/riscv
port/priv_include
.) .)
set(required_components app_trace esp_timer)
endif() endif()
list(APPEND srcs list(APPEND srcs
"port/port_common.c" "port/port_common.c"
"port/port_systick.c"
"croutine.c" "croutine.c"
"event_groups.c" "event_groups.c"
"list.c" "list.c"
@ -62,8 +62,11 @@ if(CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY)
list(APPEND srcs "port/xtensa/xtensa_loadstore_handler.S") list(APPEND srcs "port/xtensa/xtensa_loadstore_handler.S")
endif() endif()
# esp_timer is required by FreeRTOS because we use esp_tiemr_get_time() to do profiling
# app_trace is required by FreeRTOS headers only when CONFIG_APPTRACE_SV_ENABLE=y, # app_trace is required by FreeRTOS headers only when CONFIG_APPTRACE_SV_ENABLE=y,
# but requirements can't depend on config options, so always require it. # REQUIRES can't depend on config options, so always require it.
set(required_components app_trace esp_timer)
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${include_dirs} INCLUDE_DIRS ${include_dirs}
PRIV_INCLUDE_DIRS ${private_include_dirs} PRIV_INCLUDE_DIRS ${private_include_dirs}

Wyświetl plik

@ -18,9 +18,23 @@ menu "FreeRTOS"
hex hex
default 0x7FFFFFFF default 0x7FFFFFFF
config FREERTOS_TICK_SUPPORT_CORETIMER
bool
default y if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2
config FREERTOS_TICK_SUPPORT_SYSTIMER
bool
default y if !FREERTOS_TICK_SUPPORT_CORETIMER
# ESP32-S3, ESP32-C3 and ESP32-H2 can use Systimer for FreeRTOS SysTick
# ESP32S2 also has SYSTIMER but it can not be used for the FreeRTOS SysTick because:
# - It has only one counter, which already in use esp_timer.
# A counter for SysTick should be stall in debug mode but work esp_timer.
# - It is not possible to allocate two handlers for esp_timer and SysTick.
choice FREERTOS_CORETIMER choice FREERTOS_CORETIMER
prompt "Xtensa timer to use as the FreeRTOS tick source" prompt "Xtensa timer to use as the FreeRTOS tick source"
default FREERTOS_CORETIMER_0 default FREERTOS_CORETIMER_0 if FREERTOS_TICK_SUPPORT_CORETIMER
default FREERTOS_CORETIMER_SYSTIMER_LVL1 if FREERTOS_TICK_SUPPORT_SYSTIMER
help help
FreeRTOS needs a timer with an associated interrupt to use as FreeRTOS needs a timer with an associated interrupt to use as
the main tick source to increase counters, run timers and do the main tick source to increase counters, run timers and do
@ -29,16 +43,38 @@ menu "FreeRTOS"
config FREERTOS_CORETIMER_0 config FREERTOS_CORETIMER_0
bool "Timer 0 (int 6, level 1)" bool "Timer 0 (int 6, level 1)"
depends on FREERTOS_TICK_SUPPORT_CORETIMER
help help
Select this to use timer 0 Select this to use timer 0
config FREERTOS_CORETIMER_1 config FREERTOS_CORETIMER_1
bool "Timer 1 (int 15, level 3)" bool "Timer 1 (int 15, level 3)"
depends on FREERTOS_TICK_SUPPORT_CORETIMER
help help
Select this to use timer 1 Select this to use timer 1
config FREERTOS_CORETIMER_SYSTIMER_LVL1
bool "SYSTIMER 0 (level 1)"
depends on FREERTOS_TICK_SUPPORT_SYSTIMER
help
Select this to use systimer with the 1 interrupt priority.
config FREERTOS_CORETIMER_SYSTIMER_LVL3
bool "SYSTIMER 0 (level 3)"
depends on FREERTOS_TICK_SUPPORT_SYSTIMER
help
Select this to use systimer with the 3 interrupt priority.
endchoice endchoice
config FREERTOS_SYSTICK_USES_SYSTIMER
bool
default y if FREERTOS_CORETIMER_SYSTIMER_LVL1 || FREERTOS_CORETIMER_SYSTIMER_LVL3
config FREERTOS_SYSTICK_USES_CCOUNT
bool
default y if FREERTOS_CORETIMER_0 || FREERTOS_CORETIMER_1
config FREERTOS_OPTIMIZED_SCHEDULER config FREERTOS_OPTIMIZED_SCHEDULER
bool "Enable FreeRTOS pĺatform optimized scheduler" bool "Enable FreeRTOS pĺatform optimized scheduler"
depends on FREERTOS_UNICORE depends on FREERTOS_UNICORE
@ -343,6 +379,7 @@ menu "FreeRTOS"
config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK
bool "Use CPU Clock for run time stats" bool "Use CPU Clock for run time stats"
depends on FREERTOS_SYSTICK_USES_CCOUNT
help help
CPU Clock will be used as the clock source for the generation of run CPU Clock will be used as the clock source for the generation of run
time stats. The CPU Clock has a frequency dependent on time stats. The CPU Clock has a frequency dependent on

Wyświetl plik

@ -7,7 +7,7 @@ ifdef CONFIG_FREERTOS_DEBUG_OCDAWARE
endif endif
COMPONENT_ADD_INCLUDEDIRS := include port/xtensa/include COMPONENT_ADD_INCLUDEDIRS := include port/xtensa/include
COMPONENT_PRIV_INCLUDEDIRS := include/freertos port/xtensa/include/freertos port/xtensa . COMPONENT_PRIV_INCLUDEDIRS := include/freertos port/xtensa/include/freertos port/xtensa port/priv_include .
COMPONENT_SRCDIRS += port port/xtensa COMPONENT_SRCDIRS += port port/xtensa
ifndef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY ifndef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY

Wyświetl plik

@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "soc/cpu.h"
#include "FreeRTOS.h"
#include "task.h"
#include "esp_intr_alloc.h"
#include "esp_err.h"
#include "esp_log.h"
#include "sdkconfig.h"
#ifdef CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
#include "soc/periph_defs.h"
#include "soc/system_reg.h"
#include "hal/systimer_hal.h"
#include "hal/systimer_ll.h"
#endif
BaseType_t xPortSysTickHandler(void);
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
extern void _frxt_tick_timer_init(void);
extern void _xt_tick_divisor_init(void);
#ifdef CONFIG_FREERTOS_CORETIMER_0
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER0_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif
#ifdef CONFIG_FREERTOS_CORETIMER_1
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif
/**
* @brief Initialize CCONT timer to generate the tick interrupt
*
*/
void vPortSetupTimer(void)
{
/* Init the tick divisor value */
_xt_tick_divisor_init();
_frxt_tick_timer_init();
}
#elif CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
void SysTickIsrHandler(void *arg);
#ifdef CONFIG_FREERTOS_UNICORE
static uint32_t s_handled_systicks[1] = { 0 };
#else
static uint32_t s_handled_systicks[2] = { 0 };
#endif
#define SYSTICK_INTR_ID (ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE)
/**
* @brief Set up the systimer peripheral to generate the tick interrupt
*
* Both timer alarms are configured in periodic mode.
* It is done at the same time so SysTicks for both CPUs occur at the same time or very close.
* Shifts a time of triggering interrupts for core 0 and core 1.
*/
void vPortSetupTimer(void)
{
unsigned cpuid = xPortGetCoreID();
#ifdef CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3
const unsigned level = ESP_INTR_FLAG_LEVEL3;
#else
const unsigned level = ESP_INTR_FLAG_LEVEL1;
#endif
#ifdef CONFIG_FREERTOS_UNICORE
const unsigned max_cpu = 1;
#else
const unsigned max_cpu = 2;
#endif
/* Systimer HAL layer object */
static systimer_hal_context_t systimer_hal;
/* set system timer interrupt vector */
ESP_ERROR_CHECK(esp_intr_alloc(ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE + cpuid, ESP_INTR_FLAG_IRAM | level, SysTickIsrHandler, &systimer_hal, NULL));
if (cpuid == 0) {
systimer_hal_init(&systimer_hal);
systimer_ll_set_counter_value(systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK, 0);
systimer_ll_apply_counter_value(systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK);
for (cpuid = 0; cpuid < max_cpu; ++cpuid) {
uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
/* configure the timer */
systimer_hal_connect_alarm_counter(&systimer_hal, alarm_id, SYSTIMER_LL_COUNTER_OS_TICK);
systimer_hal_set_alarm_period(&systimer_hal, alarm_id, 1000000UL / CONFIG_FREERTOS_HZ);
systimer_hal_select_alarm_mode(&systimer_hal, alarm_id, SYSTIMER_ALARM_MODE_PERIOD);
systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, true);
if (cpuid == 0) {
systimer_hal_enable_alarm_int(&systimer_hal, alarm_id);
systimer_hal_enable_counter(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK);
// SysTick of core 0 and core 1 are shifted by half of period
systimer_hal_counter_value_advance(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, 1000000UL / CONFIG_FREERTOS_HZ / 2);
}
}
} else {
uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
systimer_hal_enable_alarm_int(&systimer_hal, alarm_id);
}
}
/**
* @brief Systimer interrupt handler.
*
* The Systimer interrupt for SysTick works in periodic mode no need to calc the next alarm.
* If a timer interrupt is ever serviced more than one tick late, it is necessary to process multiple ticks.
*/
IRAM_ATTR void SysTickIsrHandler(void *arg)
{
uint32_t cpuid = xPortGetCoreID();
systimer_hal_context_t *systimer_hal = (systimer_hal_context_t *)arg;
#ifdef CONFIG_PM_TRACE
ESP_PM_TRACE_ENTER(TICK, cpuid);
#endif
uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
do {
systimer_ll_clear_alarm_int(systimer_hal->dev, alarm_id);
uint32_t diff = systimer_hal_get_counter_value(systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK) / systimer_ll_get_alarm_period(systimer_hal->dev, alarm_id) - s_handled_systicks[cpuid];
if (diff > 0) {
if (s_handled_systicks[cpuid] == 0) {
s_handled_systicks[cpuid] = diff;
diff = 1;
} else {
s_handled_systicks[cpuid] += diff;
}
do {
xPortSysTickHandler();
} while (--diff);
}
} while (systimer_ll_is_alarm_int_fired(systimer_hal->dev, alarm_id));
#ifdef CONFIG_PM_TRACE
ESP_PM_TRACE_EXIT(TICK, cpuid);
#endif
}
#endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/**
* @brief Handler of SysTick
*
* The function is called from:
* - _frxt_timer_int for xtensa with CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
* - SysTickIsrHandler for xtensa with CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
* - SysTickIsrHandler for riscv
*/
BaseType_t xPortSysTickHandler(void)
{
portbenchmarkIntLatency();
traceISR_ENTER(SYSTICK_INTR_ID);
BaseType_t ret = xTaskIncrementTick();
if(ret != pdFALSE) {
portYIELD_FROM_ISR();
} else {
traceISR_EXIT();
}
return ret;
}

Wyświetl plik

@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set up the SysTick interrupt
*/
void vPortSetupTimer(void);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -91,6 +91,7 @@
#include "riscv/riscv_interrupts.h" #include "riscv/riscv_interrupts.h"
#include "riscv/interrupt.h" #include "riscv/interrupt.h"
#include "port_systick.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_private/crosscore_int.h" #include "esp_private/crosscore_int.h"
@ -115,8 +116,6 @@ StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOIN
static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
static void vPortSysTickHandler(void *arg);
static void vPortSetupTimer(void);
static void prvTaskExitError(void); static void prvTaskExitError(void);
extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM only extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM only
@ -141,27 +140,6 @@ void vPortExitCritical(void)
} }
} }
/**
* @brief Set up the systimer peripheral to generate the tick interrupt
*
*/
void vPortSetupTimer(void)
{
/* Systimer HAL layer object */
static systimer_hal_context_t systimer_hal;
/* set system timer interrupt vector */
ESP_ERROR_CHECK(esp_intr_alloc(ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE, ESP_INTR_FLAG_IRAM, vPortSysTickHandler, &systimer_hal, NULL));
/* configure the timer */
systimer_hal_init(&systimer_hal);
systimer_hal_connect_alarm_counter(&systimer_hal, SYSTIMER_LL_ALARM_OS_TICK_CORE0, SYSTIMER_LL_COUNTER_OS_TICK);
systimer_hal_enable_counter(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK);
systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, 0, true);
systimer_hal_set_alarm_period(&systimer_hal, SYSTIMER_LL_ALARM_OS_TICK_CORE0, 1000000UL / CONFIG_FREERTOS_HZ);
systimer_hal_select_alarm_mode(&systimer_hal, SYSTIMER_LL_ALARM_OS_TICK_CORE0, SYSTIMER_ALARM_MODE_PERIOD);
systimer_hal_enable_alarm_int(&systimer_hal, SYSTIMER_LL_ALARM_OS_TICK_CORE0);
}
void prvTaskExitError(void) void prvTaskExitError(void)
{ {
/* A function that implements a task must not exit or attempt to return to /* A function that implements a task must not exit or attempt to return to
@ -293,35 +271,13 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
return (StackType_t *)frame; return (StackType_t *)frame;
} }
IRAM_ATTR void vPortSysTickHandler(void *arg)
{
systimer_hal_context_t *systimer_hal = (systimer_hal_context_t *)arg;
systimer_ll_clear_alarm_int(systimer_hal->dev, SYSTIMER_LL_ALARM_OS_TICK_CORE0);
#ifdef CONFIG_PM_TRACE
ESP_PM_TRACE_ENTER(TICK, xPortGetCoreID());
#endif
if (!uxSchedulerRunning) {
return;
}
if (xTaskIncrementTick() != pdFALSE) {
vPortYieldFromISR();
}
#ifdef CONFIG_PM_TRACE
ESP_PM_TRACE_EXIT(TICK, xPortGetCoreID());
#endif
}
BaseType_t xPortStartScheduler(void) BaseType_t xPortStartScheduler(void)
{ {
uxInterruptNesting = 0; uxInterruptNesting = 0;
uxCriticalNesting = 0; uxCriticalNesting = 0;
uxSchedulerRunning = 0; uxSchedulerRunning = 0;
/* Setup the hardware to generate the tick. */
vPortSetupTimer(); vPortSetupTimer();
esprv_intc_int_set_threshold(1); /* set global INTC masking level */ esprv_intc_int_set_threshold(1); /* set global INTC masking level */

Wyświetl plik

@ -50,6 +50,7 @@ Should be included by all Xtensa generic and RTOS port-specific sources.
#include <xtensa/corebits.h> #include <xtensa/corebits.h>
#include <xtensa/config/system.h> #include <xtensa/config/system.h>
#include "sdkconfig.h"
/* /*
Include any RTOS specific definitions that are needed by this header. Include any RTOS specific definitions that are needed by this header.
@ -145,7 +146,9 @@ May be coded in or called from C or assembly, per ABI conventions.
RTOS may optionally define XT_TICK_PER_SEC in its own way (eg. macro). RTOS may optionally define XT_TICK_PER_SEC in its own way (eg. macro).
*/ */
// void XT_RTOS_TIMER_INT(void) // void XT_RTOS_TIMER_INT(void)
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
#define XT_RTOS_TIMER_INT _frxt_timer_int #define XT_RTOS_TIMER_INT _frxt_timer_int
#endif
#define XT_TICK_PER_SEC configTICK_RATE_HZ #define XT_TICK_PER_SEC configTICK_RATE_HZ
/* /*

Wyświetl plik

@ -130,25 +130,16 @@
#include "esp32s3/spiram.h" #include "esp32s3/spiram.h"
#endif #endif
#include "port_systick.h"
#include "esp_private/startup_internal.h" // [refactor-todo] for g_spiram_ok #include "esp_private/startup_internal.h" // [refactor-todo] for g_spiram_ok
#include "esp_app_trace.h" // [refactor-todo] for esp_app_trace_init #include "esp_app_trace.h" // [refactor-todo] for esp_app_trace_init
/* Defined in portasm.h */
extern void _frxt_tick_timer_init(void);
/* Defined in xtensa_context.S */ /* Defined in xtensa_context.S */
extern void _xt_coproc_init(void); extern void _xt_coproc_init(void);
static const char* TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but static const char* TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
// for now maintain the same log output // for now maintain the same log output
#if CONFIG_FREERTOS_CORETIMER_0
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER0_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif
#if CONFIG_FREERTOS_CORETIMER_1
#define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
#endif
_Static_assert(tskNO_AFFINITY == CONFIG_FREERTOS_NO_AFFINITY, "incorrect tskNO_AFFINITY value"); _Static_assert(tskNO_AFFINITY == CONFIG_FREERTOS_NO_AFFINITY, "incorrect tskNO_AFFINITY value");
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
@ -303,11 +294,8 @@ BaseType_t xPortStartScheduler( void )
_xt_coproc_init(); _xt_coproc_init();
#endif #endif
/* Init the tick divisor value */
_xt_tick_divisor_init();
/* Setup the hardware to generate the tick. */ /* Setup the hardware to generate the tick. */
_frxt_tick_timer_init(); vPortSetupTimer();
port_xSchedulerRunning[xPortGetCoreID()] = 1; port_xSchedulerRunning[xPortGetCoreID()] = 1;
@ -319,23 +307,6 @@ BaseType_t xPortStartScheduler( void )
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
BaseType_t xPortSysTickHandler( void )
{
BaseType_t ret;
portbenchmarkIntLatency();
traceISR_ENTER(SYSTICK_INTR_ID);
ret = xTaskIncrementTick();
if( ret != pdFALSE )
{
portYIELD_FROM_ISR();
} else {
traceISR_EXIT();
}
return ret;
}
void vPortYieldOtherCore( BaseType_t coreid ) { void vPortYieldOtherCore( BaseType_t coreid ) {
esp_crosscore_int_send_yield( coreid ); esp_crosscore_int_send_yield( coreid );
} }

Wyświetl plik

@ -270,6 +270,7 @@ _frxt_int_exit:
* *
********************************************************************************************************** **********************************************************************************************************
*/ */
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
.globl _frxt_timer_int .globl _frxt_timer_int
.type _frxt_timer_int,@function .type _frxt_timer_int,@function
.align 4 .align 4
@ -321,7 +322,7 @@ _frxt_timer_int:
s32i a3, sp, 8 s32i a3, sp, 8
#endif #endif
/* Call the FreeRTOS tick handler (see port.c). */ /* Call the FreeRTOS tick handler (see port_systick.c). */
#ifdef __XTENSA_CALL0_ABI__ #ifdef __XTENSA_CALL0_ABI__
call0 xPortSysTickHandler call0 xPortSysTickHandler
#else #else
@ -347,6 +348,7 @@ _frxt_timer_int:
#endif // CONFIG_PM_TRACE #endif // CONFIG_PM_TRACE
RET(16) RET(16)
#endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/* /*
********************************************************************************************************** **********************************************************************************************************
@ -358,6 +360,7 @@ _frxt_timer_int:
* *
********************************************************************************************************** **********************************************************************************************************
*/ */
#ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
.globl _frxt_tick_timer_init .globl _frxt_tick_timer_init
.type _frxt_tick_timer_init,@function .type _frxt_tick_timer_init,@function
.align 4 .align 4
@ -391,6 +394,7 @@ _frxt_tick_timer_init:
#endif #endif
RET(16) RET(16)
#endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
/* /*
********************************************************************************************************** **********************************************************************************************************

Wyświetl plik

@ -289,9 +289,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
rsil a3, \level - 1 /* lower interrupt level by 1 */ rsil a3, \level - 1 /* lower interrupt level by 1 */
#endif #endif
#ifdef XT_RTOS_TIMER_INT
movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */
wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */ wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */
beq a3, a4, 7f /* if timer interrupt then skip table */ beq a3, a4, 7f /* if timer interrupt then skip table */
#else
wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */
#endif // XT_RTOS_TIMER_INT
find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */
@ -316,6 +320,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
j .L_xt_user_int_&level& /* check for more interrupts */ j .L_xt_user_int_&level& /* check for more interrupts */
#endif #endif
#ifdef XT_RTOS_TIMER_INT
7: 7:
.ifeq XT_TIMER_INTPRI - \level .ifeq XT_TIMER_INTPRI - \level
@ -335,6 +340,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
call4 XT_RTOS_TIMER_INT call4 XT_RTOS_TIMER_INT
#endif #endif
.endif .endif
#endif // XT_RTOS_TIMER_INT
#ifdef XT_USE_SWPRI #ifdef XT_USE_SWPRI
j 8f j 8f

Wyświetl plik

@ -124,6 +124,11 @@ __attribute__((always_inline)) static inline void systimer_ll_set_alarm_period(s
dev->target_conf[alarm_id].target_period = period; dev->target_conf[alarm_id].target_period = period;
} }
__attribute__((always_inline)) static inline uint32_t systimer_ll_get_alarm_period(systimer_dev_t *dev, uint32_t alarm_id)
{
return dev->target_conf[alarm_id].target_period;
}
__attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id) __attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id)
{ {
dev->comp_load[alarm_id].val = 0x01; dev->comp_load[alarm_id].val = 0x01;

Wyświetl plik

@ -124,6 +124,11 @@ __attribute__((always_inline)) static inline void systimer_ll_set_alarm_period(s
dev->target_conf[alarm_id].target_period = period; dev->target_conf[alarm_id].target_period = period;
} }
__attribute__((always_inline)) static inline uint32_t systimer_ll_get_alarm_period(systimer_dev_t *dev, uint32_t alarm_id)
{
return dev->target_conf[alarm_id].target_period;
}
__attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id) __attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id)
{ {
dev->comp_load[alarm_id].val = 0x01; dev->comp_load[alarm_id].val = 0x01;

Wyświetl plik

@ -135,6 +135,11 @@ __attribute__((always_inline)) static inline void systimer_ll_set_alarm_period(s
dev->target_conf[alarm_id].target_period = period; dev->target_conf[alarm_id].target_period = period;
} }
__attribute__((always_inline)) static inline uint32_t systimer_ll_get_alarm_period(systimer_dev_t *dev, uint32_t alarm_id)
{
return dev->target_conf[alarm_id].target_period;
}
__attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id) __attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id)
{ {
(void)dev; (void)dev;

Wyświetl plik

@ -125,6 +125,11 @@ __attribute__((always_inline)) static inline void systimer_ll_set_alarm_period(s
dev->target_conf[alarm_id].target_period = period; dev->target_conf[alarm_id].target_period = period;
} }
__attribute__((always_inline)) static inline uint32_t systimer_ll_get_alarm_period(systimer_dev_t *dev, uint32_t alarm_id)
{
return dev->target_conf[alarm_id].target_period;
}
__attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id) __attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id)
{ {
dev->comp_load[alarm_id].val = 0x01; dev->comp_load[alarm_id].val = 0x01;

Wyświetl plik

@ -251,6 +251,7 @@ static void get_time_task(void *pvParameters)
// although exit flag is set in another task, checking (exit_flag == false) is safe // although exit flag is set in another task, checking (exit_flag == false) is safe
while (exit_flag == false) { while (exit_flag == false) {
gettimeofday(&tv_time, NULL); gettimeofday(&tv_time, NULL);
vTaskDelay(1500 / portTICK_PERIOD_MS);
} }
xSemaphoreGive(*sema); xSemaphoreGive(*sema);
vTaskDelete(NULL); vTaskDelete(NULL);
@ -259,13 +260,9 @@ static void get_time_task(void *pvParameters)
static void start_measure(int64_t* sys_time, int64_t* real_time) static void start_measure(int64_t* sys_time, int64_t* real_time)
{ {
struct timeval tv_time; struct timeval tv_time;
int64_t t1, t2; // there shouldn't be much time between gettimeofday and esp_timer_get_time
do { gettimeofday(&tv_time, NULL);
t1 = esp_timer_get_time(); *real_time = esp_timer_get_time();
gettimeofday(&tv_time, NULL);
t2 = esp_timer_get_time();
} while (t2 - t1 > 40);
*real_time = t2;
*sys_time = (int64_t)tv_time.tv_sec * 1000000L + tv_time.tv_usec; *sys_time = (int64_t)tv_time.tv_sec * 1000000L + tv_time.tv_usec;
} }
@ -301,7 +298,7 @@ static void measure_time_task(void *pvParameters)
int64_t sys_time_us[2] = { main_sys_time_us[0], 0}; int64_t sys_time_us[2] = { main_sys_time_us[0], 0};
// although exit flag is set in another task, checking (exit_flag == false) is safe // although exit flag is set in another task, checking (exit_flag == false) is safe
while (exit_flag == false) { while (exit_flag == false) {
esp_rom_delay_us(2 * 1000000); // 2 sec vTaskDelay(2000 / portTICK_PERIOD_MS);
start_measure(&sys_time_us[1], &real_time_us[1]); start_measure(&sys_time_us[1], &real_time_us[1]);
result_adjtime_correction_us[1] += calc_correction("measure", sys_time_us, real_time_us); result_adjtime_correction_us[1] += calc_correction("measure", sys_time_us, real_time_us);
@ -322,7 +319,7 @@ static void measure_time_task(void *pvParameters)
vTaskDelete(NULL); vTaskDelete(NULL);
} }
TEST_CASE("test time adjustment happens linearly", "[newlib][timeout=35]") TEST_CASE("test time adjustment happens linearly", "[newlib][timeout=15]")
{ {
exit_flag = false; exit_flag = false;
@ -335,8 +332,8 @@ TEST_CASE("test time adjustment happens linearly", "[newlib][timeout=35]")
xTaskCreatePinnedToCore(get_time_task, "get_time_task", 4096, &exit_sema[0], UNITY_FREERTOS_PRIORITY - 1, NULL, 0); xTaskCreatePinnedToCore(get_time_task, "get_time_task", 4096, &exit_sema[0], UNITY_FREERTOS_PRIORITY - 1, NULL, 0);
xTaskCreatePinnedToCore(measure_time_task, "measure_time_task", 4096, &exit_sema[1], UNITY_FREERTOS_PRIORITY - 1, NULL, 1); xTaskCreatePinnedToCore(measure_time_task, "measure_time_task", 4096, &exit_sema[1], UNITY_FREERTOS_PRIORITY - 1, NULL, 1);
printf("start waiting for 30 seconds\n"); printf("start waiting for 10 seconds\n");
vTaskDelay(30000 / portTICK_PERIOD_MS); vTaskDelay(10000 / portTICK_PERIOD_MS);
// set exit flag to let thread exit // set exit flag to let thread exit
exit_flag = true; exit_flag = true;

Wyświetl plik

@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s3"
TEST_COMPONENTS=esp_pm
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y