/* * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #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 #ifdef CONFIG_PM_TRACE #include "esp_private/pm_trace.h" #endif //CONFIG_PM_TRACE 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 _Static_assert( SOC_CPU_CORES_NUM <= SOC_SYSTIMER_ALARM_NUM - 1, "the number of cores must match the number of core alarms in SYSTIMER" ); void SysTickIsrHandler( void * arg ); static uint32_t s_handled_systicks[ portNUM_PROCESSORS ] = { 0 }; #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 /* 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 < SOC_CPU_CORES_NUM; cpuid++ ) { systimer_hal_counter_can_stall_by_cpu( &systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, false ); } for( cpuid = 0; cpuid < portNUM_PROCESSORS; ++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 ); #ifndef CONFIG_FREERTOS_UNICORE /* 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 ); #endif } } } 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 ) { traceISR_EXIT_TO_SCHEDULER(); portYIELD_FROM_ISR(); } else { traceISR_EXIT(); } return ret; }