1 /*
2 FreeRTOS V8.2.3 - Copyright (C) 2015 Real Time Engineers Ltd.
3 All rights reserved
4
5 VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
6
7 This file is part of the FreeRTOS distribution and was contributed
8 to the project by Technolution B.V. (www.technolution.nl,
9 freertos-riscv@technolution.eu) under the terms of the FreeRTOS
10 contributors license.
11
12 FreeRTOS is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License (version 2) as published by the
14 Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.
15
16 ***************************************************************************
17 >>! NOTE: The modification to the GPL is included to allow you to !<<
18 >>! distribute a combined work that includes FreeRTOS without being !<<
19 >>! obliged to provide the source code for proprietary components !<<
20 >>! outside of the FreeRTOS kernel. !<<
21 ***************************************************************************
22
23 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
24 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25 FOR A PARTICULAR PURPOSE. Full license text is available on the following
26 link: http://www.freertos.org/a00114.html
27
28 ***************************************************************************
29 * *
30 * FreeRTOS provides completely free yet professionally developed, *
31 * robust, strictly quality controlled, supported, and cross *
32 * platform software that is more than just the market leader, it *
33 * is the industry's de facto standard. *
34 * *
35 * Help yourself get started quickly while simultaneously helping *
36 * to support the FreeRTOS project by purchasing a FreeRTOS *
37 * tutorial book, reference manual, or both: *
38 * http://www.FreeRTOS.org/Documentation *
39 * *
40 ***************************************************************************
41
42 http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
43 the FAQ page "My application does not run, what could be wrong?". Have you
44 defined configASSERT()?
45
46 http://www.FreeRTOS.org/support - In return for receiving this top quality
47 embedded software for free we request you assist our global community by
48 participating in the support forum.
49
50 http://www.FreeRTOS.org/training - Investing in training allows your team to
51 be as productive as possible as early as possible. Now you can receive
52 FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
53 Ltd, and the world's leading authority on the world's leading RTOS.
54
55 http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
56 including FreeRTOS+Trace - an indispensable productivity tool, a DOS
57 compatible FAT file system, and our tiny thread aware UDP/IP stack.
58
59 http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
60 Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
61
62 http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
63 Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
64 licenses offer ticketed support, indemnification and commercial middleware.
65
66 http://www.SafeRTOS.com - High Integrity Systems also provide a safety
67 engineered and independently SIL3 certified version for use in safety and
68 mission critical applications that require provable dependability.
69
70 1 tab == 4 spaces!
71 */
72
73 /*-----------------------------------------------------------------------
74 * Implementation of functions defined in portable.h for the RISC-V port.
75 *----------------------------------------------------------------------*/
76
77 #include "sdkconfig.h"
78 #include <string.h>
79 #include "soc/soc_caps.h"
80 #include "soc/periph_defs.h"
81 #include "soc/system_reg.h"
82 #include "hal/systimer_hal.h"
83 #include "hal/systimer_ll.h"
84 #include "riscv/rvruntime-frames.h"
85 #include "riscv/riscv_interrupts.h"
86 #include "riscv/interrupt.h"
87 #include "esp_private/crosscore_int.h"
88 #include "esp_attr.h"
89 #include "esp_system.h"
90 #include "esp_intr_alloc.h"
91 #include "esp_debug_helpers.h"
92 #include "esp_log.h"
93 #include "FreeRTOS.h" /* This pulls in portmacro.h */
94 #include "task.h"
95 #include "portmacro.h"
96 #include "port_systick.h"
97
98
99
100 /* ---------------------------------------------------- Variables ------------------------------------------------------
101 *
102 * ------------------------------------------------------------------------------------------------------------------ */
103
104 static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
105
106 /**
107 * @brief A variable is used to keep track of the critical section nesting.
108 * @note This variable has to be stored as part of the task context and must be initialized to a non zero value
109 * to ensure interrupts don't inadvertently become unmasked before the scheduler starts.
110 * As it is stored as part of the task context it will automatically be set to 0 when the first task is started.
111 */
112 static UBaseType_t uxCriticalNesting = 0;
113 static UBaseType_t uxSavedInterruptState = 0;
114 BaseType_t uxSchedulerRunning = 0;
115 UBaseType_t uxInterruptNesting = 0;
116 BaseType_t xPortSwitchFlag = 0;
117 __attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE];
118 StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
119
120
121
122 /* ------------------------------------------------ FreeRTOS Portable --------------------------------------------------
123 * - Provides implementation for functions required by FreeRTOS
124 * - Declared in portable.h
125 * ------------------------------------------------------------------------------------------------------------------ */
126
127 // ----------------- Scheduler Start/End -------------------
128
129 extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM only
xPortStartScheduler(void)130 BaseType_t xPortStartScheduler(void)
131 {
132 uxInterruptNesting = 0;
133 uxCriticalNesting = 0;
134 uxSchedulerRunning = 0;
135
136 /* Setup the hardware to generate the tick. */
137 vPortSetupTimer();
138
139 esprv_intc_int_set_threshold(1); /* set global INTC masking level */
140 riscv_global_interrupts_enable();
141
142 vPortYield();
143
144 /*Should not get here*/
145 return pdFALSE;
146 }
147
vPortEndScheduler(void)148 void vPortEndScheduler(void)
149 {
150 /* very unlikely this function will be called, so just trap here */
151 abort();
152 }
153
154 // ------------------------ Stack --------------------------
155
prvTaskExitError(void)156 static void prvTaskExitError(void)
157 {
158 /* A function that implements a task must not exit or attempt to return to
159 its caller as there is nothing to return to. If a task wants to exit it
160 should instead call vTaskDelete( NULL ).
161
162 Artificially force an assert() to be triggered if configASSERT() is
163 defined, then stop here so application writers can catch the error. */
164 configASSERT(uxCriticalNesting == ~0UL);
165 portDISABLE_INTERRUPTS();
166 abort();
167 }
168
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)169 StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
170 {
171 extern uint32_t __global_pointer$;
172 uint8_t *task_thread_local_start;
173 uint8_t *threadptr;
174 extern char _thread_local_start, _thread_local_end, _flash_rodata_start;
175
176 /* Byte pointer, so that subsequent calculations don't depend on sizeof(StackType_t). */
177 uint8_t *sp = (uint8_t *) pxTopOfStack;
178
179 /* Set up TLS area.
180 * The following diagram illustrates the layout of link-time and run-time
181 * TLS sections.
182 *
183 * +-------------+
184 * |Section: | Linker symbols:
185 * |.flash.rodata| ---------------
186 * 0x0+-------------+ <-- _flash_rodata_start
187 * ^ | |
188 * | | Other data |
189 * | | ... |
190 * | +-------------+ <-- _thread_local_start
191 * | |.tbss | ^
192 * v | | |
193 * 0xNNNN|int example; | | (thread_local_size)
194 * |.tdata | v
195 * +-------------+ <-- _thread_local_end
196 * | Other data |
197 * | ... |
198 * | |
199 * +-------------+
200 *
201 * Local variables of
202 * pxPortInitialiseStack
203 * -----------------------
204 * +-------------+ <-- pxTopOfStack
205 * |.tdata (*) | ^
206 * ^ |int example; | |(thread_local_size
207 * | | | |
208 * | |.tbss (*) | v
209 * | +-------------+ <-- task_thread_local_start
210 * 0xNNNN | | | ^
211 * | | | |
212 * | | | |_thread_local_start - _rodata_start
213 * | | | |
214 * | | | v
215 * v +-------------+ <-- threadptr
216 *
217 * (*) The stack grows downward!
218 */
219
220 uint32_t thread_local_sz = (uint32_t) (&_thread_local_end - &_thread_local_start);
221 thread_local_sz = ALIGNUP(0x10, thread_local_sz);
222 sp -= thread_local_sz;
223 task_thread_local_start = sp;
224 memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz);
225 threadptr = task_thread_local_start - (&_thread_local_start - &_flash_rodata_start);
226
227 /* Simulate the stack frame as it would be created by a context switch interrupt. */
228 sp -= RV_STK_FRMSZ;
229 RvExcFrame *frame = (RvExcFrame *)sp;
230 memset(frame, 0, sizeof(*frame));
231 frame->ra = (UBaseType_t)prvTaskExitError;
232 frame->mepc = (UBaseType_t)pxCode;
233 frame->a0 = (UBaseType_t)pvParameters;
234 frame->gp = (UBaseType_t)&__global_pointer$;
235 frame->tp = (UBaseType_t)threadptr;
236
237 //TODO: IDF-2393
238 return (StackType_t *)frame;
239 }
240
241
242
243 /* ---------------------------------------------- Port Implementations -------------------------------------------------
244 *
245 * ------------------------------------------------------------------------------------------------------------------ */
246
247 // --------------------- Interrupts ------------------------
248
xPortInIsrContext(void)249 BaseType_t xPortInIsrContext(void)
250 {
251 return uxInterruptNesting;
252 }
253
xPortInterruptedFromISRContext(void)254 BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void)
255 {
256 /* For single core, this can be the same as xPortInIsrContext() because reading it is atomic */
257 return uxInterruptNesting;
258 }
259
260 // ---------------------- Spinlocks ------------------------
261
262
263
264 // ------------------ Critical Sections --------------------
265
vPortEnterCritical(void)266 void vPortEnterCritical(void)
267 {
268 BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
269 uxCriticalNesting++;
270
271 if (uxCriticalNesting == 1) {
272 uxSavedInterruptState = state;
273 }
274 }
275
vPortExitCritical(void)276 void vPortExitCritical(void)
277 {
278 if (uxCriticalNesting > 0) {
279 uxCriticalNesting--;
280 if (uxCriticalNesting == 0) {
281 portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptState);
282 }
283 }
284 }
285
286 // ---------------------- Yielding -------------------------
287
vPortSetInterruptMask(void)288 int vPortSetInterruptMask(void)
289 {
290 int ret;
291 unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
292 ret = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG);
293 REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, RVHAL_EXCM_LEVEL);
294 RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE);
295 /**
296 * In theory, this function should not return immediately as there is a
297 * delay between the moment we mask the interrupt threshold register and
298 * the moment a potential lower-priority interrupt is triggered (as said
299 * above), it should have a delay of 2 machine cycles/instructions.
300 *
301 * However, in practice, this function has an epilogue of one instruction,
302 * thus the instruction masking the interrupt threshold register is
303 * followed by two instructions: `ret` and `csrrs` (RV_SET_CSR).
304 * That's why we don't need any additional nop instructions here.
305 */
306 return ret;
307 }
308
vPortClearInterruptMask(int mask)309 void vPortClearInterruptMask(int mask)
310 {
311 REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, mask);
312 /**
313 * The delay between the moment we unmask the interrupt threshold register
314 * and the moment the potential requested interrupt is triggered is not
315 * null: up to three machine cycles/instructions can be executed.
316 *
317 * When compilation size optimization is enabled, this function and its
318 * callers returning void will have NO epilogue, thus the instruction
319 * following these calls will be executed.
320 *
321 * If the requested interrupt is a context switch to a higher priority
322 * task then the one currently running, we MUST NOT execute any instruction
323 * before the interrupt effectively happens.
324 * In order to prevent this, force this routine to have a 3-instruction
325 * delay before exiting.
326 */
327 asm volatile ( "nop" );
328 asm volatile ( "nop" );
329 asm volatile ( "nop" );
330 }
331
vPortYield(void)332 void vPortYield(void)
333 {
334 if (uxInterruptNesting) {
335 vPortYieldFromISR();
336 } else {
337
338 esp_crosscore_int_send_yield(0);
339 /* There are 3-4 instructions of latency between triggering the software
340 interrupt and the CPU interrupt happening. Make sure it happened before
341 we return, otherwise vTaskDelay() may return and execute 1-2
342 instructions before the delay actually happens.
343
344 (We could use the WFI instruction here, but there is a chance that
345 the interrupt will happen while evaluating the other two conditions
346 for an instant yield, and if that happens then the WFI would be
347 waiting for the next interrupt to occur...)
348 */
349 while (uxSchedulerRunning && uxCriticalNesting == 0 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {}
350 }
351 }
352
vPortYieldFromISR(void)353 void vPortYieldFromISR( void )
354 {
355 traceISR_EXIT_TO_SCHEDULER();
356 uxSchedulerRunning = 1;
357 xPortSwitchFlag = 1;
358 }
359
vPortYieldOtherCore(BaseType_t coreid)360 void vPortYieldOtherCore(BaseType_t coreid)
361 {
362 esp_crosscore_int_send_yield(coreid);
363 }
364
365 // ------------------- Hook Functions ----------------------
366
vApplicationStackOverflowHook(TaskHandle_t xTask,char * pcTaskName)367 void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
368 {
369 #define ERR_STR1 "***ERROR*** A stack overflow in task "
370 #define ERR_STR2 " has been detected."
371 const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2};
372
373 char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = {0};
374
375 char *dest = buf;
376 for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
377 dest = strcat(dest, str[i]);
378 }
379 esp_system_abort(buf);
380 }
381
382 // ----------------------- System --------------------------
383
xPortGetTickRateHz(void)384 uint32_t xPortGetTickRateHz(void)
385 {
386 return (uint32_t)configTICK_RATE_HZ;
387 }
388
389 #define STACK_WATCH_AREA_SIZE 32
390 #define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1)
391
vPortSetStackWatchpoint(void * pxStackStart)392 void vPortSetStackWatchpoint(void *pxStackStart)
393 {
394 uint32_t addr = (uint32_t)pxStackStart;
395 addr = (addr + (STACK_WATCH_AREA_SIZE - 1)) & (~(STACK_WATCH_AREA_SIZE - 1));
396 esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, STACK_WATCH_AREA_SIZE, ESP_WATCHPOINT_STORE);
397 }
398
399
400
401 /* ---------------------------------------------- Misc Implementations -------------------------------------------------
402 *
403 * ------------------------------------------------------------------------------------------------------------------ */
404
405 // --------------------- App Start-up ----------------------
406
407 /* [refactor-todo]: See if we can include this through a header */
408 extern void esp_startup_start_app_common(void);
409
esp_startup_start_app(void)410 void esp_startup_start_app(void)
411 {
412 esp_startup_start_app_common();
413
414 ESP_LOGI(TAG, "Starting scheduler.");
415 vTaskStartScheduler();
416 }
417