1 /*
2 * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include "soc/cpu.h"
10 #include "FreeRTOS.h"
11 #include "task.h"
12 #include "esp_intr_alloc.h"
13 #include "esp_err.h"
14 #include "esp_log.h"
15 #include "sdkconfig.h"
16 #ifdef CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
17 #include "soc/periph_defs.h"
18 #include "soc/system_reg.h"
19 #include "hal/systimer_hal.h"
20 #include "hal/systimer_ll.h"
21 #endif
22
23 #ifdef CONFIG_PM_TRACE
24 #include "esp_private/pm_trace.h"
25 #endif //CONFIG_PM_TRACE
26
27 BaseType_t xPortSysTickHandler( void );
28
29 #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
30 extern void _frxt_tick_timer_init( void );
31 extern void _xt_tick_divisor_init( void );
32
33 #ifdef CONFIG_FREERTOS_CORETIMER_0
34 #define SYSTICK_INTR_ID ( ETS_INTERNAL_TIMER0_INTR_SOURCE + ETS_INTERNAL_INTR_SOURCE_OFF )
35 #endif
36 #ifdef CONFIG_FREERTOS_CORETIMER_1
37 #define SYSTICK_INTR_ID ( ETS_INTERNAL_TIMER1_INTR_SOURCE + ETS_INTERNAL_INTR_SOURCE_OFF )
38 #endif
39
40 /**
41 * @brief Initialize CCONT timer to generate the tick interrupt
42 *
43 */
vPortSetupTimer(void)44 void vPortSetupTimer( void )
45 {
46 /* Init the tick divisor value */
47 _xt_tick_divisor_init();
48
49 _frxt_tick_timer_init();
50 }
51
52
53 #elif CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
54
55 _Static_assert( SOC_CPU_CORES_NUM <= SOC_SYSTIMER_ALARM_NUM - 1, "the number of cores must match the number of core alarms in SYSTIMER" );
56
57 void SysTickIsrHandler( void * arg );
58
59 static uint32_t s_handled_systicks[ portNUM_PROCESSORS ] = { 0 };
60
61 #define SYSTICK_INTR_ID ( ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE )
62
63 /**
64 * @brief Set up the systimer peripheral to generate the tick interrupt
65 *
66 * Both timer alarms are configured in periodic mode.
67 * It is done at the same time so SysTicks for both CPUs occur at the same time or very close.
68 * Shifts a time of triggering interrupts for core 0 and core 1.
69 */
vPortSetupTimer(void)70 void vPortSetupTimer( void )
71 {
72 unsigned cpuid = xPortGetCoreID();
73
74 #ifdef CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3
75 const unsigned level = ESP_INTR_FLAG_LEVEL3;
76 #else
77 const unsigned level = ESP_INTR_FLAG_LEVEL1;
78 #endif
79 /* Systimer HAL layer object */
80 static systimer_hal_context_t systimer_hal;
81 /* set system timer interrupt vector */
82 ESP_ERROR_CHECK( esp_intr_alloc( ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE + cpuid, ESP_INTR_FLAG_IRAM | level, SysTickIsrHandler, &systimer_hal, NULL ) );
83
84 if( cpuid == 0 )
85 {
86 systimer_hal_init( &systimer_hal );
87 systimer_ll_set_counter_value( systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK, 0 );
88 systimer_ll_apply_counter_value( systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK );
89
90 for( cpuid = 0; cpuid < SOC_CPU_CORES_NUM; cpuid++ )
91 {
92 systimer_hal_counter_can_stall_by_cpu( &systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, false );
93 }
94
95 for( cpuid = 0; cpuid < portNUM_PROCESSORS; ++cpuid )
96 {
97 uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
98
99 /* configure the timer */
100 systimer_hal_connect_alarm_counter( &systimer_hal, alarm_id, SYSTIMER_LL_COUNTER_OS_TICK );
101 systimer_hal_set_alarm_period( &systimer_hal, alarm_id, 1000000UL / CONFIG_FREERTOS_HZ );
102 systimer_hal_select_alarm_mode( &systimer_hal, alarm_id, SYSTIMER_ALARM_MODE_PERIOD );
103 systimer_hal_counter_can_stall_by_cpu( &systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, true );
104
105 if( cpuid == 0 )
106 {
107 systimer_hal_enable_alarm_int( &systimer_hal, alarm_id );
108 systimer_hal_enable_counter( &systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK );
109 #ifndef CONFIG_FREERTOS_UNICORE
110 /* SysTick of core 0 and core 1 are shifted by half of period */
111 systimer_hal_counter_value_advance( &systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, 1000000UL / CONFIG_FREERTOS_HZ / 2 );
112 #endif
113 }
114 }
115 }
116 else
117 {
118 uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
119 systimer_hal_enable_alarm_int( &systimer_hal, alarm_id );
120 }
121 }
122
123 /**
124 * @brief Systimer interrupt handler.
125 *
126 * The Systimer interrupt for SysTick works in periodic mode no need to calc the next alarm.
127 * If a timer interrupt is ever serviced more than one tick late, it is necessary to process multiple ticks.
128 */
SysTickIsrHandler(void * arg)129 IRAM_ATTR void SysTickIsrHandler( void * arg )
130 {
131 uint32_t cpuid = xPortGetCoreID();
132 systimer_hal_context_t * systimer_hal = ( systimer_hal_context_t * ) arg;
133
134 #ifdef CONFIG_PM_TRACE
135 ESP_PM_TRACE_ENTER( TICK, cpuid );
136 #endif
137
138 uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
139
140 do
141 {
142 systimer_ll_clear_alarm_int( systimer_hal->dev, alarm_id );
143
144 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 ];
145
146 if( diff > 0 )
147 {
148 if( s_handled_systicks[ cpuid ] == 0 )
149 {
150 s_handled_systicks[ cpuid ] = diff;
151 diff = 1;
152 }
153 else
154 {
155 s_handled_systicks[ cpuid ] += diff;
156 }
157
158 do
159 {
160 xPortSysTickHandler();
161 } while( --diff );
162 }
163 } while( systimer_ll_is_alarm_int_fired( systimer_hal->dev, alarm_id ) );
164
165 #ifdef CONFIG_PM_TRACE
166 ESP_PM_TRACE_EXIT( TICK, cpuid );
167 #endif
168 }
169
170 #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
171
172 /**
173 * @brief Handler of SysTick
174 *
175 * The function is called from:
176 * - _frxt_timer_int for xtensa with CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
177 * - SysTickIsrHandler for xtensa with CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
178 * - SysTickIsrHandler for riscv
179 */
xPortSysTickHandler(void)180 BaseType_t xPortSysTickHandler( void )
181 {
182 portbenchmarkIntLatency();
183 traceISR_ENTER( SYSTICK_INTR_ID );
184 BaseType_t ret = xTaskIncrementTick();
185
186 if( ret != pdFALSE )
187 {
188 traceISR_EXIT_TO_SCHEDULER();
189 portYIELD_FROM_ISR();
190 }
191 else
192 {
193 traceISR_EXIT();
194 }
195
196 return ret;
197 }
198