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 #ifdef CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3
74 const unsigned level = ESP_INTR_FLAG_LEVEL3;
75 #else
76 const unsigned level = ESP_INTR_FLAG_LEVEL1;
77 #endif
78 /* Systimer HAL layer object */
79 static systimer_hal_context_t systimer_hal;
80 /* set system timer interrupt vector */
81 ESP_ERROR_CHECK(esp_intr_alloc(ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE + cpuid, ESP_INTR_FLAG_IRAM | level, SysTickIsrHandler, &systimer_hal, NULL));
82
83 if (cpuid == 0) {
84 systimer_hal_init(&systimer_hal);
85 systimer_ll_set_counter_value(systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK, 0);
86 systimer_ll_apply_counter_value(systimer_hal.dev, SYSTIMER_LL_COUNTER_OS_TICK);
87
88 for (cpuid = 0; cpuid < SOC_CPU_CORES_NUM; cpuid++) {
89 systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, false);
90 }
91
92 for (cpuid = 0; cpuid < portNUM_PROCESSORS; ++cpuid) {
93 uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
94
95 /* configure the timer */
96 systimer_hal_connect_alarm_counter(&systimer_hal, alarm_id, SYSTIMER_LL_COUNTER_OS_TICK);
97 systimer_hal_set_alarm_period(&systimer_hal, alarm_id, 1000000UL / CONFIG_FREERTOS_HZ);
98 systimer_hal_select_alarm_mode(&systimer_hal, alarm_id, SYSTIMER_ALARM_MODE_PERIOD);
99 systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, cpuid, true);
100 if (cpuid == 0) {
101 systimer_hal_enable_alarm_int(&systimer_hal, alarm_id);
102 systimer_hal_enable_counter(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK);
103 #ifndef CONFIG_FREERTOS_UNICORE
104 // SysTick of core 0 and core 1 are shifted by half of period
105 systimer_hal_counter_value_advance(&systimer_hal, SYSTIMER_LL_COUNTER_OS_TICK, 1000000UL / CONFIG_FREERTOS_HZ / 2);
106 #endif
107 }
108 }
109 } else {
110 uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
111 systimer_hal_enable_alarm_int(&systimer_hal, alarm_id);
112 }
113 }
114
115 /**
116 * @brief Systimer interrupt handler.
117 *
118 * The Systimer interrupt for SysTick works in periodic mode no need to calc the next alarm.
119 * If a timer interrupt is ever serviced more than one tick late, it is necessary to process multiple ticks.
120 */
SysTickIsrHandler(void * arg)121 IRAM_ATTR void SysTickIsrHandler(void *arg)
122 {
123 uint32_t cpuid = xPortGetCoreID();
124 systimer_hal_context_t *systimer_hal = (systimer_hal_context_t *)arg;
125 #ifdef CONFIG_PM_TRACE
126 ESP_PM_TRACE_ENTER(TICK, cpuid);
127 #endif
128
129 uint32_t alarm_id = SYSTIMER_LL_ALARM_OS_TICK_CORE0 + cpuid;
130 do {
131 systimer_ll_clear_alarm_int(systimer_hal->dev, alarm_id);
132
133 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];
134 if (diff > 0) {
135 if (s_handled_systicks[cpuid] == 0) {
136 s_handled_systicks[cpuid] = diff;
137 diff = 1;
138 } else {
139 s_handled_systicks[cpuid] += diff;
140 }
141
142 do {
143 xPortSysTickHandler();
144 } while (--diff);
145 }
146 } while (systimer_ll_is_alarm_int_fired(systimer_hal->dev, alarm_id));
147
148 #ifdef CONFIG_PM_TRACE
149 ESP_PM_TRACE_EXIT(TICK, cpuid);
150 #endif
151 }
152
153 #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
154
155 /**
156 * @brief Handler of SysTick
157 *
158 * The function is called from:
159 * - _frxt_timer_int for xtensa with CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
160 * - SysTickIsrHandler for xtensa with CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
161 * - SysTickIsrHandler for riscv
162 */
xPortSysTickHandler(void)163 BaseType_t xPortSysTickHandler(void)
164 {
165 portbenchmarkIntLatency();
166 traceISR_ENTER(SYSTICK_INTR_ID);
167 BaseType_t ret = xTaskIncrementTick();
168 if(ret != pdFALSE) {
169 portYIELD_FROM_ISR();
170 } else {
171 traceISR_EXIT();
172 }
173 return ret;
174 }
175