1 /***********************************************************************************************//**
2  * \file cyabs_freertos_helpers.c
3  *
4  * \brief
5  * Provides implementations for functions required to enable static allocation and
6  * tickless mode in FreeRTOS.
7  *
8  ***************************************************************************************************
9  * \copyright
10  * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
11  * an affiliate of Cypress Semiconductor Corporation
12  *
13  * SPDX-License-Identifier: Apache-2.0
14  *
15  * Licensed under the Apache License, Version 2.0 (the "License");
16  * you may not use this file except in compliance with the License.
17  * You may obtain a copy of the License at
18  *
19  *     http://www.apache.org/licenses/LICENSE-2.0
20  *
21  * Unless required by applicable law or agreed to in writing, software
22  * distributed under the License is distributed on an "AS IS" BASIS,
23  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24  * See the License for the specific language governing permissions and
25  * limitations under the License.
26  **************************************************************************************************/
27 #include "FreeRTOS.h"
28 #include "task.h"
29 #include "cyabs_rtos.h"
30 #if defined(CY_USING_HAL)
31 #include "cyhal.h"
32 #include "cyhal_syspm.h"
33 #endif
34 
35 // This is included to allow the user to control the idle task behavior via the configurator
36 // System->Power->RTOS->System Idle Power Mode setting.
37 #include "cybsp.h"
38 
39 #define pdTICKS_TO_MS(xTicks)    ( ( ( TickType_t ) ( xTicks ) * 1000u ) / configTICK_RATE_HZ )
40 
41 #if defined(CY_USING_HAL)
42 static cyhal_lptimer_t* _lptimer = NULL;
43 
44 //--------------------------------------------------------------------------------------------------
45 // cyabs_rtos_set_lptimer
46 //--------------------------------------------------------------------------------------------------
cyabs_rtos_set_lptimer(cyhal_lptimer_t * timer)47 void cyabs_rtos_set_lptimer(cyhal_lptimer_t* timer)
48 {
49     _lptimer = timer;
50 }
51 
52 
53 //--------------------------------------------------------------------------------------------------
54 // cyabs_rtos_get_lptimer
55 //--------------------------------------------------------------------------------------------------
cyabs_rtos_get_lptimer(void)56 cyhal_lptimer_t* cyabs_rtos_get_lptimer(void)
57 {
58     return _lptimer;
59 }
60 
61 
62 #if (configUSE_TICKLESS_IDLE != 0)
63 //--------------------------------------------------------------------------------------------------
64 // cyabs_rtos_get_deepsleep_latency
65 //--------------------------------------------------------------------------------------------------
cyabs_rtos_get_deepsleep_latency(void)66 uint32_t cyabs_rtos_get_deepsleep_latency(void)
67 {
68     uint32_t latency = 0;
69 
70     #if defined(CY_CFG_PWR_DEEPSLEEP_LATENCY)
71     latency = CY_CFG_PWR_DEEPSLEEP_LATENCY;
72     #endif //defined(CY_CFG_PWR_DEEPSLEEP_LATENCY)
73 
74     #if defined (CYHAL_API_AVAILABLE_SYSPM_GET_DEEPSLEEP_MODE)
75     cyhal_syspm_system_deep_sleep_mode_t deep_sleep_mode = cyhal_syspm_get_deepsleep_mode();
76 
77     switch (deep_sleep_mode)
78     {
79         case CYHAL_SYSPM_SYSTEM_DEEPSLEEP:
80         case CYHAL_SYSPM_SYSTEM_DEEPSLEEP_OFF:
81         case CYHAL_SYSPM_SYSTEM_DEEPSLEEP_NONE:
82             #if defined(CY_CFG_PWR_DEEPSLEEP_LATENCY)
83             latency = CY_CFG_PWR_DEEPSLEEP_LATENCY;
84             #endif //defined(CY_CFG_PWR_DEEPSLEEP_LATENCY)
85             break;
86 
87         case CYHAL_SYSPM_SYSTEM_DEEPSLEEP_RAM:
88             #if defined(CY_CFG_PWR_DEEPSLEEP_RAM_LATENCY)
89             latency = CY_CFG_PWR_DEEPSLEEP_RAM_LATENCY;
90             #endif //defined(CY_CFG_PWR_DEEPSLEEP_RAM_LATENCY)
91             break;
92 
93         default:
94             #if defined(CY_CFG_PWR_DEEPSLEEP_LATENCY)
95             latency = CY_CFG_PWR_DEEPSLEEP_LATENCY;
96             #endif //defined(CY_CFG_PWR_DEEPSLEEP_LATENCY)
97             break;
98     }
99     #endif // if defined (CYHAL_API_AVAILABLE_SYSPM_GET_DEEPSLEEP_MODE)
100     return latency;
101 }
102 
103 
104 #endif //(configUSE_TICKLESS_IDLE != 0)
105 
106 #endif //defined(CY_USING_HAL)
107 
108 
109 // The following implementations were sourced from https://www.freertos.org/a00110.html
110 
111 //--------------------------------------------------------------------------------------------------
112 // vApplicationGetIdleTaskMemory
113 //
114 // configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an implementation of
115 // vApplicationGetIdleTaskMemory() to provide the memory that is used by the Idle task.
116 //--------------------------------------------------------------------------------------------------
vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize)117 __WEAK void vApplicationGetIdleTaskMemory(StaticTask_t** ppxIdleTaskTCBBuffer,
118                                           StackType_t** ppxIdleTaskStackBuffer,
119                                           uint32_t* pulIdleTaskStackSize)
120 {
121     // If the buffers to be provided to the Idle task are declared inside this function then they
122     // must be declared static – otherwise they will be allocated on the stack and so not exists
123     // after this function exits.
124     static StaticTask_t xIdleTaskTCB;
125     static StackType_t  uxIdleTaskStack[configMINIMAL_STACK_SIZE];
126 
127     // Pass out a pointer to the StaticTask_t structure in which the Idle task’s state will be
128     // stored.
129     *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
130 
131     // Pass out the array that will be used as the Idle task’s stack.
132     *ppxIdleTaskStackBuffer = uxIdleTaskStack;
133 
134     // Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.  Note that, as the
135     // array is necessarily of type StackType_t, configMINIMAL_STACK_SIZE is specified in words, not
136     // bytes.
137     *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
138 }
139 
140 
141 /*———————————————————–*/
142 
143 //--------------------------------------------------------------------------------------------------
144 // vApplicationGetTimerTaskMemory
145 //
146 // configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the application must
147 // provide an implementation of vApplicationGetTimerTaskMemory() to provide the memory that is used
148 // by the Timer service task.
149 //--------------------------------------------------------------------------------------------------
vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,StackType_t ** ppxTimerTaskStackBuffer,uint32_t * pulTimerTaskStackSize)150 __WEAK void vApplicationGetTimerTaskMemory(StaticTask_t** ppxTimerTaskTCBBuffer,
151                                            StackType_t** ppxTimerTaskStackBuffer,
152                                            uint32_t* pulTimerTaskStackSize)
153 {
154     // If the buffers to be provided to the Timer task are declared inside this function then they
155     // must be declared static – otherwise they will be allocated on the stack and so not exists
156     // after this function exits.
157     static StaticTask_t xTimerTaskTCB;
158     static StackType_t  uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH];
159 
160     // Pass out a pointer to the StaticTask_t structure in which the Timer task’s state will be
161     // stored.
162     *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
163 
164     // Pass out the array that will be used as the Timer task’s stack.
165     *ppxTimerTaskStackBuffer = uxTimerTaskStack;
166 
167     // Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.  Note that, as the
168     // array is necessarily of type StackType_t, configTIMER_TASK_STACK_DEPTH is specified in words,
169     // not bytes.
170     *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
171 }
172 
173 
174 #if defined(CY_USING_HAL) && (configUSE_TICKLESS_IDLE != 0)
175 //--------------------------------------------------------------------------------------------------
176 // vApplicationSleep
177 //
178 /** User defined tickless idle sleep function.
179  *
180  * Provides a implementation for portSUPPRESS_TICKS_AND_SLEEP macro that allows
181  * the device to attempt to deep-sleep for the idle time the kernel expects before
182  * the next task is ready. This function disables the system timer and enables low power
183  * timer that can operate in deep-sleep mode to wake the device from deep-sleep after
184  * expected idle time has elapsed.
185  *
186  * @param[in] xExpectedIdleTime     Total number of tick periods before
187  *                                  a task is due to be moved into the Ready state.
188  */
189 //--------------------------------------------------------------------------------------------------
vApplicationSleep(TickType_t xExpectedIdleTime)190 __WEAK void vApplicationSleep(TickType_t xExpectedIdleTime)
191 {
192     #if (defined(CY_CFG_PWR_MODE_DEEPSLEEP) && \
193     (CY_CFG_PWR_SYS_IDLE_MODE == CY_CFG_PWR_MODE_DEEPSLEEP)) || \
194     (defined(CY_CFG_PWR_MODE_DEEPSLEEP_RAM) && \
195     (CY_CFG_PWR_SYS_IDLE_MODE == CY_CFG_PWR_MODE_DEEPSLEEP_RAM))
196     #define DEEPSLEEP_ENABLE
197     #endif
198     static cyhal_lptimer_t timer;
199     uint32_t               actual_sleep_ms = 0;
200     cy_rslt_t result = CY_RSLT_SUCCESS;
201 
202     if (NULL == _lptimer)
203     {
204         result = cyhal_lptimer_init(&timer);
205         if (result == CY_RSLT_SUCCESS)
206         {
207             _lptimer = &timer;
208         }
209         else
210         {
211             CY_ASSERT(false);
212         }
213     }
214 
215     if (NULL != _lptimer)
216     {
217         /* Disable interrupts so that nothing can change the status of the RTOS while
218          * we try to go to sleep or deep-sleep.
219          */
220         uint32_t         status       = cyhal_system_critical_section_enter();
221         eSleepModeStatus sleep_status = eTaskConfirmSleepModeStatus();
222 
223         if (sleep_status != eAbortSleep)
224         {
225             // By default, the device will deep-sleep in the idle task unless if the device
226             // configurator overrides the behaviour to sleep in the System->Power->RTOS->System
227             // Idle Power Mode setting.
228             #if defined (CY_CFG_PWR_SYS_IDLE_MODE)
229             uint32_t sleep_ms = pdTICKS_TO_MS(xExpectedIdleTime);
230             #if defined DEEPSLEEP_ENABLE
231             bool deep_sleep = true;
232             // If the system needs to operate in active mode the tickless mode should not be used in
233             // FreeRTOS
234             CY_ASSERT(CY_CFG_PWR_SYS_IDLE_MODE != CY_CFG_PWR_MODE_ACTIVE);
235             deep_sleep =
236                 #if defined(CY_CFG_PWR_MODE_DEEPSLEEP_RAM)
237                 (CY_CFG_PWR_SYS_IDLE_MODE == CY_CFG_PWR_MODE_DEEPSLEEP_RAM) ||
238                 #endif
239                 (CY_CFG_PWR_SYS_IDLE_MODE == CY_CFG_PWR_MODE_DEEPSLEEP);
240             if (deep_sleep)
241             {
242                 // Adjust the deep-sleep time by the sleep/wake latency if set.
243                 #if defined(CY_CFG_PWR_DEEPSLEEP_LATENCY) || \
244                 defined(CY_CFG_PWR_DEEPSLEEP_RAM_LATENCY)
245                 uint32_t deep_sleep_latency = cyabs_rtos_get_deepsleep_latency();
246                 if (sleep_ms > deep_sleep_latency)
247                 {
248                     result = cyhal_syspm_tickless_deepsleep(_lptimer,
249                                                             (sleep_ms - deep_sleep_latency),
250                                                             &actual_sleep_ms);
251                 }
252                 else
253                 {
254                     result = CY_RTOS_TIMEOUT;
255                 }
256                 #else \
257                 // defined(CY_CFG_PWR_DEEPSLEEP_LATENCY) ||
258                 // defined(CY_CFG_PWR_DEEPSLEEP_RAM_LATENCY)
259                 result = cyhal_syspm_tickless_deepsleep(_lptimer, sleep_ms, &actual_sleep_ms);
260                 #endif \
261                 // defined(CY_CFG_PWR_DEEPSLEEP_LATENCY) ||
262                 // defined(CY_CFG_PWR_DEEPSLEEP_RAM_LATENCY)
263                 //maintain compatibility with older HAL versions that didn't define this error
264                 #ifdef CYHAL_SYSPM_RSLT_DEEPSLEEP_LOCKED
265                 //Deepsleep has been locked, continuing into normal sleep
266                 if (result == CYHAL_SYSPM_RSLT_DEEPSLEEP_LOCKED)
267                 {
268                     deep_sleep = false;
269                 }
270                 #endif
271             }
272             if (!deep_sleep)
273             {
274             #endif // if defined DEEPSLEEP_ENABLE
275             uint32_t sleep_latency =
276                 #if defined (CY_CFG_PWR_SLEEP_LATENCY)
277                 CY_CFG_PWR_SLEEP_LATENCY +
278                 #endif
279                 0;
280             if (sleep_ms > sleep_latency)
281             {
282                 result = cyhal_syspm_tickless_sleep(_lptimer, (sleep_ms - sleep_latency),
283                                                     &actual_sleep_ms);
284             }
285             else
286             {
287                 result = CY_RTOS_TIMEOUT;
288             }
289             #if defined DEEPSLEEP_ENABLE
290         }
291             #endif
292             #else // if defined (CY_CFG_PWR_SYS_IDLE_MODE)
293             CY_UNUSED_PARAMETER(xExpectedIdleTime);
294             #endif // if defined (CY_CFG_PWR_SYS_IDLE_MODE)
295             if (result == CY_RSLT_SUCCESS)
296             {
297                 // If you hit this assert, the latency time (CY_CFG_PWR_DEEPSLEEP_LATENCY) should
298                 // be increased. This can be set though the Device Configurator, or by manually
299                 // defining the variable in cybsp.h for the TARGET platform.
300                 CY_ASSERT(actual_sleep_ms <= pdTICKS_TO_MS(xExpectedIdleTime));
301                 vTaskStepTick(convert_ms_to_ticks(actual_sleep_ms));
302             }
303         }
304 
305         cyhal_system_critical_section_exit(status);
306     }
307 }
308 
309 
310 #endif // defined(CY_USING_HAL) && (configUSE_TICKLESS_IDLE != 0)
311