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