xref: /Kernel-v10.6.2/portable/ThirdParty/GCC/Xtensa_ESP32/port_systick.c (revision 963abe6c4833e7c969279b537c103d7a0bdd5116)
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