mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-30 20:51:41 +00:00 
			
		
		
		
	 8b98e4e3b8
			
		
	
	8b98e4e3b8
	
	
	
		
			
			Due to SMP, critical sections have been added to xTaskIncrementTick() and vTaskSwitchContext() (to take the xKernelLock). However, this is technically not necessary when building for single-core as FreeRTOS expect these funcitons to be called with interrupts already disabled. This commit makes the critical secitons in those functions depend on "configNUM_CORES > 1", and ensures that interrupts are disabled when calling those functions. This effectively restores the vanilla behavior for these functions when building for single-core.
		
			
				
	
	
		
			211 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
 | |
|  *
 | |
|  * SPDX-License-Identifier: Apache-2.0
 | |
|  */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include "FreeRTOS.h"
 | |
| #include "task.h"
 | |
| #include "esp_intr_alloc.h"
 | |
| #include "esp_err.h"
 | |
| #include "esp_log.h"
 | |
| #include "esp_private/systimer.h"
 | |
| #include "esp_private/periph_ctrl.h"
 | |
| #include "sdkconfig.h"
 | |
| #ifdef CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
 | |
| #include "soc/periph_defs.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) {
 | |
|         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_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
 | |
| 
 | |
| 
 | |
| extern void esp_vApplicationTickHook(void);
 | |
| /**
 | |
|  * @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)
 | |
| {
 | |
| #if configBENCHMARK
 | |
|     portbenchmarkIntLatency();
 | |
| #endif //configBENCHMARK
 | |
|     traceISR_ENTER(SYSTICK_INTR_ID);
 | |
| 
 | |
|     // Call IDF Tick Hook
 | |
|     esp_vApplicationTickHook();
 | |
| 
 | |
|     // Call FreeRTOS Increment tick function
 | |
|     BaseType_t xSwitchRequired;
 | |
| #if ( configNUM_CORES > 1 )
 | |
|     /*
 | |
|     For SMP, xTaskIncrementTick() will internally enter a critical section. But only core 0 calls xTaskIncrementTick()
 | |
|     while core 1 should call xTaskIncrementTickOtherCores().
 | |
|     */
 | |
|     if (xPortGetCoreID() == 0) {
 | |
|         xSwitchRequired = xTaskIncrementTick();
 | |
|     } else {
 | |
|         xSwitchRequired = xTaskIncrementTickOtherCores();
 | |
|     }
 | |
| #else // configNUM_CORES > 1
 | |
|     /*
 | |
|     Vanilla (single core) FreeRTOS expects that xTaskIncrementTick() cannot be interrupted (i.e., no nested interrupts).
 | |
|     Thus we have to disable interrupts before calling it.
 | |
|     */
 | |
|     UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
 | |
|     xSwitchRequired = xTaskIncrementTick();
 | |
|     portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
 | |
| #endif
 | |
| 
 | |
|     // Check if yield is required
 | |
|     if (xSwitchRequired != pdFALSE) {
 | |
|         portYIELD_FROM_ISR();
 | |
|     } else {
 | |
|         traceISR_EXIT();
 | |
|     }
 | |
|     return xSwitchRequired;
 | |
| }
 |